一些有趣的B+树优化实验(上)

发布于 2022-8-2 18:06
浏览
0收藏

作为目前数据库引擎的两种主要数据结构,LSM-tree和B+-tree在业界已经有非常广泛的研究。相比B+-tree,LSM-tree牺牲一定的读性能以换取更小的写放大以及更低的存储成本,但这必须建立在已有的HDD和SSD的基础上。

 

探索前沿研究,聚焦技术创新,本期DB·洞见由腾讯云数据库高级工程师王宏博进行分享,主要介绍一篇2022年FAST的论文,主题为“基于硬件透明压缩的B+树优化”。本次分享的论文针对可计算存储SSD(支持硬件透明压缩)提出了三种有趣的设计方法,从而极大地减少了B+-tree的写放大(10X)以使其接近甚至超越LSM-tree。

 

论文简介

 

本期分享的是2022年FAST的一篇论文,主题是基于硬件透明压缩的B+树优化,该论文在内容上主要分为三个部分:


第一部分是背景介绍,作者分别介绍了现有的B+树及其软件压缩、存储硬件透明压缩、B+树和LSM-tree的简要对比以及B+树写放大的构成分析。


第二部分是算法部分,作者针对性地提出了三种设计来优化B+树的写放大:


确定性的page shadowing来解决页面原子写引入的写放大。
通过页面本地增量日志来减少刷脏页引入的写放大。
通过稀疏日志来减少redo日志引入的写放大。


第三部分是实验部分,作者通过前面所述的三个方法实现了一个新的B+树(我们称之为B−-tree为以示区别),并详细比较了多种条件下的测试结果。

一些有趣的B+树优化实验(上)-开源基础软件社区

背景部分

2.1 现有的B+树及其软件压缩

我们熟悉的开源数据库有很多都使用B+树作为存储引擎,比如MySQL、MongoDB、PostgreSQL等,腾讯云数据库TDSQL-PG也是基于B+树来实现的。B+树的结构如下方的左图所示,它通过一个个的数据页面构成一个树状结构以提供索引加速数据查找。常见的数据页面大小有8k和16k,通过数据页面压缩可以减少B+树的空间占用以降低成本,此外还可以减少B+树的写放大。


在前面所提到的基于B+树实现的数据库中,MySQL、MongoDB都实现了B+树的软件压缩特性,TDSQL-PG的商业化版本也有实现。软件压缩具有不依赖于特定硬件、灵活性高的优点,但美中不足的是,受限于现有的IO 4K对齐的约束会产生一些额外的空间浪费。比如下方右图,一个16K的数据库页面经过压缩后产生了5K的数据。受限于IO 4K对齐的要求,当落盘时这5K的数据实际上要占用8K的空间,产生了额外的3K的空间浪费。


常见的B+树压缩算法有Lz4、zlib、ZSTD,后面所提的LBA则是指logical block adressing,即逻辑上的磁盘块大小。

一些有趣的B+树优化实验(上)-开源基础软件社区

2.2 存储硬件透明压缩

我们先简单回顾下闪存的特性。如下图所示,闪存支持三种基本操作:读、写、擦除。闪存的读、写都以page为单位,一般是4K,擦除则是以block为单位,一个block一般包含多个page,比如包含64个page。之所以闪存需要支持擦除,是由于闪存的写比较特殊,一个page只能在擦除之后一次写入,而不能更新。但从用户的角度来看,SSD需要支持更新,因此我们在SSD里会有一个FTL层来实现该逻辑,即下图中间蓝色部分所示。


CSD,顾名思义是一种带有计算能力的存储。如下图所示,一个通用的CSD在存储层可以支持API调用来进行计算下推。硬件透明压缩存储作为CSD的一种,支持在其内部对数据进行压缩和解压,后续我们提到的CSD主要是指带有硬件透明压缩的CSD


对比最下方的两张图,左边是普通的SSD,右边是CSD。可以看到普通SSD的LBA和闪存都是以页面为基础,且都是4K大小,可以由FTL直接进行映射。CSD和SSD类似,都对外提供了4K的LBA接口,但4K的数据经过压缩后长度不一定,所以在CSD内部,FTL的闪存管理需要支持对变长数据的管理,这也使得CSD的FTL变得更加复杂。如此一来CSD能带来什么好处呢?

 

一些有趣的B+树优化实验(上)-开源基础软件社区

总体来看,CSD的好处体现在它有更优的成本、更好的性能,但在此不做详细讨论,我们主要关注CSD的技术特点。CSD可以实现逻辑地址和物理地址的解耦。如下方左图所示,对一个实际闪存大小只有4T的CSD来说,它可以对外提供远大于闪存大小的LBA。另外,如下方右图所示,对于稀疏数据的写入可以进行透明压缩而不产生空间浪费。基于CSD的这两个特性,我们在上层应用如数据库中,可以做一些针对性的设计,以进一步利用CSD的特性。

一些有趣的B+树优化实验(上)-开源基础软件社区

2.3 B+树和LSM-tree的简要对比

我们先回顾LSM-tree的写入流程。如右上图所示,一条数据记录的写入,会先写log,再进入memtbale,memtable写满之后会变成 immutable memtable, 再与L0的SSTable进行合并,上层的SSTbale也会在合适的时机与下层SSTable进行合并。


