HBase、Cassandra、LevelDB、RocksDB底层数据结构是什么?

大家好,我是球哥。没啥球用的球,目前在965互联网公司做架构。今天分享开源数据框架中最最常用的底层数据结构。

一、概述

当前已被广泛运用在一些开源的数据产品中,如:HBase、Cassandra、LevelDB、RocksDB等等,他们的底层数据结构都用了什么呢?答案是: LSM树(Log-Structured-Merge-Tree 日志结构合并树),它是Google发表的论文 Big Table 中提到的一种很有趣的文件组织数据结构,这篇文章将详细讲解LSM,有不对之处,欢迎指正。

LSM的核心思想是放弃部分读的能力,换取最大化提升写入的能力。LSM的思路非常简单,就是假定内存足够大,那么每次数据更新就不需要将数据写入到磁盘了,而是将数据驻留在内存中,等积累到一定程度之后,再使用归并排序的方式,将内存中的数据合并追加到磁盘的末尾(由于LSM Tree是有序的,可以通过合并排序的方式快速合并到磁盘中),本质的原因是磁盘随机操作慢,顺序读写快的问题。所以要提升些的吞吐量,最好的办法就是将写入的文件添加到文件后面,因为完全顺序,所以可以提供非常好的写操作的性能,可以达到磁盘的理论速度。

492ca92f836178778a95ee1e89ca81ac.png

磁盘顺序写的性能有些颠覆我们的常识的,如上图的报告,磁盘顺序写的吞吐量甚至能够超过内存随机写的吞吐量。而LSM树正是利用了这一点,它通过将磁盘随机写操作转化为顺序写操作,从而将随机写操作的吞吐量提高了好几个数量级。


二、LSM Tree 存储模型

WAL

通过WAL的方式将操作备份到磁盘中,以防止因为内存掉电而丢失数据。在很多数据库都有类似的设计,当插入一条数据时,数据先顺序写入到 WAL 文件中,之后插入到内存中的MemTable。这样既保证了数据的持久化,也不会丢失数据,并且文件的顺序写,效率非常高,不会影响插入的性能。当服务器down机了,可以从WAL文件中重新恢复内存中的 Mem Table。

MemTable

Mem Table对应WAL文件,是WAL文件在内存中的存储结构,通常采用SkipList来实现,MemTable提供了 k-v 数据的写入、删除以及读取的操作接口。以及内部将 k-v对 按照key值有序存储,这样方面之后快速序列化到SSTable文件中,保存到SSTable文件仍然保存了数据的有序性。

Immutable MemTable

Immutable MemTable就是在内存中只读的MemTable,由于内存有限,通常我们会设置一个阈值,当超过这个阈值,则将MemTable的数据转化成Immutable MemTable,然后系统生成新的MemTable供读写继续写入。

使用 Immutable Memtable的本质,就是为了避免将MemTable中的内容序列化到磁盘中时会阻塞写操作。

SSTable

SSTable(Sorted String Table)即为有序键值对集合,是 MemTable 中的数据在磁盘上的有序存储,其内部数据是根据key有序排列的。通常为了加快查询速度,需要在SSTable中加入数据索引,这样可以快速定位到指定的 k-v 数据。

03ef806c1db8f2f7987d037cf402a828.png

MemTable的数据最终会转化为SSTable保存在磁盘中,后续相应的SSTable会进行合并操作,这也是LSM树结构的重点。最终LSM树结构可以由下图表示:

98985cd52b8459e7efc63f12ff052525.png

SSTable 通常采用的分级的结构,例如 LevelDB 和 Hbase 就是如此。MemTable 中的数据达到指定阀值后会在 Level 0 层创建一个新的 SSTable。当某个 Level 下的文件数超过一定数量后,就会将这个 Level 下的一个 SSTable 文件和更高一级的 SSTable 文件合并,由于 SSTable 中的 k-v 数据都是有序的,相当于是一个多路归并排序,所以合并操作相当快速,最终生成一个新的 SSTable 文件,将旧的文件删除,这样就完成了一次合并过程。


三、LSM增删改查

写入

LSM Tree 写入效率非常高,只需要在 WAL 文件中顺序写入当次操作的内容,成功之后将该 k-v 数据写入 MemTable 中即可。尽管做了一次磁盘 IO,但是由于是顺序追加写入操作,效率非常高,并不会导致写入速度的降低。数据写入 MemTable 中其实就是往 SkipList 中插入一条数据,过程也相当简单高效。

更新

