InfluxDB 内部原理 101 - 第一部分

导航至

Paul Dix 主导了一系列 InfluxDB 内部原理 101 内部课程,以向新手讲授 InfluxDB 内部原理。我从这些讲座中学到了很多,并希望与社区分享这些内容。我还撰写本文以整理我对 InfluxDB 的理解,并可能帮助其他想要了解 InfluxDB 架构的人。很多信息也来自 InfluxDB 文档——本系列的目的是提供 InfluxDB 架构的综合概述。

内容很多,因此分为三部分呈现。第一篇文章解释了数据模型和写入路径。第二篇文章解释了查询路径。第三篇文章解释了 InfluxDB Enterprise 集群。

系列目录

  1. 数据模型和写入路径:向 InfluxDB 添加数据
    • 数据模型术语
    • 从客户端接收数据点
    • 将数据点持久化到存储
    • 压缩持久化的数据点
  2. 查询路径:从 InfluxDB 读取数据
    • 为查询索引数据点
    • 关于 TSI(磁盘索引)的说明
    • 解析和规划
    • 执行查询
    • 关于 IFQL 的说明
    • DELETE 和 DROP - 从 InfluxDB 中删除数据
    • 更新数据点
  3. 集群:InfluxDB Enterprise
    • 理解元服务
    • 理解数据节点
    • 理解数据分发和复制

数据模型和写入路径:向 InfluxDB 添加数据

数据模型和术语

InfluxDB 数据库存储 points(数据点)。一个数据点包含四个组成部分:measurement(度量),tagset(标签集),fieldset(字段集)和 timestamp(时间戳)。

measurement(度量)提供了一种关联可能具有不同 tagsets(标签集)或 fieldsets(字段集)的相关数据点的方法。tagset(标签集)是一个键值对字典,用于存储数据点的元数据。fieldset(字段集)是一组类型化的标量值——即数据点记录的数据。

数据点的序列化格式由 line protocol(行协议)定义(如果您想了解更多细节,其中包含其他示例和说明)。规范中的一个示例数据点有助于解释这些术语

’’ temperature,machine=unit42,type=assembly internal=32,external=100 1434055562000000035

measurement(度量)是 *temperature*(温度)。

tagset(标签集)是 *machine=unit42,type=assembly*(机器=unit42,类型=assembly)。tagset(标签集)中的键,*machine*(机器)和 *type*(类型),被称为 tag keys(标签键)。tagset(标签集)中的值,*unit42*(unit42)和 *assembly*(assembly),被称为 tag values(标签值)。

fieldset(字段集)是 *internal=32,external=100*(内部=32,外部=100)。fieldset(字段集)中的键,*internal*(内部)和 *external*(外部),被称为 field keys(字段键)。fieldset(字段集)中的值,*32*(32)和 *100*(100),被称为 field values(字段值)。

每个数据点都存储在唯一一个 database(数据库)和唯一一个 retention policy(保留策略)中。database(数据库)是用户、保留策略和数据点的容器。retention policy(保留策略)配置 InfluxDB 保留数据点的时间(duration,持续时间)、集群中存储的数据点副本数(replication factor,复制因子)以及分片组覆盖的时间范围(shard group duration,分片组持续时间)。retention policy(保留策略)使用户可以轻松(并且数据库可以高效地)删除不再需要的旧数据。这是时间序列应用中的常见模式。

我们将在稍后描述 InfluxDB 写入路径的工作原理时解释 replication factor(复制因子)、shard groups(分片组)和 shards(分片)。

还有一个我们需要开始了解的术语:series(序列)。序列是一组共享 measurement + tag set + field key 的数据点。

