RedisJSON 指令精讲JSON.STRLEN 高效统计字符串长度

发布于:2025-07-25 ⋅ 阅读:(20) ⋅ 点赞:(0)

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; do
  redis-cli JSON.STRLEN $k $.bio
done | awk '{s+=$1;c++} END{print s/c}'

6 · 踩坑与注意

症状 解决方案
路径非字符串 返回 nil,误以为不存在 JSON.TYPE 或保证字段类型一致
多路径扫描过大文档 延迟抖动 精准路径,避免 $..field
空键 / 空路径 直接 nil 先用 EXISTSJSON.TYPE 判断
把数组当字符串 长度不是元素数 STRLEN 只算字节数,不是元素数,数组用 JSON.ARRLEN

7 · 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", "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 · 性能建议

  1. 批量调用使用 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)
    
  2. 监控慢查询
    复杂 JSON + 多路径 ($..field) 会触发 SLOWLOG,需关注。

  3. 字符串膨胀预警
    可用 MEMORY USAGE key + JSON.STRLEN 建立长度阈值报警。

9 · 与其它指令的协同

需求 组合 说明
追加后检测 JSON.STRAPPENDJSON.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 文档读写将更加高效、细粒度且安全。祝编码愉快,欢迎留言交流实践体会!


网站公告

今日签到

点亮在社区的每一天
去签到