3. InfluxDB 架构解析:存储引擎 TSM、WAL、Cache、Compactor 工作流程

InfluxDB 的存储引擎是其高性能写入与查询能力的核心。它并非直接使用传统的 B+ 树或 LSM-Tree,而是基于 LSM-Tree 思想进行了深度定制,形成了以 TSM (Time-Structured Merge Tree) 为核心的存储架构。该架构主要由四个核心组件构成:Cache、WAL (Write-Ahead Log)、TSM 文件 和 Compactor。理解它们之间的协作流程,是进行数据保留策略配置与查询优化的前提。

3.1 核心组件概述

下表总结了四个核心组件的角色、存储位置与生命周期,帮助建立整体认知。

组件 角色 存储位置 生命周期
Cache 写入缓冲层,将新写入的数据点按时间与序列组织成内存中的 map 内存 (RAM) 从数据写入开始,直到被快照并写入 TSM 文件后释放
WAL 持久化日志,确保写入数据不丢失,是 Cache 的故障恢复来源 磁盘 (WAL 目录) 持续追加写入,直到对应的 Cache 被成功刷入 TSM 文件后,旧的 WAL 段文件被删除
TSM 文件 只读的列式存储文件,是数据最终的持久化格式 磁盘 (数据目录) 由 Compactor 生成,根据保留策略自动删除过期文件
Compactor 后台合并与压缩引擎,负责将多个小 TSM 文件合并成大文件,并清理过期/删除的数据 后台进程 周期性运行,持续优化磁盘存储结构
设计哲学: InfluxDB 采用“先写内存,后落盘”的策略。Cache 提供高速写入,WAL 保证数据安全,TSM 文件提供高效的只读查询,Compactor 则负责后台的“整理”工作。这种设计使得写入吞吐量极高,同时查询性能在时间范围过滤下表现优异。

3.2 数据写入与持久化工作流程

一条数据点从写入到最终存储在 TSM 文件中,经历了以下标准流程:

  1. 写入请求到达: 客户端通过 HTTP/HTTPS 或 UDP 协议发送写入请求(Line Protocol 格式)。
  2. 写入 WAL: 数据首先被序列化并追加写入到当前活跃的 WAL 文件中。这一步是顺序 I/O,速度极快,确保数据在系统崩溃后可以恢复。
  3. 更新 Cache: 数据同时被写入内存中的 Cache 结构。Cache 按照 measurement + tags + time 作为 key 进行组织,便于后续快速合并相同序列的数据。
  4. 返回成功: 一旦 WAL 写入成功,InfluxDB 立即向客户端返回 204 状态码,表示写入成功。此时数据已安全持久化,但尚未进入查询优化的存储格式。
  5. Cache 快照: 当 Cache 的大小达到阈值(默认 25MB)或超过一定时间(默认 10 分钟),当前 Cache 会被“冻结”并创建一个快照。同时,一个新的空 Cache 和新的 WAL 段文件开始服务新的写入。
  6. 写入 TSM 文件: 后台进程将 Cache 快照中的数据排序、去重、压缩,并写入一个新的只读 TSM 文件。写入完成后,对应的 WAL 段文件可以被安全删除。
关键指标: 监控 wal_write_points_ok 和 cache_write_points_ok 可以评估写入健康度。如果 cache_write_points_dropped 持续增长,说明写入速度超过了 Cache 和 TSM 文件的处理能力,需要调整 cache-snapshot-write-cold-duration 或 cache-snapshot-memory-size 参数。
数据写入与持久化工作流程 客户端写入 WAL (磁盘) Cache (内存) 返回 204 Cache 快照 TSM 文件 删除旧 WAL 流程说明:写入先顺序追加到 WAL 保证持久性,同时更新内存 Cache 提供高速缓冲。 当 Cache 达到阈值后,生成快照并写入只读的 TSM 文件,最后清理对应的 WAL 段。

3.3 TSM 文件结构

TSM 文件是 InfluxDB 存储引擎的基石,它是一种列式、只读、压缩的文件格式。一个 TSM 文件内部由四个主要区域组成:

  • Header (头部): 文件标识和版本信息。
  • Data Blocks (数据块): 存储实际的时间序列数据。每个块包含一个特定序列和时间范围内的数据,并按列(时间戳、field 值)进行压缩存储。常见的压缩算法包括 simple8b(用于整数)、gorilla(用于浮点数)和 snappy。
  • Index (索引区): 位于数据块之后,用于快速定位某个序列的数据块在文件中的偏移量。索引条目包含 measurement + tags + field key 的哈希值、时间范围以及数据块的起始位置和大小。
  • Footer (尾部): 指向 Index 区起始位置的指针,用于快速加载索引。
查询优化启示: TSM 文件的列式存储和索引结构,使得 InfluxDB 在查询时能够快速跳过不相关的数据块。查询优化应尽量利用时间范围过滤,因为索引是按时间排序的。避免查询跨度极大的时间范围,这会导致需要扫描大量 TSM 文件的索引和数据块。
TSM 文件内部结构 Header (文件标识 + 版本) Data Blocks (列式压缩数据) Block 1 (Series A, Time Range T1-T2) | Block 2 (Series B, Time Range T3-T4) | ... Index (序列哈希 → 数据块偏移量) Footer (指向 Index 起始位置的指针) 查询时,先加载 Footer 获取 Index 位置,再通过 Index 快速定位目标数据块。

