数据库中写放大问题,大牛居然用“透明压缩技术”缓解了

Java之间

共 4838字,需浏览 10分钟

 · 2021-01-26

往期热门文章:
1、为什么要放弃 JSP ?
2、请谨慎使用Arrays.asList、ArrayList的subList
3、人脸识别“抓”错了人,他在监狱呆了 10 天
4、这四种情况下,才是考虑分库分表的时候!
5、求求你别再用offset和limit分页了

来源 | ScaleFlux系统团队郑宁,王欢、许树堃

数据库中的写放大

在数据库的使用过程(包括其它多种应用)中,我们通常会关注一些系统指标,比如CPU的使用率,内存的占用量,或者IO的带宽消耗等等。这些系统指标可以帮助我们评估应用对系统资源的占用情况,进而找到应用进一步优化的方向。当前常见的一些数据库,例如MySQL,MariaDB,PostgreSQL,RockDB等等,从设计思路上可以分为如下所示的两大类:

  • 基于B/B+树的结构——这种结构通过B/B+树上的节点管理和组织数据库内的记录,当记录有增减时,树里的节点可以进行相应的分裂与合并。

  • 基于日志结构合并(LSM,log structure merge)树的结构——这种结构将数据库内的记录存储在多个level中,level之间通过相应的合并来适应数据库记录的增减以及维持树的形状。

B+树
LSM树

通常在数据库运行的时候,其占用的写带宽是远远大于上层所能看到的TPS(transactions per second,每秒事务数)折算后所需要的带宽的。例如,对于记录大小为1KB,TPS为1K的情况,理想情况下写带宽的占用是1KB * 1K/s = 1MB/s,但实际中带宽的占用可能是十数MB/s甚至数十MB/s。这是因为在数据库(包括更底层的软件栈)中,为了保证数据的安全性和一致性,以及设计上的简洁性和高效性,不可避免地引入了写放大问题。

写放大带来的最直接的问题就是写入带宽的占用量大幅增长,另一个问题就是SSD寿命的快速消耗。在SSD中,受NAND擦写物理条件的限制,新的数据总是写在新的地方,而NAND总的擦写次数是有限的,这就造成了写入的数据量越多,NAND越容易被写满,进而需要更频繁的擦除和垃圾回收,因此寿命消耗地也越快。

写放大问题对于即将量产的QLC(每个NAND cell对应4个比特),甚至是已经提上议程的PLC(每个NAND cell对应5个比特),是非常不利的,因为随着每个NAND cell对应的比特数的增加,NAND的可擦写次数会直线下降。如图2所示,Intel 的D5-P4326 QLC系列,顺序写P/E cycle为1600次左右,随机写只有300余次。

D5-P4326 QLC系列可靠性参数

本文主要讨论透明压缩给数据库中的写放大问题带来的契机。下文将首先针对一些主流数据库分析写放大的主要来源,然后介绍透明压缩的概念和实现,最后评估透明压缩对数据库中写放大的减少效果。

写放大的来源

我们选取MySQL(B+树结构)和RocksDB(LSM树结构)作为研究对象。MySQL作为最广泛应用的关系型数据库,在国内有巨大的存量市场;RocksDB作为NoSQL的代表,也在越来越多的场景中被使用。对这两种数据库的研究,不仅可以用来评估透明压缩对不同种类数据库中写放大的影响,还可以帮助我们比较不同的树形结构下数据库中写放大的情况。

对于MySQL,其数据的写入主要分为图3所示的四个部分:

  • Binlog——MySQL server端的逻辑日志,负责记录对MySQL数据库进行更改的所有操作。
  • Redolog——InnoDB存储引擎的物理日志,负责记录数据修改之后的值(不论事务提交与否)。
  • 双写缓存(double write buffer)——为防止页面的部分写入问题而引入的“预写”过程。
  • 数据页——数据库存储记录的主体部分。