您可以参考[文档词汇表](https://docs.influxdb.org.cn/influxdb/v1.8/concepts/glossary/)了解这些术语或本博客系列中可能使用的其他术语。

从客户端接收数据点

客户端以 [line protocol](https://docs.influxdb.org.cn/influxdb/v1.8/write_protocols/)(行协议)格式将数据点 POST 到 InfluxDB 的 HTTP `/write` 端点。数据点可以单独发送;但是,为了提高效率,大多数应用程序以批处理方式发送数据点。典型的批处理大小范围从数百到数千个数据点。POST 请求通过查询参数指定数据库和可选的保留策略。如果未指定保留策略,则使用默认保留策略。请求体中的所有数据点都将写入该数据库和保留策略。POST 请求体中的数据点可以来自任意数量的序列;批处理中的数据点不必来自相同的度量或标签集。

当数据库接收到新数据点时,它必须 (1) 使这些数据点持久化,以便在数据库或服务器崩溃时可以恢复,以及 (2) 使数据点可查询。本文重点介绍前半部分,即如何使数据点持久化。

将数据点持久化到存储

为了使数据点持久化,每个批处理都写入并 fsynced 到预写日志(WAL)。WAL(预写日志)是一个仅追加的文件,仅在数据库恢复期间读取。为了提高空间和磁盘 IO 效率,WAL 中的每个批处理在使用 [snappy compression](https://ggdocs.cn/snappy/) 压缩后才写入磁盘。

虽然 WAL 格式有效地使传入数据持久化,但它是一种非常糟糕的读取格式——使其不适合支持查询。为了允许立即查询新数据,传入的数据点也会写入内存中的 cache(缓存)。cache(缓存)是一种内存数据结构,针对查询和插入性能进行了优化。cache 数据结构是将 series(序列)映射到按时间排序的字段列表的映射。

WAL 使新数据点持久化。cache 使新数据点可查询。如果系统在 cache 写入 TSM 文件之前崩溃或关闭,则在数据库启动时,通过读取和重放存储在 WAL 中的批处理来重建 cache

WALcache 的组合对于传入数据效果良好,但对于长期存储来说是不够的。由于 WAL 必须在启动时重放,因此必须将其大小限制在合理范围内。cache 受 RAM 大小的限制,这对于许多时间序列用例来说也是不希望的。因此,需要将数据组织起来并写入磁盘上的长期存储块,这些存储块需要具有大小效率(以便数据库可以存储大量数据点)并且查询效率高。

时间序列查询通常是对时间的聚合——扫描限定时间范围内的数据点,然后通过诸如平均值、最大值或移动窗口之类的摘要函数进行缩减。列式数据库存储技术,其中数据在磁盘上按列而不是按行组织,非常适合这种查询模式。此外,列式系统可以非常好地压缩数据,满足高效存储数据的需求。关于列式存储有很多文献。[面向列的数据库系统](https://searchdatamanagement.techtarget.com/definition/columnar-database) 就是其中一个概述。

时间序列应用程序通常会在一段时间后从存储中清除数据。例如,许多监控应用程序会将最近一两个月的数据在线存储,以支持监控查询。如果配置的生存时间到期,则需要高效地从数据库中删除数据。从列式存储中删除数据成本很高,因此 InfluxDB 还将其列式格式组织成按时间界定的块。当生存时间到期时,可以简单地从文件系统中删除按时间界定的文件,而无需对持久化数据进行大量更新。

最后,当 InfluxDB 作为集群系统运行时,它会在多台服务器之间复制数据,以在发生故障时实现可用性和持久性。

可选的生存时间持续时间、生存时间段内时间块的粒度以及副本数量都使用 InfluxDB 保留策略进行配置

CREATE RETENTION POLICY <retention_policy_name> ON <database_name> DURATION <duration> REPLICATION <n> [SHARD DURATION <duration>] [DEFAULT]

duration 是可选的生存时间(如果数据不应过期,请将 duration 设置为 INF)。SHARD DURATION 是过期时间段内数据的粒度。例如,一个小时的 shard duration(分片持续时间)和 24 小时的 duration(持续时间)配置数据库存储 24 个一小时的分片。每小时,最旧的分片将从数据库中过期(删除)。设置 REPLICATION 以配置复制因子——集群中应存在的分片副本数。

具体来说,数据库在磁盘上创建数据的这种物理组织结构

'' Database director  /db
    '' Retention Policy directory /db/rp
        '' Shard Group (time bounded). (Logical)
            '' Shard directory (db/rp/Id#)
                '' TSM0001.tsm (data file)
                '' TSM0002.tsm (data file)
                '' …

内存中的 cache(缓存)以 TSM 格式刷新到磁盘。刷新完成后,刷新的数据点将从缓存中删除,并且相应的 WAL(预写日志)将被截断。(WALcache 也按分片维护。)TSM 数据文件存储按列组织的数据点。TSM 文件一旦写入,就不可更改。[InfluxDB 文档] 中提供了 TSM 文件布局的详细描述。

压缩 TSM 数据

cache(缓存)的数据量相对较小。当 TSM 列式格式可以在单个块中存储序列值的长运行序列时,效果最佳。更长的运行序列可以产生更好的压缩效果,并减少扫描字段以进行查询的寻道次数。TSM 格式主要基于日志结构合并树。新的(一级)TSM 文件由缓存刷新生成。这些文件稍后合并(压缩)为二级文件。二级文件进一步合并为三级文件。随着文件变得更大,并且最终变为冷数据(它们覆盖的时间范围不再是写入热点),会发生其他级别的压缩。上面引用的文档提供了压缩的详细描述。

TSM 压缩代码中有很多逻辑和复杂性。但是,高级目标非常简单:将序列的值组织在一起形成长运行序列,以最大限度地优化压缩和扫描查询。

第一部分总结

总而言之,数据点批处理以 POST 方式发送到 InfluxDB。这些批处理经过 snappy 压缩并写入 WAL 以实现即时持久性。数据点也写入内存中的缓存,以便新写入的数据点可以立即查询。缓存定期刷新到 TSM 文件。随着 TSM 文件的积累,它们被合并和压缩为更高级别的 TSM 文件。TSM 数据被组织成分片。分片覆盖的时间范围以及集群部署中分片的复制因子由保留策略配置。

希望这篇文章有助于解释 InfluxDB 如何接收和持久化传入的写入操作。在下一篇文章中,我们将讨论系统如何支持查询、更新和删除操作。