Redis

关注公众号 jb51net

关闭
首页 > 数据库 > Redis > RedisJSON JSON.SET

RedisJSON中JSON.SET的用法小结

作者:Hello.Reader

本文全面解析 RedisJSON 插件的核心指令JSON.SET,涵盖语法规则、典型用法、易错点和性能优化建议,感兴趣的可以了解一下

1 · 为什么要写这篇文章?

在结构化数据存储场景里,传统的字符串键值对已难以满足灵活查询与字段级更新的需求。RedisJSON 插件为 Redis 带来了对原生 JSON 文档的读写支持,其中最核心也最常用的指令就是 JSON.SET。本文将带你彻底吃透 JSON.SET 的各个细节——从语法、时间复杂度到多路径批量更新,再到在生产环境中容易踩到的坑和性能调优方法,并提供一套 Go-Redis 的完整示例代码,帮助你快速落地。

2 · RedisJSON 与 JSON.SET 概览

特性说明
插件版本RedisJSON ≥ 1.0 (推荐 2.x 与 Redis 7.x+ 搭配使用)
指令`JSON.SET key path value [NXXX]`
功能按 JSONPath 将 value 写入 key 对应的 JSON 文档
ACL 标记@json @write @slow
时间复杂度O(M + N) —— M 为原值大小,N 为新值大小 × 匹配路径数

3 · 语法详解

JSON.SET <key> <path> <value> [NX | XX]
参数必填说明
keyRedis 中的键;不存在时必须从根路径写入
pathJSONPath 表达式;$ 或 . 代表根
value合法 JSON 字符串,或原样字符串(无需额外引号)
NX仅当目标不存在时写入
XX仅当目标已存在时覆盖

小贴士:NX 与 XX 不仅作用于 Redis 键,也作用于 JSON 文档内部键。这点经常被忽略!

4 · JSONPath 规则速查

注意:JSON.SET 不支持在一次调用中写入 不同 的值到多条路径;如果传入多路径,所有匹配点都会被同一个 value 覆盖。

5 · 返回值与错误处理

情况返回
写入成功OK
路径不存在且无法创建(nil)
NX/XX 条件不满足(nil)
根键不存在但路径不是 $(error) ERR new objects must be created at the root

生产环境中推荐显式检查返回值,而不要只依赖 err == nil。示例见 § 9。

6 · 典型用法示例

6.1 替换已有字段

redis> JSON.SET doc $ '{"a":2}'
OK
redis> JSON.SET doc $.a 3
OK
redis> JSON.GET doc $
"[{\"a\":3}]"

6.2 追加新字段

redis> JSON.SET doc $ '{"a":2}'
OK
redis> JSON.SET doc $.b 8
OK
redis> JSON.GET doc $
"[{\"a\":2,\"b\":8}]"

6.3 一次性批量更新多路径

redis> JSON.SET doc $ '{"f1":{"a":1},"f2":{"a":2}}'
OK
redis> JSON.SET doc $..a 3
OK
redis> JSON.GET doc
"{\"f1\":{\"a\":3},\"f2\":{\"a\":3}}"

6.4 结合 NX / XX 条件

# 仅当字段不存在时写入
redis> JSON.SET user:1 $.nickname '"neo"' NX
OK          # 第一次成功
redis> JSON.SET user:1 $.nickname '"smith"' NX
(nil)       # 条件未满足

7 · 易踩坑汇总

现象解决方案
键不存在却写子路径抛错 ERR new objects must be created at the root第一次写必须用根路径 $
值未用 JSON 字符串包裹若忘记加引号:JSON.SET $.name neo ⇒ 解析失败非数值 / 布尔需双引号或 'neo'
NX/XX 作用域误解误以为只检查 Redis 键其实同时检查 JSON 内目标字段
字符串过长超出 512 MB 限制拆分存储或压缩再存

8 · 性能调优与并发安全

  1. Pipeline / MULTI
    连续多次 JSON.SET 可使用 Pipelining 或事务批量发送,减少 RTT。
  2. 避免大文档频繁写
    时间复杂度包含原值大小 M:越大的 JSON,写一次越慢。可将热点字段拆分为独立键或使用 JSON.NUMINCRBY 等增量指令。
  3. 合理选择 NX/XX
    在幂等场景下使用 NX 或 XX 可避免无谓的重写,降低写放大。
  4. 监控慢日志
    Redis 会将执行时间 > slowlog-log-slower-than 的指令记录;JSON 操作本质属 @slow 类别,应重点关注。
  5. Lua/RedisGears 乐观锁
    高并发写同一字段,可结合 WATCH/MULTI 或 Lua 脚本实现 CAS。

9 · Go-Redis 完整示例(可直接运行)

依赖:Go ≥ 1.22、github.com/redis/go-redis/v9

// 文件 main.go
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"

	"github.com/redis/go-redis/v9"
)

var ctx = context.Background()

type Profile struct {
	Name  string `json:"name"`
	Age   int    `json:"age"`
	Tags  []string `json:"tags"`
}

func must(err error) {
	if err != nil {
		log.Fatalf("fatal: %v", err)
	}
}

func main() {
	rdb := redis.NewClient(&redis.Options{
		Addr: "localhost:6379",
	})
	defer rdb.Close()

	// ---------- 1. 写入根文档 ----------
	profile := Profile{Name: "Alice", Age: 27, Tags: []string{"gopher", "redis"}}
	raw, _ := json.Marshal(profile)

	if res, err := rdb.Do(ctx, "JSON.SET", "user:1001", "$", raw).Text(); err != nil {
		must(err)
	} else {
		fmt.Println("set root:", res) // OK
	}

	// ---------- 2. 更新子字段 ----------
	if res, err := rdb.Do(ctx, "JSON.SET", "user:1001", "$.age", 28, "XX").Text(); err != nil {
		must(err)
	} else if res == "" {
		fmt.Println("XX unmet, age not updated")
	} else {
		fmt.Println("update age:", res) // OK
	}

	// ---------- 3. 读取文档 ----------
	data, err := rdb.Do(ctx, "JSON.GET", "user:1001", "$").Result()
	must(err)
	fmt.Println("profile =", data.(string))

	// ---------- 4. 错误示例 ----------
	if _, err := rdb.Do(ctx, "JSON.SET", "user:1002", "$.name", "\"Bob\"").Result(); err != nil {
		fmt.Println("expected error:", err)
	}

	// Output:
	// set root: OK
	// update age: OK
	// profile = [{"name":"Alice","age":28,"tags":["gopher","redis"]}]
	// expected error: ERR new objects must be created at the root
}

运行后,你将看到成功写入、条件更新、读回以及错误处理的完整流程。

10 · 进阶话题

方向价值
与 RediSearch 集成对 JSON 字段建立二级索引,支持全文检索与聚合分析
存储版本化配置使用 NX 写入新版本,配合 JSON.NUMINCRBY 维护版本号
灰度发布通过数组/对象结构保存多线路配置信息,再用 JSON.DEL 快速回滚
Streaming JSON将大文档拆分到 Stream 或 List,再按需合并到 RedisJSON

11 · 总结

到此这篇关于RedisJSON中JSON.SET的用法小结的文章就介绍到这了,更多相关RedisJSON JSON.SET内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文