在之前的文章中,我们提到 GreptimeDB 为用户提供了时序表的数据模型,并且支持将数据存储到本地磁盘,或者云服务厂商提供的对象存储上,而这些都是基于存储引擎提供的能力来实现的。
我们在对时序数据进行分析处理时,需要进行高频写入,而删除较少,并且查询模式相对固定(通常会以时间范围来聚合展示数据)。因此我们在设计 GreptimeDB 的存储引擎时,采用了对写入更友好,易于实现冷热分离和 TTL 的 LSM Tree 模型。
这篇文章简要地介绍了 GreptimeDB 存储引擎的设计理念。由于存储引擎还处于快速迭代的阶段,文章中提到的特性有些仍处于草案或者实现阶段,因此不会深入到具体的实现细节。我们计划在第一个正式版本中交付其中的大部分功能。
文章分为三部分,第一部分描述了存储引擎设计理念和整体架构,第二和第三部分介绍了我们计划实现的智能索引以及自适应压缩功能。
面向时序的 LSM Tree
我们在设计存储引擎时,需要针对时序场景进行优化和定制,以实现以下目标:
支持智能索引和自适应压缩
云原生,更大程度地发挥云的能力和优势
支持在非云环境中使用
可扩展性良好
可观测
低成本
尽管我们有非常多的目标要完成,幸运的是,我们并不需要开发一个通用的存储引擎,因为时序数据库不需要处理复杂的在线事务。在技术选型上,我们选择了基于 LSM Tree 来研发面向时序的存储引擎,因为它对写入友好,易于实现冷热分离和 TTL,从而方便我们将数据存储到对象存储上。
基于以上构想,我们设计的存储引擎整体架构如图所示,
数据会先写入 WAL 和 memtable 中,等到 memtable 中的数据到达一定阈值后再以 Parquet 的格式持久化到 level 0 ,这些文件可以被存储到对象存储上
- 我们将针对时序场景优化 memtable 的实现,使其对于读写一根时间线上的数据更友好
目前,我们直接将用户的数据不加处理地写入到 Parquet 文件中,同时加上一些内部列来存储额外的信息
我们在存储引擎里引入了 schema 和列存,因为列存更有利于数据的压缩,同时方便我们使用 Parquet 等成熟的存储格式
用户数据直接存储成 Parquet 格式也能为我们后续对接 Spark SQL 等大数据系统带来好处
我们计划实现 Compaction 功能
将小的文件进行合并
对文件按照时间分区,实现 TTL ,我们希望后续能够自动为用户选择合适的时间分区
为文件构造索引,提高查询效率(详见本文的第二部分)
将 level 0 的文件进一步压缩,降低磁盘占用 (详见本文的第三部分)
我们将开发一个称为 bunshin 的分布式日志存储作为存储引擎的 WAL ,以保证 memtable 中的数据不会丢失。
基于现有的存储引擎如 RocksDB 来实现上述功能一方面十分困难,另一方面也会引入额外的复杂性,不利于后续维护。而使用自研的存储引擎,能更容易地引入新的优化手段,方便进行快速迭代。
智能索引
在时序分析和查询的场景下,最常见的查询模式是给定标签(tags)和时间范围查找符合条件的时间线。传统时序数据库实现标签到时间线的快速检索通常有两种方法:
使用倒排索引,但倒排索引的规模是各个标签值空间大小的笛卡尔积,这将导致随着标签数量增长,倒排索引大小会快速膨胀导致 spill 到磁盘;
采用 MPP 的思路不使用索引,但每次查询的请求都会扫描所有的 SST 文件,IO 复杂度较高。这一点在将来的云版本中将 SST 文件 offload 到对象存储之后会更加明显。
GreptimeDB 的方案是两者的融合,即为 MPP 式的并行查询加上智能构建的索引以加速剪枝和过滤。
在 GreptimeDB 中,我们设计的索引机制通过独立的索引文件记录类似 Parquet row group meta 的统计信息。索引格式支持 MinMax、字典、Bloomfilter 等类型。
虽然当查询条件选择度较高时,存储层的统计信息可以提高查询效率,但是当查询条件所在列的数据部分并不具有局部性时,符合查询条件的数据往往分布在大量不同的 block 上从而导致查询效率退化。
但好在,时序分析场景往往具备较为固定的查询模式,因此我们通过内置的 metrics 记录不同查询语句下的工作负载,通过代价优化器结合用户自定义的 hint 启发式地创建更加高效的索引。
自适应压缩
时序数据库因其特定的使用场景,每天都会产生海量数据。因此,数据压缩能力对于时序数据库是至关重要的。如果无法对这些时序数据进行很好的管理和压缩,将极大增加用户的存储和维护成本。
GreptimeDB 将针对不同的字段类型和字段值的基数(cardinality),自动选择不同的压缩算法来匹配最优的时间和空间复杂度。
在时序数据库中,最为常见的数据类型是字符串和浮点数。针对字符串类型的字段,当某个 block 的基数超过特定的阈值,会自动进行字典化。
而在浮点类型的压缩方面,GreptimeDB 将考虑选用 Chimp 压缩算法。真实世界的时序浮点数据,往往变化并不会特别剧烈,这就导致两个相邻的数据点经过按位异或操作后会出现大量头部和尾部的 0,为数据压缩提供了大量空间。
和 Facebook 提出的 Gorilla 相比, Chimp 分析了真实世界的时序数据集,并在此基础上对 Gorilla 算法作出了部分修改,从而在压缩耗时上远远优于传统通用压缩算法(如 zstd、Snappy 等)的同时,获得了比 Gorilla 更高的空间效率。
Serverless 化
GreptimeDB 建索引、压缩数据完全不阻塞数据的写入。通过类似 Compaction 的后台任务完成索引的构建,整个流程可以完全托管给按需付费的弹性函数服务,从而降低索引构建的成本。
基于 LSM Tree 的结构,同时针对时序场景的特点,GreptimeDB 设计的存储引擎,希望通过智能索引和自适应压缩提高数据存储和查询的效率。
在接下来的实现过程中,我们将在后续博客中持续分享所总结的经验,欢迎关注我们的公众号和官方网站: https://greptime.com/blogs/ ,了解 GreptimeDB 更多动态。
如果你对我们计划中的内容感兴趣,或者有自己的想法,也欢迎在 GitHub 和 Slack 频道加入我们,一起共建!
关于 Greptime
Greptime 格睿科技专注于为可观测、物联网及车联网等领域提供实时、高效的数据存储和分析服务,帮助客户挖掘数据的深层价值。目前基于云原生的时序数据库 GreptimeDB 已经衍生出多款适合不同用户的解决方案,更多信息或 demo 展示请联系下方小助手(微信号:greptime)。
欢迎对开源感兴趣的朋友们参与贡献和讨论,从带有 good first issue 标签的 issue 开始你的开源之旅吧~期待在开源社群里遇见你!添加小助手微信即可加入“技术交流群”与志同道合的朋友们面对面交流哦~
Star us on GitHub Now: https://github.com/GreptimeTeam/greptimedb
Twitter: https://twitter.com/Greptime
Slack: https://greptime.com/slack