OceanBase的内存结构存储 LSM-Tree
OceanBase的内存结构存储 LSM-Tree
前言
当下更多较新的数据库选择LSM-Tree作为存储结构,包括OceanBase、Leveldb、Cassandra、MyRocks(RocksDB)、TiDB(RocksDB)都是以LSM-Tree作为存储结构实现的存储引擎,本文对是在学习OB中关于存储引擎的一些总结,先LSM-Tree做个介绍,逐步延展到OceanBase内存及转储/合并管理
LSM-Tree简介
对于数据库IO密集型应用,有着各种优化方式去减少对磁盘的开销,例如:buffer , Redo顺序写、Inster Buffer等,能充分发挥出磁盘的优势是关键,对于磁盘的顺序写性能要好于随机写,尤其是在HDD时代IOPS只有一百出头,但是吞吐可以到200多MB/s,LSM-Tree与B-Tree核心的区别是将随机写转化为顺序写
LSM每次都通过Append方式进行写入,这种方式可以很大程度提升了写入性能,RocksDB是以LSM-Tree实现的存储引擎,从图中看到数据写入先写到内存中Memtable,当Memtable满后会生成一个新的Memtable,将满的Memtable刷新到磁盘中的SST file中,SST file是一种分层结构,每层存储多个SST file。
L0层为最高层存储最新的数据,Ln为最低层存储版本最老的数据 ,每层存储数据大小逐层增加

LSM-Tree写优势明显,但对于读性能是有影响,因为每一次都是Append方式写入,删除和修改也是插入,可能要查询的数据版本比较老,查询时访问的数据在内存中不能命中,这时就需要访问SST file,极端情况下可能会访问多层中的多个SST file这就放大了读。并且Append方式写入对空间也是一种放大,存在大量无效的数据。因此LSM-Tree中最重要一点就是Compaction操作,对文件做合并减少读放大和空间放大
我们可以总结出LSM-Tree中三个"放大":
-
- 读放大: 读取的数据量/实际返回的数据量 扫描的数据大于返回的行数,向上面提到那样查询可能会访问多个SST file,造成扫描的数据增加,读IO被放大
-
- 空间放大: 存储的数据量/实际存在的数据量 由于数据Append方式写入,过期的数据可能未及时清理,造成存储数据量>实际存在的数据量
-
- 写放大: 磁盘的写入量/实际的写入量 一次写入的可能会触发Compaction,造成写IO被放大
Compaction算法
Compaction就是在上面提到这三点中追求平衡,常见的Compaction算法有几种:
- Classic Leveled Leveled算法下每一层都是独立的"Sorted Run" ,代表是按Key排序且同层SST file之间的Key值没有重合,数量大小是逐层增大。相邻的两层SST file比称之为fanout(扇出),每次做Compaction的条件是Ln层大小达到了阈值,将Ln层数据与Ln-1层数据进行合并。
• 空间放大:
a. 最好情况:假设Level之间SST file大小倍数是10,L1层10个SST file ,L2层100个SST file,L3层1000个SSTfile(全部都写满),因为L3层是一个Run(没有重复数据),所以L3层大概有90%的数据(1000/110),那么最多有大约10%数据与L1和L2层数据重复(L2+L3占10%,L2和L3的数据全部被L3层覆盖),那么空间放大大约是:存储的数据量/实际存在的数据量 1/0.9=1.1
b. 最坏情况下:如果L3层并没有写满,可能只比L1+L2层多一点的情况下,比如只有220个SST file,这时L3层大概就只有50%的数据,那么最多就可以有50%的重复数据,那么空间放大大约是: 1/0.5=2
• 写放大:每次做Compaction都将Ln层数据写入到Ln-1中,如果L1 ,L2 两层fanout是10,那么L1层写满后与L2层做排序合并,重写生成新的L2层,那么写放大最坏情况下等于fanout 当然在不同的数据库实现中,不一定是将Ln层所有的数据都合并写入到Ln-1中,像RocksDB中就是选择有重合的数据进行合并。

浙公网安备 33010602011771号