Redis 命令及数据类型 -- JSON
摘要
- 本文介绍 Redis 扩展模型 RedisJSON 中的 JSON 数据类型
- 本文基于
redis-7.4.7,springboot-3.5.8 - Redis官网:https://redis.io/
- Redis 命令文档:https://redis.io/docs/latest/commands/
- RedisJSON 的安装参见 Redis 扩展模块 -- RedisJSON
JSON vs String
-
Redis JSON存储数据的性能更高。
- Redis JSON 底层其实是以一种高效的二进制的格式存储。
- 相比简单的文本格式,二进制格式进行 JOSN 格式读写的性能更高,也更节省内存。
- 根据官网的性能测试报告,使用 Redis JSON 读写 JSON数据,性能已经能够媲美 MongoDB 以及 ElasticSearch 等传统 NoSQL 数据库。
-
Redis JSON 使用树状结构来存储JSON。
- 这种存储方式可以快速访问子元素。
- 与传统的文本存储方案相比,树状存储结构能够更高效的执行查询操作。
-
与Redis生态集成度高。
- 作为Redis的扩展模块,Redis JSON 和Redis的其他功能和工具无缝集成。
- 这意味着开发者可以继续使用TTL、Redis事务、发布/订阅、Lua脚本等功能。
JSONPath
-
JSONPath 是一种用于查询和修改 JSON 数据的语法。
-
JSONPath 语法
| 语法元素 | 中文说明 | 示例 |
|---|---|---|
$ |
根节点(最外层 JSON 元素),JSONPath 的起点 | $、$.user.name |
. |
访问子字段(对象属性访问) | $.user.age |
[] |
子元素选择器(字段名或数组索引) | $['user']['name'] |
.. |
递归下降,遍历所有层级的节点 | $..id |
* |
通配符,匹配当前层级的所有元素 | $.users[*] |
[index] |
数组下标访问(支持负索引) | $.scores[0] |
[a,b,c] |
联合选择,返回多个指定元素 | $.scores[0,2,4] |
[start:end:step] |
数组切片(起始索引 : 结束索引 : 步长) | $.scores[1:5:2] |
[*] |
选择数组中的所有元素 | $.items[*] |
[:] |
选择数组中的所有元素(切片写法) | $.items[:] |
?() |
过滤表达式,用于数组或对象的条件筛选 | $.users[?(@.age>=18)] |
() |
脚本表达式,用于计算或复杂逻辑判断 | $.items[?((@.a+@.b)>10)] |
@ |
当前元素引用,常用于过滤或脚本表达式中 | @.price<100 |
-
数组切片
[start:end:step]详解
| 写法 | 含义 |
|---|---|
[3:] |
从索引 3 到末尾 |
[:8] |
从头到索引 7 |
[:8:2] |
从头到 7,每隔 2 个取一个 |
[::] |
等价于 [:],取全部 |
[*] |
推荐写法,取全部 |
默认值规则:
start 默认 0
end 默认数组末尾
step 默认 1
-
过滤表达式
?()支持的运算符
比较运算符,实测中仅支持数字比较,且注意两边不能有空格
| 运算符 | 含义 |
|---|---|
== |
等于 |
!= |
不等于 |
< |
小于 |
<= |
小于等于 |
> |
大于 |
>= |
大于等于 |
=~ |
正则匹配 |
逻辑运算符
| 运算符 | 含义 |
|---|---|
&& |
与 |
|| |
或 |
() |
分组优先级 |
-
Redis 的 RedisJSON 模块(提供 JSON 操作功能)支持的 JSONPath 语法是受限的子集,尤其是:
- ❌ 不支持复杂表达式
- ❌ 不支持嵌套数组条件(@.items[*].xxx)
- ⚠️ 对 ?() 的支持非常有限
-
如果需要复杂的查询推荐使用
RediSearch模块,这个我们后面的章节会介绍。
JSON 命令
-
JSON 命令 汇总
| 分类 | 命令 |
|---|---|
| 基础读写 | SET / MSET / GET / MGET |
| 数组操作 | ARRAPPEND / ARRINSERT / ARRPOP / ARRLEN / ARRINDEX / ARRTRIM |
| 对象操作 | OBJKEYS / OBJLEN / TYPE |
| 数值运算 | NUMINCRBY / NUMMULTBY / TOGGLE |
| 字符串 | STRAPPEND / STRLEN |
| 删除清空 | DEL / FORGET / CLEAR |
| 合并输出 | MERGE / RESP |
| 调试诊断 | DEBUG / DEBUG MEMORY |
一、基础读写类(CRUD)
| 命令 | 作用 | 关键参数说明 | 示例 |
|---|---|---|---|
| JSON.SET | 设置或更新某个 path 的值 | key:键名path:JSONPath(如 $、$.a.b)value:JSON 值 |
JSON.SET user:1 $ '{"name":"Tom","age":18}' |
| JSON.MSET | 批量设置多个 key | (key path value)... |
JSON.MSET k1 $ '{"a":1}' k2 $.b 2 |
| JSON.GET | 获取一个 key 的 JSON 值 | keypath...:可多个 |
JSON.GET user:1 $.name $.age |
| JSON.MGET | 从多个 key 获取同一路径 | key... path |
JSON.MGET k1 k2 $.a |
-
说明:
- JSON.GET 返回 字符串化 JSON
- JSON.MGET 返回数组,对应多个 key
二、数组操作类(Array)
| 命令 | 作用 | 参数含义 | 示例 |
|---|---|---|---|
| JSON.ARRAPPEND | 向数组末尾追加元素 | key path value... |
JSON.ARRAPPEND k $.tags "redis" "json" |
| JSON.ARRINSERT | 在指定索引插入元素 | key path index value... |
JSON.ARRINSERT k $.tags 1 "nosql" |
| JSON.ARRPOP | 删除并返回指定索引元素 | key path [index] |
JSON.ARRPOP k $.tags -1 |
| JSON.ARRLEN | 获取数组长度 | key path |
JSON.ARRLEN k $.tags |
| JSON.ARRINDEX | 查找元素索引 | key path value |
JSON.ARRINDEX k $.tags "redis" |
| JSON.ARRTRIM | 截取数组区间 | key path start stop |
JSON.ARRTRIM k $.scores 0 9 |
-
说明:
- 索引支持负数(-1 表示最后一个)
- ARRTRIM 类似 LTRIM
三、对象操作类(Object)
| 命令 | 作用 | 参数说明 | 示例 |
|---|---|---|---|
| JSON.OBJKEYS | 获取对象的所有 key | key path |
JSON.OBJKEYS k $.user |
| JSON.OBJLEN | 获取对象字段数量 | key path |
JSON.OBJLEN k $.user |
| JSON.TYPE | 返回 path 对应值类型 | key path |
JSON.TYPE k $.user.name |
-
JSON.TYPE 返回类型包括:object / array / string / number / boolean / null
四、数值运算类(Numeric)
| 命令 | 作用 | 参数说明 | 示例 |
|---|---|---|---|
| JSON.NUMINCRBY | 数值自增 | key path number |
JSON.NUMINCRBY k $.count 1 |
| JSON.NUMMULTBY | 数值乘法 | key path number |
JSON.NUMMULTBY k $.price 0.8 |
| JSON.TOGGLE | 布尔值取反 | key path |
JSON.TOGGLE k $.enabled |
-
说明:
- 保证 原子性
- 常用于计数、开关状态
五、字符串操作类(String)
| 命令 | 作用 | 参数说明 | 示例 |
|---|---|---|---|
| JSON.STRAPPEND | 追加字符串 | key path value |
JSON.STRAPPEND k $.msg " world" |
| JSON.STRLEN | 字符串长度 | key path |
JSON.STRLEN k $.msg |
六、删除 / 清空类
| 命令 | 作用 | 参数说明 | 示例 |
|---|---|---|---|
| JSON.DEL | 删除指定 path | key path |
JSON.DEL k $.user.age |
| JSON.FORGET | 等价 JSON.DEL | 同上 | JSON.FORGET k $.tmp |
| JSON.CLEAR | 清空值 | key path |
JSON.CLEAR k $.list |
-
JSON.CLEAR 行为说明:
- 数组 → []
- 对象 → {}
- 数值 → 0
七、合并与高级操作
| 命令 | 作用 | 参数说明 | 示例 |
|---|---|---|---|
| JSON.MERGE | 合并 JSON(RFC7396) | key path value |
JSON.MERGE k $ '{"a":2,"b":3}' |
| JSON.RESP | RESP 格式返回 | key path |
JSON.RESP k $.user |
-
说明:
- MERGE 支持字段覆盖、删除、扩展
- RESP 适合客户端直接解析
八、调试与诊断类(Debug)
| 命令 | 作用 | 参数说明 | 示例 |
|---|---|---|---|
| JSON.DEBUG | 调试命令容器 | 子命令 | JSON.DEBUG MEMORY k |
| JSON.DEBUG MEMORY | 查看 JSON 占用内存 | key |
JSON.DEBUG MEMORY user:1 |
应用示例
-
场景选用:电商用户画像 + 订单系统(结构复杂、字段多、非常适合 JSONPath)
1 | 我们要存储一个用户的完整画像: |
-
SpringBoot暂时没有支持RedisJSON,你可以编写Lua脚本来实现相应的功能,另外 Redissson已经提供了对JSON的支持,但是实际使用中发现还有一些Bug,下面结合命令给出代码示例。
1 | <!-- 引入 Redisson ,这里要注意,现在最新版是 4.0.0,需要 springboot 4.x --> |
-
创建完整 JSON 文档(JSON.SET)
1 | # 创建 JSON 文档,实际执行中不支持多行,这里只是为了看着清楚些,实际运行时要将其改成一行 |
-
通用代码
1 |
|
1 | User user = objectMapper.readValue(userStr, User.class); |
JSONPath 查询示例
-
1️⃣ 基础路径访问
1 | # 获取全部数据 |
1 | User user = bucket.get(new JacksonCodec<>(new TypeReference<User>() {})); |
-
2️⃣ 数组访问 & 通配符
1 | # 获取所有订单ID |
1 | List<String> orderIds = bucket.get(new JacksonCodec<>(new TypeReference<List<String>>() {}),"$.orders[*].orderId"); |
-
3️⃣ 数组下标与切片
1 | # 获取第一个订单 |
1 | List<OrdersBean> orders = bucket.get(new JacksonCodec<>(new TypeReference<List<OrdersBean>>() {}),"$.orders[0]"); |
-
4️⃣ 递归查询(…)
1 | # 👉 找出所有商品价格(不管在哪一层) |
1 | List<Double> prices = bucket.get(new JacksonCodec<>(new TypeReference<List<Double>>() {}),"$..price"); |
-
5️⃣ 条件过滤(JSONPath 核心能力)
✅ 支持的查询
1 | # 👉 找出所有订单金额大于100的订单,✅ 数字比较 |
1 | List<OrdersBean> orders = bucket.get(new JacksonCodec<>(new TypeReference<List<OrdersBean>>() {}),"$.orders[?(@.amount>100)]"); |
❌ 不支持的查询
1 | # 👉 找出所有已支付订单,❌ 不支持字符串的比较 |
-
6️⃣ 修改数据(JSON.SET / JSON.NUMINCRBY)
1 | # 👉 修改用户名 |
1 | bucket.set("$.profile.name", "Alice Zhang"); |
-
7️⃣ 数组操作(ARRAPPEND / ARRINSERT)
1 | # 👉 添加一个订单 |
1 | OrdersBean ordersBean = OrdersBean.builder() |
-
8️⃣ 删除 & 清理操作(JSON.DEL & JSON.CLEAR)
1 | # 👉 删除用户年龄,$.profile.age 不存在了 |
1 | long delete = bucket.delete("$.profile.age"); |
-
9️⃣ 统计数组长度
1 | # 👉 统计用户订单的长度 |
1 | // 会抛异常,应该是Redisson的bug |
🔟 查看 JSON 占用内存
1 | 127.0.0.1:6379> JSON.DEBUG MEMORY user:10001 |
1 | long sizeInMemory = bucket.sizeInMemory(); |