1 · 场景与价值
在日志累加、指标采集、消息追踪等场景中,我们常需快速判断某个字符串字段“到底有多长”,以便:
- 阻止过大日志:若长度超限则截断或归档;
- 动态分桶:按长度选择不同存储策略;
- 性能监控:突然飙长可能暗示异常循环或恶意输入。
有了 JSON.STRLEN
,无需把整段内容取回客户端,也无需额外存副本,即可 O(1) 地获取字段长度。
2 · 指令概览
指令 | 作用 | 复杂度 |
---|---|---|
JSON.STRLEN key [path] | 返回指定路径 JSON 字符串的长度 | O(1)(单路径) / O(N)(多路径,与键大小相关) |
- 可用版本:RedisJSON ≥ 1.0
- ACL 标签:
@json @read @slow
- 默认路径:
$
(根)
3 · 语法参数
JSON.STRLEN <key> [<path>]
参数 | 必填 | 说明 |
---|---|---|
key | ✔ | 目标键名 |
path | JSONPath,缺省为 $ |
返回值
- 若匹配为字符串:返回其长度(整型)。
- 若路径不存在 / 类型非字符串:返回
nil
。 - 多路径:递归数组,顺序对应各匹配点。
4 · 基本示例
redis> JSON.SET doc $ '{"a":"foo","nested":{"a":"hello"},"nested2":{"a":31}}'
OK# 多路径统计
redis> JSON.STRLEN doc $..a
1) (integer) 3 # $.a -> "foo"
2) (integer) 5 # $.nested.a -> "hello"
3) (nil) # $.nested2.a 不是字符串
5 · 常见用法场景
5.1 日志超长保护
> len=$(redis-cli JSON.STRLEN log:123 $.trace)
> if [ "$len" -gt 2048 ]; then
> redis-cli JSON.SET log:123 $.trace '"<truncated>"'
> fi
5.2 统计动态字段占用
# 查看所有用户简介 bio 的平均长度
redis-cli --raw KEYS "user:*" | while read k; doredis-cli JSON.STRLEN $k $.bio
done | awk '{s+=$1;c++} END{print s/c}'
6 · 踩坑与注意
坑 | 症状 | 解决方案 |
---|---|---|
路径非字符串 | 返回 nil ,误以为不存在 | 先 JSON.TYPE 或保证字段类型一致 |
多路径扫描过大文档 | 延迟抖动 | 精准路径,避免 $..field |
空键 / 空路径 | 直接 nil | 先用 EXISTS 或 JSON.TYPE 判断 |
把数组当字符串 | 长度不是元素数 | STRLEN 只算字节数,不是元素数,数组用 JSON.ARRLEN |
7 · Go-Redis 完整示例
package 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", "msg:1", "$",`{"body":"hello world","meta":{"note":"short"}}`).Result()if err != nil { log.Fatal(err) }// 1️⃣ 单路径长度len1, _ := rdb.Do(ctx, "JSON.STRLEN", "msg:1", "$.body").Int64()fmt.Println("body len =", len1) // 11// 2️⃣ 多路径长度res, _ := rdb.Do(ctx, "JSON.STRLEN", "msg:1", "$..note").Slice()fmt.Println("meta.note len =", res[0]) // 5// 3️⃣ 类型不符示例_, _ = rdb.Do(ctx, "JSON.SET", "msg:1", "$.count", 100)res2, _ := rdb.Do(ctx, "JSON.STRLEN", "msg:1", "$.count").Slice()fmt.Printf("count -> %#v\n", res2[0]) // <nil>
}
8 · 性能建议
-
批量调用使用 Pipeline
pipe := rdb.Pipeline() keys := []string{"user:1", "user:2", "user:3"} for _, k := range keys {pipe.Do(ctx, "JSON.STRLEN", k, "$.bio") } _, _ = pipe.Exec(ctx)
-
监控慢查询
复杂 JSON + 多路径 ($..field
) 会触发 SLOWLOG,需关注。 -
字符串膨胀预警
可用MEMORY USAGE key
+JSON.STRLEN
建立长度阈值报警。
9 · 与其它指令的协同
需求 | 组合 | 说明 |
---|---|---|
追加后检测 | JSON.STRAPPEND → JSON.STRLEN | 先写后查,防止长度爆表 |
长度分片 | JSON.STRLEN + JSON.SET | 达阈值切换到新字段 |
类型校验 | JSON.TYPE + JSON.STRLEN | 防止对非字符串统计 |
10 · 总结
JSON.STRLEN
提供 O(1) 字段级长度统计,是日志与配额场景的利器。- 返回值为整型或
nil
,多路径时请按序处理。 - 精准路径 ≫ 通配路径,能显著降低 O(N) 扫描成本。
- 在 Go-Redis 中用
Do()
一行即可调用,并能与 Pipeline/事务无缝整合。
至此,字符串家族三兄弟 STRLEN / STRAPPEND / SET 已全部集齐。灵活运用它们,你的 RedisJSON 文档读写将更加高效、细粒度且安全。祝编码愉快,欢迎留言交流实践体会!