了解数据库中常用存储引擎数据结构(4)

目录

深入了解LSM树及其发展

一条数据的整体写入过程

读操作(Bloom Filter优化)

合并策略(Merging Policy)

LSM-Tree并发控制机制

一些Compaction优化方案


深入了解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 等额外的硬件上执行。

视频地址B+树,B-link树,LSM树...一个视频带你了解常用存储引擎数据结构(合集)_哔哩哔哩_bilibili

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值