“每次刷仪表盘都要重新算一次过去一年的日均值”“同一个聚合查询被反复问,每次都要扫一遍原始数据”——这是时序数据库里非常典型的性能痛点。这篇文章介绍 TDengine 用来解决这个问题的核心手段:TSMA(时间范围小型预聚合),并核实了它和传统"SMA 索引"概念之间的关系,避免用户查资料时被两个相似的名字搞混。
先说清楚:TSMA 和传统意义上的 SMA 索引不是一回事
TDengine 文档和语法里同时出现过CREATE SMA INDEX和CREATE TSMA两种写法,容易让人以为是同一个东西的两种写法。经过源码核实,这是两个不同阶段的设计:早期版本里,块级 SMA 索引的执行代码在当前代码库中已经被整体禁用(相关源码文件被完整包裹在编译屏蔽区域内,且转换层里对应的处理分支目前会直接报错),也就是说这条老路径在当前版本实质上已经不再是可用功能。真正在用、并且被规划器实际利用的,是TSMA(Time-Range Small Materialized Aggregates,时间范围小型预聚合)。
建议:如果你在旧资料里看到CREATE SMA INDEX的用法,不要直接照搬到新版本上使用,应该用CREATE TSMA这套语法。
TSMA 解决的具体问题:按时间窗口提前算好聚合结果
TSMA 的思路很直接:用户指定一个函数列表(比如AVG、SUM、COUNT)和一个时间窗口(比如 1 小时),TDengine 会在数据写入的同时,持续按这个时间窗口预先算好聚合结果并存下来。之后如果用户的查询恰好命中这个时间窗口和函数组合,查询优化器会自动改写查询计划,直接读预聚合好的结果,而不是重新扫描原始明细数据。
几个实际使用中需要注意的边界:
- 一个集群默认最多可以创建 10 个 TSMA,不是无限叠加的,需要挑选真正高频、重复率高的聚合模式来建。
- 时间窗口的取值范围大致是从 1 分钟到 1 年(或对应的月粒度)之间,不支持任意粒度。
- TSMA 支持"递归"建立——可以在一个已有的 TSMA 基础上,再建一个更粗粒度的 TSMA(比如已经有按小时聚合的 TSMA,再建一个按天聚合的 TSMA 直接复用小时级结果),不需要每次都从原始数据重新算。
- TSMA 的计算逻辑底层是通过 TDengine 的流计算机制实现的,所以创建一个 TSMA 会占用一个流计算任务的名额,规划的时候要把这个成本算进去。
什么时候该用 TSMA,什么时候不该用
适合用的场景:固定的、高频重复的聚合模式,比如仪表盘上"每小时平均能耗""每天设备在线率"这类查询,每次都是同样的聚合函数加同样的时间粒度,只是查询的时间范围在变——这正是 TSMA 命中率最高的场景。
不太适合的场景:如果每次查询的聚合函数、分组维度都在变(比如临时的探索式分析),TSMA 提前算好的固定聚合模式很难命中,这类场景更适合直接优化原始查询或者靠合理的VGROUPS规划来提升扫描效率,而不是指望 TSMA。
小结
如果你的业务里有固定套路的聚合查询在反复被问,且当前查询延迟已经影响用户体验,先看看能不能用 TSMA 把这部分计算挪到写入侧提前做完。建库前想清楚要覆盖的时间粒度组合,避免超过默认的 TSMA 数量上限后来回调整。