第八章:存储机制
深入讲解 Prometheus TSDB 存储原理、块存储结构、数据压缩、远程存储等核心概念
最后更新: 2024-01-01
页面目录
第八章:存储机制
8.1 TSDB 概述
Prometheus 采用自定义的时序数据库 (Time Series Database, TSDB) 存储监控数据,专为高吞吐量写入和高效查询设计。
8.1.1 存储特点
| 特性 | 说明 |
|---|---|
| 块存储 | 数据按时间块存储,每个块 2 小时 |
| 只追加 | WAL (Write-Ahead Log) 保证数据持久化 |
| 压缩 | 2小时块压缩为更高一级块 |
| 内存索引 | Series 索引常驻内存 |
| 远程存储 | 支持对接远程存储系统 |
8.1.2 存储架构
┌─────────────────────────────────────────────────────────────┐
│ Write Path │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────────┐ ┌───────────────┐ │
│ │ Incoming │───▶│ WAL (Log) │───▶│ Memory (Head) │ │
│ │ Data │ │ 预写日志 │ │ 内存缓冲区 │ │
│ └──────────┘ └──────────────┘ └───────┬───────┘ │
│ │ │
│ │ Checkpoint │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐│
│ │ TSDB Blocks ││
│ │ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ││
│ │ │Block-48│ │Block-46│ │Block-44│ │ Head │ ││
│ │ │ 2h │ │ 2h │ │ 2h │ │ <2h │ ││
│ │ └────────┘ └────────┘ └────────┘ └────────┘ ││
│ └──────────────────────────────────────────────────────────┘│
│ │
└─────────────────────────────────────────────────────────────┘
8.2 块存储结构
8.2.1 目录结构
./data/
├── wal/ # Write-Ahead Log
│ ├── checkpoint.000000
│ └── 000000000
├── 01BKGV7BMYTRH3DAEGFQ2H44A1/
│ ├── index # 索引文件
│ ├── meta.json # 元数据
│ ├── chunks/
│ │ ├── 000001 # 数据块文件
│ │ └── 000002
│ └── tombstones # 删除标记
├── 01BKGV7C42TVQK4GE6FQHD9CGF/
│ └── ...
└── chunks_head/
└── 000000001 # Head 块
8.2.2 块文件格式
┌─────────────────────────────────────────┐
│ Block Header │
│ ┌───────────────────────────────────┐ │
│ │ Magic Number │ Version │ │
│ └───────────────────────────────────┘ │
├─────────────────────────────────────────┤
│ Series Data │
│ ┌────────────┐ ┌────────────┐ │
│ │ Chunk 1 │ │ Chunk 2 │ ... │
│ │ Series 1 │ │ Series 1 │ │
│ │ Series 2 │ │ Series 2 │ │
│ │ Series 3 │ │ Series 3 │ │
│ └────────────┘ └────────────┘ │
├─────────────────────────────────────────┤
│ Index │
│ - Posting List (标签索引) │
│ - Series Index │
│ - Label Name/Value Table │
└─────────────────────────────────────────┘
8.2.3 元数据文件
{
"ulid": "01BKGV7BMYTRH3DAEGFQ2H44A1",
"minTime": 1704067200000,
"maxTime": 1704074400000,
"stats": {
"numSamples": 1000000,
"numSeries": 50000,
"numChunks": 100000
},
"compaction": {
"level": 2,
"sources": ["01BKGV7BMYTRH3DAEGFQ2H44A0"]
},
"version": 1
}
8.3 写入流程
8.3.1 写入路径
// 写入流程伪代码
func Write(samples []Sample) error {
// 1. 写入 WAL
for _, s := range samples {
wal.Log(s)
}
// 2. 追加到内存
for _, s := range samples {
head.Append(s)
}
// 3. 更新索引
index.Update(s)
return nil
}
8.3.2 WAL (预写日志)
# WAL 配置
storage:
# WAL 段大小
wal:
segment_size: 128MB
# 是否压缩 WAL
wal_compression: true
# 检查点间隔
checkpoint_duration: 5m
8.3.3 内存管理
storage:
# 最大内存使用
head:
memory_after: 1GB
# 最小/最大时间范围
head_min_time: -2h
head_max_time: 2h
8.4 查询流程
8.4.1 查询执行
// 伪代码
func Query(query string, start, end int64) {
// 1. 解析 PromQL
expr := Parse(query)
// 2. 创建查询计划
plan := CreateQueryPlan(expr)
// 3. 执行计划
for _, block := range SelectBlocks(start, end) {
result := QueryBlock(block, plan)
MergeResult(result)
}
// 4. 从 Head 读取最新数据
result := QueryHead(plan)
MergeResult(result)
}
8.4.2 索引结构
┌─────────────────────────────────────────────────┐
│ Index File │
├─────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────┐ │
│ │ Label Names Table │ │
│ │ ┌────────┬────────────┐ │ │
│ │ │Offset │ Label Name│ │ │
│ │ ├────────┼────────────┤ │ │
│ │ │ 0x100 │ __name__ │ │ │
│ │ │ 0x200 │ job │ │ │
│ │ │ 0x300 │ instance │ │ │
│ │ └────────┴────────────┘ │ │
│ └──────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────┐ │
│ │ Posting Table │ │
│ │ Label Name → Series IDs │ │
│ │ ┌──────────────┬──────────────────┐ │ │
│ │ │ {job="api"} │ [1, 5, 10, ...] │ │ │
│ │ │ {job="web"} │ [2, 6, 11, ...] │ │ │
│ │ └──────────────┴──────────────────┘ │ │
│ └──────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────┘
8.5 数据压缩
8.5.1 压缩级别
| 级别 | 块大小 | 说明 |
|---|---|---|
| Level 1 | 2h | 初始块大小 |
| Level 2 | 8h (4 × 2h) | 第一次压缩 |
| Level 3 | 2d (6 × 8h) | 第二次压缩 |
| Level 4 | 16d (8 × 2d) | 第三次压缩 |
8.5.2 压缩过程
# 压缩配置
storage:
# 保留时间
retention.time: 30d
# 保留块数
retention.size: 100GB
# 压缩配置
tsdb:
# 块时间范围
block.duration: 2h
# 启用压缩
enable_compression: true
# 最大块大小
max_block_size: 128MB
8.5.3 手动压缩
# 查看存储使用
curl http://localhost:9090/api/v1/status/tsdb
# 触发压缩
curl -X POST http://localhost:9090/api/v1/admin/tsdb/compact
# 使用 promtool 分析
./promtool tsdb analyze /data/prometheus
8.6 远程存储
8.6.1 远程写入
# Prometheus 配置
remote_write:
- url: http://remote-storage:9200/write
# 远程写入队列
queue_config:
capacity: 10000
max_shards: 30
min_shards: 1
max_samples_per_send: 2000
batch_send_deadline: 30s
retry_on_http_429: true
# 签名配置
basic_auth:
username: admin
password: password
# TLS 配置
tls_config:
ca_file: /certs/ca.crt
cert_file: /certs/client.crt
key_file: /certs/client.key
8.6.2 远程读取
remote_read:
- url: http://remote-storage:9200/read
# 读取的 matchers
remote_timeout: 60s
# 包含历史数据
read_recent: true
# 基本认证
basic_auth:
username: admin
password: password
8.6.3 支持的远程存储
| 存储后端 | 说明 |
|---|---|
| Thanos | 长效存储和全局视图 |
| Cortex | 多租户时序数据库 |
| InfluxDB | InfluxDB 适配器 |
| PostgreSQL | 使用 postgres adapter |
| M3DB | Uber 开源的 TSDB |
8.7 存储配置
8.7.1 基本配置
# prometheus.yml
storage:
# 数据目录
tsdb:
path: /data/prometheus
# 保留时间
retention.time: 30d
# 保留样本数
retention.size: 100GB
8.7.2 生产配置
# prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
storage:
tsdb:
# 数据路径
path: /prometheus
# 块持续时间
block.duration: 2h
# 启用压缩
enable_compression: true
# 最大内存
max_bytes: 50GB
# WAL 配置
wal:
# WAL 压缩
wal_compression: true
# 保留策略
retention:
time: 30d
size: 200GB
8.7.3 命令行参数
./prometheus \
--storage.tsdb.path=/data/prometheus \
--storage.tsdb.retention.time=30d \
--storage.tsdb.retention.size=100GB \
--storage.tsdb.wal-compression \
--storage.tsdb.block-duration=2h \
--storage.tsdb.max-block-duration=36h \
--web.enable-lifecycle
8.8 存储最佳实践
8.8.1 容量规划
| 指标数量 | 采集间隔 | 保留时间 | 磁盘估算 |
|---|---|---|---|
| 10,000 | 15s | 15d | ~50 GB |
| 50,000 | 15s | 30d | ~250 GB |
| 100,000 | 30s | 30d | ~300 GB |
| 500,000 | 30s | 90d | ~1 TB |
8.8.2 性能优化
storage:
tsdb:
# 减少内存使用
max_bytes: 40GB
# 优化查询
query:
# 最大并发查询
max_concurrent: 20
# 查询超时
query_timeout: 10m
# 查询日志
query_log_file: /var/log/prometheus/query.log
8.8.3 监控存储健康
# 存储使用
prometheus_tsdb_storage_blocks_bytes
# 样本数量
prometheus_tsdb_head_series
# 压缩队列
prometheus_tsdb_compactions_triggered_total
# 内存使用
process_resident_memory_bytes{job="prometheus"}
8.9 本章小结
本章深入介绍了 Prometheus TSDB 存储机制:
- TSDB 概述 - 块存储和 WAL 设计
- 块存储结构 - 目录布局和文件格式
- 写入流程 - WAL 和内存缓冲区
- 查询流程 - 索引结构和查询执行
- 数据压缩 - 多级压缩机制
- 远程存储 - 远程读写配置
- 最佳实践 - 容量规划和性能优化
📖 下一步
- 第九章:Grafana 集成 - 可视化展示
- 第十章:记录规则 - 预计算指标