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

目录

深入了解LSM树及其发展

一条数据的整体写入过程

[读操作(Bloom Filter优化)](#读操作(Bloom Filter优化))

[合并策略(Merging Policy)](#合并策略(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

相关推荐
努力写代码的熊大4 小时前
List迭代器和模拟(迭代器的模拟)
数据结构·windows·list
长路归期无望5 小时前
C语言小白实现多功能计算器的艰难历程
c语言·开发语言·数据结构·笔记·学习·算法
恒悦sunsite5 小时前
Ubuntu之apt安装ClickHouse数据库
数据库·clickhouse·ubuntu·列式存储·8123
奥尔特星云大使6 小时前
MySQL 慢查询日志slow query log
android·数据库·mysql·adb·慢日志·slow query log
来自宇宙的曹先生6 小时前
MySQL 存储引擎 API
数据库·mysql
间彧6 小时前
MySQL Performance Schema详解与实战应用
数据库
间彧6 小时前
MySQL Exporter采集的关键指标有哪些,如何解读这些指标?
数据库
weixin_446260856 小时前
Django - 让开发变得简单高效的Web框架
前端·数据库·django
mpHH6 小时前
babelfish for postgresql 分析--todo
数据库·postgresql
zizisuo7 小时前
解决在使用Lombok时maven install 找不到符号的问题
java·数据库·maven