Command Palette
Search for a command to run...
如何向 SSD 写入
如何向 SSD 写入
Bohyun Lee Tobias Ziegler Viktor Leis
摘要
本文论证了采用异处写入对于数据库系统充分发挥 SSD 性能并延长 SSD 寿命至关重要。我们提出了一组异处写入优化方案,在 DBMS 和 SSD 两个层面共同降低写放大。我们重新设计了基于 B 树的原地写入引擎 LeanStore,使其支持异处写入并实现上述优化,并在多种 OLTP 基准测试、数据集规模和 SSD 设备上进行了评估。最终设计在 YCSB-A 上将吞吐量提升了 1.65–2.24 倍,每次操作的闪存写入量降低了 6.2–9.8 倍。在包含 15,000 个仓库的 TPC-C 测试中,吞吐量提升了 2.45 倍,闪存写入量降低了 7.2 倍。最后,我们展示了该架构能够无缝支持 ZNS 和 FDP 等新型 SSD 接口。
一句话总结
慕尼黑工业大学和 TigerBeetle 证明,异址写入对于数据库充分利用 SSD 性能与寿命至关重要,并提出一系列在 DBMS 和 SSD 两个层面降低写放大的优化;当这些优化被整合到重新设计的、基于 B-tree 的 LeanStore 中时,可实现最高 2.24× 吞吐量提升,并将闪存写入量降低 9.8×,同时支持 ZNS 和 FDP 等新型 SSD 接口。
核心贡献
- 该论文提出一种异址写入架构,重新设计原本采用原地更新的、基于 B-tree 的 LeanStore,并引入页级优化,从而在保留 B-tree 索引的同时降低写放大。
- 它加入数据库级垃圾回收机制,利用 DBMS 自身语义预测每个页的失效时间,并可选支持 ZNS 和 FDP,使分区式或放置感知优化可灵活采用,而非强制要求。
- 在 YCSB-A 上的评估显示,吞吐量提升 1.65 到 2.24 倍,每次操作的闪存写入量减少 6.2 到 9.8 倍;在 15,000 个仓库的 TPC-C 测试中,吞吐量提高 2.45 倍,闪存写入量减少 7.2 倍。
引言
现代数据库越来越依赖 SSD 作为主要存储介质,但大多数系统仍主要针对 SSD 读取而非写入进行优化。这很重要,因为 SSD 会随着反复写入而磨损,并且会通过内部垃圾回收放大写入,因此低效的写入路径可能损害性能、增加功耗并缩短 SSD 寿命。
关键挑战在于,写放大发生在多个层次之间。数据库可能由于双写缓冲等机制发出额外写入,而 SSD 之后又可能在垃圾回收期间迁移页时再次放大这些写入。已有工作通常分开处理这些效应,要么关注数据库级写放大,例如通过索引或压缩优化,要么关注 ZNS 或 FDP 等专用硬件。因此,如果忽略与其他层的交互,优化某一层有时反而会增加总写入量。
作者通过论证 DBMS 自身应当协调写入以最小化端到端写放大来弥补这一缺口。他们从原地写入转向异址写入,并引入互补技术,包括压缩页的页打包、按预期失效时间对页分组、将数据库垃圾回收与 SSD 内部机制对齐,以及能够在 ZNS 和商用 SSD 上都实现 SSD 写放大为 1 的模式。在他们的 LeanStore 实现中,这些方法显著减少了闪存写入,并将吞吐量接近翻倍,同时延长设备寿命。
方法
作者利用异址写入流水线来降低数据库级和设备级写放大。系统不是原地覆盖页,而是将新的页版本追加到追加式管理区域(称为区域),维护从逻辑页标识符到物理偏移和压缩大小的映射,并作为协调工作流控制失效、压缩、放置和垃圾回收。核心组件包括跟踪页修订历史的缓冲管理器、执行压缩和页打包的 I/O 接口、将分组页放置到区域中的空间管理器,以及在保持工作负载感知对齐的同时回收过时页的垃圾收集器。
一个早期关键模块是按页压缩。数据库页通常以固定逻辑粒度管理,例如 4 KiB,并在刷新前独立压缩。在持久化之前压缩每个页会降低写入的逻辑字节数,并减少磁盘上的数据集占用。借助异址写入,变长压缩页可以顺序写入,并记录在 PID 到偏移表中,因此后续读取可以定位当前版本,而不覆盖原始物理槽位。这避免了原地压缩的主要限制:全量对齐覆盖会抵消物理空间节省,或维护可变偏移需要昂贵的级联迁移。在异址设计中,压缩页会被分组为更大的批次并作为顺序写入发出,使压缩更容易集成到存储层中。
由于压缩页小于原始 4 KiB 页,放置过程必须保留底层 SSD 的高效读取单元。因此系统应用页打包:压缩页被放入 4 KiB 对齐槽位中,使压缩页不会跨越 4 KiB 边界。可以使用最佳适配打包策略来减少内部空隙,同时让每个逻辑页能够通过一次对齐的 4 KiB 访问读取。这一设计防止单个逻辑读取变成多个物理读取,否则会引入读放大并抵消压缩收益。
数据库级垃圾回收是下一个主要模块。在异址系统中,更新会追加新的页版本并将旧版本标记为无效。存储空间被划分为区域,每个区域作为基本回收单元。在 GC 期间,系统选择受害区域,读取尚未在内存中的有效页,将它们压实到更紧凑的区域中,回写,然后释放回收的空间。这种复制回写过程会产生写放大,因为有效页即使未被用户事务修改也会被重写。如果受害区域的有效页比例为 v,节省的空间对应于无效数据 1−v,一个简化的数据库写放大可以表示为:
WAF=1−v1.受害区域中的有效页比例越高,单位回收空间需要移动的数据就越多。如下图所示:
垃圾收集器选择包含有效页和无效页混合的受害区域,将有效页加载到内存中,重新压实它们,将压实后的结果回写到设备中,然后将释放的空间提供给后续写入使用。贪婪受害者选择会选择有效页最少的区域,在均匀工作负载下可能有效。然而,真实工作负载通常是倾斜的,导致热页和冷页共处同一区域,从而提高受害区域的有效页比例。
为解决倾斜问题,作者引入按失效时间分组(Grouping by Death Time, GDT)。其思想是估计每个页何时可能失效或被覆盖,然后将预期失效时间相近的页放入同一区域。这使同一区域中的页共同失效,从而降低 GC 时的有效页比例。系统维护每个页的近期写入时间戳,并通过近期写入间隔外推预期失效时间(EDT):
EDT=current LSN+n−1WHn−WH1.这里,WHn 和 WH1 表示小型写入历史中的最新和最早写入时间戳,n 是保留的近期时间戳数量。对于首次写入,系统可以使用索引标识符或其他结构元数据推断相似访问模式。在放置过程中,页按 EDT 分组、进行页打包,并分配给现有页平均 EDT 最接近的区域。如果不存在合适的活跃区域,空间管理器会打开新区域。
GDT 放置与 GDT 感知 GC 配套使用。在回收期间,受害区域中的有效页会被加载、按 EDT 降序排序、分组并打包,使更冷页被重写到更冷区域。只读页可以被分配最大 EDT,被视为最冷数据。在一个 GC 周期之后,系统重新分配区域对应的 EDT 范围,使空区域和部分占用区域能够吸引短生命周期或温页,而全满区域保留最冷页。这使失效行为在多个 GC 周期之间保持一致。如下图所示:
当预期失效时间差异很大的页被随机交错到各区域时,GC 触发时可能遇到许多仍然有效的页。当页按失效时间分组时,受害区域在回收时刻可能几乎不包含有效页,从而显著降低由 GC 引发的数据库写放大。
该方法随后将控制从数据库级 GC 扩展到 SSD 级 GC。现代 SSD 内部也使用异址写入,其控制器将逻辑块地址映射到物理块地址,并将写入追加到擦除块对齐区域,这些区域通常称为超块。当设备容量接近耗尽时,SSD 会以厂商特定粒度执行内部 GC。系统尝试将其区域大小与 SSD 内部 GC 单元对齐,使一起写入的页在物理布局中也一起失效。如下图所示:
当数据库区域大小匹配 SSD 超块或 GC 单元时,来自一个区域的所有写入会放置在一个超块中。后续数据库 GC 重写该区域时,会使对应超块中的所有页失效,并产生完全空的物理区域。如果区域小于超块,来自不同数据库区域的写入可能交织,使短生命周期和长生命周期数据混在同一超块中。当仅重写一个逻辑区域时,超块只部分失效,迫使 SSD 执行更复杂的内部回收。
这种对齐原则很好地支持 Zoned Namespace SSD。ZNS 设备在主机管理区域内强制顺序写入并消除内部 GC,因此设备写放大可接近 1。数据库设计已经使用异址追加、转换层和自身 GC,因此可自然映射到 ZNS 区域。系统将数据库区域大小设置为 ZNS 区域大小,发起区域追加操作,跟踪区域状态,并在 GC 复制回写后重置区域。ZNS 还将预留空间返还给主机,使数据库级 GC 有更多空间等待更多页失效,从而降低数据库级写放大。
对于内部 GC 仍不可见的商用 SSD,系统使用两种技术:GC 单元推断和 NoWA 写入模式。对于支持 FDP 的设备,回收单元大小直接暴露最小 GC 粒度,允许将数据库区域大小设置为该单元。对于标准 SSD,系统可以通过观察类 ZNS 写入模式推断 SSD GC 单元的上界:随着区域大小增加,当区域足够大以匹配内部追加和回收粒度时,SSD 写放大可降至 1。可以从这一过渡中选择一个实际上界;当没有更强设备信息时,可使用保守的大区域大小。
NoWA 模式处理多线程执行下多个活跃区域更复杂的情况。当数据库并发写入多个区域时,无法区分区域边界的 SSD 可能会将它们写入复用到相同超块中。复用本身并非致命问题,但当同一活跃组中的区域以不同速率失效时会造成写放大。一些区域可能产生完全失效超块,而另一些区域产生仍包含有效页的部分失效超块。如下图所示:
NoWA 通过在 SSD 达到内部空闲空间阈值之前确保至少有一个完全失效超块可用,来防止这种结果。它会推迟打开新区域,直到当前打开的区域完全写满,并检测并发追加区域之间的频率失衡。如果一个相对弱势的区域需要更多物理放置机会,系统会为该区域发起补偿写入。补偿写入将分散的页重新压实到一个超块中,使其旧版本失效,并增加完全空超块的数量。如下图所示:
补偿写入会略微增加数据库级 GC 工作量,但开销受数据库受害区选择控制,通常远小于可避免的 SSD 级写放大。NoWA 还允许灵活的区域大小:只要并发打开区域的最大数量与区域大小的乘积等于或整除推断出的 SSD GC 单元,就可以保持均衡失效特性。
对于 FDP SSD,同样可以通过放置提示而非补偿写入解决这种失衡问题。FDP 暴露回收单元句柄,每个句柄代表独立写入流。数据库为每个区域分配由 RUH 数量推导出的放置 ID,并将该放置 ID 用于属于该区域的所有写入。不同区域因此映射到不同 RUH,防止具有不同失效时间的逻辑区域在内部被复用。当区域大小等于回收单元大小且并发打开区域数量不超过可用 RUH 数量时,可实现 SSD 写放大为 1。
实现将这些组件组合到单一写入路径中。在页换出时,脏 PID 与其内容和估计失效时间一起传递给 I/O 接口。I/O 接口压缩每个页,将 EDT 相似的 PID 分组,将它们打包到对齐的 4 KiB 槽位中,并向空间管理器请求目标区域。空间管理器维护 PID 到偏移映射、偏移反向 PID 映射、填充因子、失效计数、平均 EDT、区域状态和活跃组历史。它选择平均 EDT 最接近的活跃区域,或在必要时打开新区域。如果活跃区域容量耗尽,则在请求的写入继续之前运行 GC。GC 将有效页读入共享缓冲池,按 EDT 排序、压实,并通过相同的压缩、分组、打包和放置路径重写。恢复过程持久化 PID 到偏移表和活跃组历史的检查点,重放后续 WAL 条目,并根据最终映射状态重建存储空间布局。
实验
评估使用多款企业级 SSD,包括 ZNS 和 FDP 设备,在小缓冲池下运行写入密集的 YCSB-A 和 TPC-C 工作负载。实验验证了:将数据库 GC 粒度与 SSD GC 粒度对齐,使用异址放置、失效时间感知 GC 和抑制写放大的模式,可显著降低总写放大并提升吞吐量。压缩会放大这些收益,而 ZNS 和 FDP 通过暴露额外容量和放置控制进一步降低放大。总体而言,该方法在不同设备、数据集大小和工作负载变化下均保持稳健。
LZ4 和 ZSTD 都能将每个被评估的 OLTP 工作负载压缩到足以降低存储数据量,从而支持压缩作为降低写入量和存储占用的实用方法。在 TPC-C、YCSB、Email、URL 和 Wiki 数据集中,ZSTD 在压缩尺寸上始终小于 LZ4。URL 和 Wiki 数据压缩效果最强,而 TPC-C 和 YCSB 虽然降幅更温和,但仍具有显著意义。压缩降低了所有被评估工作负载的数据量。ZSTD 在每个工作负载上的压缩效果都优于 LZ4。URL 和 Wiki 数据集表现出最强降幅
与原地写入相比,应用所提出优化可提升吞吐量并降低总写放大。异址写入消除了数据库写入开销,但在高填充因子下可能提高写放大;而压缩、倾斜感知放置和对齐垃圾回收会降低数据库和 SSD 两个层面的放大。与原地写入相比,所有优化都显著提升吞吐量。压缩和页打包降低数据库写放大以及每次操作的写入流量。NoWA 和 GC 单元对齐将 SSD 写放大降至理想水平。移除压缩会提高数据库写放大,即使 SSD 写放大保持较低。
与传统 CNS SSD 上的原地写入相比,异址写入显著提升吞吐量。当数据集能容纳在两类盘上时,ZNS 也提升性能;当 ZNS 更大的可用容量允许比 CNS 更大的数据集时,其收益更大。在相同填充因子下,ZNS 的优势收窄,表明可用容量是收益的主要来源。在相同数据集大小下,CNS 上的异址写入带来约原地写入两倍吞吐量。当两者使用相同数据集大小时,ZNS 相比异址 CNS 仅带来温和的吞吐量增益。使用更大数据集时,ZNS 相比 CNS 展现更明显的吞吐量优势,主要来自更大可用容量。匹配填充因子会降低 ZNS 的性能优势,表明大部分收益来自容量,而非降低的 NoWA 开销。
FDP 放置提示允许存储系统在不使用 NoWA 补偿写入的情况下维持 SSD 级写放大为 1。启用所有优化的异址写入相比原地写入产生显著更低的总写放大和更高吞吐量。启用 FDP 而不是 NoWA 会进一步略微降低数据库写放大,并带来最大吞吐量提升。FDP 防止 SSD 复用并实现 SSD 写放大为 1。优化后的异址写入相比原地写入大幅降低总写放大。启用 FDP 的异址写入在被比较配置中提供最佳吞吐量
跨 OLTP 工作负载和数据集的实验评估了压缩、写入放置优化以及存储设备行为。ZSTD 始终优于 LZ4 压缩效果,其中 URL 和 Wiki 数据压缩效果最强;所提出的异址优化相比原地写入显著提升吞吐量并降低总写放大。传统 CNS 与 ZNS 的比较表明,异址写入相比原地写入带来主要收益,而 ZNS 优势很大程度反映可用容量;FDP 放置提示实现理想 SSD 写放大和最佳总体吞吐量。