目录
深入了解LSM树及其发展
LSM Tree 的概念起源于 1996年的论文《The Log Structure Merge Tree》,此后由 Google Bigtable 第一个商业化实现并于 2006 年发表论文《Bigtable:A distributed strorage system for structured data》。
随后,Google 的两位专家基于 BigTable 的经验实现了 LevelDB,一个单机 LSM Tree 存储引擎,并开源。
此后,FaceBook 基于 LevelDB 开发了 RocksDB(非常棒的 KV 数据库,非常值得学习!)!
RocksDB 做了相当多的迭代演进,如:多线程、Column Family(类似于关系型数据库中表的概念)、Compaction策略等。
目前,RocksDB 已经成为 LSM Tree 领域的一个事实标准!
RocksDB 的结构图:
- 写入的数据首先要记录 WAL(Write-ahead Log),用来做实时落盘,以实现持久性。
- 随后,数据有序的写入 Active Memtable 中;同时,Active Memtable 也是这里唯一可变的结构! 在一个 Active Memtable 写满后,就把它转换为 Immutable Memtable。
- 上面两类 Memtable 都在内存中,使用的数据结构基本上是跳跃表(也有vector、hash-skiplist等)
- 当 Immutable Memtable 达到指定的数量后,就将 Immutable Memtable 落盘到磁盘中的 L0 层-----这步操作被称为 minor merge。
- 通常,对于 minor merge 的 Memtable 不做整理(无 Compaction 过程),直接刷入磁盘。因此,L0 层可能会存在重复的数据。
- 当 L0 层的数据满了之后,就会触发 major merge,也就是关键的 Compaction 操作。
- 将 L0 层的数据和 L1 层的数据进行合并,全部整理为 “固定大小的、不可变的数据块”,称为 SSTable(Sorted String Table),并放在 L1 层。
- 这样,除了 L0 层之外的磁盘中的每一层都是由一个个 SST 组成的,这些 SST 之间互不重叠!
- SST 的出现结合后文会讲到的的 Bloom Filter,在很大程度上提升了 LSM Tree 的读性能!
- 并且,L1 和之后层次间的合并,可以仅合并部分重叠的 SST,使 Compaction 过程更加灵活、效率更高。
SSTable 是由 LevelDB 最初实现的一种数据格式,被称为 Sorted String Table(有序字符串表)。
一个 SST 通常由两个部分组成:
- 索引文件:可以是 BTree 或者哈希表
- 数据文件:就是要存储的 KV 数据
可以将 SST 理解为一个小型的聚簇索引结构,只是这个结构整体是不可变的!
一条数据的整体写入过程
一条数据进入到 LSM Tree 后会:
- 首先写入 active memtable,然后进入 immutable memtable,接下来被刷入 L0 层,然后随着 Compaction 操作一层层向下。
- 这个过程如果碰到了更下层的同 key 数据,那么就会将对方合并。
- 如果在 Compaction 过程中遇到了从更高层来的同 key 新的数据,那么就会被合并。
读操作(Bloom Filter优化)
从 LSM Tree 中读取的过程就是从上至下层层扫描,直至找到数据。
在查找的过程中,有一个非常关键的优化,可以加速我们对数据的筛选,那就是:Bloom Filter!
Bloom Filter 用来筛选一层中是否包含我们要查找的数据。
注意到,它可能会返回假阳性
的结果,也就是返回一个 key 在这一层,但是实际查找下来是不存在的!但是一定不会返回假阴性的结果!
即:如果 Bloom Filter 返回一个 key 不在这一层,那么这个 key 一定是不存在的!
通常,如果只有一个 Hash 函数的话,Hash 值重合的概率比较高,误报率较高。因此,可以设置多个 Hash 函数,这样进来一个 key 的话,只有所有 bytes 映射都命中,才需要真正查询,可以极大程度上降低误报率!
但是如果 Hash 函数过多,Bloom Filter 的代价就会过大,占用的内存也会增多。因此需要好好协调,这里是一个重要的调参方向;
合并策略(Merging Policy)
上面讲述的是目前主流的 LSM Tree 的实现,本小节来简单介绍一下另一些 LSM Tree 的实现和探索。
LevelDB 等一系列 LSM Tree 实现采用的方法都是 Leveling Merge Policy 方法。Leveling 合并策略就是将相连两层的数据做合并,然后一起写入下面一层。
而除此之外,还有另一种合并策略,就是Tiering Merge Policy。Tiering 合并策略的每一层都有多个重叠的组件,合并时也并非将相连两层合并,而是将一层中所有组件进行合并,并放入下一层。
相比于 Leveling 合并策略,Tiering 合并策略显而易见的对写入更加友好,但读取的性能会进一步降低:因为每一层也有多个重叠的区域,查找时都是要查找的!
Cassandra 数据库使用的便是 Teiring 合并策略。
LSM-Tree并发控制机制
总体来讲,LSM Tree 因为其天然的 Out-of-place update 特性,在并发控制方面的问题比 BTree 少很多!
对于 LSM Tree 而言,关注的重点主要在于会引起结构变更的操作:
- Memtable 落盘;
- Compaction 过程;
在早期只有一个 Memtable 的情况下,Memtable 的落盘会造成一段时间的不可写!
目前,区分 active memtable 和 immutable memtable 的设计就能在很大程度上避免 memtable 落盘造成的问题。
一些Compaction优化方案
Compaction 一种都是 LSM Tree 的瓶颈所在。Compaction 过程中占用大量资源,并调整数据位置,同时会引发缓冲池中数据的大量丢失,影响 LSM Tree 结构的读取性能,严重情况下,还可能会造成写停顿(Write Stall)!
因此,关于 Compaction 的优化一直也是 LSM 领域的关注重点!
使用 Tiering 合并策略是提升综合写性能、减少写放大的一个重要手段。还有另外一些手段来优化 Compaction:
- 采用流水线技术:将读取、合并、写入三个操作以流水线的形式执行,以增强合并操作的资源利用率,减少耗时。
- 复用组件:在合并的过程中识别出不变的部分并保留。
- 主动更新Cache:在 Compaction 结束后主动更新 Cache,或采用机器学习的方式预测回填。
- 单独硬件执行Compaction:把 Compaction 操作 Offload 到例如 FPGA 等额外的硬件上执行。
本站资源均来自互联网,仅供研究学习,禁止违法使用和商用,产生法律纠纷本站概不负责!如果侵犯了您的权益请与我们联系!
转载请注明出处: 免费源码网-免费的源码资源网站 » 了解数据库中常用存储引擎数据结构(4)
发表评论 取消回复