监控平台设计 之 Prometheus 存储 LevelDB
Posted 魏小言
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了监控平台设计 之 Prometheus 存储 LevelDB相关的知识,希望对你有一定的参考价值。
目录
监控平台设计 之 Prometheus 存储 LevelDB
1.1 LSM-Tree「The Log-Structured Merge Tree」
监控平台设计 之 Prometheus 存储 LevelDB
“ 在为拥抱云原生而生的 Prometheus 称霸市场的背后,是 LevelDB 作为坚实的后盾。LevelDB 的设计都有哪些值得我们学习的呢?”
LevelDB 出色性能背后的设计
LevelDB 采用 LSM-Tree 结构实现,将磁盘的随机写转换成有序写,从而提高写速度。
1.1 LSM-Tree「The Log-Structured Merge Tree」
LSM 顾名思义,日志结构 + Merge「合并」,写入速度快,利用了磁盘的顺序写。在读写性能中折中,适合主put\\get场景,主要针对的场景是写密集、少量查询的场景;主在 key-value 存储系统应用,如 LevelDB,RocksDB,还有分布式行式存储数据库 Cassandra 也用了 LSM-tree 的存储架构。
-
LSM-Tree模型
LSM-Tree 是一个多层结构,就更一个树一样,上小下大。首先是内存的 C0 层,保存了所有最近写入的 (k,v),这个内存结构是有序的,并且可以随时原地更新,同时支持随时查询。剩下的 C1 到 Ck 层都在磁盘上,每一层都是一个在 key 上有序的结构。
-
Put
首先存在 WAL 机制「Write Ahead Log,写入之前记录的日志」中,接下来加到 memory 中 C0 层。
当 C0 层的数据达到一定大小,就把 C0 层 和 C1 层合并,类似归并排序,这个过程就是 Compaction(合并)。
合并出来的新的 new-C1 会顺序写磁盘,替换掉原来的 old-C1。当 C1 层达到一定大小,会继续和下层合并。合并之后所有旧文件都可以删掉,留下新的。
「注意数据的写入可能重复,新版本需要覆盖老版本。什么叫新版本,我先写(a=1),再写(a=233),233 就是新版本了。假如 a 老版本已经到 Ck 层了,这时候 C0 层来了个新版本,这个时候不会去管底下的文件有没有老版本,老版本的清理是在合并的时候做的。」
-
Get
最新的数据在 C0 层,最老的数据在 Ck 层,所以查询也是先查 C0 层,如果没有要查的 k,再查 C1,逐层查。
LevelDB 数据模型
LSM是一种思想模型,而 LevelDB 是现实实现,基于 LSM,但又不同于LSM,更具实操意义。
在 LevelDB 中 LSM-tree 被分成三种文件,第一种是内存中的两个 memtable,一个是正常的接收写入请求的 memtable,一个是不可修改的immutable memtable。另外一部分是磁盘上的 SStable (Sorted String Table),有序字符串表,这个有序的字符串就是数据的 key。SStable 一共有七层(L0 到 L6)。下一层的总大小限制是上一层的 10 倍。
-
Put
首先将写入操作加到写前日志中,接下来把数据写到 memtable中,当 memtable 满了,就将这个 memtable 切换为不可更改的 immutable memtable,并新开一个 memtable 接收新的写入请求。而这个 immutable memtable 就可以刷磁盘了。这里刷磁盘是直接刷成 L0 层的 SSTable 文件,并不直接跟 L0 层的文件合并。
每一层的所有文件总大小是有限制的,每下一层大十倍。一旦某一层的总大小超过阈值了,就选择一个文件和下一层的文件合并。
「数据重复场景:L0 层的多个文件在同一层,有先后关系的,后面的同个 key 的数据也会覆盖前面的。会为每个key-value加个版本号。在 Compaction 时候应该只会留下最新的版本」
-
Get
先查 memtable,再查 immutable memtable,然后查 L0 层的所有文件,最后一层一层往下查。
2.1 读写放大问题
读写放大(read and write amplification)是 LSM-tree 的主要问题,这么定义的:读写放大 = 磁盘上实际读写的数据量 / 用户需要的数据量。注意是和磁盘交互的数据量才算,这份数据在内存里计算了多少次是不关心的。比如用户本来要写 1KB 数据,结果你在内存里计算了1个小时,最后往磁盘写了 10KB 的数据,写放大就是 10,读也类似。
-
写放大
我们以 RocksDB 的 Level Style Compaction 机制为例,这种合并机制每次拿上一层的所有文件和下一层合并,下一层大小是上一层的 r 倍。这样单次合并的写放大就是 r 倍,这里是 r 倍还是 r+1 倍跟具体实现有关。
我们举个例子:假如现在有三层,文件大小分别是:9,90,900,r=10。又写了个 1,这时候就会不断合并,1+9=10,10+90=100,100+900=1000。总共写了 10+100+1000。按理来说写放大应该为 1110/1,但是各种论文里不是这么说的,论文里说的是等号右边的比上加号左边的和,也就是10/1 + 100/10 + 1000/100 = 30 = r * level。个人感觉写放大是一个过程,用一个数字衡量不太准确,而且这也只是最坏情况。
-
读放大
为了查询一个 1KB 的数据。最坏情况需要读 L0 层的 8 个文件,再读 L1 到 L6 的每一个文件,一共 14 个文件。而每一个文件内部需要读 16KB 的索引,4KB的布隆过滤器,4KB的数据块(看不懂不重要,只要知道从一个SSTable里查一个key,需要读这么多东西就可以了)。一共 24*14/1=336倍。key-value 越小读放大越大。
「而写放大、读放大、空间放大,三者就像 CAP 定理一样,需要做好权衡和取舍。」
推荐阅读:
以上是关于监控平台设计 之 Prometheus 存储 LevelDB的主要内容,如果未能解决你的问题,请参考以下文章
监控平台设计 之 GraphitePrometheus 竞对
监控平台设计 之 GraphitePrometheus 竞对