1 · 背景与价值
- 动态 JSON 文档 经常混合数字、布尔、字符串、数组、对象等多种类型。
- 在对字段做 条件写入(
NX/XX
)、增量操作(NUMINCRBY
)、追加 等之前,我们必须先确认它的真实类型。 JSON.TYPE
提供 O(1) 的字段级类型探测,省去把整段 JSON 拉回客户端再解析的麻烦,也避免了类型误判导致的运行时错误。
2 · 指令总览
指令 | 功能 | 复杂度 |
---|---|---|
JSON.TYPE key [path] | 返回指定路径 JSON 值的类型 | O(1)(单路径) / O(N)(多路径,与键大小相关) |
- 可用版本:RedisJSON ≥ 1.0
- ACL 标签:
@json @read @slow
- 默认路径:
$
(根) - 可返回类型:
string
、integer
、number
、boolean
、object
、array
、null
RedisJSON 将整数/浮点区分为
integer
/number
;空值返回null
字面量。
3 · 语法与返回值
JSON.TYPE <key> [<path>]
- 单路径:直接返回指定节点的类型字符串;
- 多路径:按匹配点顺序,返回一个字符串数组;
- 不存在/路径不匹配:返回空数组
[]
; - 目标为 JSON.null:返回
"null"
而非nil
。
示例:
redis> JSON.SET doc $ '{"a":2,"nested":{"a":true},"foo":"bar"}'
OK
redis> JSON.TYPE doc $..a
1) "integer"
2) "boolean"
redis> JSON.TYPE doc $.nested
"object"
4 · 典型使用场景
场景 | 描述 | 推荐组合 |
---|---|---|
安全写入 | 写前确认字段类型,避免类型冲突 | JSON.TYPE → JSON.SET NX/XX |
动态路由 | 根据字段不同类型走不同业务流程 | JSON.TYPE 结果做 switch case |
增量/追加 | 只有当字段是数字才 NUMINCRBY ,是字符串才 STRAPPEND | JSON.TYPE 判别后分支调用 |
数据治理 | 批量扫描文档结构,统计字段类型分布 | 对所有键执行 JSON.TYPE 收集 |
5 · 踩坑与注意
坑 | 现象 | 解决方案 |
---|---|---|
多路径扫描大文档 | $..field 性能差 | 精确路径替代通配,或分层调用 |
空数组/空对象 | 返回 array / object ,但内容为空 | 仍需业务层判断长度 |
null 与 nil 混淆 | JSON 值为 null 时类型是 "null" ,路径不存在则空数组 | 判空时区分两者 |
浮点数类型 | 返回 number 而非 float | 整数 integer ,其余数字 number |
6 · Go-Redis 实战代码
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()
// 初始化测试文档
_, err := rdb.Do(ctx, "JSON.SET", "conf:1", "$",
`{"flag":true,"quota":100,"meta":{"desc":"demo"},"items":null}`).Result()
if err != nil { log.Fatal(err) }
// 1️⃣ 单路径判型
t, _ := rdb.Do(ctx, "JSON.TYPE", "conf:1", "$.flag").Text()
fmt.Println("$.flag ->", t) // boolean
// 2️⃣ 根据类型做不同操作
switch t {
case "boolean":
rdb.Do(ctx, "JSON.TOGGLE", "conf:1", "$.flag")
case "integer":
rdb.Do(ctx, "JSON.NUMINCRBY", "conf:1", "$.flag", 1)
default:
fmt.Println("未知类型,不操作")
}
// 3️⃣ 多路径批量判型
res, _ := rdb.Do(ctx, "JSON.TYPE", "conf:1", "$..*").Slice()
fmt.Println("全字段类型:", res)
}
7 · 性能与监控建议
-
Pipeline
- 大批量键 (万级) 需要类型检查时,使用 Pipeline 降低 RTT。
-
配合 Lua 做原子校验-写入
-
示例:只在类型为
integer
时自增,否则返回错误。local t = redis.call('JSON.TYPE', KEYS[1], ARGV[1]) if t[1] == 'integer' then return redis.call('JSON.NUMINCRBY', KEYS[1], ARGV[1], ARGV[2]) end return redis.error_reply('type mismatch')
-
-
慢日志监控
- 通配多路径 (
$..field
) 会遍历整个文档,关注SLOWLOG
。
- 通配多路径 (
8 · 与其他指令的协同
目标 | 指令组合 | 说明 |
---|---|---|
安全增量 | JSON.TYPE → NUMINCRBY | 仅在 integer 时自增 |
安全追加 | JSON.TYPE → STRAPPEND | 先确保是 string |
布尔翻转 | JSON.TYPE → TOGGLE | 遇到非布尔则跳过或初始化 |
结构迁移 | JSON.TYPE + OBJKEYS /ARRLEN | 先探测类型,再读取子结构 |
9 · 小结
- JSON.TYPE 是 RedisJSON 的“类型探针”,在动态文档场景中必备。
- O(1) 查型可大幅简化客户端逻辑、避免运行时错误。
- 区分
"null"
与空数组返回值[]
,避免误判。 - 精确路径优于通配路径,尤其在大文档或高 QPS 场景。
- 搭配 Lua/Pipeline,可实现原子校验-写入与批量治理。
至此,RedisJSON 字段级读写 + 类型判断工具链已经完整。灵活运用 TYPE + SET/STRAPPEND/STRLEN/TOGGLE/NUMINCRBY
,即可构建一个高效、类型安全、可扩展的 JSON 数据层。
如有更多疑问,欢迎留言讨论!