> 技术文档 > RedisJSON 的 JSON.STRAPPEND字符串追加的正确姿势_json 追加

RedisJSON 的 JSON.STRAPPEND字符串追加的正确姿势_json 追加


1 · 写在前面

在数据模型中,“追加”是个高频需求:日志拼接、状态堆栈、消息跟踪……如果把这些信息存在 RedisJSON 文档里,与其整段读出再写回,不如直接用 JSON.STRAPPEND 就地完成。本文将带你从语法、返回值到性能陷阱,全方位掌握 JSON.STRAPPEND 的使用,并配套一段 Go-Redis 代码示例,随取随用。

2 · 指令总览

指令 功能 复杂度 JSON.STRAPPEND key [path] valuevalue 追加到指定路径JSON 字符串 O(1)(单路径) / O(N)(多路径,与键大小相关)
  • 插件要求:RedisJSON ≥ 1.0
  • ACL 标签@json @write @slow
  • 默认路径$(根)

3 · 详细语法

JSON.STRAPPEND  [] 
参数 必填 说明 key ✔ 目标键名 path JSONPath;省略时为根 $ value ✔ 要追加的 JSON 字符串,支持追加到多路径

字符串写法陷阱
RedisJSON 需要 “字符串的字符串”:

  • 直接写 \"hello\" 会被解析成 JSON 字符串 hello
  • 若它又是数组元素,应再套一层:\'\"hello\"\'

4 · 返回值解读

执行结果为一个 整数数组,对应每条路径上目标字符串的新长度;若匹配值不是字符串则返回 nil

1) (integer) 6 # 路径1 的新长度2) (integer) 8 # 路径2 的新长度3) (nil) # 路径3 不是字符串

5 · 核心示例

5.1 基础追加

redis> JSON.SET doc $ \'{\"a\":\"foo\"}\'OKredis> JSON.STRAPPEND doc $.a \'\"bar\"\'1) (integer) 6redis> JSON.GET doc $.a\"foobar\"

5.2 多路径一次追加

redis> JSON.SET doc $ \'{\"a\":\"foo\",\"nested\":{\"a\":\"hello\"}}\'OKredis> JSON.STRAPPEND doc $..a \'\"baz\"\'1) (integer) 6 # $.a -> foobaz2) (integer) 8 # $.nested.a -> hellobaz

5.3 非字符串路径

redis> JSON.SET doc $ \'{\"num\":123}\'OKredis> JSON.STRAPPEND doc $.num \'\"x\"\'1) (nil) # $.num 不是字符串

6 · 常见踩坑 & 对策

场景 症状 解决方案 路径值非字符串 返回 nil,数据未改变 先用 JSON.TYPE 检查或存储时保持类型一致 忘记双层引号 追加失败 / JSON 解析错误 对字面量字符串使用 \'\"text\"\' 一次性大量追加 文档变“胖”,查询变慢 定期归档旧字段或改用 Redis Stream 多路径 O(N) 时间 键很大时操作卡顿 精确指定路径;不要过度使用 $..a 通配

7 · Go-Redis 实战

// go.mod 需加入: github.com/redis/go-redis/v9package 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()// 初始化文档_, err := rdb.Do(ctx, \"JSON.SET\", \"sess:42\", \"$\", `{\"trace\":\"start\"}`).Result()if err != nil { log.Fatal(err) }// 1️⃣ 追加一段日志res, err := rdb.Do(ctx, \"JSON.STRAPPEND\", \"sess:42\", \"$.trace\", `\"-step1\"`).Slice()if err != nil { log.Fatal(err) }fmt.Println(\"new length:\", res[0]) // (integer)// 2️⃣ 读回验证trace, _ := rdb.Do(ctx, \"JSON.GET\", \"sess:42\", \"$.trace\").Text()fmt.Println(\"trace =\", trace) // \"start-step1\"// 3️⃣ 错误示例:对非字符串追加_, err = rdb.Do(ctx, \"JSON.STRAPPEND\", \"sess:42\", \"$\", `\"-oops\"`).Result()fmt.Println(\"expected err:\", err) // nil,但返回 slice 中含 (nil)}

生产小贴士

  • 批量操作时用 Pipeline 减少 RTT。
  • 若日志量大可考虑将 trace 切分到数组,用 JSON.ARRAPPEND 追加,查询时再 STRJOIN

8 · 与 JSON.SET/NX/XX 的协同

如果你需要 先写字段(不存在才写),然后持续追加,常见流程是:

  1. JSON.SET key $.log \'\"init\"\' NX —— 初始化
  2. 若返回 OK 或已存在
  3. JSON.STRAPPEND key $.log \'\"|stepX\"\' —— 追加

这样可保证字段类型先被固定为字符串,后续 STRAPPEND 不会因为类型错误返回 nil

9 · 性能与监控

  • 慢日志:JSON 字符串越长,追加越慢;关注 SLOWLOG 超阈值指令。
  • 阈值报警:使用 MONITOR 或 Keyspace 通知,当字段长度超过上限及时拆分。
  • 内存碎片化:大量追加会导致底层 realloc;可定期对冷数据执行 MEMORY PURGE(Redis 7.2+)。

10 · 小结

  • JSON.STRAPPEND 是 RedisJSON原地字符串追加利器,避免全量读写。
  • 当目标值不是字符串时会静默返回 nil,务必检查返回数组。
  • 复杂路径(如 $..field)会提升时间复杂度,生产中应尽量 精准定位
  • 在 Go-Redis 中调用 Do() 即可,无需额外封装;并配合 Pipeline 与类型校验写出安全高效的业务代码。

掌握了 JSON.STRAPPEND,你的 JSON 文档字符串操作将更加优雅、高效。动手试试,把日志、追踪信息或动态状态存进 RedisJSON,感受无需搬数据就能追加的丝滑体验吧!