2.2 分区与压缩编码


2.2 分区与压缩编码

本节摘要:TSDB 按时间切分数据:Influx TSM 文件对应时间窗口,Timescale hypertable chunk 按 interval 自动分裂,Prometheus block 约 2h 一段,QuestDB 按 day/month 分区。压缩层常用 delta-of-delta 存时间戳、Gorilla 存浮点。本节对照四家分区策略与 retention 配置。

核心问题

阅读完本节,你应当能够:

  1. 解释 TSM 文件与时间窗口的对应关系
  2. 说明 Gorilla 压缩适用的数据分布
  3. 配置 Prometheus retention 与 Influx bucket RP

一、问题与直觉

时序数据 append-only + 时间局部性——查询 cpu[1h] 只需打开最近若干文件/chunk,而非全库扫描。若不分区,B+ 树随机 IO 会把 P99 查询拖垮。分区必须与 retention(保留策略) 绑定:监控常 15–90 天热数据,IoT 可能要 1 年温数据 + 对象存储冷归档。

二、核心原理

产品 分区单元 压缩亮点 保留配置
InfluxDB TSM ~1h 文件,列存 delta-of-delta + Gorilla bucket retention
Prometheus ~2h block XOR-Gorilla 变种 --storage.tsdb.retention.time
TimescaleDB hypertable chunk PG + 可选列压缩 retention policy job
QuestDB 按 day/month 列式 delta + LZ4 partition detach drop

delta-of-delta 与 Gorilla

时间戳列:相邻点时间间隔稳定时,对间隔再求差,多数值变小,位打包后体积骤降——Influx TSM 实测 整体压缩 1:10+

Gorilla(Facebook 2015 论文):浮点 XOR 前后值,Leading/trailing zero 多时用更少 bit;适合 缓慢漂移的 metrics(CPU、温度)。

用一个具体例子理解 Gorilla 为什么省空间。假设连续三个温度样本为 23.5、23.5、23.6,二进制上它们的前后差异很小:

样本1: 23.5 (完整双精度 64 bit) 样本2: 23.5 XOR = 0 → 只记录「全零」标记,几 bit 搞定 样本3: 23.6 XOR = 0x3f6... → 差异集中在末段,控制位压缩

对平滑序列,XOR 结果前导与后导零大量出现,Gorilla 用「控制位 + 变长存储」把平均开销压到 1–2 byte/样本;而随机跳变或阶跃信号会让 XOR 结果几乎全 1,压缩比显著下降。这就是「压缩算法吃平滑序列」的机制来源。

Prometheus block 与 compaction

Prometheus 每约 2 小时生成一个 block,block 内每个 series 的样本按时间排成 chunk;index 文件记录 series 与 chunk 的映射。后台 compaction 把 2h 小 block 合并成更大的 block(如 2h→6h→24h),过期 block 直接删除目录。这套机制让「查询最新数据」与「清理最旧数据」都是 O(文件数) 级别的操作。

TimescaleDB chunk 与 chunk exclusion

hypertable 把时间轴切成 chunk,每个 chunk 是一张底层 PG 表。time > now()-1h 查询会被 planner 用 chunk exclusion 排除掉时间范围外的 chunk,只扫命中的 1–2 个。创建 hypertable 与设置 chunk 间隔的 SQL 大致如下:

-- 创建超表并指定时间列与 chunk 间隔 CREATE TABLE metrics ( time timestamptz NOT NULL, device_id text, usage double precision ); SELECT create_hypertable('metrics', by_range('time', INTERVAL '1 day')); -- 查看当前 chunk SELECT chunk_schema, chunk_name, range_start, range_end FROM timescaledb_information.chunks WHERE hypertable_name = 'metrics';

QuestDB 分区与保留

QuestDB 支持 PARTITION BY DAYPARTITION BY MONTH,数据按分区目录存放,删除旧数据即 detach 或 drop 分区目录,代价远低于逐行删除。其 ingest 路径批量写 WAL 再异步 merge,适合 百万 points/s 级 ingest。

02-02-fig01-4

02-02-fig01-4

02-02-fig01-4

三、工程实践要点

策略 适用 注意
短 retention + Remote Write 边缘 Prometheus 长期查 Thanos/VM
降采样 continuous aggregate Timescale 长周期 保留 raw 与 agg 两套
ZSTD vs 默认压缩 VictoriaMetrics 类 写入吞吐 -37%,查询 P99 -62% 的权衡
对象存储冷层 合规 7 年 跨层查询延迟上升

三家 retention 配置对照

# Prometheus:命令行参数控制本地保留 prometheus --storage.tsdb.retention.time=15d \ --storage.tsdb.retention.size=50GB # InfluxDB 2.x:bucket 的保留规则(保留 30 天) influx bucket create --name metrics30d --retention 30d
-- TimescaleDB:为超表注册保留策略任务 SELECT add_retention_policy('metrics', INTERVAL '90 days');

WAL 与刷盘:时序库常 ack 仅表示进 WAL,异步 Flush Daemon 按水位/时间刷盘——用户 204 返回 ≠ 已 fsync 落盘,RPO 取决于 WAL 策略。

分层保留的常见形态

生产上最常见的分层不是两套引擎,而是同一引擎内的多级保留:原始数据 90 天、小时聚合 2 年、日聚合 5 年。Timescale 用 continuous aggregate + 两张 retention policy 实现;Prometheus 系用 recording rule 输出到单独 metric 并给不同 retention;Influx 用 task 写进不同 bucket。核心原则只有一条:raw 与 agg 并存,各有生命周期,查询按粒度路由

一个磁盘估算实例

用前面表格里的数据做一个粗算:假设 1M samples/s 的写入速率、压缩后每样本约 1.5 byte、压缩比约 10(平滑指标),保留 90 天:

磁盘/天 ≈ (1,000,000 × 1.5 × 86400) / 10 ≈ 12.96 GB/天 保留 90 天 ≈ 1.17 TB

如果保留 2 年则接近 9.5 TB。这个数量级在采购时可以直接用来反推:是接受 10 倍压缩比的小容量机型,还是用更短的原始保留 + 降采样来省盘。注意索引、WAL 与压缩中间文件通常还要再预留 20–30% 的 headroom。

⚠️ 常见坑:retention 只删数据不删 series 元数据——高 cardinality 下索引仍膨胀,需 metric_relabel 或标签治理。

💡 关键直觉:压缩算法吃「平滑序列」;突发阶跃或稀疏传感器,压缩比会显著下降,要预留磁盘 headroom。

自测题

  1. 为什么「时间范围查询只需打开最近若干文件」?分区在这里起什么作用?
  2. Gorilla 对一条剧烈抖动的曲线压缩比会怎样?为什么?
  3. retention 删除数据后,高基数场景下索引为什么还在膨胀?

补充一个记忆锚点:把分区想象成「按时间切好的抽屉」,查询只抽需要的抽屉;把压缩想象成「抽屉里的打包」,平滑的数据打包率高。两者共同决定查询快慢与磁盘开销,缺一不可。

要点串联

  • 分区:按时间切 TSM/chunk/block
  • delta-of-delta:时间戳列压缩
  • Gorilla:浮点 XOR 压缩
  • Retention:与分区耦合的生命周期
  • 冷热分层:热 SSD + 冷对象存储是常态

下一章进入查询层:窗口聚合与 PromQL/SQL 方言对照。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U