redis-py与RedisJSON:在Python中操作JSON数据的完整指南
【免费下载链接】redis-py 项目地址: https://gitcode.com/gh_mirrors/red/redis-py
为什么需要RedisJSON?
传统关系型数据库存储JSON数据时,通常需要将JSON序列化为字符串后存储在字段中,查询时需要完整读取后解析,无法直接操作JSON内部结构。而RedisJSON(Redis的JSON数据类型扩展)允许你在Redis中直接存储、更新和查询JSON值,支持复杂的JSONPath查询和原子操作。配合redis-py客户端,Python开发者可以轻松实现对JSON数据的高效管理。
快速开始:环境准备
要使用redis-py操作RedisJSON,需要先安装Redis服务器(需启用RedisJSON模块)和redis-py客户端:
# 安装redis-py
pip install redis
# 克隆仓库
git clone https://gitcode.com/gh_mirrors/red/redis-py
RedisJSON的核心实现位于redis/commands/json/commands.py,提供了完整的JSON操作方法。
基础操作:CRUD与数据类型
连接Redis并设置JSON数据
import redis
from redis.commands.json.path import Path
# 连接Redis
r = redis.Redis(host='localhost', port=6379, decode_responses=True)
# 设置JSON数据
bike_data = {
"model": "Deimos",
"brand": "Ergonom",
"price": 4972,
"specs": {"material": "carbon", "weight": 13.1},
"colors": ["black", "silver"]
}
# 使用JSON.SET命令设置键值对
r.json().set("bike:1", Path.root_path(), bike_data)
获取JSON数据
# 获取完整JSON
full_bike = r.json().get("bike:1", Path.root_path())
print(full_bike) # 输出完整自行车信息
# 获取指定路径数据
model = r.json().get("bike:1", "$.model")
print(model) # 输出: Deimos
# 获取嵌套字段
weight = r.json().get("bike:1", "$.specs.weight")
print(weight) # 输出: 13.1
更新与删除操作
# 更新价格(原子操作)
r.json().numincrby("bike:1", "$.price", -500) # 降价500
# 添加新颜色
r.json().arrappend("bike:1", "$.colors", "blue")
# 删除字段
r.json().delete("bike:1", "$.specs.material")
# 检查数据类型
print(r.json().type("bike:1", "$.colors")) # 输出: array
高级查询:JSONPath的艺术
JSONPath是查询JSON文档的强大工具,redis-py完全支持RedisJSON的JSONPath语法。
基础JSONPath示例
# 获取所有自行车型号
r.json().set("bikes:inventory", "$", {
"mountain_bikes": [
{"model": "Phoebe", "price": 1920},
{"model": "Quaoar", "price": 2072},
{"model": "Weywot", "price": 3264}
],
"commuter_bikes": [
{"model": "Salacia", "price": 1475},
{"model": "Mimas", "price": 3941}
]
})
# 获取所有山地自行车型号
mtb_models = r.json().get("bikes:inventory", "$.mountain_bikes[*].model")
print(mtb_models) # 输出: ['Phoebe', 'Quaoar', 'Weywot']
条件过滤查询
# 查询价格低于2000的自行车
affordable = r.json().get("bikes:inventory",
"$..[?(@.price < 2000)].model")
print(affordable) # 输出: ['Phoebe', 'Salacia']
# 查询材质为合金的自行车
alloy_bikes = r.json().get("bikes:inventory",
"$..[?(@.specs.material == 'alloy')].model")
索引与搜索:RedisJSON + RediSearch
通过RediSearch模块,可对JSON数据创建二级索引并执行复杂搜索。
创建索引
from redis.commands.search.field import TextField, NumericField, TagField
from redis.commands.search.indexDefinition import IndexDefinition, IndexType
# 定义索引结构
schema = (
TextField("$..model", as_name="model"),
NumericField("$..price", as_name="price"),
TagField("$..specs.material", as_name="material")
)
# 创建索引(针对所有以"bike:"为前缀的键)
r.ft().create_index(
schema,
definition=IndexDefinition(prefix=["bike:"], index_type=IndexType.JSON)
)
执行搜索
# 基本搜索
result = r.ft().search("Phoebe")
print(result.total) # 输出匹配数量
for doc in result.docs:
print(doc.json) # 输出匹配的JSON文档
# 带过滤条件的搜索
query = Query("*").add_filter(NumericFilter("price", 1500, 3000))
result = r.ft().search(query)
print([doc.model for doc in result.docs]) # 输出价格范围内的型号
批量操作与性能优化
批量设置JSON数据
# 准备多条数据
inventory = [
("bike:2", {"model": "Quaoar", "price": 2072, "specs": {"material": "aluminium"}}),
("bike:3", {"model": "Weywot", "price": 3264, "specs": {"material": "alloy"}}),
("bike:4", {"model": "Salacia", "price": 1475, "specs": {"material": "aluminium"}})
]
# 使用mset批量设置
r.json().mset([(key, Path.root_path(), data) for key, data in inventory])
批量查询与聚合
# 多键查询
prices = r.json().mget(["bike:1", "bike:2", "bike:3"], "$.price")
print(prices) # 输出各型号价格列表
# 聚合查询(需RediSearch支持)
from redis.commands.search.aggregation import AggregateRequest
from redis.commands.search.reducers import count, avg
# 按材质分组统计数量和平均价格
req = AggregateRequest("*").group_by(
"@material",
count().alias("count"),
avg("@price").alias("avg_price")
)
result = r.ft().aggregate(req)
print(result.rows) # 输出分组统计结果
实际案例:电商库存管理
假设你正在构建一个电商平台的库存管理系统,需要高效存储和查询产品信息:
# 存储产品数据
product = {
"name": "Mountain Bike Pro",
"category": "sports",
"inventory": {
"total": 50,
"reserved": 5,
"available": 45
},
"variants": [
{"size": "S", "color": "black", "price": 1299},
{"size": "M", "color": "silver", "price": 1399},
{"size": "L", "color": "blue", "price": 1499}
]
}
r.json().set("product:1001", Path.root_path(), product)
# 实时更新库存
def reserve_items(product_id, quantity):
# 原子减少可用库存并增加预留库存
r.json().numincrby(f"product:{product_id}", "$.inventory.available", -quantity)
r.json().numincrby(f"product:{product_id}", "$.inventory.reserved", quantity)
return True
# 查询特定条件的变体
def find_affordable_variants(product_id, max_price):
return r.json().get(
f"product:{product_id}",
f"$..variants[?(@.price <= {max_price})].{['size', 'color', 'price']}"
)
# 使用示例
reserve_items(1001, 3)
affordable = find_affordable_variants(1001, 1400)
print(affordable)
测试与最佳实践
单元测试示例
redis-py的测试用例提供了JSON操作的完整测试方法,可参考tests/test_json.py:
def test_json_crud_operations(client):
# 设置测试数据
client.json().set("test:doc", Path.root_path(), {"a": 1, "b": "foo", "c": [1, 2, 3]})
# 验证设置
assert client.json().get("test:doc", "$.a") == 1
# 测试更新
client.json().numincrby("test:doc", "$.a", 2)
assert client.json().get("test:doc", "$.a") == 3
# 测试数组操作
client.json().arrappend("test:doc", "$.c", 4)
assert client.json().arrlen("test:doc", "$.c") == 4
# 测试删除
client.json().delete("test:doc", "$.b")
assert client.json().get("test:doc", "$.b") is None
性能优化建议
- 路径精确化:查询时尽量使用精确路径而非通配符,减少数据传输量
- 批量操作:使用mset/mget替代多次单键操作
- 索引优化:为频繁查询的字段创建RediSearch索引
- 连接池:使用redis-py的连接池减少连接开销
# 配置连接池
pool = redis.ConnectionPool(host='localhost', port=6379, max_connections=10)
r = redis.Redis(connection_pool=pool)
总结与进阶
通过redis-py和RedisJSON,Python开发者可以获得媲美文档数据库的JSON操作能力,同时保持Redis的高性能和低延迟优势。本文介绍的基础操作、查询技巧和最佳实践只是RedisJSON功能的冰山一角,更多高级特性如:
- JSON合并(MERGE)
- 正则表达式匹配
- 事务与管道支持
- 地理空间数据类型
可参考官方文档docs/examples/search_json_examples.ipynb和doctests/dt_json.py中的示例代码。
RedisJSON与redis-py的组合为实时应用、缓存系统和数据分析提供了强大的数据存储解决方案,特别适合需要高频读写和复杂查询的场景。
【免费下载链接】redis-py 项目地址: https://gitcode.com/gh_mirrors/red/redis-py
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



