Prometheus 存储层深度解析:本地 TSDB 布局、压缩策略、远程存储集成与数据回填
本文以 docs/storage.md 为骨架,结合当前仓库源码,系统讲解 Prometheus 本地时序数据库(TSDB)的磁盘布局、WAL 机制、压缩与保留策略、远程读/写集成,以及基于 OpenMetrics 与 Recording Rules 的数据回填方案。读完后你可以独立完成 TSDB 容量规划、保留策略配置、损坏恢复判断,以及跨系统数据迁移的完整实操。
一、本地存储:TSDB 的磁盘布局
Prometheus 的本地时序数据库将数据以自定义的高效格式存储在本机磁盘上。理解其物理布局是后续容量规划与故障恢复的基础。
1.1 两小时 Block 的结构
摄入的样本(sample)会被分组为两小时(2h)的 Block。每个 Block 是一个目录,包含:
chunks/子目录:该时间窗口内所有时间序列的样本数据;meta.json:块元数据;index文件:将指标名与标签映射到chunks目录中的时间序列;tombstones:删除记录文件。
chunks 目录内的样本被组织为一个或多个 segment 文件,每个默认最大 512 MB。当通过 API 删除时间序列时,删除操作并不会立即从 chunk 段中移除数据,而是先写入独立的 tombstone 文件,由后续压缩(compaction)统一清理——这是典型的"标记删除 + 后台重写"设计。
当前正在写入的 Block(Head)常驻内存,尚未完全落盘。它依靠**预写日志(WAL)**抵御崩溃:重启时可重放 WAL 恢复数据。WAL 文件存放在 wal 目录中,segment 为 128MB。由于 WAL 保存的是尚未压缩的原始数据,其体积显著大于常规 block 文件;Prometheus 至少保留 3 个 WAL 文件,高流量实例可能保留更多,以保证至少覆盖两小时的原始数据。
一个典型的 Prometheus 数据目录如下(引自 docs/storage.md):
./data
├── 01BKGV7JBM69T2G1BGBGM6KB12
│ └── meta.json
├── 01BKGTZQ1SYQJTR4PB43C8PD98
│ ├── chunks
│ │ └── 000001
│ ├── tombstones
│ ├── index
│ └── meta.json
├── 01BKGTZQ1HHWHV8FBJXW1Y3W0K
│ └── meta.json
├── 01BKGV7JC0RY8A6MACW02A2PJD
│ ├── chunks
│ │ └── 000001
│ ├── tombstones
│ ├── index
│ └── meta.json
├── chunks_head
│ └── 000001
└── wal
├── 000000002
└── checkpoint.00000001
└── 00000000
目录名是 ULID 格式的块标识符;chunks_head/ 是内存 Head 中已 m-map 落盘的 chunk;checkpoint.* 是 WAL 检查点,用于截断旧 WAL 段。
文件级别的格式细节(index、chunks、tombstones、WAL 的二进制结构)可参阅仓库中的 TSDB 格式文档,其下分为 index.md、chunks.md、head_chunks.md、tombstones.md、wal.md 与 memory_snapshot.md。
1.2 单节点局限与备份建议
本地存储的限制是没有集群化与复制,不能随意扩展,也难以抵御磁盘或节点故障,应像管理任何单节点数据库一样管理它;在合理架构下,本地存储仍可保留多年数据。
- 备份推荐使用 Snapshot:直接拷贝数据目录而不走 snapshot,可能丢失自上一个 TSDB block 创建以来(通常每 2 小时一次)的最新数据,覆盖最近约 3 小时的样本。备份/恢复时排除 WAL 文件(
chunks_head/、wal/、wbl/目录)可以保证数据一致性,代价是丢失 WAL 所覆盖的时间范围。Snapshot 的使用方式见 查询 API 文档的 snapshot 章节。 - 或者通过 remote read/write API 使用外部存储(见下文第四节),但需要针对持久性、性能与效率仔细评估。
二、压缩(Compaction)与保留策略
2.1 后台压缩如何工作
初始的两小时 block 最终会在后台被压缩为更长的 block。压缩会创建跨度最多为**保留时间的 10% 或 31 天(取较小者)**的新 block。由于源块与新压缩块必须同时在磁盘上共存,磁盘占用会短暂超过 storage.tsdb.retention.size 的设定值;当下一次保留清理删除源块后,多余空间即被释放。
这一机制在源码中可以印证:主程序在初始化时会根据保留时间计算最大块时长上限,即 maxBlockDuration = retentionDuration / 10(见 cmd/prometheus/main.go 中 retention 解析段落)。
2.2 关键配置项
本地存储的核心配置项及其含义(继承自 docs/storage.md,并结合当前仓库源码更新):
| 配置 | 说明 | 默认值 |
|---|---|---|
--storage.tsdb.path |
Prometheus 写入数据库的基路径 | data/ |
--storage.tsdb.retention.time |
样本保留时长。与 size 都未设置时默认 15d。支持单位:y, w, d, h, m, s, ms |
15d |
--storage.tsdb.retention.size |
存储块的最大字节数,最旧数据优先删除。支持单位:B, KB, MB, GB, TB, PB, EB(2 的幂,1KB=1024B),如 512MB。0 表示禁用。只有持久化 block 会被删除以满足该限制,但 WAL 与 m-map chunk 会计入总大小,因此磁盘最低需求 = wal(WAL+Checkpoint)与 chunks_head 峰值之和(每 2 小时出现一次峰值) |
0(禁用) |
--storage.tsdb.wal-compression |
启用 WAL 压缩。视数据而定,WAL 体积大约可减半,额外 CPU 开销很小。2.11.0 引入,2.20.0 起默认开启;一旦启用,降级到 2.11.0 之前的版本需要删除 WAL | 按版本默认 |
重要现状说明(以当前仓库为准):在 cmd/prometheus/main.go 中,storage.tsdb.retention.time 与 storage.tsdb.retention.size 两个命令行 flag 已标记为 [DEPRECATED],官方建议改用配置文件中的 storage.tsdb.retention.time / storage.tsdb.retention.size 字段;配置文件顶层的 storage: tsdb: 段落(见 configuration.md 存储配置 的 storage 部分)同时新增了按容量百分比保留的能力(storage.tsdb.retention.percentage 指定后,size 限制会被忽略,见 cmd/prometheus/main.go 中的告警逻辑)。
此外,从 flag 定义可以看出本地存储还有若干与本篇主题相关的高级开关,例如:
--storage.tsdb.wal-segment-size(10MB–256MB 之间):调整 WAL 段大小;--storage.tsdb.allow-overlapping-compaction:是否允许重叠块的垂直压缩;--storage.tsdb.block-reload-interval:检查新增/移除 block 的间隔,手动回填或删除 block 后需等待至多该时长才生效——这与第五、六节手动搬移 block 的操作直接相关。
2.3 容量规划公式
Prometheus 平均每个样本只占 1–2 字节(得益于系列内样本压缩,尤其是 XOR 编码),因此容量规划可用近似公式:
needed_disk_space = retention_time_seconds * ingested_samples_per_second * bytes_per_sample
若要降低样本摄入速率,可以:减少抓取的时间序列数量(更少的 target,或每个 target 更少的 series),或增大抓取间隔。其中减少序列数量往往更有效,因为同一序列内的样本会被压缩。
2.4 保留大小右-sizing(Right-Sizing)
若使用 storage.tsdb.retention.size 设置容量上限,应使其相对于为 Prometheus 分配的磁盘留出缓冲,确保旧数据在磁盘写满之前先被清除。官方建议:
保留大小最多设为已分配磁盘空间的 80–85%。剩余 15–20% 的缓冲用于覆盖压缩进行中所需的临时空间(源块与新压缩块同时驻留磁盘,见上文 2.1)。
2.5 过期清理与损坏恢复
- 若同时指定时间与大小两种保留策略,先触发者生效;
- 过期 block 的清理在后台进行,最长可能需要两小时才会移除过期块,且块必须完全过期才会被删除;
- 若本地存储损坏到 Prometheus 无法启动:先备份存储目录,再从备份恢复损坏的 block 目录;没有备份时最后手段是删除损坏文件(逐个 block 目录或 WAL 文件),代价是丢失相应时间范围的数据;
- CAUTION:非 POSIX 文件系统不受支持,可能造成不可恢复损坏;NFS(包括 AWS EFS)不受支持——NFS 理论上可以符合 POSIX,但多数实现并不。强烈建议使用本地文件系统。
三、远程存储集成:Remote Read / Write
本地存储受限于单节点的可扩展性与持久性。Prometheus 没有在自身内部解决集群化存储,而是提供一组接口以集成外部存储系统。
3.1 四种集成方式
Prometheus 与远程存储的集成有四种形态:
- 将摄入的样本以 Remote Write 格式写往远程 URL;
- 以 Remote Write 格式接收其他客户端发来的样本;
- 以 Remote Read 格式从远程 URL 读回样本数据;
- 以 Remote Read 格式向客户端返回其请求的样本数据。
读/写协议均为 snappy 压缩的 Protocol Buffers 编码,传输于 HTTP 之上。协议定义见 prompb/remote.proto:WriteRequest 携带 repeated TimeSeries 与 metadata(L22-L28);ReadRequest 携带一组 Query(每个 query 含 start/end 时间戳与 label matchers,L31-L72),并支持 SAMPLES 与 STREAMED_XOR_CHUNKS 两种响应类型协商——流式 XOR 响应直接以 XOR 编码的 chunk 逐序列传输,省去解码-再编码开销。
读协议目前不被视为稳定的 API;写协议则有 1.0 稳定版与 2.0 实验版两代规范,Prometheus server 均支持。
3.2 读路径的固有限制
读路径上,Prometheus 仅从远端按"标签选择器集合 + 时间范围"取回原始序列数据,所有 PromQL 评估仍发生在 Prometheus 自身。这意味着 remote read 查询存在扩展性上限:所有必要数据必须先加载进查询端 Prometheus 再处理。官方认为完全分布式的 PromQL 评估在现阶段不可行。
3.3 让 Prometheus 自身充当 Remote 服务端
Prometheus 同时提供这两种协议的服务端实现:
- Remote write 接收端:需设置
--web.enable-remote-write-receiver命令行 flag 启用,端点为/api/v1/write。flag 定义见 cmd/prometheus/main.go;若未启用即访问该端点,会返回 404 并提示 "remote write receiver needs to be enabled with --web.enable-remote-write-receiver"(见 web/api/v1/api.go)。同一文件中还可通过--web.remote-write-receiver.accepted-protobuf-messages控制接收时接受的 protobuf 消息类型。 - Remote read 端点:
/api/v1/read,使用方式见 Remote Read API 文档。
作为客户端发起 remote write / remote read 的完整配置(remote_write:、remote_read: 配置块,含队列参数、重试与 relabel 等)见 配置文档的 remote_write 章节 与 remote_read 章节。集成生态的具体列表以官方 Integrations 文档为准。
四、从 OpenMetrics 格式回填数据(promtool tsdb create-blocks-from openmetrics)
4.1 适用场景与安全性约束
当需要从 OpenMetrics 格式数据在 TSDB 中创建 block 时使用 backfilling。注意:回填最近 3 小时的数据是不安全的——该时间范围可能与仍在变更的当前 Head block 重叠。
回填会创建新的 TSDB block,每个含两小时数据,从而限制建块时的内存需求;后续把 2h 块压缩为更大的块由 Prometheus server 自行完成。典型用例是把其他监控系统或时序数据库的数据迁入 Prometheus:先将源数据转换为 OpenMetrics 格式(backfill 的输入格式)。
限制:原生直方图(native histograms)与 staleness 标记不被支持,因为 OpenMetrics 格式无法表达它们。
4.2 使用方式
通过 promtool 使用,实现见 cmd/promtool/backfill.go。输出目录默认 ./data/,可用子命令的可选参数指定:
promtool tsdb create-blocks-from openmetrics <input file> [<output directory>]
块创建完成后,将输出目录中的 block 移入 Prometheus 数据目录。对于 v2.38 及更早版本,若与现有 block 存在时间重叠,需设置 --storage.tsdb.allow-overlapping-blocks(当前版本已默认允许重叠块;且注意移动 block 后需等待 --storage.tsdb.block-reload-interval 间隔内被重新加载)。任何回填数据都受 Prometheus server 配置的保留策略(时间或大小)约束,超出部分会被正常清理。
4.3 更长的块时长(--max-block-duration)
默认情况下 promtool 使用默认块时长(2h),这是最通用、最正确的行为。但回填大时间范围的数据时,更大的块时长可以加快回填并避免 TSDB 后续额外压缩。--max-block-duration 允许用户配置块的最大时长,工具会自动选取不超过该值且合适的块时长。
但大块的代价同样存在:
- 基于时间的保留策略:只要块内(可能很大的块)有一个样本仍在保留期内,就必须保留整个块;
- 基于大小的保留策略:哪怕只是轻微超出大小上限,整个块也会被删除。
因此,用更少(更大)的块做回填必须谨慎,不建议在任何生产实例上这样做。
五、为 Recording Rules 回填历史数据
5.1 动机
新建的 recording rule 没有历史数据——规则数据只从创建时刻起存在。promtool 提供了补建历史 recording rule 数据的能力。
5.2 使用方式
查看全部选项:
$ promtool tsdb create-blocks-from rules --help
示例(--start / --end 为 Unix 秒级时间戳):
$ promtool tsdb create-blocks-from rules \
--start 1617079873 \
--end 1617097873 \
--url http://mypromserver.com:9090 \
rules.yaml rules2.yaml
要点:
- 提供的 recording rule 文件应是标准的 Prometheus 规则文件;
- 输出是一个包含所有规则历史数据 block 的目录,默认
data/; - 要使用该数据,需将 block 移入运行中 Prometheus 实例的
storage.tsdb.path(v2.38 及以下需启用--storage.tsdb.allow-overlapping-blocks)。移入后,新 block 会在下一次压缩时与现有块合并。
5.3 已知限制
- 多次以重叠的 start/end 时间运行规则回填器,每次都会创建含相同数据的 block(重复数据);
- 规则文件中的所有规则都会被求值;
- 若规则文件中设置了
interval,其优先级高于回填命令的eval-intervalflag; - 规则文件中的 Alerts 目前被忽略;
- 同一 group 内的规则看不到前一个规则的结果,即不支持回填引用其他被回填规则的规则。变通方法:多次回填,先创建被依赖的数据(并移动到 Prometheus 数据目录,使其可通过 API 访问),再回填依赖它的规则。
六、要点速查
| 主题 | 关键结论 |
|---|---|
| 磁盘布局 | 2h block(ULID 命名)+ chunks/(≤512MB 段)+ index + tombstones + meta.json;Head 靠 wal/(128MB 段)抗崩溃 |
| 压缩 | 目标块 = min(保留时间 × 10%, 31d);源块与新块共存会短暂超容 |
| 保留策略 | 默认 15d;time/size 同时设置时先触发者生效;过期清理最长滞后 2h |
| 容量规划 | retention_seconds × 摄入速率 × 1~2 字节/样本;size 上限建议 ≤ 磁盘的 80–85% |
| 文件系统 | 仅支持 POSIX;NFS/EFS 明确不支持 |
| 远程集成 | 4 种角色(读/写 × 客户端/服务端);snappy 压缩 protobuf over HTTP;读回数据仍在本地做 PromQL 求值 |
| 回填 | OpenMetrics 与 rules 两个入口;勿回填最近 3h;大 --max-block-duration 仅限非生产 |
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
