LSMT存储引擎浅析 | 青训营笔记

157 阅读5分钟

这是我参与「第四届青训营 」笔记创作活动的第14天

LSMT:Log-Structured Merge-Tree

1.LSMT与存储引擎介绍

①LSMT是什么

一言以蔽之,通过Append-only Write + 择机Compact来维护结构的索引树

image.png

  • 首先,数据会写入WAL来确保其原子性
  • 接着,数据会写入Memtable(内存中的索引)
  • 当Memtable写入达到阈值,会冻结生成Immutable(而所有修改都只会在Memtable)
  • Immutable交给flush线程进行写盘

②存储引擎是什么

image.png

以单机数据库MySQL为例,大致可以分为

  • 计算层:主要负责SQL解析/查询优化/计划执行
  • 存储层(存储引擎层):数据库著名的ACID特性,在MySQL中全部强依赖于存储引擎

存储引擎的哪些组件保障了这些特性

  • Atomicity:Write-Ahead Log(WAL)/Redo Log
  • Consistency:依赖于数据整体
  • Isolation:Snapshot/2PL(Phase Lock)
  • Durability:Flusher遵循Sync语意

除了保障ACID以外,存储引擎还要负责

  • 屏蔽IO细节提供更好的抽象
  • 提供统计信息与Predicate Push Down能力

存储引擎不掌控IO细节,让操作系统接管,例如mmap,会有如下问题(因为操作系统并不完全感知数据库的特性)

  • 落盘时机不确定造成的事务不安全
  • IO Stall
  • 错误处理繁琐
  • 无法完全发挥硬件性能

2.LSMT存储引擎的优势与实现

①LSMT与B+Tree的异同

  • 在B+Tree中,数据插入是原地更新的
  • B+Tree在发生不平衡或者节点容量达到阈值后,必须立即进行分裂来平衡

image.png

  • LSMT数据的加入是追加写入的,当树不平衡或者垃圾过多时,有专门的compact线程进行compact(lazy compact延迟compact)
  • LSMT与B+Tree可以用统一模型描述
  • 从高层次的数据结构角度来看,二者没有本质的不同,可以互相转化
  • 工程实践上还是用LSMT来表示一个Append-only和Lazy Compact的索引树,B+Tree来表示一个Inplace-Update和Instant Compact的索引树
  • Append-only和Lazy Compact这两个特性更符合现代计算机设备的特性

②为什么要采用LSMT模型

  • 在计算机存储乃至整个工程界都在利用Indirection(抽象)处理资源的不对称性
  • 存储引擎面对的资源不对称性在不同时期是不同的

HDD时代

  • 顺序与随机操作性能不对称
  • 由于机械硬盘需要磁盘旋转和机械臂移动来进行读写,顺序写吞吐是随机读的25倍

SSD时代

  • 顺序写与随机写性能不对称
  • 由于SSD随机写会给主控带来GC(Garbage Collection)压力,顺序写吞吐是随机写的6倍(随机写会导致逻辑上可用的Page在物理上不可用)

这二者的共性是顺序写是一个对设备很友好的操作,LSMT符合这一点,而B+Tree依赖原地更新,导致随机写

③以RocksDB为例分析LSMT存储引擎的实现

  • RocksDB是一款十分流行的开源LSMT存储引擎,最早来自Facebook(Meta),应用于MyRocks,TiDB等数据库

(1)Write

image.png

  • Rocks DB写入流程主要有两个优化,批量WAL写入(继承自LevelDB)与并发MemTable更新

  • RocksDB在真正执行修改之前会先将变更写入WAL,WAL写成功则写入成功

  • 多个写入者会选出一个Leader,由这个Leader来一次性写入WAL,避免小IO

  • 不要求WAL强制落盘(Sync)时,批量提交亦有好处,Leader可以同时唤醒其余Writer,降低了系统线程调度开销

image.png

  • 没有批量提交的话,只能链式唤醒
  • 链式唤醒加大前台延迟

image.png

  • 写完WAL还要写MemTable

  • RocksDB在继承LevelDB的基础上又添加了并发MemTable写入的优化

  • WAL一次性写入完成后,唤醒所有Writer并行写入MemTable

  • 由最后一个完成MemTable写入的Writer执行收尾工作

(2 )Snapshot&SuperVision

image.png

  • RocksDB的数据由三部分组成,MemTable/ImmemTable/SST。持有这三部分数据并且提供快照功能的组件叫做SuperVersion

  • MemTable和SST的释放依赖于引用计数。对于读取来说,只要拿到SuperVersion,从MemTable一级一级向下,就能查到记录。拿着SuperVersion不释放,等于是拿到了快照

  • 如果所有读者都给SuperVersion的计数+1,读完后再-1,那么这个原子引用计数器就会成为热点。CPU在多核之间同步缓存是有开销的,核越多开销越大

  • 为了让读操作更好的scale,RocksDB做了一个优化:Thread Local SuperVersion Cache

  • 没有Thread Local缓存时,读取操作要频繁Acquire和Release SuperVersion

  • CPU缓存不友好

  • 有Thread Local缓存,读取只需要检查以下SuperVersion并标记Thread Local缓存正在使用即可

  • CPU缓存友好

(3)Get & BloomFilter

  • RocksDB的读取在大框架上和B+Tree类似,就是层层向下
  • 相对于B+Tree。LSMT点查需要访问的数据块更多,为了加速点查,一般LSMT引擎都会在SST中嵌入BloomFilter
  • [1,10]表示这个索引块存储数据的区间在1-10之间。查询2,就是顺着标绿色的块往下

(4)Compact-Level & Tier

  • Compact在LSMT中是将Key区间有重叠或无效数据较多的SST进行合并,以此来加速读取或者回收空间。Compact策略可以分成两大类:Level和Tier

  • Level策略直接来自于LevelDB,也是Rocks DB的默认策略。每一层不允许有SST的Key区间重合(小的SST去和大的SST合并,不够优良)

  • Tier策略允许LSMT每层有多个区间重合的SST(merge的SST大小相近,会有较好的性能)

3.LSMT模型理论分析

①Cloud-Native LSMT Storage Engine - HBase

  • RocksDB是单机存储引擎,那么现在都说云原生,HBase比RocksDB更云一些,SST直接存储于HDFS上
  • 二者在理论存储模型上都是LSMT

②LSMT模型算法复杂度分析

image.png