2. Loki 存储模型:Chunk 存储原理、Index 存储原理(单 TSDB 与多 TSDB 模式)、对象存储(S3/GCS/MinIO)作为后端

Loki 的存储模型是其高性能与低成本的核心基石。与 Prometheus 或 Cortex 不同,Loki 不进行全文索引,而是采用 “仅索引元数据 + 压缩存储日志块” 的设计哲学。本章深入剖析 Chunk(数据块)与 Index(索引)的存储原理,并详解对象存储作为后端的最佳实践。

2.1 Chunk 存储原理

Chunk 是 Loki 中存储日志数据的最小物理单元。当日志流(Log Stream)被采集后,Ingester 组件会将多条日志条目压缩并封装为一个 Chunk。

2.1.1 Chunk 的生命周期

  1. 写入阶段:Ingester 接收日志流,按 stream hash + labels 分组,将日志追加到内存中的 Chunk 缓冲区。
  2. 压缩与封装:当 Chunk 达到目标大小(默认 1.5 MB)或最大超时时间(默认 1 小时)时,Ingester 使用 gzip(默认)或 snappy / lz4 压缩日志数据。
  3. 上传:压缩后的 Chunk 被上传至对象存储(S3/GCS/MinIO),同时将 Chunk 元数据(如时间范围、标签集、Checksum)写入 Index。
  4. 只读与查询:上传后的 Chunk 变为只读,Querier 通过 Index 定位 Chunk 并直接从对象存储读取。

2.1.2 Chunk 内部结构

字段说明大小占比
HeaderChunk 版本号、压缩算法标识、元数据长度~0.1%
Metadata日志流标签集、时间范围(最小/最大时间戳)、条目数~0.5%
Log Entries压缩后的日志行数据,按时间戳排序~99%
FooterChecksum(CRC32)用于完整性校验~0.4%
💡 优化要点: 调整 chunk_target_size(默认 1.5MB)可平衡内存占用与对象存储请求次数。增大 Chunk 大小可减少存储 API 调用,但会增加内存压力。

2.2 Index 存储原理:单 TSDB 与多 TSDB 模式

Index 负责存储日志流的元数据(标签、Chunk 引用、时间范围),是查询加速的关键。Loki 从 2.8 版本开始引入 TSDB (Time Series Database) 索引格式,逐步替代旧的 BoltDB 索引。

2.2.1 单 TSDB 模式(Single TSDB)

  • 架构:每个 Ingester 或 Compactor 维护一个全局的 TSDB 索引文件。
  • 存储方式:索引文件以 TSDB 块 形式写入对象存储,每个块包含完整的标签倒排索引与 Chunk 引用列表。
  • 适用场景:中小规模集群(< 100 个 Ingester),日志流数量 < 50 万。
  • 优点:部署简单,索引查询延迟低(单文件顺序读取)。
  • 缺点:索引文件可能过大(> 10GB),导致 Compactor 合并压力大,查询时需加载整个索引。

2.2.2 多 TSDB 模式(Multi TSDB / Sharded TSDB)

  • 架构:将全局索引按 时间范围 + 标签哈希 拆分为多个独立的 TSDB 分片(Shard)。
  • 存储方式:每个分片是一个独立的 TSDB 块,Compactor 定期合并小分片为更大块。
  • 适用场景:大规模集群(> 100 个 Ingester),日志流数量 > 100 万。
  • 优点:索引分片可并行查询,降低单文件大小;Compactor 可独立合并每个分片,提升吞吐。
  • 缺点:需要配置分片策略(如 tsdb_shipper.resync_interval),查询时需合并多个分片结果。
对比维度单 TSDB 模式多 TSDB 模式
索引文件大小单个大文件(可能 > 10GB)多个小文件(每个 < 1GB)
查询延迟低(单文件顺序读)中(需合并多分片)
Compactor 压力高(合并大文件)低(并行合并小分片)
扩展性上限~50 万日志流~500 万+ 日志流
配置复杂度低中(需调分片数)
📌 生产建议: 新集群默认启用多 TSDB 模式(schema_config.configs[].index.type: tsdb)。对于已有集群,可通过 tsdb_shipper.active_index_directory 和 tsdb_shipper.cache_location 控制本地缓存路径。

2.3 对象存储作为后端:S3 / GCS / MinIO

Loki 将 Chunk 和 Index 统一存储在对象存储中,实现计算与存储分离。对象存储的高可用、低成本、无限扩展特性使其成为理想后端。

2.3.1 支持的存储后端对比

后端协议典型延迟成本(每 GB/月)适用场景
AWS S3S3 API5-20 ms~$0.023生产环境,高可用,全球部署
GCSGCS API5-20 ms~$0.020GCP 原生集成,强一致性
MinIOS3 兼容 API1-5 ms(本地)自建硬件成本本地/私有云,低延迟,合规需求
Azure BlobAzure API10-30 ms~$0.018Azure 生态,冷数据归档

2.3.2 对象存储配置示例(MinIO)

storage_config:
  aws:
    s3: http://minio:9000
    bucketnames: loki-data
    access_key_id: minioadmin
    secret_access_key: minioadmin
    s3forcepathstyle: true
  boltdb_shipper:
    active_index_directory: /data/loki/index
    cache_location: /data/loki/index-cache
    shared_store: s3
⚠️ 关键配置:
  • s3forcepathstyle: true 必须为 MinIO 启用,否则路径解析错误。
  • shared_store: s3 确保 Index 也写入对象存储,实现无状态 Ingester。
  • 生产环境建议启用 版本控制 和 生命周期策略(如 30 天后转为冷存储)。

2.4 存储架构总览

下图展示了 Loki 存储模型中 Chunk、Index 与对象存储的交互关系:

Loki 存储架构:Chunk + Index + 对象存储 Loki 存储模型架构图 日志采集 (Promtail) 日志采集 (Docker) 日志采集 (Syslog) Ingester (内存 Chunk 缓冲区) 压缩 → 封装 → 上传 Chunk 存储 压缩日志数据 (gzip/snappy) 按时间 + 标签分组 Index 存储 (TSDB) 标签倒排索引 + Chunk 引用 单 TSDB / 多 TSDB 模式 对象存储后端 S3 / GCS / MinIO / Azure Blob Ingester 将日志压缩为 Chunk 并上传对象存储,同时将元数据写入 TSDB 索引

2.5 存储优化最佳实践

  • Chunk 大小调优:设置 chunk_target_size: 2097152(2MB)可减少对象存储 PUT 请求次数,降低存储成本。
  • 索引分片策略:多 TSDB 模式下,tsdb_shipper.resync_interval: 5m 控制索引同步频率;compactor.working_directory 需有足够磁盘空间(建议 100GB+)。
  • 对象存储生命周期:配置 S3 生命周期策略,将超过 30 天的 Chunk 自动转为 Glacier 或 Deep Archive,节省 80% 存储费用。
  • 本地缓存:启用 chunk_store_config.chunk_cache_config 和 index_cache_config,使用 Redis 或 Memcached 缓存热数据,减少对象存储读取延迟。
  • 压缩算法选择:gzip 压缩比最高(~5:1),但 CPU 开销大;snappy 压缩比略低(~3:1),但速度更快。日志量大时推荐 snappy。
🔧 实战案例: 某电商平台每天产生 10TB 原始日志。通过将 Chunk 目标大小从 1.5MB 提升至 3MB,并启用 snappy 压缩,对象存储 PUT 请求减少 40%,存储成本降低 25%。同时启用多 TSDB 模式(16 分片),查询 P99 延迟从 2.3s 降至 1.1s。