3.4 Compactor 工作流程

Compactor 是 InfluxDB 的后台“清道夫”和“整理师”。它持续运行,负责将多个小的 TSM 文件合并成更大的文件,并在此过程中执行数据清理和压缩优化。其工作流程分为两个阶段:

3.4.1 一级合并 (Level 1 Compaction)

  • 触发条件: 当 Cache 快照生成的 TSM 文件数量超过阈值(默认 2 个)时触发。
  • 操作: 将多个小的、未压缩的 TSM 文件合并成一个稍大的、经过全量压缩的 TSM 文件。此阶段会应用 simple8b、gorilla 等压缩算法,显著减少磁盘空间占用。
  • 输出: 生成一个或多个 Level 1 的 TSM 文件。

3.4.2 二级合并 (Level 2 Compaction)

  • 触发条件: 当 Level 1 的 TSM 文件数量达到阈值(默认 4 个)时触发。
  • 操作: 将多个 Level 1 的 TSM 文件合并成一个更大的 Level 2 文件。此阶段还会执行 去重 和 删除标记清理。如果某个数据点被覆盖或删除(通过删除序列或保留策略),其标记会在合并时被物理移除。
  • 输出: 生成一个 Level 2 的 TSM 文件。

3.4.3 全量合并 (Full Compaction)

  • 触发条件: 当 Level 2 的 TSM 文件数量达到阈值(默认 1 个,且文件大小超过一定值)时触发。
  • 操作: 将多个 Level 2 的大文件合并成一个最终的、高度优化的 TSM 文件。这是最高级别的合并,会进行最彻底的压缩和数据清理。
  • 输出: 生成一个最终的、只读的 TSM 文件,直到其被保留策略删除。
性能影响: Compaction 是 I/O 和 CPU 密集型操作。在写入负载极高时,如果 Compactor 处理速度跟不上 TSM 文件的生成速度,会导致大量小文件堆积,进而影响查询性能。监控 compactions_failed 和 tsm1_files 指标,可以判断是否需要调整 Compaction 并发度或增加磁盘 I/O 能力。
Compactor 多级合并工作流程 小 TSM 小 TSM 小 TSM 一级合并 L1 TSM L1 TSM 二级合并 L2 TSM (去重) 全量合并 最终 TSM 合并过程:小文件 → 一级合并(全量压缩) → 二级合并(去重+清理删除标记) → 全量合并(最终优化)。 每次合并都会减少文件数量、降低磁盘占用、提升查询时的索引效率。

3.5 数据保留策略与 Compactor 的协作

数据保留策略(Retention Policy, RP)是 InfluxDB 管理数据生命周期的核心机制。它定义了数据在数据库中保留的时长,以及副本数量。RP 与 Compactor 的协作关系如下:

  • 自动删除: Compactor 在运行过程中,会检查 TSM 文件中数据的时间戳。如果某个 TSM 文件中的所有数据点的时间戳都早于 RP 定义的 DURATION,该文件会被标记为“可删除”。
  • 物理删除时机: 文件的实际物理删除并非立即发生,而是在下一次 Compactor 的“全量合并”或专门的“删除清理”周期中执行。这避免了频繁的磁盘 I/O 操作。
  • Shard 生命周期: InfluxDB 将数据按时间划分为 Shard。每个 Shard 对应一个时间范围(如 1 天或 7 天)。当 Shard 的时间范围完全超出 RP 后,整个 Shard 目录及其包含的所有 TSM 文件会被一次性删除,这是最高效的清理方式。
配置建议: 合理设置 Shard 的持续时间 (SHARD DURATION) 非常重要。如果 Shard 持续时间过长,会导致单个 Shard 内文件过多,增加 Compactor 的合并压力。如果过短,则会产生大量小 Shard,增加元数据管理的开销。一般建议 Shard 持续时间与 RP 时长匹配,例如 RP 为 7 天,Shard 持续时间可设为 1 天。

3.6 查询优化与存储引擎的关联

理解存储引擎的工作流程,可以直接指导查询优化实践:

优化方向 存储引擎原理关联 实践建议
时间范围过滤 TSM 文件的索引按时间排序,查询时能快速跳过不包含目标时间的数据块。 始终在 WHERE 子句中指定精确的 time 范围,避免全表扫描。
减少序列基数 高基数(大量唯一的 tag 组合)会导致 Cache 中 map 膨胀,增加内存压力,并导致 TSM 文件中产生大量小数据块。 合理设计 tag 的粒度,避免将高基数数据(如 UUID、时间戳)作为 tag。
避免频繁删除 删除操作不会立即释放磁盘空间,而是通过 Compactor 在合并时进行物理清理。频繁删除会导致 Compactor 负担加重。 优先使用 RP 进行自动数据过期管理,而非手动执行 DELETE 语句。
批量写入 批量写入可以减少 WAL 的写入次数和 Cache 的更新频率,提高吞吐量。 建议每次写入请求包含数千到数万个数据点,而非逐点写入。
总结: InfluxDB 的 TSM 存储引擎是为时序数据量身定做的。它通过 Cache 和 WAL 实现了极高的写入性能,通过 TSM 文件的列式存储和索引实现了高效的只读查询,并通过 Compactor 的后台合并与清理机制,保证了数据的长期存储效率和健康度。掌握这些核心组件的协作原理,是后续章节中配置数据保留策略和进行查询优化的理论基础。