MySQL中写入数据的主要构成

在这四部分中,只有数据页才是我们真正希望固化下来的存储部分,其它的部分都是为了数据完整性、安全性等考量而引入的,显然同时也引入了写放大问题。即使是数据页本身,也存在着写放大问题。默认配置下,一个MySQL的数据页是16KB,而一条记录可能只有几百个字节;而更新数据页中的一条记录时,我们需要将整个16KB页面重新写一次,其写放大程度可想而知。

除了应用产生的数据之外,MySQL一般还运行于文件系统之上。当我们需要同步更新数据的时候,也会涉及到文件系统本身的日志和元数据的更改,这部分写入的数据是应用层不感知的,但是很多时候往往会占据相当一部分的写入流量(例如,更新文件中一个4KB的页面,可能会引起12KB的日志更新),从而带来了进一步的写放大。

在RocksDB中,除了WAL引入的写放大之外(WAL也会涉及到文件系统级别的日志和元数据的写入),其内部的写入操作基本都是大块数据的顺序落盘,这部分写入在没有compaction的情况下写放大是比较小的。但是基于LSM树的结构在每个相应level的数据量达到一定程度之后,需要后台的compaction操作对相邻两个level中的部分数据进行归并排序,然后重新写入到较低的level中(如图4所示),这样就引入了另一个层面的写放大问题。随着数据量的增加,compaction的次数也会越多,写放大程度相应的也越大。

图4. MyRocks中的compaction

在评估这两种类型的数据库的写放大之前,我们先来看一下透明压缩。

透明压缩

提到压缩,我们一般会想到基于软件的CPU压缩,如图5(a)所示。数据通过不同的压缩算法压缩后,数据量会减少,这样在存储的时候所需的存储空间会更小;在数据读取时,先经由CPU解压缩,然后应用再做进一步的处理。不同的压缩算法,往往是在效率和压缩程度之间做取舍:简单的压缩算法,如LZ4和Snappy,压缩解压缩速度快,CPU占用少,但是压缩效果差一些;复杂的压缩算法,如Zlib和Bzip2,压缩解压缩速度慢,CPU占用多,但是压缩效果会好一些。

但是,无论压缩算法本身复杂与否,基于CPU的软件压缩,都需要占用系统的CPU资源,而且需要应用集成相应的压缩/解压缩模块。而采用透明压缩,则可以避免CPU的开销,同时做到压缩解压缩对应用完全透明(应用对于数据的压缩和解压缩是无感知的),如图5(b)所示。

图5. (a)基于CPU的软件压缩和(b)透明压缩

在透明压缩中,当应用将原始数据写入硬盘时,由硬盘本身对数据进行压缩操作,然后将压缩后的数据写入NAND;当应用读取数据时,先从NAND中读入压缩后的数据,经由硬盘解压缩后返还给应用。与软件压缩不同的是,经过透明压缩的数据,在写入NAND时并没有4KB扇区对齐的约束,其所占用的物理空间是按需分配的。这样就需要在透明压缩的驱动中,实现逻辑块地址(LBA)和物理块地址(PBA)的变长映射(在传统驱动中是定长映射的,即一个4KB的LBA对应一个4KB的PBA),如图6所示。

图6. LBA和PBA的变长映射

变长映射表的存在给硬盘驱动的设计带来了新的挑战,因为其一方面要实时记录数据压缩前后在逻辑空间和物理空间上的对应关系,另一方面也要提供高效的读写访问机制,以保证硬盘的极致性能。但正是由于驱动中的变长映射,上层应用才可以透明地使用压缩解压缩功能,且无需消耗额外的CPU,还可拥有线性的压缩/解压缩扩展能力。

写放大的评估

