何时(不)使用 varbit 数据块

Prometheus 服务器的嵌入式时间序列数据库(TSDB)将每个时间序列的原始样本数据组织成大小恒定的 1024 字节的数据块。除了原始样本数据外,一个数据块还包含一些元数据,这允许为每个数据块选择不同的编码。最根本的区别是编码版本。您可以通过命令行标志 -storage.local.chunk-encoding-version 为新创建的数据块选择版本。到目前为止,仅支持两个版本:0 表示原始的增量编码,1 表示改进的双重增量编码。在 0.18.0 版本中,我们添加了版本 2,它是另一种双重增量编码的变体。我们称其为 varbit 编码,因为它涉及数据块内每个样本的可变位宽。虽然版本 1 在几乎所有方面都优于版本 0,但版本 1 和 2 之间存在真正的权衡。这篇博文将帮助您做出决定。版本 1 仍然是默认编码,因此如果您在阅读本文后想尝试版本 2,则必须通过命令行标志显式选择它。来回切换不会造成任何损害,但请注意,一旦创建了现有数据块,它们就不会更改其编码版本。但是,这些数据块将根据配置的保留时间逐步淘汰,并因此被命令行标志中指定的编码的数据块替换。

什么是 varbit 编码?

从一开始,我们就为分块样本存储设计了易于添加新编码的机制。当 Facebook 发表了一篇关于他们内存 TSDB Gorilla 的 论文 时,我们对 Gorilla 和 Prometheus 独立开发的方法之间的许多相似之处感到好奇。然而,也存在许多根本性的差异,我们对此进行了详细研究,想知道是否可以从 Gorilla 中获得一些启发来改进 Prometheus。

在我前面难得有一个空闲的周末时,我决定尝试一下。在一次编码狂欢中,我实现了后来(经过大量的测试和调试)成为 varbit 编码的东西。

在未来的博文中,我将描述该编码的技术细节。现在,您只需要了解一些特征,以便在新 varbit 编码和传统的双重增量编码之间做出决定。(我以后将后者简称为“双重增量编码”,但请注意,varbit 编码也使用双重增量,只是方式不同。)

varbit 编码的优势是什么?

简而言之:它提供了更好的压缩率。虽然双重增量编码对于实际数据集每个样本需要大约 3.3 个字节,但 varbit 编码在 SoundCloud 的典型大型生产服务器上每个样本低至 1.28 个字节。这几乎是三倍的空间效率(甚至略好于 Gorilla 报告的每个样本 1.37 个字节 - 但请谨慎对待,因为 SoundCloud 的典型数据集可能与 Facebook 的典型数据集不同)。

现在考虑一下其影响:RAM 中的样本是原来的三倍,磁盘上的样本是原来的三倍,磁盘操作只有三分之一,并且由于磁盘操作目前是摄取速度的瓶颈,因此它还将允许摄取速度提高三倍。实际上,最近报告的每秒 800,000 个样本的新的摄取记录只有在使用了 varbit 数据块的情况下才有可能 - 并且显然使用了 SSD。使用机械硬盘,瓶颈会更早达到,因此 3 倍的增益更为重要。

这一切听起来好得令人难以置信…

那么问题在哪里?

首先,varbit 编码更复杂。因此,编码和解码值的计算成本有所增加,这从根本上影响了所有写入或读取样本数据的操作。幸运的是,这只是成比例的增加,而这通常只占操作总成本的一小部分。

varbit 编码的另一个属性可能更相关:varbit 数据块中的样本只能按顺序访问,而双重增量编码数据块中的样本可以通过索引随机访问。由于 Prometheus 中的写入是仅追加的,因此不同的访问模式只会影响样本数据的读取。实际影响很大程度上取决于始发 PromQL 查询的性质。

一个非常无害的例子是检索时间间隔内的所有样本。当评估范围选择器或渲染分辨率与抓取频率相似的仪表板时,会发生这种情况。Prometheus 存储引擎需要找到间隔的起始点。对于双重增量数据块,它可以执行二进制搜索,而对于 varbit 数据块,它必须按顺序扫描。然而,一旦找到起始点,间隔中的所有剩余样本都需要按顺序解码,而 varbit 编码只会稍微增加成本。

对于从数据块中检索少量不相邻的样本,或者简单地在所谓的即时查询中检索单个样本,其权衡是不同的。存储引擎可能必须迭代大量的样本才能找到要返回的少数几个样本。幸运的是,即时查询最常见的来源是引用每个涉及的时间序列中最新样本的规则评估。并非完全巧合的是,我最近改进了时间序列最新样本的检索。本质上,现在会缓存添加到时间序列的最后一个样本。仅需要时间序列最新样本的查询甚至不会再访问数据块层,并且数据块编码在这种情况下无关紧要。

即使即时查询引用了过去的样本,因此必须访问数据块层,查询的其他部分(如索引查找)也很有可能主导总查询时间。但是,在某些实际查询中,varbit 数据块所需的顺序访问模式将开始变得非常重要。

varbit 数据块最坏情况的查询是什么?

对于 varbit 数据块而言,最坏的情况是,如果您只需要从非常长的时间序列的每个数据块的中间某个位置提取一个样本。不幸的是,这种情况确实存在实际的用例。假设一个时间序列的压缩效果足够好,可以使每个数据块持续大约八个小时。那大约是一天三个数据块,或者一个月大约 100 个数据块。如果您的仪表板显示过去一个月的问题时间序列,分辨率为 100 个数据点,则仪表板将执行一个查询,从 100 个不同的数据块中检索单个样本。即便如此,数据块编码之间的差异也会被查询执行时间的其他部分所主导。根据具体情况,我猜想,使用双重增量编码查询可能需要 50 毫秒,而使用 varbit 编码可能需要 100 毫秒。

但是,如果您的仪表板查询不仅触及单个时间序列,而且还汇总了数千个时间序列,那么要访问的数据块数量也会相应增加,并且顺序扫描的开销将变得主要。(此类查询不受欢迎,我们通常建议对频繁使用的此类查询(例如在仪表板中)使用记录规则。)但是,使用双重增量编码,查询时间可能仍然可以接受,例如在一秒钟左右。切换到 varbit 编码后,相同的查询可能会持续数十秒,这显然不是您想要的仪表板效果。

经验法则是什么?

尽可能简单地说:如果您不受磁盘容量或磁盘操作的限制,请不要担心并坚持使用经典的双重增量编码的默认设置。

但是,如果您想要更长的保留时间,或者您当前在磁盘操作方面遇到了瓶颈,我邀请您尝试新的 varbit 编码。使用 -storage.local.chunk-encoding-version=2 启动您的 Prometheus 服务器,并等待一段时间,直到您有足够多的使用 varbit 编码的新数据块来评估效果。如果您看到查询速度变得不可接受地慢,请检查是否可以使用记录规则来加快速度。即使使用旧的双重增量编码,这些查询也很有可能从中受益。

如果您有兴趣了解 varbit 编码在幕后如何工作,请继续关注不久的将来发布的另一篇博文。