3、数据压缩原理:常见时序压缩算法(差值、旋转门、死区)的原理与成本收益
聊到时序索引的存储成本,压缩算法是绕不开的核心。说白了,时序数据天生就有规律——相邻点的值变化不大,时间戳间隔固定。如果我们能把这种“规律”利用起来,存储空间就能大幅缩减。
我最早接触时序压缩是在一个物联网项目中。当时每天要处理几十亿条传感器数据,原始存储方案撑了不到两周就爆了。嗯,从那以后我就开始认真研究压缩算法了。
3.1 为什么时序数据适合压缩?
先看一组典型数据:
| 时间戳 | 温度值 |
|---|---|
| 2024-01-01 00:00:00 | 25.3 |
| 2024-01-01 00:00:10 | 25.4 |
| 2024-01-01 00:00:20 | 25.3 |
| 2024-01-01 00:00:30 | 25.5 |
你看,温度值在 25.3 到 25.5 之间波动,时间戳间隔固定为 10 秒。如果直接存原始值,每个浮点数占 8 字节,时间戳占 8 字节,一条记录就是 16 字节。4 条数据就是 64 字节。
但如果我们只存“变化的部分”呢?
- 时间戳:只存起始时间 + 间隔(10秒),后续时间戳可以推算出来
- 数值:只存差值(+0.1, -0.1, +0.2),而不是完整浮点数
这就是压缩的基本思路——去掉冗余,只保留差异。
核心结论:时序数据的压缩率通常在 5:1 到 20:1 之间,具体取决于数据波动程度和算法选择。
3.2 差值编码(Delta Encoding)
差值编码是最基础的时序压缩算法。它的原理很简单:不存原始值,存当前值与上一个值的差。
举个例子:
原始值:25.3, 25.4, 25.3, 25.5, 25.6
差值: 25.3, +0.1, -0.1, +0.2, +0.1
第一个值作为基准,后续只存差值。为什么能省空间?因为差值通常很小,可以用更少的比特位来表示。
我在项目中遇到过一个问题:如果数据突然跳变,差值会变得很大。比如温度传感器故障,从 25.3 突然跳到 85.2,差值就是 59.9。这时候差值编码的优势就没了。
实战建议:差值编码适合平稳变化的数据(如温度、湿度、电压)。对于突发性数据(如告警、事件触发),建议配合其他算法使用。
差值编码的存储成本:
| 数据量 | 原始大小 | 差值编码后 | 压缩率 |
|---|---|---|---|
| 1000 条 | 16 KB | ~4 KB | 4:1 |
| 10000 条 | 160 KB | ~40 KB | 4:1 |
| 100000 条 | 1.6 MB | ~400 KB | 4:1 |
3.3 旋转门算法(Swinging Door)
旋转门算法是我个人比较喜欢的一种。它不追求精确还原,而是在允许的误差范围内,用最少的点来拟合原始曲线。
原理是这样的:
- 设定一个最大误差阈值 ε
- 从第一个点开始,画两条斜线(上边界和下边界)
- 后续的点如果落在两条斜线之间,就跳过不存
- 一旦有点超出边界,就把前一个点作为关键点保存,然后重新开始
你想想看,这就像一扇旋转门——门在误差范围内摆动,只有当数据要“撞门”了,才记录一次。
注意:旋转门算法是有损压缩。误差阈值 ε 决定了压缩率和精度之间的平衡。ε 越大,压缩率越高,但数据失真也越严重。
我曾经在一个风电项目中用过旋转门算法。风机振动数据每秒采集一次,一天就是 86400 条。用旋转门算法,误差阈值设为 0.5%,压缩率能达到 15:1。也就是说,一天的数据从 1.4 MB 压缩到不到 100 KB。
旋转门算法的成本收益对比:
| 误差阈值 | 压缩率 | 适用场景 |
|---|---|---|
| 0.1% | 3:1 ~ 5:1 | 高精度要求(如金融数据) |
| 0.5% | 8:1 ~ 15:1 | 工业监控(如温度、压力) |
| 1.0% | 15:1 ~ 30:1 | 趋势分析(如长期统计) |
| 5.0% | 30:1 ~ 50:1 | 粗略监控(如告警阈值判断) |
3.4 死区压缩(Deadband Compression)
死区压缩的思路更直接:如果当前值与上一个保存的值相差不大,就丢弃当前值。
具体做法:
- 设定一个死区范围 δ(比如 ±0.5)
- 保存第一个值作为基准
- 后续值如果与基准值的差在 δ 以内,就丢弃
- 一旦超出 δ,保存当前值,并更新基准
举个例子:
原始值:25.3, 25.4, 25.3, 25.5, 25.8, 25.9
死区 δ = 0.3
保存值:25.3, 25.5, 25.8
解释:25.4 和 25.3 与 25.3 的差都在 0.3 以内,丢弃
25.5 超出 0.3,保存并更新基准
25.8 超出 0.3,保存并更新基准
25.9 与 25.8 差 0.1,在死区内,丢弃
死区压缩的优点是实现简单,计算开销小。缺点也很明显——它只考虑数值变化,不考虑时间维度。如果数据在死区范围内缓慢漂移,你可能很长时间都看不到更新。
避坑指南:我曾经在一个水位监测项目中用了死区压缩,结果水位在死区范围内缓慢上升了 2 米,系统一直没记录。后来我加了一个“最大时间间隔”限制——即使数值没变化,超过一定时间也必须保存一次。
3.5 三种算法对比
我把三种算法的核心差异整理了一下:
| 特性 | 差值编码 | 旋转门 | 死区压缩 |
|---|---|---|---|
| 压缩类型 | 无损 | 有损 | 有损 |
| 计算复杂度 | 低 | 中 | 低 |
| 压缩率 | 2:1 ~ 5:1 | 5:1 ~ 50:1 | 3:1 ~ 20:1 |
| 适用数据 | 平稳变化 | 波动较大 | 缓慢变化 |
| 恢复精度 | 完全还原 | 误差可控 | 误差可控 |
| 实现难度 | 简单 | 中等 | 简单 |
3.6 算法选择建议
没有最好的算法,只有最合适的算法。我的经验是:
- 金融交易数据:必须用无损压缩,差值编码是首选
- 工业传感器:旋转门算法性价比最高,误差可控且压缩率高
- 环境监测:死区压缩够用,实现简单,维护成本低
- 混合场景:可以组合使用,比如先差值编码,再对差值做旋转门
最后说一句:压缩不是目的,成本控制才是。选算法的时候,一定要算清楚“压缩节省的存储成本”和“解压消耗的计算成本”之间的账。有时候省了 80% 的存储,却多花了 200% 的 CPU,得不偿失。