第八章:存储机制

深入讲解 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 存储机制:

  1. TSDB 概述 - 块存储和 WAL 设计
  2. 块存储结构 - 目录布局和文件格式
  3. 写入流程 - WAL 和内存缓冲区
  4. 查询流程 - 索引结构和查询执行
  5. 数据压缩 - 多级压缩机制
  6. 远程存储 - 远程读写配置
  7. 最佳实践 - 容量规划和性能优化

📖 下一步