这是我参与「第四届青训营」笔记创作活动的第15天。
1. LSMT 与存储引擎
LSMT 简介
LSMT(Log-Structured Merge-Tree)由1996年提出。B-Tree在1970年就已经提出。早期一般使用B-Tree作为索引,如MySOL。2000年后大多使用LSMT索引。
LSMT就是通过Append-only Write+择机Compact来维护结构的索引树。
存储引擎简介
以mySQL为例,数据库可以分为 计算层 和 存储层(存储引擎)。计算层主要负责SQL解析/查询优化/计划执行。数据库的ACID特性,在mysql中依赖于存储引擎。
ACID
-
Atomicity: 原子性依赖于存储引擎 WAL(Redo Log)
-
Consistency (Correctness) :依赖于数据库整体
-
Isolation: 一般的是实现是 2PL(2 Phase Lock) + MVCC。2PL 可以简单理解为对所有需要修改的资源上锁。
-
Durability:持久性依赖于存储引擎确保在 Transaction Commit 后通过操作系统 fsync 之类的接口确保落盘了
存储引擎
-
屏蔽 IO 细节提供更好的抽象
IO 是一种具体实现很复杂,但是逻辑边界很清晰的任务,存储引擎需要屏蔽不同 IO 硬件设备(HDD,SSD,PMem etc),不同系统 API(pread / libaio / iouring)的差别,给出统一的抽象。 -
提供统计信息与 Predicate Push Down 能力
数据库绝大部分持久化状态和数据都存放在存储引擎里,因此存储引擎相比于上层有着对数据更准确的信息。例如 RocksDB 提供了 ApproxSize 接口,可以让优化器在估算代价的时候,得到区间内大概有多少元素,生成更优的执行计划。
存储引擎不掌控IO细节,操作系统接管,比如使用 mmap 接口。由于操作系统并不完全感知数据库任务的特性,会造成以下问题:
- 事务不安全
- IO Stall 不可控
- 错误处理
- 无法完全发挥硬件性能
2. LSMT 存储引擎的优势与实现
LSMT 与 B+Tree
在 B+Tree 中,数据插入是原地更新的,装有 (10, 20, 30, 40) 的节点在插入和分裂后,原节点覆写成 (10, 15)。此外,B+Tree 在发生不平衡或者节点容量到达阈值后,必须立即进行分裂来平衡。
反观 LSMT,数据的插入是追加的(Append-only),当树不平衡或者垃圾过多时,有专门 Compact 线程进行 Compact,可以称之为延迟(Lazy)的。
从高层次的数据结构角度来看,B+Tree 和 LSMT 并没有本质的不同,可以统一到一个模型里,根据 Workload 的不同互相转换。
相对于 B+Tree 优势
-
顺序写模型对于 SSD 设备更友好
-
SST 不可修改的特性使得其能使用更加紧凑的数据排列和加上压缩
-
后台延迟 Compact 能更好利用 CPU 多核处理能力,降低前台请求延迟
相对于 HashTable 的优势
LSMT 存储引擎是有序索引抽象,HashTable 是无序索引抽象。无序索引是有序索引的真子集。LSMT 相比于 HashTable 更加通用。 LSMT 能处理 TopK 请求,但 HashTable 就不行了。为了避免维护多套存储引擎,绝大多数数据库都直接采用一套有序的存储引擎而非针对点查和顺序读取分别维护两个引擎。
小结
早期一般使用B-Tree作为索引,如MySOL。2000年后大多使用LSMT索引。B+Tree 中,数据插入是原地更新的,LSMT数据的插入是追加的。