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,得不偿失。

时序压缩算法选择决策流程 原始时序数据 是否需要无损还原? 是 否 差值编码 数据波动特征? 平稳 波动大 死区压缩 旋转门算法 选择依据:数据精度要求 × 波动特征 × 计算资源 → 最优压缩策略 实际项目中常组合使用,例如:差值编码 + 旋转门二次压缩