何时(不)使用 varbit 块
2016年5月8日作者 Björn “Beorn” Rabenstein
Prometheus 服务器的嵌入式时间序列数据库 (TSDB) 以 1024 字节的固定大小块来组织每个时间序列的原始样本数据。除了原始样本数据之外,块还包含一些元数据,这允许为每个块选择不同的编码。最基本的区别是编码版本。您可以通过命令行标志 -storage.local.chunk-encoding-version 为新创建的块选择版本。到目前为止,只有两个支持的版本:0 代表原始的 delta 编码,1 代表改进的双 delta 编码。随着 0.18.0 版本的发布,我们增加了版本 2,这是另一种双 delta 编码。我们称之为varbit 编码,因为它涉及块内每个样本的可变位宽。虽然版本 1 在几乎所有方面都优于版本 0,但版本 1 和 2 之间存在真正的权衡。这篇博文将帮助您做出决定。版本 1 仍然是默认编码,因此如果您在阅读本文后想尝试版本 2,则必须通过命令行标志显式选择它。来回切换没有坏处,但请注意,已创建的块一旦创建就不会更改其编码版本。但是,这些块将根据配置的保留时间逐渐淘汰,从而被具有命令行标志中指定的编码的块所取代。
什么是 varbit 编码?
从一开始,我们就设计了分块样本存储,以便于添加新编码。当 Facebook 发布了他们关于内存 TSDB Gorilla 的论文时,我们对 Gorilla 和 Prometheus 独立开发的方法之间的许多相似之处感到着迷。但是,也存在许多根本性的差异,我们对其进行了详细研究,思考是否能从 Gorilla 中获得灵感来改进 Prometheus。
在我难得的空闲周末,我决定尝试一下。经过一番编码,我实现了后来(经过大量的测试和调试)成为 varbit 编码的功能。
在未来的博文中,我将详细描述编码的技术细节。现在,您只需要了解一些特性,以便在新的 varbit 编码和传统的双 delta 编码之间做出选择。(我将后者简称为“双 delta 编码”,但请注意,varbit 编码也使用双 delta,只是方式不同。)
varbit 编码的优势是什么?
简而言之:它提供了更好的压缩比。双 delta 编码在真实数据集上每个样本需要约 3.3 字节,而在 SoundCloud 的典型大型生产服务器上,varbit 编码每个样本仅需 1.28 字节。这几乎是三倍的空间效率(甚至略优于 Gorilla 报告的每样本 1.37 字节——但请注意,SoundCloud 的典型数据集可能与 Facebook 的不同)。
现在想想其影响:RAM 中样本数量增加三倍,磁盘上样本数量增加三倍,磁盘 I/O 减少三分之二,并且由于磁盘 I/O 目前是摄取速度的瓶颈,摄取速度也将提高三倍。事实上,最近报告的每秒 800,000 个样本的新摄取记录只有通过 varbit 块才可能实现——当然,还要配合 SSD。对于旋转硬盘,瓶颈会更早出现,因此 3 倍的增益就显得更加重要。
这一切听起来好得令人难以置信……
那么,有什么问题呢?
首先,varbit 编码更复杂。因此,编码和解码值的计算成本有所增加,这从根本上影响了所有写入或读取样本数据的操作。幸运的是,这只是通常只占操作总成本一小部分的东西的比例性增加。
varbit 编码的另一个更重要的特性是:varbit 块中的样本只能顺序访问,而双 delta 编码块中的样本可以通过索引随机访问。由于 Prometheus 中的写入是追加写入,因此不同的访问模式仅影响样本数据的读取。实际影响在很大程度上取决于原始 PromQL 查询的性质。
一个相对无害的情况是检索时间间隔内的所有样本。当评估范围选择器或渲染具有类似于抓取频率的分辨率的仪表板时,就会发生这种情况。Prometheus 存储引擎需要找到间隔的起始点。对于双 delta 块,它可以执行二分查找,而对于 varbit 块,它必须顺序扫描。但是,一旦找到起始点,间隔内的所有剩余样本都需要顺序解码,这对于 varbit 编码来说只会稍微昂贵一些。
对于从块中检索少量不相邻样本,或者在所谓的即时查询中检索单个样本,权衡是不同的。潜在地,存储引擎可能必须遍历大量样本才能找到要返回的少数样本。幸运的是,即时查询最常见的来源是规则评估,它引用每个涉及时间序列的最新样本。并非完全巧合,我最近改进了时间序列的最新样本检索。本质上,时间序列中添加的最后一个样本现在被缓存了。需要时间序列最新样本的查询甚至不再命中块层,在这种情况下,块编码无关紧要。
即使即时查询引用了过去的样本,因此必须命中块层,但查询的其他部分(例如索引查找)很可能会主导总查询时间。但是,确实存在一些实际查询,其中 varbit 块所需的顺序访问模式将开始变得非常重要。
varbit 块的最坏情况查询是什么?
varbit 块的最坏情况是,如果您只需要一个非常长的时间序列的每个块中间的样本。不幸的是,这种情况确实存在。假设一个时间序列的压缩效果很好,使得每个块持续约八小时。这大约是每天三个块,或每月约 100 个块。如果您有一个仪表板,以 100 个数据点的分辨率显示过去一个月中的时间序列,仪表板将执行一个查询,从 100 个不同的块中检索一个样本。即使如此,块编码之间的差异也会被查询执行的其他部分所掩盖。根据情况,我的猜测是,使用双 delta 编码的查询可能需要 50 毫秒,而使用 varbit 编码的查询需要 100 毫秒。
但是,如果您的仪表板查询不仅涉及单个时间序列,而是聚合了数千个时间序列,则需要访问的块数量会相应增加,顺序扫描的开销将成为主导。(此类查询不被提倡,我们通常建议对这类频繁使用的查询使用记录规则,例如在仪表板中。)但是,使用双 delta 编码,查询时间可能仍然可以接受,比如说大约一秒。切换到 varbit 编码后,相同的查询可能需要数十秒,这显然不是您想要的仪表板。
经验法则是什么?
尽可能简单地说:如果您既不受到磁盘容量的限制,也不受到磁盘 I/O 的限制,请不要担心,并坚持使用经典的双 delta 编码的默认设置。
但是,如果您希望更长的保留时间,或者您目前受到磁盘 I/O 的瓶颈,我邀请您尝试新的 varbit 编码。使用 -storage.local.chunk-encoding-version=2 启动您的 Prometheus 服务器,并等待一段时间,直到您拥有足够多的具有 varbit 编码的新块来评估效果。如果您发现某些查询变得难以接受地慢,请检查您是否可以使用记录规则来加速它们。即使使用旧的双 delta 编码,这些查询很可能也会从中获益良多。
如果您有兴趣了解 varbit 编码在后台是如何工作的,请继续关注不久的将来另一篇博文。