多级聚合架构:时序数据的自动降采样实践

尔悦

2026-10-23 /

数据生命周期的本质

任何一个运行超过一年的工业数据库,都会面临同一个问题:原始点数据按秒级或毫秒级采集,但99%的用户查询关注的是分钟、小时、天甚至月粒度的趋势。原始数据的存储成本随时间线性增长,但访问频率随粒度放大而指数衰减。

这就引出了一个设计原则:原始点数据需要高保真存储用于故障追溯和精确计算,聚合数据则是日常监控和报表的主体。两者不应共存于同一存储层,也不应该由应用层临时计算——计算开销和存储浪费都不可接受。

聚合链的设计方法

一条完整的聚合链,需要回答三个核心问题:聚合函数是什么、窗口大小如何设定、以及聚合节点之间的依赖关系。时序数据库的连续查询引擎是整个聚合链的执行载体,其调度能力直接决定了链的稳定性。

聚合层级

输入粒度

窗口函数

典型保留期

输出粒度

L0 原始

100ms/1s

—

30~90天

原始点

L1 分钟级

L0

mean / max / min / stddev

180天

1分钟窗口

L2 小时级

L1

mean / max / min / p95

365天

1小时窗口

L3 天级

L2

mean / max / min / sum

5年

24小时窗口

L4 月级

L3

mean / sum / max

10年+

30天窗口

聚合函数的选择不能简单复制上一层。例如L1分钟级需要保留stddev来反映波动特征,L2小时级保留p95来反映极端值倾向,L3天级则重点在sum来反映累计量。TDengine的连续查询允许为每个粒度独立指定聚合函数集合。

-- L1:分钟连续查询
CREATE CONTINUOUS QUERY cq_minute
RESAMPLE INTERVAL(1m) ALIGNMENT(START)
INSERT INTO sensor_minute
SELECT ts, avg(val) as mean_val, max(val) as max_val, 
       min(val) as min_val, stddev(val) as stddev_val
FROM sensor_raw
GROUP BY ts, tag_sensor;

-- L2:小时连续查询(从L1读取,减少计算量)
CREATE CONTINUOUS QUERY cq_hour
RESAMPLE INTERVAL(1h) ALIGNMENT(START)
INSERT INTO sensor_hour
SELECT ts, avg(mean_val) as avg_val, max(max_val) as peak_val,
       avg(stddev_val) as volatility
FROM sensor_minute
GROUP BY ts, tag_sensor;

连续查询的依赖与调度

多级连续查询之间存在数据依赖:L2必须等待L1完成对应时间窗口的聚合后才能启动计算。如果直接从原始表计算每一级,不仅重复计算,还会造成I/O放大。TDengine的调度引擎内置了依赖解析——当连续查询的输入是另一个连续查询的输出表时,自动建立DAG依赖,按拓扑序执行。

需要注意的边界情况:当某一级连续查询因为系统负载过高而延迟执行时,下游依赖的连续查询也会被阻塞。实践中应设置最大执行延迟阈值,超过阈值时触发告警,同时允许下游连续查询在缺失上游数据时使用次优路径(直接从原始表计算)。

另一个常见陷阱是时区对齐。连续查询的时间窗口默认使用UTC对齐,而业务可能需要本地时区的自然日边界。TDengine允许在连续查询定义中指定ALIGNMENT(TIME_ZONE 'Asia/Shanghai'),但代价是跨时区部署时查询性能会下降5%~15%。

存储分层的硬件选型

不同粒度的数据对硬件的需求完全不同。原始点数据写入密集、随机写多,需要NVMe SSD;分钟级聚合读多写少、顺序访问,可以用SATA SSD;天级及以上几乎只读、访问频率低,归档到HDD或对象存储即可。时序数据库的存储分层能力,直接决定了长期数据留存的成本天花板。

存储层

介质

典型规格

单位存储成本

访问模式

热层

NVMe SSD

每节点2~4TB,读3GB/s,写2GB/s

¥3~5/GB

随机写密集、热查询

温层

SATA SSD

每节点8~16TB,读500MB/s

¥0.5~1/GB

顺序读写、近实时查询

冷层

HDD/对象存储

归档节点20~40TB

¥0.05~0.1/GB

冷数据查询、批量导出

TDengine自动分层存储功能可以按时间阈值将数据从热层转移到温层、再从温层转移到冷层。查询时,引擎根据查询的时间范围判断需要扫描哪些层,并对冷层查询施加速率限制,避免影响热层性能。

查询路由的智能匹配

多级聚合架构的核心价值在于查询路由——当用户查询”过去7天的温度趋势”时,系统应该自动选择天级或小时级聚合表,而不是扫描数十亿条原始记录。

路由策略有两种流派:显式路由要求应用在查询时指定粒度参数,隐式路由由数据库引擎自动判断。TDengine选择后者,引擎维护一张粒度选择表,根据查询的时间跨度和精度要求自动匹配最优粒度:

查询时间跨度

自动选择粒度

预估加速比

≤ 1小时

L0 原始点

基准

1小时 ~ 1天

L1 分钟级

10~100×

1天 ~ 30天

L2 小时级

100~1000×

30天 ~ 1年

L3 天级

1000~10000×

> 1年

L4 月级

>10000×

如果用户在查询中指定了精确到分钟的精度要求,即使时间跨度很大,引擎也会优先选择分钟级聚合或降级到原始点。

时间窗口对齐的坑

聚合计算中的时间窗口对齐是一个容易被忽略的细节。假设原始数据的时间戳分布为10:00:00.001、10:00:00.502、10:00:00.998,要按1分钟窗口做聚合,窗口的起始边界选在哪里?如果选0:00对齐(即10:00:00.000~10:00:59.999),那这三个点会落在同一个窗口里;如果选任意起始时间,结果可能不同。

TDengine默认使用ALIGNMENT(START)模式,窗口从min(timestamp) - floor((min(timestamp) - EPOCH) / interval) * interval计算得到。也可以指定ALIGNMENT(SLIDE)做滑窗聚合,用于趋势预测场景。

一个真实案例:某电力公司在做故障录波数据聚合时,因为连续查询窗口对齐方式与业务预期不一致,导致录波起止点的时间差偏差约500ms,影响了故障分析报告的准确性。修复方式是在连续查询定义中显式指定ALIGNMENT(TIME_ZONE 'Asia/Shanghai')并固定窗口边界。

结语

多级聚合架构不是一个独立的技术优化,而是整个时序数据管理体系的骨架。聚合链的设计、连续查询的调度、存储分层的硬件、以及查询路由的策略——这四个环节需要协同设计,任何一个环节的短板都会放大到整个数据通路的末端。对于正在评估或已在使用TDengine的团队,建议先用一条业务线做试点,把聚合链的每一级都跑通、验证完数据质量后再推广。时序数据库的真正价值,不在于能写入多少原始点,而在于能为不同精度的查询场景提供合适的数据视图。