时序数据压缩:delta-of-delta、XOR 浮点编码与十进制数据上的失效
内容提要
Gorilla的XOR压缩在时间戳等间隔、值不变或缓变时可达每点1.37字节,但毫秒抖动和十进制小数会使其失效。对比Prometheus、InfluxDB、VictoriaMetrics的实现,时间戳对齐比细分桶更有效;XOR在十进制数据上每值需50多比特,Chimp、ALP等后续工作通过十进制编码改善。
延伸解读
时间戳对齐比细分桶更有效
文章通过实验表明,在编码前将时间戳对齐到理想网格,能大幅减少非零delta-of-delta,从而降低时间戳编码开销。对齐后,非零D_i减少了80%,Gorilla和Prometheus XOR的每时间戳比特数分别从2.00和2.88降至1.20和1.38。相比之下,仅细化分桶对毫秒抖动带来的非零D_i改善有限,因为最小非零档仍需16比特。因此,对于抓取间隔固定的监控系统,优先实现时间戳对齐是更有效的优化手段。
XOR编码在十进制数据上的失效与改进
XOR编码利用浮点比特模式的高位和低位稳定性,但对十进制小数(如温度、价格)效果很差。因为十进制小数在二进制中尾数低位随机,异或后尾随零很少,有效位常达40-50位,导致每个值需50多比特。后续工作如Chimp、Chimp128和ALP通过不同策略改善:Chimp128在少量小数位时表现较好,ALP则采用十进制编码,每增加一位小数约增加3.3比特,更符合信息量。
生产实现的差异与选择考量
Prometheus、InfluxDB和VictoriaMetrics在压缩实现上各有特点。Prometheus的XOR2格式合并时间戳和值编码,在短块(如120样本)时比XOR节省6%-7%,但块超过127样本时因ST头处理反而可能增加开销。InfluxDB的时间戳列使用一阶差分加RLE或Simple-8b,适合等间隔数据;其值编码存在前导零掩码bug,导致大整数计数器压缩率下降。VictoriaMetrics先将浮点转十进制整数再zstd压缩,对监控数据高效但有损(超过12位有效数字可能精度降低)。选择时需权衡
Q&A
Gorilla 的 XOR 压缩在什么条件下能达到每点 1.37 字节?
Gorilla 论文报告 Facebook 生产数据平均压到每个点 1.37 字节,但前提是时间戳近乎等间隔,值要么不变、要么是小整数或变化缓慢的量。
为什么 XOR 编码在十进制小数数据上会失效?
十进制小数如 20.37、3.99 存成 float64 时尾数是无限循环二进制小数截断后的 52 位,低位几乎随机。相邻值哪怕只差 0.01,异或的尾随零也接近 0,有效位常达 40 到 50 位,加上控制位每个值需 45 到 55 比特,比不压缩好不了多少。
Prometheus 如何解决抓取时间戳抖动问题?
Prometheus 在编码前通过 AlignScrapeTimestamps 默认开启,若实际时间与理想网格偏差不超过 ScrapeTimestampTolerance(2 ms 且不超过间隔的 1%),就把样本时间戳改写成网格时间。这能把非零 delta-of-delta 减少 80%,比换更细的分桶更有效。
VictoriaMetrics 如何压缩浮点数据?
VictoriaMetrics 不用 XOR,而是先把 float64 转成 int64 尾数加公共十进制指数,再按内容选类型:全相同用 Const,等差用 DeltaConst,像计数器用二阶最近差分 ZSTDNearestDelta2,否则用一阶 ZSTDNearestDelta,差分经 zigzag 变长整数后交给 zstd。节点数据上每值 4.10 比特,比 Gorilla 的 6.81 少 40%。
Chimp、ALP 等后续工作对 XOR 编码做了哪些改进?
Chimp 和 Chimp128 改进了 XOR 编码,在十进制数据上表现更好,例如 d≤2 时 Chimp128 约 18-22 比特。ALP 则按十进制思路编码,随小数位数平滑增长,每位小数约 3.32 比特,在真实数据集上常优于 XOR。
InfluxDB 的浮点编码存在什么缺陷?
InfluxDB v1.13.1 的浮点编码中,前导零钳位代码先做 leading &= 0x1F 再判断 >= 32,导致判断永远不成立。L∈[32,63] 被存成 L-32,解码端多读、编码端多写 32 位高位零,结果无损但浪费。在节点数据上,这使每值比特数从 6.81 增加到 10.63,增加 56%。