RedisJSON中JSON.SET的用法小結(jié)
1 · 為什么要寫這篇文章?
在結(jié)構(gòu)化數(shù)據(jù)存儲場景里,傳統(tǒng)的字符串鍵值對已難以滿足靈活查詢與字段級更新的需求。RedisJSON 插件為 Redis 帶來了對原生 JSON 文檔的讀寫支持,其中最核心也最常用的指令就是 JSON.SET。本文將帶你徹底吃透 JSON.SET 的各個細節(jié)——從語法、時間復(fù)雜度到多路徑批量更新,再到在生產(chǎn)環(huán)境中容易踩到的坑和性能調(diào)優(yōu)方法,并提供一套 Go-Redis 的完整示例代碼,幫助你快速落地。
2 · RedisJSON 與 JSON.SET 概覽
| 特性 | 說明 | |
|---|---|---|
| 插件版本 | RedisJSON ≥ 1.0 (推薦 2.x 與 Redis 7.x+ 搭配使用) | |
| 指令 | `JSON.SET key path value [NX | XX]` |
| 功能 | 按 JSONPath 將 value 寫入 key 對應(yīng)的 JSON 文檔 | |
| ACL 標記 | @json @write @slow | |
| 時間復(fù)雜度 | O(M + N) —— M 為原值大小,N 為新值大小 × 匹配路徑數(shù) |
3 · 語法詳解
JSON.SET <key> <path> <value> [NX | XX]
| 參數(shù) | 必填 | 說明 |
|---|---|---|
| key | ? | Redis 中的鍵;不存在時必須從根路徑寫入 |
| path | ? | JSONPath 表達式;$ 或 . 代表根 |
| value | ? | 合法 JSON 字符串,或原樣字符串(無需額外引號) |
| NX | 僅當目標不存在時寫入 | |
| XX | 僅當目標已存在時覆蓋 |
小貼士:NX 與 XX 不僅作用于 Redis 鍵,也作用于 JSON 文檔內(nèi)部鍵。這點經(jīng)常被忽略!
4 · JSONPath 規(guī)則速查
- 根路徑:$ 或 .
- 點式:$.address.city
- 數(shù)組下標:$.items[0]
- 切片:$.items[1:3](RedisJSON 2.x 支持)
- 通配:$..price 匹配所有層級的 price 字段
- 多路徑:$..a 與 $..b 可一次性傳入多條
注意:JSON.SET 不支持在一次調(diào)用中寫入 不同 的值到多條路徑;如果傳入多路徑,所有匹配點都會被同一個 value 覆蓋。
5 · 返回值與錯誤處理
| 情況 | 返回 |
|---|---|
| 寫入成功 | OK |
| 路徑不存在且無法創(chuàng)建 | (nil) |
| NX/XX 條件不滿足 | (nil) |
| 根鍵不存在但路徑不是 $ | (error) ERR new objects must be created at the root |
生產(chǎn)環(huán)境中推薦顯式檢查返回值,而不要只依賴 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 結(jié)合 NX / XX 條件
# 僅當字段不存在時寫入 redis> JSON.SET user:1 $.nickname '"neo"' NX OK # 第一次成功 redis> JSON.SET user:1 $.nickname '"smith"' NX (nil) # 條件未滿足
7 · 易踩坑匯總
| 坑 | 現(xiàn)象 | 解決方案 |
|---|---|---|
| 鍵不存在卻寫子路徑 | 拋錯 ERR new objects must be created at the root | 第一次寫必須用根路徑 $ |
| 值未用 JSON 字符串包裹 | 若忘記加引號:JSON.SET $.name neo ⇒ 解析失敗 | 非數(shù)值 / 布爾需雙引號或 'neo' |
| NX/XX 作用域誤解 | 誤以為只檢查 Redis 鍵 | 其實同時檢查 JSON 內(nèi)目標字段 |
| 字符串過長 | 超出 512 MB 限制 | 拆分存儲或壓縮再存 |
8 · 性能調(diào)優(yōu)與并發(fā)安全
- Pipeline / MULTI
連續(xù)多次 JSON.SET 可使用 Pipelining 或事務(wù)批量發(fā)送,減少 RTT。 - 避免大文檔頻繁寫
時間復(fù)雜度包含原值大小 M:越大的 JSON,寫一次越慢。可將熱點字段拆分為獨立鍵或使用 JSON.NUMINCRBY 等增量指令。 - 合理選擇 NX/XX
在冪等場景下使用 NX 或 XX 可避免無謂的重寫,降低寫放大。 - 監(jiān)控慢日志
Redis 會將執(zhí)行時間 > slowlog-log-slower-than 的指令記錄;JSON 操作本質(zhì)屬 @slow 類別,應(yīng)重點關(guān)注。 - Lua/RedisGears 樂觀鎖
高并發(fā)寫同一字段,可結(jié)合 WATCH/MULTI 或 Lua 腳本實現(xiàn) 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 維護版本號 |
| 灰度發(fā)布 | 通過數(shù)組/對象結(jié)構(gòu)保存多線路配置信息,再用 JSON.DEL 快速回滾 |
| Streaming JSON | 將大文檔拆分到 Stream 或 List,再按需合并到 RedisJSON |
11 · 總結(jié)
- JSON.SET 是 RedisJSON 最核心的寫入指令,先掌握根寫入,再逐步學(xué)習(xí)多路徑與條件寫。
- 時間復(fù)雜度與文檔大小 線性相關(guān),因此避免頻繁重寫大 JSON。
- 正確使用 NX/XX 可實現(xiàn)冪等更新、樂觀鎖等高級場景。
- 在 Go-Redis 中通過 Do(ctx, "JSON.SET", …) 可無縫調(diào)用,務(wù)必檢查返回值而非只關(guān)注 err。
- 生產(chǎn)環(huán)境要結(jié)合 慢日志、Pipeline、事務(wù) 等手段進行性能與可靠性保障。
到此這篇關(guān)于RedisJSON中JSON.SET的用法小結(jié)的文章就介紹到這了,更多相關(guān)RedisJSON JSON.SET內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
分布式使用Redis實現(xiàn)數(shù)據(jù)庫對象自增主鍵ID
本文介紹在分布式項目中使用Redis生成對象的自增主鍵ID,通過Redis的INCR等命令實現(xiàn)計數(shù)器功能,具有一定的參考價值,感興趣的可以了解一下2024-12-12
詳解Redis BoundValueOperations使用及實現(xiàn)
BoundValueOperations是Spring Data Redis中一個非常實用的工具,本文主要介紹了Redis BoundValueOperations使用,具有一定的參考價值,感興趣的可以了解一下2025-09-09
通過docker和docker-compose安裝redis兩種方式詳解
這篇文章主要介紹了通過docker和docker-compose安裝redis的兩種方式,Docker安裝方式包括拉取鏡像、查看本地鏡像、運行容器和測試連接,Docker Compose安裝方式包括目錄結(jié)構(gòu)、配置文件、啟動和關(guān)閉容器、檢查啟動情況以及查看CPU和內(nèi)存使用狀態(tài),需要的朋友可以參考下2024-12-12
shell腳本批量導(dǎo)出redis key-value方式
為避免keys全量掃描導(dǎo)致Redis卡頓,可先通過dump.rdb備份文件在本地恢復(fù),再使用scan命令漸進導(dǎo)出key-value,通過CNT和INTERVAL參數(shù)控制負載,且scan不指定游標可減少阻塞2025-08-08
淺談一下如何保證Redis緩存與數(shù)據(jù)庫的一致性
這篇文章主要介紹了一下如何保證Redis緩存與數(shù)據(jù)庫的一致性,今天這篇文章就帶你詳細了解一下四種同步策略,需要的朋友可以參考下2023-03-03
Web-ssrfme:redis 未授權(quán)訪問攻擊的問題解決
本文主要介紹了Web-ssrfme:redis 未授權(quán)訪問攻擊的問題解決,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-04-04

