数据生命周期的本质
任何一个运行超过一年的工业数据库,都会面临同一个问题:原始点数据按秒级或毫秒级采集,但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的团队,建议先用一条业务线做试点,把聚合链的每一级都跑通、验证完数据质量后再推广。时序数据库的真正价值,不在于能写入多少原始点,而在于能为不同精度的查询场景提供合适的数据视图。

























