时序数据降采样与保留策略:自动聚合与冷热分层,让存储成本下降80%

📅 2026/7/22 0:42:45 👁️ 阅读次数 📝 编程学习
时序数据降采样与保留策略:自动聚合与冷热分层,让存储成本下降80%

时序数据降采样与保留策略:自动聚合与冷热分层,让存储成本下降80%

一、三年前的秒级数据从来没人看,但它占了80%的存储空间

某光伏电站监控系统存储了5年的数据——近1亿块光伏板的电流、电压、功率,每秒一条,累计数据量2.3PB。运维团队分析发现:过去3个月的秒级数据被查询了95%的请求,近1年的5分钟级聚合数据覆盖了剩余4.9%,而3年前的秒级数据在过去一年中只被访问过3次——全部是因为监管抽查。这意味着80%的存储空间服务于0.001%的查询需求。

降采样的核心思想很简单:数据越老,精度越低。最新的秒级数据保留完整精度,1周后降采样到1分钟级,1个月后降到1小时级,1年后仅保留每日统计值。这个策略在存储和查询能力之间找到了工程上的最优平衡。

二、多级聚合的金字塔模型

金字塔每上升一层,数据量减少约60倍(1分钟粒度从60个秒级点变为1个点),查询速度提升约60倍,但信息量也减少了——1分钟的AVG无法还原该分钟内出现的瞬间尖峰。

保留策略的关键参数是每层的数据保留时长聚合函数选择。保留时长由业务查询模式和监管要求共同决定。聚合函数的选择取决于分析需求——如果需要峰值分析就保留MAX/MIN,如果只需要趋势就保留AVG即可。

三、基于ClickHouse物化视图的自动降采样实现

-- 原始秒级数据表 CREATE TABLE raw_sensor_data ( device_id String, metric_name String, value Float64, timestamp DateTime64(3) ) ENGINE = MergeTree() PARTITION BY toYYYYMMDD(timestamp) ORDER BY (device_id, metric_name, timestamp) TTL timestamp + INTERVAL 7 DAY; -- 7天后自动删除 -- 1分钟聚合物化视图 CREATE MATERIALIZED VIEW sensor_1min ENGINE = AggregatingMergeTree() PARTITION BY toYYYYMM(timestamp_1min) ORDER BY (device_id, metric_name, timestamp_1min) TTL timestamp_1min + INTERVAL 90 DAY AS SELECT device_id, metric_name, toStartOfMinute(timestamp) AS timestamp_1min, avgState(value) AS avg_value, maxState(value) AS max_value, minState(value) AS min_value, countState() AS sample_count, sumState(value) AS total_value FROM raw_sensor_data GROUP BY device_id, metric_name, timestamp_1min; -- 1小时聚合(从1分钟聚合表再做聚合) CREATE MATERIALIZED VIEW sensor_1hour ENGINE = AggregatingMergeTree() PARTITION BY toYYYYMM(timestamp_1hour) ORDER BY (device_id, metric_name, timestamp_1hour) TTL timestamp_1hour + INTERVAL 365 DAY AS SELECT device_id, metric_name, toStartOfHour(timestamp_1min) AS timestamp_1hour, avgMergeState(avg_value) AS avg_value, maxMergeState(max_value) AS max_value, minMergeState(min_value) AS min_value, countMergeState(sample_count) AS sample_count, sumMergeState(total_value) AS total_value FROM sensor_1min GROUP BY device_id, metric_name, timestamp_1hour;
# 降采样TTL策略配置 def generate_retention_policy(data_retention_config: dict) -> str: """根据业务需求生成降采样配置""" policies = [] for layer, config in data_retention_config.items(): retention = config.get('retention_days', 30) granularity = config.get('granularity', '1h') aggregations = config.get('aggregations', ['avg', 'max', 'min']) policies.append({ 'name': f'sensor_{granularity}', 'retention': retention, 'granularity': granularity, 'agg_functions': aggregations, }) # 按粒度从细到粗排序 granularity_order = {'1s': 0, '1min': 1, '5min': 2, '15min': 3, '1h': 4, '1d': 5} policies.sort(key=lambda p: granularity_order.get(p['granularity'], 99)) return policies

ClickHouse的TTL功能原生支持自动数据过期——TTL timestamp + INTERVAL 7 DAY会在后台自动清理7天前的数据分区。结合物化视图,每一层的数据在过期前已经被聚合到下一层,形成完整的降采样链。整个过程完全自动化,无需外部调度任务。

四、降采样聚合函数的选取:AVG丢失峰值,MAX/MIN丢失分布

降采样最大的信息损失不在于精度下降,而在于统计特征的丢失。一个小时内AVG温度=25°C——这个值可能是"全程稳定在25°C",也可能是"30分钟40°C+30分钟10°C的平均值"。前者正常,后者可能代表严重故障(冷却系统间歇性失效)。仅保留AVG无法区分这两种情况。

多统计量保留策略是最务实的解法——每个聚合窗口同时保留AVG、MAX、MIN、STDDEV和P95,五项统计量能够还原大部分数据分布信息。存储开销是只存AVG的5倍,但对于故障排查场景价值是100倍——瞬间尖峰不会在降采样后消失。如果存储成本敏感,至少保留AVG+MAX+MIN三项。

五、总结

降采样和保留策略是原始存储成本的10倍优化手段。多级聚合金字塔将80%的历史数据以90%的压缩率存储,仅在查询要求的精度范围内保留原始数据。ClickHouse的物化视图+TTL提供了一套"零运维"的自动降采样方案。降采样不是简单的"只保留平均值"——MAX/MIN/STDDEV等统计量对于故障排查的价值远超其存储成本。最容易被忽略的细节是:降采样窗口的边界对齐(整点开始而非从第一条数据开始)对于跨系统数据对齐至关重要。