我们再简要回顾B+树的写入流程。一条记录的写入也是先写入log,再写入B+树的数据页面,当数据页面写满时就需要进行页面分裂,所以大部分时间B+树的页面其实是不满的。另外,由于B+树是直接操作目标页面,当内存不够时就会涉及到页面的淘汰问题,这时就需要刷脏页。极端场景是每写一条记录就需要刷一次脏页,如果一条记录是128字节,页面是8k,那么对应的写放大就是64倍。


B+树和LSM-tree在写方面的主要区别是:LSM-tree是一个紧密排布的结构,可以占用更小的写空间以及产生更小的写放大。B+树的写放大与数据集的大小成正相关,与单条记录的大小负相关。对于数据完全可以放入内存的场景,B+树可以产生比LSM-tree更小的写放大。因此这篇论文聚焦在大数据集以及小数据记录的场景,也就是说,B+树在该场景下写放大明显劣于LSM-tree。


作者通过在CSD上执行一个随机写入的测试,初步展示了B+树和LSM-tree的差异。结果如右下图所示,可以看到LSM-tree占用的逻辑空间更小,这与LSM-tree的数据紧密排列有关,经过CSD压缩之后,B+树的物理空间占用也大幅度减小。此外,通过对比发现,LSM-tree的写放大显著小于B+树,因此本论文的目标就是需要通过改进B+树的实现来大幅度减小B+树的写放大。

 

一些有趣的B+树优化实验(上)-开源基础软件社区

2.4 B+树写放大的构成分析


B+树的写放大构成主要包含三个部分:

  • 为了保证事务原子性采用redo log引入的写放大 ;
  • 由于lru buffer淘汰或者checkpoint 刷脏页而引入的写放大;
  • 为了保证页面写的原子性而引入的写放大,如MySQL的 double write。


当考虑CSD的压缩特性时,公式1所列的写放大会引入表示压缩比的系数α ,其含义为 压缩后的数据大小比压缩前的数据大小,其位于0-1之间,也就是说,CSD会降低写放大。

一些有趣的B+树优化实验(上)-开源基础软件社区

算法部分

3.1 Deterministic Page Shadowing

算法1是通过确定性的page Shadowing来减少page原子写引入的写放大,其本质上是copy on write,一次页面刷盘写只需要写一次即可。具体实现如右图所示,1个数据页面在磁盘上会有2倍的连续LBA空间与之对应,这个LBA被划分为两个slot,两个slot轮流写入以实现copy on write,从而保证每次写入不管是否成功,页面的前镜像总是保持稳定。在页面写入成功后,可以通过trim 操作清理前一个slot的物理空间占用。在页面的写失败时,我们可以通过 checksum进行检测,并通过页面前镜像+redo恢复出最新的页面。对于写成功后crash保留两个完整页面的场景,则可以通过比较页面的LSN找到最新的页面,并trim掉旧的页面。


本方法带来的一个负面影响就是每次读需要产生额外的数据传输,但是作者认为pcie的传输能力远大于flash的速度,因此不成问题。此外,对于支持计算下推的CSD,我们可以将该逻辑下推,也可以避免掉额外的数据传输。

 

一些有趣的B+树优化实验(上)-开源基础软件社区

3.2 Localized Page Modification Logging


算法2是通过本地修改日志来减少刷脏页引起的写放大。如右图所示,一个数据页面在存储层对应一个数据页面+一个4K增量log页面,两者的逻辑地址连续。在内存中数据页面被分为k个segment,每个segment大小是Ds,每次页面修改所涉及到的segment会在bitmap f里面记录。当需要刷脏页时,我们可以通过bitmap f找到所有的修改增量,并把它记为δ,再把δ、bitmap f、补零后的数据一起写入盘中。以一个16K的数据页面为例,对应的写放大会减少75%。经过CSD透明压缩后,写放大可进一步减小。


在该方法中,由于额外引入了增量日志,会造成一定的物理空间的放大,我们可以通过参数T来控制物理空间的放大。当增量日志大于T时直接刷脏页并重置增量日志,T越大写放大越小,但是对应的物理空间放大就越大。读取页面时我们需要读原始数据页面+增量日志部分,再构造最新的数据页面。由于读页面需要额外读所以会造成一定的读放大,但由于数据页面的地址和增量日志LBA是连续的,因此一次读请求即可解决。另外,增量日志相对较小,所以可以认为这部分的读放大影响较小。

一些有趣的B+树优化实验(上)-开源基础软件社区

3.3 Sparse Redo Logging

算法3是稀疏日志,用于减少Redo log造成的写放大。目前常见的Redo log的实现如左图所示,日志采用连续紧密排列,事务1提交时日志L1落盘,事务2提交时由于日志buffer未满,因此会在L1的buffer中追加L2,L1和L2一起落盘,事务3提交时,L1、 L2 需要连同L3再落盘一次,直到当前日志buffer页面被写满,才会开启新的页面。在该例子中,从flash的角度看,L1被写了3次,后两次都是额外的写放大。当采用右图所示的稀疏日志时,每次日志写入都是用一个新的页面,页面经过透明压缩后落盘,则完全可以避免写放大的问题,另外由于透明压缩的存在也不会产生额外的空间占用。此外稀疏日志也是可以用于LSM-tree的。

 

一些有趣的B+树优化实验(上)-开源基础软件社区

一些有趣的B+树优化实验(下)

 

 

 

文章转载自公众号:腾讯云数据库

分类
已于2022-8-2 18:06:32修改
收藏
回复
举报
回复
添加资源
添加资源将有机会获得更多曝光,你也可以直接关联已上传资源 去关联
    相关推荐