摘要
本文研究了一种可以充分利用现代存储硬件自带透明压缩功能的B-tree的设计。近年来,将日志结构的合并树(LSM-tree)作为B-tree的替代方法得到了广泛的应用。目前的共识是,与b -树相比,lsm -树在存储空间效率和写放大方面有明显的优势。本文认为,当内置透明压缩的存储硬件出现时,应该重新考虑这一观点。先进的存储设备(如全闪存阵列)和新兴的计算存储驱动器执行基于硬件的无损数据压缩,对操作系统和用户应用程序透明。除了直接减少b -树和lsm -树之间的物理存储成本差异外,这种现代存储硬件还带来了创新b -树实现的新机会,以便在很大程度上减少其写放大。作为探索潜力的第一步,本文提出了三种简单的设计技术(即确定性页面阴影、本地化页面修改日志记录和稀疏重做日志记录),它们可以利用现代存储硬件来显著降低b -树写放大。我们实现了这些设计技术,并在一个内置透明压缩的商业存储驱动器上进行了实验。结果表明,所提出的设计方法可以将b树的写放大率降低10倍以上。与RocksDB(基于LSM-tree构建的一种流行的键值存储)相比,实现的B-tree可以实现类似甚至更小的写放大和物理存储空间使用。
本段总结:首先本文在现有的存储硬件的基础上研究并设计B-树来降低写放大,研究了三个方面:确定性页面阴影、本地化页面修改日志记录和稀疏重做日志记录。研究动机:近年来,LSM树在存储空间效率和写放大方面比B-树有着非常明显的优势,但是现在内置透明压缩的存储硬件已经出现,我们可以重新设计B-树来实现更小的写放大和更高的存储空间利用率。
一、介绍
本文针对越来越多的商业数据存储硬件提出了一种优化的b树设计方案,该方案在内部进行基于硬件的高速无损数据压缩,对主机操作系统和用户应用程序透明。现代全闪存阵列产品(如戴尔EMC PowerMAX[10]、HPE Nimble Storage[15]和Pure Storage FlashBlade[29])几乎总是带有内置的基于硬件的透明压缩能力。内置透明压缩的商用固态存储驱动器正在出现(例如ScaleFlux[33]的计算存储驱动器和希捷[14]的Nytro SSD)。此外,云供应商已经开始将基于硬件的压缩能力集成到他们的存储基础设施中(例如,微软Corsia[7]和AWS Graviton2[3]),这导致了内置透明压缩的基于云的存储硬件即将到来。通过使用专用的硬件压缩引擎,此类存储硬件支持在非常低的延迟和零主机CPU开销的情况下进行高吞吐量的数据压缩和解压缩。
本段总结:本文在商业数据存储硬件的基础之上提出B-树的设计方案,该方案在内部进行基于硬件的高速无损数据压缩,对主机操作系统和用户应用程序透明,这里的硬件具有专门的压缩引擎,能够在非常低的延迟和零主机CPU开销的情况下进行高吞吐量的数据压缩和解压缩。
作为最广泛使用的索引数据结构,B-tree[13]为几乎所有的关系数据库管理系统(RDBMs)提供了动力,因此在决定现代信息技术基础设施的性能和效率方面起着至关重要的作用。最近,log-structured merge tree (LSM-tree)[26]作为B-tree的竞争者引起了人们的极大兴趣,这主要是因为它的数据结构能够比B-tree实现更高的存储空间利用率和更低的写放大。内置透明压缩的存储硬件的到来可以很容易地减少甚至消除b树和lsm树之间在存储空间使用效率方面的差距。本文表明,还可以利用这种存储硬件来显著减少b -树的写放大,从而缩小与lsm -树的写放大差距。关键是利用这样一个事实,即此类存储硬件允许数据管理软件采用稀疏数据结构,而不牺牲真正的物理存储成本。特别是,在这种存储硬件上运行时,数据管理软件可能会让4KB的LBA(逻辑块地址)块部分填充真实数据,甚至完全为空,而不会浪费物理存储空间的使用。直观地说,稀疏数据结构的可行性为数据管理系统[38]的创新创造了一个新的设计空间频谱。
本段总结:具有内置透明压缩功能的存储硬件可以减少B树和LSM树之间在存储空间使用率和写放大方面的差距。这种存储硬件允许数据管理软件采用稀疏数据结构,而不浪费物理存储空间的使用。(这一点不理解)
本文表明,b树可以利用此类存储硬件实现的稀疏数据结构,极大地减小其写放大。我们注意到,写放大是根据写入物理存储介质的数据量(即在存储内压缩之后)来衡量的,而不是根据写入逻辑存储空间的数据量(即在存储内压缩之前)来衡量的。特别地,本文提出了三种简单而有效的设计技巧:(1)确定性的页面阴影,可以确保b -树页面更新原子性,而不会产生额外的写开销;(2)本地化的页面修改日志,可以减少由于b -树页面大小和每个页面中修改数据的大小不匹配而导致的写放大;(3)稀疏重做日志,可以减少由b -树重做日志(或提前写日志)引起的写放大。这三种技术都有一个共同的主题,即在不牺牲物理存储空间消耗的前提下,适当增加存储数据结构的稀疏性,以减少物理写的放大。随着写放大的显著减少,B-tree可以支持更高的插入/更新吞吐量,并且更容易地容纳低成本、低持久性的NAND闪存(例如,4位/单元QLC甚至5位/单元PLC NAND闪存)。
本段总结:B树可以利用具有内置透明压缩的存储硬件实现的稀疏数据结构来减小写放大。三种设计技巧:(1)确定性的页面阴影,可以确保B-树页面更新原子性,而不会产生额外的写开销;(2)本地化的页面修改日志,可以减少由于B-树页面大小和每个页面中修改数据的大小不匹配而导致的写放大;(3)稀疏重做日志,可以减少由B-树重做日志(或提前写日志)引起的写放大。这三种技术的共同主题是在不牺牲物理存储空间的前提下,适当增加存储数据结构的稀疏性,以减少物理写的放大。随着写放大的显著减少,B-树可以支持更高的插入/更新吞吐量,并且更容易地容纳低成本、低持久性的NAND闪存。
因此,我们实现了一个B-树(称为B9树),它包含了这三种简单的设计技术。我们以RocksDB[32]和WiredTiger [35] (MongoDB的默认存储引擎)为代表,进一步将其与LSM-tree和普通B-tree进行比较。我们在一个内置透明压缩的商用计算存储驱动器上进行了实验。结果很好地证明了所提出的设计技术在降低B-树写放大的有效性。例如,根据与128 b /记录随机写的工作负载,RocksDB和WiredTiger (8 kb的页面大小)写64年14和放大,分别而我们B9树(8 kb页面大小)写放大只有8,代表RocksDB和WiredTiger相比减少43%和88%,分别。更小的写放大可以直接转化为更高的写吞吐量。例如,我们的结果表明,在随机写负载下,B9 -tree可以实现约85K TPS(事务/秒),而RocksDB和WiredTiger的TPS分别为71K和28K。此外,我们注意到所提出的设计技术主要局限于b树的I/O模块,并且在很大程度上正交于核心b树的内存架构和操作。因此,将提议的设计技术合并到现有的b -树实现中相对容易。例如,在基线b -树实现上,我们只修改/添加了大约1200个LoC,以合并提议的三种设计技术。
本段总结:更小的写放大意味着更高的写吞吐量。本段主要写了实验结果。
二、背景
2.1 b -树数据压缩
b -树以页(或b -树节点)为单位管理其数据存储。为了降低数据存储成本,B-tree可以应用块压缩算法(例如lz4[24]、zlib[39]和ZSTD[40])来压缩每个存储上的页面(例如MySQL和MongoDB/WiredTiger中的页面压缩特性)。除了明显的CPU开销外,由于4KB对齐约束,b -树页面压缩还会造成压缩比损失,原因如下:现代存储设备以4KB LBA块为单位来处理IO请求。因此,每个b -树页面(无论是压缩的还是未压缩的)必须完全占用存储设备上的一个或多个4kB LBA块(即,没有两个页面可以共享存储设备上的一个LBA块)。当B-tree应用页面压缩时,4kb对齐约束可能会造成明显甚至严重的存储空间浪费。这可以在图1中说明:假设一个16KB的b -树页面被压缩到5KB,压缩后的页面必须占用存储设备上的两个LBA块(即8KB),浪费3KB存储空间。因此,由于4kb对齐约束带来的CPU开销和存储空间浪费,b -树页面压缩在生产环境中没有得到广泛应用。而且,众所周知,在随机写入的工作负载下,b -树页面往往只有50%到80%的完整[13]。因此,Btree的存储空间利用率通常较低。相比之下,LSM-tree具有更紧凑的数据结构,并且在压缩时不受4kb对齐约束,因此比B-tree具有更高的存储空间使用效率。
本段总结背景1:LSM-tree具有更紧凑的数据结构,并且在压缩时不受4kb对齐约束,因此比B-树具有更高的存储空间使用效率。原因如下:B-树以页为单位来管理数据存储,为了降低成本,B-树用块压缩算法来压缩每个存储块上的页。但是由于4KB对齐约束,比如一个16KB的B-树页面被压缩到5KB,压缩后的页面必须占用存储设备上的两个LBA块(即8KB),这样就浪费3KB存储空间。因此,由于4kb对齐约束带来的CPU开销和存储空间浪费,导致B-树页面压缩在生产环境中没有得到广泛应用。
2.2存储内透明压缩
图2展示了一个内置透明压缩的计算存储驱动器:在计算存储驱动器控制器芯片中,压缩和解压缩由硬件引擎直接在I/O路径上进行,FTL (flash translation layer)管理所有变长压缩数据块的映射。因此不受4kb对齐约束(即,所有压缩块都紧紧地打包在闪存中,没有任何浪费)。由于压缩是在存储驱动器内进行的,
如图3所示,内置透明压缩的存储硬件具有以下两个特性:(a)存储硬件可以公开比其内部物理存储容量更大甚至更大的LBA空间。这在概念上类似于精简配置。(b)由于某些特殊的数据模式(如全零或全一)可以被高度压缩,我们可以让一个4KB的LBA部分填充有效数据,而不会浪费物理存储空间。这两个属性从本质上将逻辑存储空间利用率与物理存储空间利用率解耦。这允许数据管理软件系统在逻辑存储空间中采用稀疏数据结构,而不牺牲真实的物理存储成本,这为数据管理系统[38]创造了一个新的设计空间频谱。
本段总结背景2:内置透明压缩的存储硬件可以让压缩和解压缩在I/O路径上进行,由于压缩是在存储驱动器内进行的,因此不受4kb对齐约束,(即,所有压缩块都紧紧地打包在闪存中,没有任何浪费)。这样一个4KB的LBA部分填充有效数据,就不会浪费物理存储空间,从本质上将逻辑存储空间利用率与物理存储空间利用率解耦。另外,这种存储硬件可以公开LBA空间。
2.3 b树与lsm树
作为b -树的替代方案,lsm -树最近受到了极大的关注(例如,见[4,9,16,22,23,30,31,37]),因为它在存储空间使用和写放大方面具有优势。当b -树和lsm -树在内置透明压缩的存储硬件上运行时,它们在存储空间使用上的差异可能会大大减小,而lsm -树在写放大方面仍然保持其独特的优势。为了演示,我们使用RocksDB和WiredTiger (MongoDB的默认存储引擎)作为LSM-tree和B-tree的代表,并在ScaleFlux[33]最近推出的一个内置透明压缩的3.2TB存储驱动器上进行了实验。我们在128字节的记录大小超过150GB数据集大小的情况下运行随机只写工作负载。对于RocksDB和WiredTiger,我们禁用了应用程序级别的压缩和预写日志(WAL),并保留所有其他设置为默认值。对于WiredTiger,我们将其B-tree页面大小设置为8KB。表1列出了LBA空间上的逻辑存储使用情况(即在存储内压缩之前)和闪存的物理存储使用情况(即在存储内压缩之后)。由于LSM-tree的数据结构比B-tree更紧凑,所以RocksDB的逻辑存储空间使用比WiredTiger更小(即218GB vs. 280GB)。然而,经过存储内透明压缩后,WiredTiger消耗的物理存储空间甚至比RocksDB更少,这很可能是由于LSM-tree的空间放大。图4显示了在不同客户端线程数下测量的写放大。我们注意到,我们基于物理写入存储驱动器内的NAND闪存的压缩后数据量来测量写放大。结果表明,RocksDB的写放大率始终比WiredTiger低约4倍。
本段总结:简要概括了实验设置、过程和结果。
以上结果表明,通过简单地用这些现代存储硬件替换普通ssd,我们可以缩小b -树和lsm -树之间的物理存储成本差距,而lsm -树在写放大方面仍然保持着显著的优势。这项工作的目标是研究我们是否可以通过适当修改b树实现来进一步缩小写放大差距。
本段总结背景3:通过简单地用内置透明压缩的现代存储硬件替换普通ssd,可以缩小b -树和lsm -树之间的物理存储成本差距,而lsm -树在写放大方面仍然保持着显著的优势。
2.4 b -树写放大
我们将b -树写入放大定义为b -树写入物理存储介质的总数据量与用户写入b -树的总数据量之比。在当前的I/O接口协议下,存储设备只保证在每个4KB LBA块上的写原子性。因此,当页面大小大于4KB时,B-tree必须自己确保页面写原子性,这可以通过使用两种不同的策略来实现:(i)就地页面更新:尽管方便的就地更新策略简化了页面存储管理,B-tree必须相应地使用页面日志(例如MySQL中的双写缓冲区和PostgreSQL中的全页写的WAL)来克服部分页写失败,导致大约2个更高的写量。(ii)写时复制(或影子)页面更新:虽然写时复制避免了页面日志的使用,并很容易支持快照,但它使页面存储管理复杂化。同时,B-tree必须使用某些机制(例如,页映射表或页更新传播)来跟踪逻辑存储LBA空间上的页位置,这仍然会带来额外的写开销。
本段总结:写放大是指写入物理存储介质的总数据量与用户写入的总数据量之比。想要减小写放大,B-树必须使用某些机制来跟踪逻辑存储块空间上的页位置,这会带来额外的开销。
据此,我们可以将b -树存储写流量分为三类:(1)确保事务原子性和隔离的日志写入(例如重做/撤销日志),(2)将内存中的脏b树页面持久化到存储设备的页面写入,以及(3)确保页面写原子性所导致的额外写入(例如,在本地更新的情况下的页面日志记录,或页映射表在页隐藏的情况下持续存在)。让𝑊𝑙𝑜𝑔,𝑊𝑝𝑔, 和𝑊𝑒表示这三个类别的数据写总额,和𝑊𝑢𝑠r表示用户的总量数据写入了b - tree。我们可以把b -树的写放大式表示为
当b -树存储硬件上运行内置透明压缩,让𝛼𝑙𝑜𝑔, 𝛼𝑝𝑔, 和 𝛼𝑒表示数据的三个类别的平均压缩比。在这里,我们通过将压缩后的数据量除以压缩前的数据量来计算压缩比。因此压缩比总是为(0,1),数据压缩率越高,压缩比越小。因此,整体的b树写放大变成
本段总结:本段主要说明了数据压缩比的计算(将压缩后的数据量除以压缩前的数据量)。
三、设计技术
根据上式(2),我们可以通过减少𝑊𝐴𝑙𝑜𝑔、𝑊𝐴𝑝𝑔和/或𝑊𝐴𝑒(即减少b树的写数据量),或者减少𝛼𝑙𝑜𝑔、𝛼𝑝𝑔和/或𝛼𝑒(即提高b树写数据的压缩性)来减少b树的写放大。通过应用内置透明压缩的存储硬件所支持的稀疏数据结构,本节介绍三种设计技术来减少b树写放大:(1)确定性页面阴影,消除𝑊𝐴𝑒,(2)本地化页面修改日志,减少𝑊𝐴𝑝𝑔和𝛼𝑝𝑔,(3)稀疏重做日志,减少𝛼𝑙𝑜𝑔。
3.1确定性页面阴影
为了消除𝑊𝐴𝑒,B-tree应该采用页面隐藏的原则,而不是就地更新页面。然而,在页面阴影的传统实现中,每个更新的b -树页面在存储器中新的位置是在运行时动态确定的,必须由b -树记录/持久化,从而导致额外的写开销和管理复杂性。为了消除额外的写开销,同时简化b树页面存储管理,我们提出了一种称为确定性页面阴影的技术,如图5所示:让𝑙𝑝𝑔表示b -树页面大小(例如,8KB或16KB)。对于每个页面,B-tree在LBA空间上分配2𝑙𝑝𝑔数量的逻辑存储区域,并将其划分为两个大小为𝑙𝑝𝑔的槽位(slot-0和slot-1)。对于每个b -树页面,逻辑存储LBA空间上固定位置的两个槽位以乒乓方式交替进行内存到存储的页面刷新。一旦页面成功地从内存中刷新到一个插槽,B-tree将在另一个插槽上发出TRIM命令。这在概念上与传统的页面阴影相同,不同的是现在阴影页面的位置是固定的。虽然B-tree在LBA空间上占用了2倍大的逻辑存储空间,但只有一半的存储空间用于存储有效数据,另一半被修剪(因此不占用物理闪存存储空间)。正如在第2.2节中指出的,内置透明压缩的存储硬件可以公开比其内部物理存储容量大得多的逻辑LBA存储空间。因此,这样的存储硬件可以很容易地支持确定性的页面隐藏。我们注意到,确定性页阴影只是为了确保页写原子性,而不需要额外的写开销。为了支持多版本并发控制(MVCC), B-tree可以使用传统的方法,如undo logging。
图5:确定性页面隐藏的演示:逻辑存储LBA空间上固定位置的两个槽交替地提供一个页面的内存到存储刷新。
本段总结:对确定性页面阴影的理解:在逻辑存储空间中都为每个B-树页面分配两个大小相等的槽位,当一个页面从内存中刷新到存储器时,其中一个槽位会记录这个页面在存储器中的位置信息,相应地,另一个页面会发出TRIM命令。这点存在的问题是:每个页面占用了2倍大的逻辑地址空间,但只有一半的空间被有效使用,另一半处于被修剪的状态。这一点文中说逻辑地址空间非常丰富。
通过提出的确定性页面阴影,B-tree使用内存中的位图来跟踪每个页面的有效槽位。与在传统页面阴影中使用的页表相比,位图要小得多,因此大大减少了内存的使用。而且,B-tree不需要持久化位图。当系统重新启动时,B-tree可以逐步重建内存中的位图:当B-tree第一次从存储器加载一个页面到内存时,它从存储设备读取两个槽位。对于裁剪后的槽位,存储设备只返回一个全零块,B-tree可以根据这个块轻松地识别出有效的槽位。当B-tree读取一个页面的两个槽位时,存储设备在内部只从物理存储介质中获取有效的(即未修剪的)槽位。因此,与读取一个槽位相比,读取两个槽位只需要通过PCIe接口进行更多的数据传输,而不会在存储设备中产生额外的读取延迟。这应该不是问题,因为即将推出的PCIe Gen5将支持16GB/s ~ 32GB/s,这比存储设备内的后端闪存访问带宽大得多,因此可以很容易地容纳额外的数据传输。当系统崩溃时,B-tree需要处理以下两种可能的情况:(i)在系统崩溃之前,槽位被部分写入:B-tree可以通过验证页面校验和,方便地识别出部分写入的槽位。(ii)一个槽位已经成功写入,但另一个槽位在系统崩溃之前还没有被裁剪:B-tree可以通过比较两个槽位上的页LSN(逻辑序列号)来识别有效的槽位。由于不需要保持内存中的位图,确定性页面阴影可以完全消除𝛼𝑒·𝑊𝐴𝑒组件从总的b -树写放大。
本段总结:B-tree使用内存中的位图来跟踪每个页面的有效槽位。当系统重新启动时,B-树可以逐步重建内存中的位图(当B-树从存储器中加载一个页面到内存中时,它从存储设备中读取到两个槽位。对于发出TRIM命令的槽位,存储设备只返回一个全零块,B-tree可以根据这个块轻松地识别出有效的槽位。)。当B-tree读取一个页面的两个槽位时,存储设备在内部从物理存储介质中获取有效的槽位。
3.2 本地化页面修改日志
第二种技术旨在减少方程中的𝛼𝑝𝑔和𝑊𝐴𝑝𝑔分量。 (2).它的动机是一个简单的观察:对于 B 树页面,让 Δ 表示其内存中映像和存储中映像之间的差异。如果差异明显小于页面大小(即|Δ| << 𝑙𝑝𝑔),我们可以通过记录页面修改Δ而不是将整个内存中的页面图像写入底层存储来大大减少写入放大设备。这在概念上与众所周知的基于相似性的重复数据删除 [2] 和增量编码 [25] 相同。不幸的是,当 B-tree 在普通存储设备上运行时(即没有内置的透明压缩),这种方法会产生大量的操作开销,因此实际上不可行:鉴于 4KB 块 IO 接口,我们必须从将不同的页面合并到一个 4KB LBA 块中,以实现写放大减少。为了提高增益,我们应该在重置此过程以构建最新的存储页面映像之前,为每个页面多次应用页面修改日志记录。因此,与同一页关联的多个 Δ 将分布在存储设备上的多个 4KB LBA 块上,但这会导致两个问题: (1) 对于每个页面,B-tree 必须跟踪其所有关联的 Δ 并定期携带退出后台垃圾收集,导致更高的存储管理复杂性。 (2) 要从存储加载页面,B-tree 必须读取现有的存储页面图像和来自多个非连续 4KB LBA 块的多个 Δ。这显然会导致显着的读取放大,导致(很多)更长的页面加载延迟。因此,据我们所知,这种简单的设计概念尚未被公开文献中报道过的真实世界的 B 树实现所使用。
本段总结:对于 B 树页面,让 Δ 表示其内存中映像和存储中映像之间的差异。如果差异明显小于页面大小,通过记录页面修改Δ而不将整个内存中的页面映像写入底层存储来减少写入放大。这样做存在的问题:(1)对于每个页面,B-树必须跟踪每个页面所有的Δ,这会导致更高的存储管理复杂性。(2)一个页面从存储器加载到内存时,B-树必须读取多个非连续的逻辑地址中的多个 Δ和现存储器中的该页,这显然会引起页面加载延迟。
内置透明压缩的存储硬件首次使实现页面修改日志记录的简单想法切实可行。通过应用此类存储硬件支持的稀疏数据结构,我们不再需要将来自不同页面的多个Δ合并到同一个4KB LBA块中。利用丰富的逻辑存储LBA空间,对于每个b -树页面,我们只需专用一个4KB LBA块作为其修改日志空间来存储Δ,这被称为本地化页面修改日志。在 4KB IO 接口下,为了实现建议的每个页面的页面修改日志记录,B-tree 将𝐷 = [Δ, O](其中 O 表示全零向量,|𝐷| 为 4KB)写入关联的 4KB 块与页面。在存储设备内部,𝐷 中的所有零都将被压缩掉,只有 Δ 的压缩版本将被物理存储。因此,当为每个内存到存储页面刷新提供页面修改日志时,我们通过向逻辑存储 LBA 空间写入 4KB 而不是 𝑙𝑝𝑔 的数据量来减少 𝑊 𝐴𝑝𝑔,并降低压缩率𝛼𝑝𝑔,因为写入的数据[Δ, O] 可以被存储设备高度压缩。通过为每个 B 树页面分配一个 4KB 的修改日志空间,我们不会产生额外的 B 树存储管理复杂性。读取放大很小,主要有两个原因:(1) B-tree 总是只读取一个额外的 4KB LBA 块。此外,每个页面及其关联的 4KB 日志块连续驻留在 LBA 空间中。因此,为了读取页面及其关联的 4KB 日志块,B-tree 仅向存储设备发出单个读取请求。 (2) 存储设备内部从闪存中取出非常少量的数据,以重构 4KB LBA 块 [Δ, O]。
本段总结:所谓本地化页面修改日志是:对于每一个B-树页面都有一个4KB的逻辑地址空间来存储页面修改的Δ。这样B-树总是只读取一个额外的4KB的逻辑地址,不会产生复杂的存储管理。
为了实际实现这个简单的想法,B-tree 必须执行两个额外的操作: (1) 要将页面从存储加载到内存中,B-tree 必须基于存储页面映像和Δ构造最新的页面映像。 (2) 要将页面从内存刷新到存储,B-tree 必须获得 Δ 并据此决定是否可以调用页面修改日志记录。为了减少操作开销,B-tree 可以应用以下策略:让 𝑃𝑚 和 𝑃𝑠 表示一个 B-tree 页面的内存和存储映像。我们在逻辑上将𝑃𝑚和𝑃𝑠分成𝑘段,即𝑃𝑚 = [𝑃𝑚,1, · · · , 𝑃𝑚,𝑘 ]和𝑃𝑠 = [𝑃𝑠,1, · · · , 𝑃𝑠,𝑘 ], [𝑃𝑚, | = |𝑃𝑠,𝑖 | ∀𝑖(即同一位置的两段 𝑃𝑚,𝑖 和 𝑃𝑠,𝑖 大小相同)。对于每一页,B 树保持一个 𝑘 位向量 𝑓 = [𝑓1, · · , 𝑓𝑘 ],其中如果 𝑃𝑚,𝑖 ≠ 𝑃𝑠,𝑖,𝑓𝑖 设置为 1。因此,我们通过将所有内存段 𝑃𝑚,𝑖 与 𝑓𝑖 = 1 连接起来构造 Δ。在运行时期间,每当内存页面中的第𝑖段被修改时,B-tree将其对应的𝑓𝑖设置为1。当B-tree将页面从内存刷新到存储时,它首先将Δ的大小计算为
本段总结:本段主要说了页面修改日志记录 Δ 是如何构成的。 𝑃𝑚 和 𝑃𝑠 表示一个 B-tree 页面的内存和存储映像。在逻辑上将𝑃𝑚和𝑃𝑠分成𝑘段,即𝑃𝑚 = [𝑃𝑚,1, · · · , 𝑃𝑚,𝑘 ]和𝑃𝑠 = [𝑃𝑠,1, · · · , 𝑃𝑠,𝑘 ], [𝑃𝑚, | = |𝑃𝑠,𝑖 | ∀𝑖(即同一位置的两段 𝑃𝑚,𝑖 和 𝑃𝑠,𝑖 大小相同)。对于每一页,B 树保持一个 𝑘 位向量 𝑓 = [𝑓1, · · , 𝑓𝑘 ],其中如果 𝑃𝑚,𝑖 ≠ 𝑃𝑠,𝑖,𝑓𝑖 设置为 1。因此,通过将所有内存段 𝑃𝑚,𝑖 与 𝑓𝑖 = 1 连接起来构造 Δ。在运行时期间,每当内存页面中的第𝑖段被修改时,B-tree将其对应的𝑓𝑖设置为1。
我们定义一个不大于 4KB 的固定阈值𝑇。如果|Δ| ≤ 𝑇 ,那么 B-tree 将调用页面修改日志,其中 Δ 可以通过简单的内存复制操作获得。我们注意到 𝑘 位向量 𝑓 应该与 Δ 一起写入专用的 4KB 页面修改日志块。当 B 树从存储加载页面到内存时,它从存储设备中获取𝑙𝑝𝑔 +4𝐾𝐵 数据量,其中大小𝑙𝑝𝑔 空间包含当前存储页面映像𝑃𝑠 和额外的 4KB 块包含相关的 𝑓 和Δ.因此,我们可以通过简单的内存复制操作轻松构建最新的页面图像。对于每个 B 树页面,其 Δ 的大小将随着 B 树进行更多的写入操作而单调增加。一旦|Δ|大于阈值𝑇,我们将通过将整个最新页面刷新到存储,以Δ =∅,𝑓为全零向量重置进程。我们注意到阈值 𝑇 配置了写放大减少和存储空间放大之间的权衡:随着我们增加 𝑇 的值,我们可以不那么频繁地重置页面修改日志记录过程,从而导致更小的写放大。同时,在较大的 𝑇 值下,更多的页面修改会在日志空间中积累,从而导致更大的存储成本开销。
本段总结:本段说明了写放大为什么会缩小。当|Δ| ≤ 𝑇(𝑇<=4KB)时,B 树从存储设备中获取𝑙𝑝𝑔 +4𝐾𝐵 数据量(其中大小𝑙𝑝𝑔 空间包含当前存储页面映像𝑃𝑠 和额外的 4KB 块包含相关的 𝑓 和Δ),通过简单的内存复制操作轻松构建最新的页面图像。当|Δ| > 𝑇(𝑇<=4KB)时,通过将整个最新页面刷新到存储,以Δ =∅,𝑓为全零向量重置进程。
图 6 进一步说明了这种实施策略。在所有的𝑘段中,第一段𝑃𝑚,1是页眉,最后一段𝑃𝑚,𝑘是页尾,这两个段都可以比其他段小很多。假设页面更新导致段 𝑃𝑚,3 和页眉/尾随修改。当 B 树从内存中驱逐这个页面时,它构造 Δ 为 [𝑃𝑚,1, 𝑃𝑚,3, 𝑃𝑚,𝑘 ],并将 Δ 和 𝑘 位向量 𝑓 写入专用的 4KB 块日志块,即 在存储设备内部进一步压缩。
本段总结:将修改的段Δ 和 𝑘 位向量 𝑓 写入专门的 4KB 日志块,然后在存储设备内部进行进一步的压缩。
图 6:本地化页面修改日志的图示,其中待刷新的内存页面 𝑃𝑚 包含三个修改段 𝑃𝑚,1、𝑃𝑚,3 和 𝑃𝑚,𝑘 。
我们注意到,如果 B-tree 将内存页面视为不可变的并使用内存增量链接来跟踪内存页面修改(在 Bw-tree [20, 21] 中使用以实现无锁操作 ),我们很可能进一步减少|Δ| 从而提高本地化页面修改日志在减少写放大方面的有效性。 然而,这种增量链接方法会使 B 树实现 [34] 大大复杂化,并导致显着的内存使用开销。 因此,这项工作在我们的实施和评估中选择了上述简单的基于页面内分段的跟踪方法。
本段总结:本段说明为什么采用上面页面分段的跟踪方法来减小写放大。
3.3 稀疏重做日志
第三种设计技术旨在减少方程(2)中的𝛼𝑙𝑜𝑔(即提高重做日志数据的可压缩性)。为了最大化可靠性,B-tree在每次事务提交时使用fsync或fdatasync刷新重做日志。为了减少日志引起的存储开销,常规做法总是将日志记录紧密地打包到重做日志中。因此,多次连续重做日志刷新可能会写入存储设备上的同一个 LBA 块,尤其是当事务记录明显小于 4KB 和/或工作负载并发性不是很高时。这可以在图 7 中说明:假设三个事务 TRX-1、TRX-2 和 TRX-3(具有日志记录𝐿1、𝐿2 和𝐿3)分别在𝑡1、𝑡2 和𝑡3 时间提交,其中𝑡1 < 𝑡2 < 𝑡3。如图7所示,在𝑡1时,4KB数据[𝐿1,O]从内存重做日志缓冲区刷新到存储设备上的LBA 0x0001,进一步在内部压缩数据。随后,日志记录𝐿2被追加到重做日志缓冲区中,在𝑡2时,4KB数据[𝐿1,𝐿2,O]被刷新到存储设备上的同一个LBA 0x0001。同样,在𝑡3时刻,4KB的数据[𝐿1,𝐿2,𝐿3,O]被刷新到存储设备上同一个LBA 0x0001。如图 7 所示,相同的日志记录(例如,𝐿1 和 𝐿2)被多次写入存储设备,导致更高的写入放大。同样的,当每个4KB的重做日志缓冲区块中积累了更多的日志记录时,重做日志数据压缩率𝛼𝑙𝑜𝑔会在连续多次刷新重做日志的过程中变得越来越差。
本段总结: 稀疏重做日志目的是提高重做日志的可压缩性。常规做法是将日志记录紧密地打包到重做日志中,多次连续重做日志刷新可能会写入存储设备上的同一个 LBA 块。
图7:重做日志的传统实现,其中日志记录被紧密地压缩到重做日志中,并且连续提交事务可以将重做日志多次刷新到同一个LBA(例如,在本例中是LBA 0x0001)。
通过应用内置透明压缩的存储硬件支持的稀疏数据结构,我们提出了一种稀疏重做日志的设计技术,使存储硬件能够最有效地压缩重做日志,从而减少日志导致的写放大。它的基本思想非常简单:在每次事务提交和它对应的重做日志内存到存储刷新时,我们总是在内存重做日志缓冲区中填充0,使其内容与4kb对齐。因此,下一条日志记录将被写入重做日志缓冲区的一个新的4KB空间。因此,每条日志记录都会被写入存储设备只有一次,与传统做法相比,写入放大率较低。这可以在图8中进一步说明:假设与图7中相同的场景,在事务TRX-1在𝑡1时刻提交后,我们将0填充到重做日志缓冲区,并将4KB的数据[𝐿1,O]刷新到存储设备上的LBA 0x0001。随后,我们将下一个日志记录𝐿2放到重做日志缓冲区的一个新的4KB空间中。在𝑡2时,4KB的数据[𝐿2,O]被刷新到存储设备上的一个新的LBA 0x0002。类似地,在𝑡3时,4KB的数据[𝐿3,O]被刷新到存储设备上的另一个新LBA 0x0003。显然,每个重做日志记录只被写入存储设备一次,并且重做日志的写入可以被存储硬件更好地压缩,导致一个更小的𝛼𝑙𝑜𝑔,从而更低的写放大。由于在常规日志记录和建议的稀疏日志记录中,每个事务提交总是调用一个4KB的写入到存储设备,所以Eq.(2)中的总重做日志写入量𝑊𝑙𝑜𝑔将保持不变。因此,通过降低日志压缩比𝛼𝑙𝑜𝑔,本文提出的稀疏日志减少了总体b树写放大中的𝛼𝑙𝑜𝑔·𝑊𝑙𝑜𝑔组件。
本段总结:在每次事务提交和它对应的重做日志时,总是在内存重做日志缓冲区中填充0,使其内容与4kb对齐。因此,下一条日志记录将被写入重做日志缓冲区的一个新的4KB空间。因此,每条日志记录都会被写入存储设备只有一次,与传统做法相比,写入放大率较低。
图8:提议的稀疏日志记录示意图,其中每个重做日志刷新总是写入一个新的LBA块。
四、评估
为了进行演示和评估,我们实现了一个b-树(称为B9 -树),它包含了上述三种简单的设计技术。为了便于比较,我们还实现并评估了一个使用传统页面阴影的基线b-树,在该树中,我们在每次页面刷新后保持页表。事实上,由于提出的三种设计技术主要局限于I/O模块,并且在很大程度上正交于核心b-树的内存架构和操作,我们通过简单地将提出的设计技术合并到基线b-树中,并添加/修改了1200个LoC,从而获得了B9-树。此外,我们还将RocksDB和WiredTiger作为LSM-tree和B-tree的代表。对于RocksDB,我们将其压缩线程和刷新线程的最大数量分别设置为12和4,并将Bloomfilter设置为每条记录10位。对于WiredTiger和我们自己的基线b-树和B9-树,我们使用4个后台写线程将内存中的脏页面刷新到存储设备。
图9:在每分钟日志刷新策略下的写放大,其中数据集大小为150GB,缓存大小为1GB。
4.1实验装置
我们在一台拥有24核2.6GHz Intel CPU、64GB DDR4 DRAM和3.2TB内置透明压缩的计算存储驱动器的服务器上运行了所有的实验,该计算存储驱动器最近由ScaleFlux[33]推向商业市场。这个3.2TB的驱动器直接沿着I/O路径对每个4KB块执行基于硬件的zlib压缩。
五、相关工作
Graefe[12]研究了各种可以提高b树写吞吐量的设计技术(例如,I/O优化、缓冲和放松事务保证),其中一些可以减少b树写放大。然而,主要旨在将随机页写转换为顺序页写的I/O优化技术只对hdd有用,因为现代ssd可以实现几乎相同的随机和顺序写速度性能。在[12]中研究的许多技术(如缓冲、放宽事务保证)与本文提出的解决方案是正交的,因此可以一起应用来进一步减少b -树的写放大。此外,写时复制或页阴影[1,19]是实现b -树数据原子性和持久性的著名技术。与使用就地更新的b树相比,它可以减少约2的写放大。
Levandoski等人[20,21]提出了一种能够更好地适应现代多核CPU架构的Bw-tree,同时减少了写放大。Bw-tree将每个内存中的页面视为不可变的,并使用增量链接来跟踪对每个页面所做的更改。这可以启用无锁操作,从而更好地利用多核cpu。同时,通过只刷新增量记录,Bw-tree可以减少写放大。Bw-tree使用日志结构的存储来持久化所有的页面和增量,但是这会受到读放大和后台垃圾收集开销的影响。当在内置透明压缩的存储硬件上运行Bw-tree时,可以通过使用本文中提供的本地化页面修改日志来替换日志结构存储来增强Bw-tree。
B𝜀 -t树[5]是B -树的另一个变体,可以显著降低写在非叶节点通过数据缓冲放大。它已经被用于文件系统[11,17,18,36]和键值存储[8,27]的设计中。从本质上说,B𝜀 -树巧妙地混合了B -树和LSM-tree的关键设计原则。类似于LSM-tree, B𝜀 -树范围扫描速度性能比B - tree差。Percona TokuDB[28]是一个公开的数据库产品,是建立在B𝜀 -树。
关于数据管理系统如何利用内置透明压缩的存储硬件的研究很少。最近,Zheng等人讨论了一些利用现代存储硬件来改进数据管理软件设计的可能选项。Chen等人提出了一种基于哈希值的键值存储,它可以利用这种现代存储硬件来避免使用昂贵的内存哈希表。
六、结论
本文提出了三种简单而有效的设计技术,使b树能够充分利用内置透明压缩的现代存储硬件。通过解耦逻辑和物理存储空间利用效率,这种新的存储硬件允许数据管理系统采用稀疏数据结构,而不牺牲真实的物理数据存储成本。这为数据管理系统设计的创新提供了一个新的但在很大程度上尚未探索的机会范围。作为探索这一设计谱的一个小步骤,本文提出了三种设计技术,可以适当地将稀疏性嵌入到b树数据结构中,以极大地减少b树的写放大。实验结果表明,所提出的设计技术可以将b树的写放大率降低10以上,基本上缩小了b树与lsm树在写放大方面的差距。这项工作表明,这种新的存储硬件的到来需要重新审视b树和LSMtree在未来数据管理系统中的作用和比较。