前言
InfluxDB 是目前全球最流行的开源时序数据库之一,广泛应用于物联网监控、DevOps 可观测性、金融量化分析、工业智能制造等场景。在 IoT 赛道中,它常用于存储和分析来自各类传感器的数据,如温度、湿度、设备状态等,这与 MQTT 的 IoT 消息传输场景形成天然的上下游配合。InfluxDB 深度理解时序数据的特点,在存储引擎、数据模型、查询优化和压缩算法等方面做了大量针对性设计,以在低成本的普通硬件上支撑每秒数百万数据点的写入。
以下将从数据模型、存储引擎、写入与查询流程、数据压缩与过期、高可用架构、版本演进等多个维度展开。
版本说明 :InfluxDB 主要分为两个技术代际:v1 系列 (核心是 TSM + TSI 引擎)和 v3 系列(完全重构的列式存储,基于 Apache Arrow + DataFusion)。当前市面上仍有大量生产环境运行 v1.x,故本文以 v1 的 TSM 引擎为主线,末节补充 v3 架构要点。
一、数据模型 ------ 4 要素 + Series
InfluxDB 的数据模型围绕时序数据的本质设计,每个数据点(Point)包含四个核心部分。
1.1 数据点的四要素
以一条典型的写入行协议为例:
cpu,host=server01,region=us-east usage=95.2,load=0.75 1672531200000000000
它对应四个要素:
| 要素 | 示例值 | 数据类型 | 作用说明 |
|---|---|---|---|
| Measurement | cpu |
String | 指标的逻辑分组,类比关系型数据库中的"表名" |
| Tag Set | host=server01,region=us-east |
String(标签值只能是字符串) | 用于标识数据源或维度的键值对;会被建立索引,用于高效过滤 |
| Field Set | usage=95.2,load=0.75 |
浮点/整型/字符串等 | 实际测量值的存储;无索引,适合存放数值型指标 |
| Timestamp | 1672531200000000000(纳秒级) |
时间戳 | 数据的精确时间;主键的核心部分 |
Measurement 和 Tag Set 的组合共同定义了一个时间线(Series) ------这是 InfluxDB 中最核心的概念。同一个 Measurement 下,每组不同的 Tag 组合都对应一条独立的时间线。例如 cpu,host=server01,region=us-east 和 cpu,host=server02,region=us-east 就是两条不同的时间线。
1.2 数据模型的独特之处
与传统的关系型数据库相比,InfluxDB 最大的特点是 Schemaless(无需预定义 Schema) :写入数据时无需提前创建 Measurement 或定义字段类型,数据库会自动识别并调整 Schema。这使得它特别适合 IoT 等数据源频繁变化的场景。
数据模型最佳实践:
- 将常作为查询条件的属性放入 Tag(如设备 ID、地域、类型),利用索引加速过滤;
- 将测量值放入 Field(如温度、电压、CPU 使用率);
- Tag 过多会导致时间线数量激增,存在内存/性能风险;Tag 过少则查询过滤效率不佳。实际选型时需在查询灵活性与系统开销之间取得平衡。
二、存储引擎 ------ TSM Tree 的演进与原理
InfluxDB 最核心的设计体现在其存储引擎上------TSM(Time-Structured Merge Tree),这是 InfluxDB 团队在尝试了多种现有方案后自研的专为时序场景优化的存储引擎。
2.1 引擎的演进历程
InfluxDB 在存储引擎上走过了一条反复试错的路线:
| 版本阶段 | 采用的引擎 | 主要问题 |
|---|---|---|
| v0.9.0 之前 | LevelDB(LSM Tree) | 删除数据代价高(墓碑机制导致性能下降);支持的数据规模下会产生海量小文件,导致文件句柄用尽;不支持热备份 |
| v0.9.0 ~ v0.9.4 | BoltDB(B+Tree,mmap) | B+树随机写入性能差,无法满足时序数据高写入吞吐需求;不支持数据压缩 |
| v0.9.6 ~ v1.2 | 自研 TSM v1(WAL + TSM File) | 解决了写入瓶颈,但倒排索引仍完全驻留内存,时间线数大时内存爆炸 |
| v1.3 ~ 至今 | 自研 TSM v2(WAL + TSM File + TSI File) | 索引持久化到磁盘,大幅降低内存占用,支持海量时间线 |
为什么自研而不继续用 LevelDB 或 RocksDB? 除了上述技术痛点,还有技术栈一致性的考量:InfluxDB 的 TSDB 上层逻辑用 Go 语言实现,团队希望存储引擎也用 Go 编写以避免跨语言交互的复杂度和部署成本,而 RocksDB 是 C++ 方案因此被排除。同时 TSM 针对时序数据特性做了特殊优化,如基于时间范围的分区和面向 Series 的文件内索引结构。
2.2 TSM 引擎的核心组件
TSM 引擎深受 LSM Tree 启发,但在细节上做了大量针对时序数据的优化。它以 Shard(分片) 为基本管理单元。
一个 Shard 包含以下核心组件:
(1)Cache
位于内存中,缓存最新写入、但尚未持久化到磁盘的数据。在查询时,Cache 中的数据与 TSM 文件中的数据会被合并返回,以保证读取到最新的写入结果。
(2)WAL(预写日志)
每条数据写入时会先写入 WAL 文件,再写入 Cache。WAL 确保了数据持久性:即使进程崩溃或断电,重启后可依据 WAL 恢复 Cache 中的数据,不会丢失已"确认"的写入。
(3)TSM File(数据文件)
内存 Cache 达到一定阈值后,会被 Flush 到磁盘,形成 TSM 文件。TSM 文件是只读的、内存映射的文件,内部被划分为多个 Block(数据块) :
- 数据块:存放按 Series + Field + 时间戳的排序后的时序数据;
- 索引块 :存放 SeriesKey、FieldKey 以及数据块在文件中的位置信息,结构类似 B+Tree 在文件中的有序映射。
TSM 文件的索引块按 SeriesKey + FieldKey 排序。查询时,可通过二分查找迅速定位到目标数据块的偏移量,再根据时间范围定位具体数据。每个 TSM 文件大小上限为 2GB。
(4)TSI File(索引文件)
TSI(Time Series Index)是 InfluxDB 为 倒排索引 设计的持久化存储格式。在引入 TSI 之前,倒排索引完全驻留内存,当时间线规模达到百万甚至千万级别时内存会爆炸。TSI 采用与 TSM 类似的文件结构将索引持久化到磁盘,支持高基数(高时间线数量)场景。
倒排索引的核心原理 :记录每个标签值(如 region=us-east)映射到哪些 Series,从而实现高效的标签过滤查询。例如查询 SELECT * FROM cpu WHERE region='us-east',倒排索引可快速找出所有符合 region=us-east 的 Series,无需扫描全部数据。
2.3 Shard Group 与 Retention Policy
Shard Group 是时间维度的逻辑分组。每个 Shard Group 只存储特定时间区间(如 7 天)的数据,且不同 Shard Group 的时间区间不重叠。
Retention Policy(保留策略,RP) 在每个数据库上独立配置,包含三个关键属性:
DURATION:数据的生命周期,过期数据按 Shard Group 整体删除(物理删除,性能高效)REPLICATION:副本数(集群版)SHARD DURATION:每个 Shard Group 覆盖的时间长度
数据库创建时即指定默认 RP,写入数据时也可显式选择 RP。Shard 按时间线哈希分布的设计保证同一 Series 的数据落在同一 Shard 中,有利于提高查询效率。
三、写入流程
当一个数据点写入 InfluxDB 时,内部的处理流程如下:
-
Shard 定位:根据数据的时间戳,确定它属于哪个 Shard Group,再根据 Series 的哈希值定位到具体的 Shard。
-
Series 检查:在内存的 Series 索引中查找该 Series 是否存在;如果不存在,先在索引中创建该 Series,并更新倒排索引。
-
写入 WAL:将数据以追加方式写入 WAL 文件,确保持久性。多个数据点可批量写入一次 WAL 操作。
-
更新 Cache:将数据写入内存的 Cache 中。Cache 中的数据按 Series + Field + 时间戳有序组织。
-
返回确认:写入流程完成,返回成功响应给客户端。
-
Flush 到 TSM 文件:当 Cache 大小达到阈值或按固定时间间隔触发时,将 Cache 中的数据排序并压缩后写入磁盘上的 TSM 文件,随后清空 Cache 和 WAL 中的已持久化部分。
-
Compaction 合并 :当 TSM 文件数量过多时,后台 Compaction 进程会将多个小的 TSM 文件合并成更大的文件(采用 size-tiered 策略),清理被覆盖或删除的数据,并重新整理索引,以控制文件数量并提升查询效率。
注意:与 LevelDB 严格保证层内文件无重叠不同,TSM 的 Compaction 容忍 TSM 文件之间的数据键范围重叠。因为时序数据的查询总是带时间范围过滤,且数据自然按时间排序,这种设计在减少合并开销和查询额外合并成本之间取得了权衡。
四、查询流程
查询的执行流程可以概括为 TSI 先行 + TSM 检索:
-
解析与规划:解析查询语句,生成逻辑执行计划,并基于启发式规则进行优化。
-
TSI 查找 Series:利用 TSI 倒排索引,根据查询中的 Tag 过滤条件,快速筛选出所有符合条件的 Series ID 列表。
-
确定 Shard 范围:根据查询的时间范围,确定需要访问的 Shard 列表,实现 Shard Group 级别的分区剪枝。
-
TSM 数据读取:对于每个符合条件的 Shard 中的每个 TSM 文件:
- 在文件的索引块中通过 SeriesKey + FieldKey 二分查找定位到相关的索引条目;
- 根据索引条目中的偏移量,加载对应的数据块到内存;
- 根据时间范围在数据块内进行扫描过滤,提取符合条件的数据点。
-
合并 Cache 数据:将 TSM 文件中读取的数据与 Cache 中尚未 Flush 的最新数据进行合并去重。
-
返回结果:将合并后的数据返回给客户端。
五、数据压缩 ------ 多样化按类型智能压缩
InfluxDB 针对不同类型的数据采用差异化的压缩算法,以在压缩率和性能之间取得最优平衡。
| 数据类型 | 压缩算法 | 原理说明 |
|---|---|---|
| 整型 | ZigZag + Simple8b | 先用 ZigZag 编码将有符号整数转为无符号整数(小型整数常用值);再用 Simple8b 将多个小整数打包进一个 64 位字 |
| 浮点型 | Gorilla XOR 压缩 | Facebook Gorilla 论文提出的算法。第一个值完整存储,后续值与前一个值进行 XOR,结果为 0 则存储一位 0;非 0 则存储 XOR 结果及控制位。特别适合时间序列中相邻两点变化不大的场景 |
| 时间戳 | Delta-of-Delta 编码 | 先计算相邻时间戳的差值(delta),再计算差值的差值(delta of delta),多数情况下该值很小,用变长编码压缩 |
| 字符串 | Snappy | Google 开发的快速压缩算法,在压缩速度和压缩比之间取得良好平衡 |
TSM 文件采用列式存储组织方式,相同类型的数据在同一个数据块中连续存放,这为上述按类型精细选择压缩算法创造了条件。实际的压缩操作在 TSM 文件写入磁盘时进行,Compaction 合并时也会重新压缩。
六、高可用架构 ------ 元数据集群 + 数据集群
InfluxDB 的高可用能力主要集中在 Enterprise(企业版),社区开源版为单节点(集群部分在 v0.12 后不再开源)。新版 InfluxDB 3 系列开始原生支持分布式部署,但 v1 时代的集群方案仍然是大量生产环境的基石。
6.1 两集群架构(v1 Enterprise)
InfluxDB Enterprise 集群由两套独立的集群组成:
| 组件 | 数量建议 | 功能 |
|---|---|---|
| 元数据节点 | 3 个(奇数) | 使用 Raft 共识协议 维护集群元数据的一致视图(数据库、RP、Shard 分布等信息);通过 HTTP API 接收管理命令,节点间用 TCP + Protobuf 通信 |
| 数据节点 | 偶数个(如 2、4、6) | 保存实际的时序数据;节点数量需能被复制因子(Replication Factor,RF)整除;数据节点间通过 TCP + Protobuf 完成数据复制和查询协作 |
元数据节点数量建议为 3:Raft 协议需要多数派才能提交操作,3 节点允许故障 1 节点;4 节点同样只允许故障 1 节点,但增加通信开销且不提升容错能力。
Hinted Handoff(提示移交) 是企业版处理节点故障的核心机制------若目标节点暂时不可用,数据暂存于本地,待其恢复后自动补发。系统采用 最终一致性 模型。
6.2 v3 及云原生化方向
InfluxDB 3 系列(原 IOx)彻底重构了架构:采用 Rust 语言构建,基于 Apache Arrow 内存列式格式和 DataFusion 查询引擎,存储层可对接对象存储(如 S3)。核心组件包括:Router (接收写入并分发)、Ingester (解析行协议,将数据以 Apache Parquet 格式持久化到对象存储)、Querier(处理查询)。每个 Ingester 维护短期 WAL 保证写入持久性。新架构实现了无状态节点和存算分离,支持无限的标签基数,进一步降低了存储成本。
七、InfluxQL / Flux / SQL ------ 查询语言对比
InfluxDB 历史上支持三种查询语言,各自对应不同的发展阶段和设计理念:
| 特性 | InfluxQL | Flux | SQL |
|---|---|---|---|
| 语法风格 | 类 SQL,为时序定制,如 GROUP BY time(1h) |
函数式数据脚本语言(Pipe 操作符 ` | >`) |
| 支持版本 | v1.x 主力 | v1.x + v2.x(已逐渐弃用) | v3.x 原生支持 |
| 特点 | 学习曲线低,适合时序聚合;表达能力有限 | 极强可编程性(变量/函数/跨源),但门槛高 | 通用性强,与数据分析生态无缝集成 |
在 v3 架构中,SQL 已成为主要的查询接口,因为 DataFusion 引擎原生支持 SQL,且与 Parquet 列存格式及 BI 工具的兼容性更佳。
八、版本演进对比总结
| 维度 | v1.x(TSM + TSI) | v3.x(IOx / Clustered) |
|---|---|---|
| 存储引擎 | 自研 TSM + 倒排索引 | 基于 Arrow + Parquet 的列式存储 |
| 查询语言 | InfluxQL / Flux | 标准 SQL + InfluxQL |
| 压缩算法 | Gorilla / Snappy / Simple8b 等 | Parquet 原生列压缩 + 字典编码 |
| 索引机制 | TSI 倒排索引 | 基于 Arrow 数据字典的元数据索引 |
| 高可用 | 企业版支持两集群架构 | 3.0 起原生分布式,核心组件无状态 |
| 存储后端 | 本地文件系统 | 支持对象存储(S3 等) |
| 部署方式 | 单进程 | 支持容器化 / K8s 编排 |
| 写入性能 | 高吞吐,适合海量设备数据 | 同样保持高吞吐,且支持更高标签基数 |
InfluxDB 的设计哲学充分体现了"为特定场景做极致优化"的思路------不追求通用数据库的完整性,而是聚焦于时序数据的高吞吐写入、高效的按时间范围查询、以及智能的数据压缩与过期管理。透彻理解这些内部原理,对于在生产环境中进行容量规划、性能调优和问题排查都至关重要。