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

83 阅读4分钟

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

0 引言

思维导图.png

1 LSMT与存储引擎介绍

Log-Structured Merge-Tree(LSMT):通过Append-only Write+择机Compact来维护结构的索引树。

关系型单机MySQL数据库包含:计算层(SQL解析/查询优化/计划执行)、存储层(存储引擎层)。

存储引擎职责:保障ACID、屏蔽IO细节提供更好的抽象、提供统计信息与Predicate Push Down能力。

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

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

工程实践上还是用LSMT来表示一个Append-only和Lazy Compact的索引树,B+Tree来表示一个lnplace-Update和Instant Compact的索引树。

为什么要采用LSMT模型
HDD(顺序与随机操作性能不对称)时代,顺序操作远快于随机操作。SSD(顺序写与随机写性能不对称)时代,顺序写操作远快于随机写操作。因此,这二者的共性是顺序写是一个对设备很友好的操作,LSMT符合这一点,而B+Tree依赖原地更新,导致随机写。

LSMT存储引擎(RocksDB)
(1)Write:
LSMT存储引擎.png

  • RocksDB写入流程主要有两个优化,批量WAL写入(继承自LevelDB)与并发MemTable更新。
  • RocksDB在真正执行修改之前会先将变更写入WAL,WAL写成功则写入成功。
  • 写完WAL还要写MemTable。
  • RocksDB在继承LevelDB的基础上又添加了并发MemTable 写入的优化。
    Write2.jpeg
  • WAL一次性写入完成后,唤醒所有Writer并行写入 MemTable。
  • 由最后一个完成 MemTable 写入的Writer执行收尾工作。

Write.jpeg
多个写入者会选出一个Leader,由这个Leader来一次性写入WAL,避免小IO。
不要求WAL强制落盘(Sync)时,批量提交亦有好处,Leader可以同时唤醒其余Writer,降低了系统线程调度开销。

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

(2)Snapshot & SuperVision:
SuperVision.jpeg RocksDB的数据由3部分组成,MemTable/ lmmemTable / SST。持有这三部分数据并且提供快照功能的组件叫做SuperVersion。
MemTable和SST的释放依赖于引用计数。对于读取来说,只要拿着SuperVersion,从MemTable一级一级向下,就能查到记录。拿着SuperVersion不释放,等于是拿到了快照。

为了达到CPU缓存友好:有Thread Local缓存,读取只需要检查一下SuperVersion并标记ThreadLocal缓存正在使用即可。

(3)Get & BloomFilter:
LSMT点查需要访问的数据块更多。为了加速点查,一般LSMT引擎都会在SST中嵌入 BloomFilter。

(4)Compact - Level和Tier:
Compact在LSMT中是将Key区间有重叠或无效数据较多的SST进行合并,以此来加速读取或者回收空间。Compact策略可以分为两大类,Level和Tier。
Level.png
Level策略直接来自于LevelDB,也是 RocksDB的默认策略。每一个层不允许有SST的Key区间重合。

Tier.png
Tier策略允许LSMT每层有多个区间重合的SST。

3 LSMT模型理论分析

云原生层面,HBase比RocksDB就更云一些,SST直接存储于HDFS上。二者在理论存储模型上都是LSMT。 HBase.png

T:size ratio,每层LSMT比上一层大多少,LO大小为1,则 L1大小为T,L2为TA2,以此类推
L:level num,LSMT层数
B:每个最小的IO单位能装载多少条记录
M:每个 BloomFilter有多少bits
N:每个BloomFilter生成时用了多少条Key
S:区间查询的记录数量

Level写(Write)操作的复杂度:O(WriteLevel)=LT1/B=TL/B{ O(Write_Level)=L* T*1/B = T * L/B }
点查(Point Lookup)复杂度:O(PointLookupLevel)=Le(M/N){ O(PointLookup_Level) = L* e^(-M/N) }

Tier写(Write)操作的复杂度:O(WriteTier)=L11/B=L/B{ O(Write_Tier)= L* 1 * 1/B = L/B }
点查(Point Lookup)复杂度:O(PointLookupTier)=LTe(M/N)=TLe(M/N){ O(PointLookup_Tier) = L* T* e^(-M/N) = T* L* e^(-M/N) }

总结,Tier策略降低了写放大,增加了读放大和空间放大,Level策略增加了写放大,降低了读和空间放大。

4 LSMT存储引擎调优案例与展望

TerarkDB aka LavaKV是字节跳动自研的KV分离功能。(Value较长的记录的Value单独存储) TerarkDB.png

TerarkDB & Abase & ByteGraph

TerarkDB & Flink

引用参考

【大数据专场 学习资料五】第四届字节跳动青训营

# 老师推荐的一些论文和文章
The Log-Structured Merge-Tree (LSM-Tree)
https://github.com/facebook/rocksdb/wiki/RocksDB-Overview
MyRocks:LSM-Tree Database Storage Engine Serving Facebook's Social Graphhttps://www.geeksforgeeks.org/insertion-in-a-b-tree/
Design Continuums and the Path Toward Self-Designing Key-Value Stores that Know and Learn
Are You Sure You Want to Use MMAP in Your Database Management System?https://zhuanlan.zhihu.com/p/470109297
LSM-based Storage Techniques: A Survey
WiscKey: Separating Keys from Values in SSD-conscious Storage