LSM Tree其实并不存在真正的更新,更新的操作和写入是完全一样,只是在读取的时候,会从 Level0 层的SSTable文件开始查询数据,数据在低层次的SSTable文件中必然比高层的文件中要更新,所以总能读取到最新那条的数据。也就是说此时在整个LSM Tree中可能会同时存在多个key值相同的数据,只有在SSTable Compaction的过程中才会删除旧的数据。

删除

删除一条记录的操作本质也是一次写入操作,LSM视线中并不立即将数据从文件中删除,而是记录下对这个 key 的删除操作标记,而删除操作插入的是 k-del 标记,如delKey:8888,只有在SSTable Compaction的过程中才会删除数据。

Compaction

Compaction是LSM树最核心的操作,Compaction主要有两个作用:

1、将内存的数据合并到磁盘。

2、随着时间的推移,内存数据合并到磁盘后会产生很多小文件,并且更新和删除的操作会产生大量冗余的数据,通过合并可以减少LSM树的冗余数据,并降低读操作线性扫描的延迟。

目前广泛使用的合并策略有两种:

size-tiered策略

size-tiered策略当数据到达一定规模之后,则将这些集合合并为一个大的集合。比如有5个50个数据的集合,那么就将他们合并为一个250个数据的集合。这种策略有一个缺点是当集合达到一定的数据量后,合并操作会变得十分的耗时。

leveled策略

size-tiered策略因为会产生大数据量的集合,所以会造成突发的IO和CPU资源的消耗,所以leveled策略使用了分层的数据结构来代替原来的大数据集合。

leveled策略将集合的大小限制在一个小的范围内如5MB,而且将集合划分为不同的层级。每一个层级的集合总大小是固定且递增的。如第一层为50MB,第二层为500MB...。当某一层的数据集合大小达到上限时,就会从这一层中选出一个文件和下一层合并,或者直接提升到下一层。如果在合并过程中发现了数据冲突,则丢弃下一层的数据,因为低层的数据总是最新的。

同时leveled策略会限制,除第一层外。其他的每一层的键值都不会重复。这是通过合并时剔除冗余数据实现的,以此来加速在同一层内数据的线性扫描速度。


读取优化

LSM Tree的读取效率并不高,当需要根据Key读取数据时,首先要在内存的 MemTable 和 Immutable MemTable 中查找,如果没有找到,则继续从Level 0 层开始,找不到就继续从更高层的SSTable文件中查找,如果查找失败,说明整个LSM Tree中都不存在,如果在每个Level找到该Key的数据,那么就是最新的数据,因为删除和更新都是写入操作,Level层次越低的文件,数据肯定是最新的。由于每一次的SSTable文件的key值范围是不重复的,所以只需要查找其中一个SSTable文件即可确定key的数据是否存在于这一层。但是Level0比较特殊,因为数据是 Immutable MemTable 直接写入此层的,所以 Level 0 层的 SSTable 文件的 key 值范围可能存在重复,查找数据时有可能需要查找多个文件。

由于SSTable文件分多层,而且还要查询MemTable 、 Immutable MemTable 、读取效率会很差,通常在实现过程中进行一些优化,例如 LevelDB 中的 Mainfest 文件,这个文件记录了 SSTable 文件的一些关键信息,例如 Level 层数,文件名,最小 key 值,最大 key 值等,这个文件通常不会太大,可以放入内存中,可以帮助快速定位到要查询的 SSTable 文件,避免频繁读取。

另外一个经常使用的方法是布隆解析器(Bloom filter),布隆解析器是一个使用内存判断文件是否包含一个关键字的有效方法。


四、总结

LSM Tree 思想是把随机写入转化成顺序写入,这样可以大幅度提升写入的性能,但是查询性能会有一部分牺牲。

在时序数据库运用这一特性非常合适。持续写入数据量大,数据和时间,将时间编码到 key 值中很容易使 key 值有序。读取操作通常是根据某个Key的值,去获取一段时间范围内的数据。这样就把 LSM Tree 读取性能差的劣势缩小了,反而因为数据在 SSTable 中是按照 key 值顺序排列,读取大块连续的数据时效率也很高。

参考文档 : https://queue.acm.org/detail.cfm?id=1563874

往期回顾

卷不动了,我选择降薪去外企来平衡工作和生活

从Log4j2原理、攻击和解决方案来聊聊这次全球性的Log4j2漏洞

2022大厂开始反内卷,阿里和蚂蚁升级员工福利,打响第一枪

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值