RedisJSON 指令精讲JSON.TOGGLE 键翻转布尔值

发布于:2025-07-23 ⋅ 阅读:(13) ⋅ 点赞:(0)

1 · 为什么要学 JSON.TOGGLE?

在特性开关、状态机、AB 实验、勾选框等场景下,我们经常需要把 truefalse 来回切换。传统做法要先 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 代表 true0 代表 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 动态翻转
任务完成标记 任务完成后置 donetrue 失败重试时再次 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 main

import (
	"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️⃣ 原子翻转 done
	res, _ := 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 · 性能与并发建议

  1. Pipeline / Lua 脚本

    • 高并发批量翻转时使用 Pipeline 减少 RTT。

    • 若需条件判断(如仅当 done=false 才翻转),可写 Lua:

      local v=redis.call('JSON.GET',KEYS[1],'$.done')
      if v=='false' then
        return redis.call('JSON.TOGGLE',KEYS[1],'$.done')[1]
      end
      return -1
      
  2. 慢日志监控

    • 大文档 + 多路径 ($..flag) 易触发 SLOWLOG
  3. 幂等 API

    • Web 接口里应返回翻转后值,而不是固定 200 来避免并发写乱序。

9 · 与其他指令的协同

目标 指令组合 说明
首次写布尔 + 后续翻转 JSON.SET ... NXJSON.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 这把利器!如有问题,欢迎评论交流。


网站公告

今日签到

点亮在社区的每一天
去签到