ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

go-redis 实战:用 TS.QUERYLABELS 实现时序标签下钻查询(TSQueryLabels / TSQueryLabelValues)

go-redis 实战:用 TS.QUERYLABELS 实现时序标签下钻查询(TSQueryLabels / TSQueryLabelValues) 后端数据库客户端缓存【免费下载链接】go-redisRedis Go client项目地址https://gitcode.com/GitHub_Trending/go/go-redis点击查看免费下载导读本文围绕 go-redis 官方示例 example/ts-querylabels 展开系统讲解 Redis 8.10 TimeSeries 模块新增的TS.QUERYLABELS命令在 go-redis 中的两种绑定形式TSQueryLabels查询一组匹配时序上出现的全部标签名与TSQueryLabelValues查询某个标签在匹配时序上取到的全部值。读完本文你将掌握用 go-redis 构建标签名 → 标签值 → 时序键三级下钻drill-down流程的完整写法理解客户端如何构造命令参数、服务器返回语义有哪些边界行为并看懂对应的单元测试与集成测试如何钉死命令的线上格式wire format。背景标签发现为何重要RedisTimeSeries 中的每条时序time series都可以携带一组标签label例如{type: sensor, location: kitchen, unit: celsius}。标签是时序检索的核心索引TS.QUERYINDEX可以返回匹配某个过滤表达式的全部时序键TS.MRANGE/TS.MGET可以跨多组时序聚合查询。但在给用户一个查询界面的真实场景中用户并不预先知道系统里存在哪些标签、某个标签有哪些可选值。仪表盘工具如 Grafana需要在运行时动态填充下拉框变量variable dropdowns这就需要一个从粗到细逐级缩窄的流程先问这批匹配的时序上都有哪些标签名再问选中的某个标签在匹配时序上有哪些取值最后问完整筛选条件下具体是哪些时序键Redis 8.10 的TS.QUERYLABELS正好补齐了前两步与既有的TSQueryIndex一起构成完整的下钻闭环。go-redis 在 timeseries_commands.go 中为其提供了两个绑定方法并配套了官方示例与测试。命令的两种形式与 Go 绑定TS.QUERYLABELS有两种形式对应 go-redis 两个方法接口声明见 timeseries_commands.go命令形式go-redis 方法语义TS.QUERYLABELS LABELS [FILTER expr ...]TSQueryLabels(ctx, filterExpr)返回匹配时序上出现过的全部标签名集合TS.QUERYLABELS VALUES label [FILTER expr ...]TSQueryLabelValues(ctx, label, filterExpr)返回指定标签在匹配时序上取到的全部值集合两个方法都返回*StringSliceCmd即一组字符串服务器端返回的是无序、已去重的集合客户端不做额外排序或去重。过滤表达式的传递规则filterExpr使用的是与TSQueryIndex、TSMRange完全相同的过滤表达式语言如typesensor、location!kitchen、l(a,b)等并且原样透传verbatim给服务器客户端不做任何解析、校验或改写。这意味着传入nil或空切片[]string{}表示不附加任何过滤条件查询数据库内所有已建立索引的时序传入多个表达式时顺序原样保留由服务器端负责解释。空过滤器必须省略 FILTER 关键字一个容易踩坑的实现细节是当过滤表达式为空时客户端必须省略FILTER关键字因为服务器会拒绝一个光秃秃的FILTER。go-redis 通过内部辅助函数 appendTSFilter 处理这一点func appendTSFilter(args []interface{}, filterExpr []string) []interface{} { if len(filterExpr) 0 { return args } args append(args, FILTER) for _, f : range filterExpr { args append(args, f) } return args }运行示例三步下钻 两个边界场景官方示例位于 example/ts-querylabels/main.go演示了一个完整的仪表盘下钻流程。先创建 4 条带标签的时序作为种子数据series : map[string]map[string]string{ ts:temp:kitchen: {type: sensor, location: kitchen, unit: celsius}, ts:temp:bedroom: {type: sensor, location: bedroom, unit: celsius}, ts:hum:kitchen: {type: sensor, location: kitchen}, ts:build:count: {type: counter}, }注意ts:hum:kitchen故意没有unit标签ts:build:count只有type标签——它们正是用来演示边界行为的。第 1 步第一个下拉框——标签名labels, err : rdb.TSQueryLabels(ctx, []string{typesensor}).Result() fmt.Printf(label names for typesensor: %v\n, labels)过滤出所有typesensor的时序返回它们携带的标签名集合。示例输出label names for typesensor: [location type unit]两个关键点示例注释与源码注释一致返回无序且去重包含过滤条件本身用到的标签名这里就是type。第 2 步第二个下拉框——标签值locations, err : rdb.TSQueryLabelValues(ctx, location, []string{typesensor}).Result() fmt.Printf(locations for typesensor: %v\n, locations)在typesensor的时序组里查询location标签的所有取值。输出locations for typesensor: [bedroom kitchen]没有location标签的时序本组内不存在此情况但见第 3 步的unit对该集合不产生任何贡献。第 3 步第三个下拉框——时序键下钻到最终条件后用既有的TSQueryIndex拿具体时序键series, err : rdb.TSQueryIndex(ctx, []string{typesensor, locationkitchen}).Result() fmt.Printf(kitchen sensor series: %v\n, series)输出kitchen sensor series: [ts:hum:kitchen ts:temp:kitchen]至此标签名 → 标签值 → 时序键三级下钻完成。TSQueryIndex的实现见 timeseries_commands.go与TS.QUERYLABELS共用同一套过滤表达式语言。边界场景 A不存在的标签返回空集合而非错误missing, err : rdb.TSQueryLabelValues(ctx, rack, []string{typesensor}).Result() fmt.Printf(values of an absent label: %d\n, len(missing))没有任何匹配时序携带rack标签时返回空回复empty reply而不是错误输出0。这对仪表盘很关键某个标签值暂时为空时界面照常工作不会中断渲染。边界场景 B无过滤条件查询全部all, err : rdb.TSQueryLabels(ctx, nil).Result() fmt.Printf(label names, all series: %v\n, all)不传任何过滤器查询数据库内所有已建立索引的时序。示例输出label names, all series: [location type unit]注意这里没有rack等标签因为种子数据中根本不存在。运行方式与环境要求示例模块声明见 example/ts-querylabels/go.mod其中replace github.com/redis/go-redis/v9 ../..让示例直接指向仓库根目录的 go-redis 源码。运行前需要Redis 8.10并加载 TimeSeries 模块服务监听在localhost:6379代码中的redis.Options{Addr: localhost:6379}。进入示例目录后直接运行go run .预期输出集合内部顺序可能不同label names for typesensor: [location type unit] locations for typesensor: [bedroom kitchen] kitchen sensor series: [ts:hum:kitchen ts:temp:kitchen] values of an absent label: 0 label names, all series: [location type unit]示例代码在main开头先seed建时序、结束前rdb.Del(ctx, keys...)清理保证可重复运行。源码级剖析命令构造与线上格式两个方法的完整实现timeseries_commands.gofunc (c cmdable) TSQueryLabels(ctx context.Context, filterExpr []string) *StringSliceCmd { args : []interface{}{TS.QUERYLABELS, LABELS} args appendTSFilter(args, filterExpr) cmd : NewStringSliceCmd(ctx, args...) _ c(ctx, cmd) return cmd } func (c cmdable) TSQueryLabelValues(ctx context.Context, label string, filterExpr []string) *StringSliceCmd { args : []interface{}{TS.QUERYLABELS, VALUES, label} args appendTSFilter(args, filterExpr) cmd : NewStringSliceCmd(ctx, args...) _ c(ctx, cmd) return cmd }由此可以推断出实际发往服务器的参数序列调用生成的参数TSQueryLabels(ctx, nil)TS.QUERYLABELS LABELSTSQueryLabels(ctx, []string{typesensor})TS.QUERYLABELS LABELS FILTER typesensorTSQueryLabelValues(ctx, location, []string{typesensor})TS.QUERYLABELS VALUES location FILTER typesensorTSQueryLabelValues(ctx, location, nil)TS.QUERYLABELS VALUES location单元测试钉死 wire formattimeseries_querylabels_unit_test.go 中的TestTSQueryLabelsArgs用captureCmdable捕获命令参数逐条比对期望值重点验证了四件事FILTER令牌的省略规则nil与空切片都生成不带FILTER的参数labels_without_filter_omits_filter_token、labels_with_empty_filter_slice_omits_filter_token过滤器原样、有序传递[]string{typesensor, location!, l(a,b)}原样出现在参数中labels_filters_verbatim_and_orderedVALUES 形式同样遵循省略规则values_without_filter_omits_filter_token、values_with_filters标签名不做任何规范化TSQueryLabelValues(ctx, LoCaTiOn , nil)生成的参数就是TS.QUERYLABELS VALUES LoCaTiOn——前导与尾随空格、大小写原样保留values_label_not_normalized。最后这一点与集成测试中的字节精确匹配遥相呼应标签名按字节精确匹配Location与location是两个不同的标签。集成测试RESP2 / RESP3 下的行为验证timeseries_querylabels_integration_test.go 用 Ginkgo/Gomega 在 RESP2 与 RESP3 两种协议下各跑一遍通过SkipBeforeRedisVersion(8.10, ...)在低于 8.10 的服务器上自动跳过并用FlushDB 组内唯一过滤值test_groupquerylabels-test-respN隔离数据。它验证的行为与示例 README 的关键点一一对应标签名集合包含过滤条件中的标签名ConsistOf(test_group, location, unit)标签值集合去重、缺标签的时序不贡献location得到kitchen、bedroom两个值而只有部分时序携带的unit只得到celsius一个值不存在的标签返回空回复而非错误TSQueryLabelValues(ctx, no-such-label, ...)期望BeEmpty()标签名字节精确匹配查询Location大写变体得到空集合省略过滤器查询全部已索引时序在FlushDB后的空库中TSQueryLabels(ctx, nil)恰好返回本组三条时序的标签集合服务端过滤错误原样传播传一个只有非包含匹配test_group!...的过滤器时错误由服务器返回且包含TSDB:前缀——客户端不参与语法校验印证了过滤器原样透传的设计。这套测试同时覆盖了 RESP2 的数组回复与 RESP3 的集合回复说明两个方法在两种协议下的返回类型*StringSliceCmd都能正确解析。小结TS.QUERYLABELS是 Redis 8.10 TimeSeries 为仪表盘下钻场景补齐的关键拼图。go-redis 通过TSQueryLabels与TSQueryLabelValues两个方法封装了它的两种形式加上既有的TSQueryIndex可以构建出标签名 → 标签值 → 时序键的完整变量下钻流程。使用时要记住三点过滤器与TSQueryIndex/TSMRange共用同一套表达式语言并被原样透传回复是无序去重、包含过滤标签名的字符串集合标签缺失或查询无结果返回空回复而非错误因此仪表盘逻辑可以放心地把空当作合法的界面状态。想要深入验证行为可以阅读仓库中的 单元测试 与 集成测试想要直接跑起来观察输出参考 示例 README 与 main.go。赞分享后端数据库客户端缓存【免费下载链接】go-redisRedis Go client项目地址https://gitcode.com/GitHub_Trending/go/go-redis点击查看免费下载相关推荐告别混乱时序Grafana标签化注释查询实战指南告别混乱时序Grafana标签化注释查询实战指南 你是否还在为时间序列数据中的关键事件标记而烦恼当系统故障发生时能否快速定位到同时段的异常指标Grafa可观测性指标监控数据可视化告警日志分析后端前端Lightdash Data App drillDown() API 实战指南从点击行构建下钻查询Lightdash Data App drillDown API 实战指南从点击行构建下钻查询 drillDown 是 Lightdash Query SDK后端前端数据分析数据可视化人工智能AI AgentIT-Tools 加密解密实战HMAC 生成、JWT 解析与 RSA 密钥三个场景讲明白IT Tools 加密解密实战HMAC 生成、JWT 解析与 RSA 密钥三个场景讲明白 接口签名死活对不上却不知道是明文、密钥还是编码的问题拿到一串 J开发工具前端上一篇BootstrapCDN by jsDelivr下一篇【亲测免费】 cron-parser: 解析Unix Cron表达式的强大工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表