RedisJSON 指令精讲JSON.TOGGLE 键翻转布尔值
1 · 为什么要学 JSON.TOGGLE?
在特性开关、状态机、AB 实验、勾选框等场景下,我们经常需要把 true
↔ false
来回切换。传统做法要先 JSON.GET
读出布尔值、在客户端翻转、再 JSON.SET
写回;这不仅多一次 RTT,还增加竞态风险。JSON.TOGGLE
直接在 Redis 端 原子翻转 布尔字段,让代码更简洁、操作更安全。
2 · 指令概览
指令 | 作用 | 复杂度 |
---|---|---|
JSON.TOGGLE key [path] | 将指定路径布尔值取反 | O(1)(单路径) / O(N)(多路径,与键大小相关) |
- 可用版本:RedisJSON ≥ 2.0.0
- ACL 标签:
@json @write @slow
- 默认路径:
$
(根)
3 · 语法解析
JSON.TOGGLE <key> [<path>]
参数 | 必填 | 说明 |
---|---|---|
key | ✔ | 目标键名 |
path | JSONPath,省略时为 $ |
返回值
- 布尔 → 数字:翻转后值,
1
代表true
,0
代表false
- 多路径:数组形式,顺序与匹配点一致
- 非布尔:返回
nil
- 路径不存在:同样返回
nil
4 · 实战示例
redis> JSON.SET feature $ '{"enabled":true,"nested":{"flag":false},"id":123}'
OK# ➊ 单路径翻转
redis> JSON.TOGGLE feature $.enabled
1) (integer) 0 # false# ➋ 多路径翻转
redis> JSON.TOGGLE feature $..flag
1) (integer) 1 # nested.flag false → true# ➌ 非布尔字段
redis> JSON.TOGGLE feature $.id
1) (nil)redis> JSON.GET feature $
"[{\"enabled\":false,\"nested\":{\"flag\":true},\"id\":123}]"
5 · 典型使用场景
场景 | 描述 | 建议组合 |
---|---|---|
特性开关 (Feature Toggle) | 打开 / 关闭实验功能 | JSON.MGET 批量查开关 + JSON.TOGGLE 动态翻转 |
任务完成标记 | 任务完成后置 done → true | 失败重试时再次 TOGGLE 复位 |
二元状态机 | 资源占用 (locked ) 字段 true/false | 配合 WATCH 做乐观锁 |
反向 AB 实验 | 随机翻转用户 groupA 字段做对照 | 批量脚本调用 JSON.TOGGLE |
6 · 踩坑与注意
坑 | 现象 | 对策 |
---|---|---|
字段类型不是布尔 | 返回 nil ,值不变 | 初始化时固定字段类型;或先 JSON.TYPE 校验 |
多路径扫描大文档 | 时间复杂度 O(N) | 避免 $..flag 通配,改用精确路径 |
版本兼容 | RedisJSON 1.x 无此指令 | 部署前确认插件版本≥2.0 |
误操作连续翻转 | 状态乱跳 | 搭配 NX/XX 或 Lua 脚本加条件判断 |
7 · Go-Redis 完整示例
依赖:
github.com/redis/go-redis/v9
、RedisJSON ≥ 2.0
package mainimport ("context""fmt""log""github.com/redis/go-redis/v9"
)func main() {ctx := context.Background()rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"})defer rdb.Close()// 1️⃣ 初始化文档_, err := rdb.Do(ctx, "JSON.SET", "task:1001", "$",`{"title":"write blog","done":false}`).Result()if err != nil { log.Fatal(err) }// 2️⃣ 原子翻转 doneres, _ := rdb.Do(ctx, "JSON.TOGGLE", "task:1001", "$.done").Slice()fmt.Println("done =", res[0]) // 1 (true)// 3️⃣ 再次翻转以复位rdb.Do(ctx, "JSON.TOGGLE", "task:1001", "$.done")// 4️⃣ 读取验证state, _ := rdb.Do(ctx, "JSON.GET", "task:1001", "$.done").Text()fmt.Println("current done:", state) // false
}
批量翻转示例(Pipeline)
pipe := rdb.Pipeline()
keys := []string{"task:1", "task:2", "task:3"}
for _, k := range keys {pipe.Do(ctx, "JSON.TOGGLE", k, "$.done")
}
if _, err := pipe.Exec(ctx); err != nil {log.Fatal(err)
}
8 · 性能与并发建议
-
Pipeline / Lua 脚本
-
高并发批量翻转时使用 Pipeline 减少 RTT。
-
若需条件判断(如仅当
done=false
才翻转),可写 Lua:local v=redis.call('JSON.GET',KEYS[1],'$.done') if v=='false' thenreturn redis.call('JSON.TOGGLE',KEYS[1],'$.done')[1] end return -1
-
-
慢日志监控
- 大文档 + 多路径 (
$..flag
) 易触发SLOWLOG
。
- 大文档 + 多路径 (
-
幂等 API
- Web 接口里应返回翻转后值,而不是固定 200 来避免并发写乱序。
9 · 与其他指令的协同
目标 | 指令组合 | 说明 |
---|---|---|
首次写布尔 + 后续翻转 | JSON.SET ... NX → JSON.TOGGLE | 先用 NX 保证类型,后续直接翻转 |
布尔统计 | FT.AGGREGATE + JSON.TOGGLE | RediSearch 聚合统计 true/false 数量 |
布尔值批量置位 | JSON.SET + JSON.TOGGLE | 批量清零后一键启用 |
10 · 小结
- JSON.TOGGLE 为布尔字段提供了 原子取反 能力,消除读-改-写竞态。
- 返回值
1
/0
表示翻转后的 true / false,可直接用作状态。 - 多路径场景要注意 O(N) 复杂度,能精准就精准。
- 在 Go-Redis 中只需
Do()
调用即可,Pipeline 可批量操作。 - 结合 Lua、RediSearch、NX/XX 等组件,可构建更复杂的布尔状态机和实验平台。
至此,RedisJSON 字符串与布尔操作的核心指令你已全部掌握。下次需要快速翻转开关或标记任务完成,别忘了 JSON.TOGGLE
这把利器!如有问题,欢迎评论交流。