本节摘要:TSDB 按时间切分数据:Influx TSM 文件对应时间窗口,Timescale hypertable chunk 按 interval 自动分裂,Prometheus block 约 2h 一段,QuestDB 按 day/month 分区。压缩层常用 delta-of-delta 存时间戳、Gorilla 存浮点。本节对照四家分区策略与 retention 配置。
阅读完本节,你应当能够:
时序数据 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 |
时间戳列:相邻点时间间隔稳定时,对间隔再求差,多数值变小,位打包后体积骤降——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 每约 2 小时生成一个 block,block 内每个 series 的样本按时间排成 chunk;index 文件记录 series 与 chunk 的映射。后台 compaction 把 2h 小 block 合并成更大的 block(如 2h→6h→24h),过期 block 直接删除目录。这套机制让「查询最新数据」与「清理最旧数据」都是 O(文件数) 级别的操作。
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 支持 PARTITION BY DAY 或 PARTITION BY MONTH,数据按分区目录存放,删除旧数据即 detach 或 drop 分区目录,代价远低于逐行删除。其 ingest 路径批量写 WAL 再异步 merge,适合 百万 points/s 级 ingest。

| 策略 | 适用 | 注意 |
|---|---|---|
| 短 retention + Remote Write | 边缘 Prometheus | 长期查 Thanos/VM |
| 降采样 continuous aggregate | Timescale 长周期 | 保留 raw 与 agg 两套 |
| ZSTD vs 默认压缩 | VictoriaMetrics 类 | 写入吞吐 -37%,查询 P99 -62% 的权衡 |
| 对象存储冷层 | 合规 7 年 | 跨层查询延迟上升 |
# 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。
补充一个记忆锚点:把分区想象成「按时间切好的抽屉」,查询只抽需要的抽屉;把压缩想象成「抽屉里的打包」,平滑的数据打包率高。两者共同决定查询快慢与磁盘开销,缺一不可。
下一章进入查询层:窗口聚合与 PromQL/SQL 方言对照。