我们选用ScaleFlux的 CSD 2000(3.2TB)作为评估的目标SSD。CSD 2000是一款实现了透明压缩的SSD,其压缩能力和Gzip Level 6相当,压缩吞吐可达2GB/s以上。同时,我们选用Sysbench 1.1.0作为基准测试,并使用insert,update-non-index和update index三种写入方式评估MySQL 5.7和MyRocks(MySQL 8.0 + RocksDB 6.9)的写放大,以及透明压缩在减少写放大上的效果。

测试中使用的CPU为24核@2.3GHz E5-2630,内存为128GB,文件系统为Ext4,线程数为24。在测试时,Sysbench先向数据库中写入200GB数据,在此基础上依次进行insert、update-non-index和update index的操作,并记录每种情况下主机端的数据写入量以及NAND的实际写入数据量(在普通SSD下这两个数据写入量基本相等,而在透明压缩的环境下,NAND的实际写入量往往会小于主机端的写入量)。MySQL的测试中关闭软件压缩,页面大小为16KB,内存池为32GB;而MyRocks中RocksDB采用LZ4软件压缩,总共7个level,其它为默认配置。此外,在测试中每个事务均进行commit和sync操作,以保证数据的安全性和一致性。

图7显示了188B的行大小下,MySQL和MyRocks在不同写入场景下的log/journal写入数据量和data的写入数据量(均以正常SSD中的写入量进行了归一化)。从图中可以看出,log/journal的写入量的占比是很可观的,尤其是在MyRocks中,基本都达到了70%以上。一般情况下,这些由log/journal写入的数据,是具有较好的压缩比的,特别是文件系统写入的日志,压缩比往往可以达到5:1甚至更高。因此,经过透明压缩后,写入的数据量会有明显的降低(测试中降低了50%以上)。

图7. 188B行大小下,MySQL和MyRocks在不同SSD下的归一化数据写入量

图8显示了188B的行大小下,MySQL和MyRocks在不同场景下的写放大绝对值的对比。可以看出,在更新场景下,写放大还是很严重的,MySQL达到了200以上,而RocksDB也有80左右。而有了透明压缩后,无论是MySQL还是MyRocks,总体的写放大都可以降低一半以上。这就意味着,相同的SSD,在使用透明压缩后其寿命可以延长一倍以上。

图8. 188B行大小下,MySQL和MyRocks在不同SSD下的写放大绝对值

我们也测试了512B的行大小下对应的归一化后的写入量以及写放大绝对值的对比,如图9和图10所示。从图中可以看出,当每一行的大小增大后,log/journal写入量的占比有所降低,但是其绝对比例依然不小(MySQL中25%以上,MyRocks 中达到60%以上);而且行大小增加后,写放大的绝对值(在更新场景下)有所减小,其中MySQL由200左右减小为80左右,而MyRocks由80左右减小为50左右。同188B的情况类似,经过透明压缩后,无论是哪种数据库,总体的写入量/写放大都减少了一半以上。

图9. 512B行大小下,MySQL和MyRocks在不同SSD下的归一化数据写入量
图10. 512B行大小下,MySQL和MyRocks在不同SSD下的写放大绝对值

总结

数据库应用大多都存在着严重的写放大,这使得SSD(尤其是刚上市的QLC SSD)的寿命问题成为一个很大的顾虑。透明压缩可以有效地减少写入NAND的数据量,为减小数据库的写放大问题提供了新的思路。

最近热文阅读:

1、为什么要放弃 JSP ?
2、请谨慎使用Arrays.asList、ArrayList的subList
3、人脸识别“抓”错了人,他在监狱呆了 10 天
4、骚操作 !IDEA 防止写代码沉迷插件 !
5、这四种情况下,才是考虑分库分表的时候!
6、求求你别再用offset和limit分页了
7、10 个最好用的重构小技巧排行榜,你用过哪些?
8、浅谈用不好缓存的几个受伤场景!
9、高并发下接口幂等性解决方案
10、给代码写注释时有哪些讲究?
关注公众号,你想要的Java都在这里

浏览 18
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报