改变数据页大小能带来多少收益?
前言
今天下午④群有位群友问了这样一个问题,关于 pgbackrest 的
各位大佬,请教个问题,pgbackrest是只支持8k的block size吗?还是编译的时候需要指定参数才可以支持其他大小的block size。我用block size 32k pg16.1 的库做测试,报下面的错误
可以看到,pgbackrest提示了一个很清晰的报错:page size is 32768 but must be 8192,即当前数据库的块大小是32KB,但是pgbackrest要求必须是8KB,关于这个限制我之前还真没注意过,于是下来去找了一下资料,也特意编译了一下,发现还真的不支持修改块大小,这是个硬限制,必须是8KB的数据块。
并且官方的回复也很耐人寻味,不打算支持不同的页面大小,因为没有经过大量测试,并且需要重新编译才能修改,并且,增大页面大小可能并不如想象中的那么有益。总而言之,pgbackrest官方不会考虑支持不同的页大小,这就和PostgreSQL不支持hint并且永远打不算支持hint有点异曲同工之妙了。
那不妨让我们思考一下,为什么pgbackrest官方会认为不同的页大小并不是那么重要?油管上面 tomas vondra 做了一篇名为 Postgres vs. page sizes 的分享,听完之后,受益匪浅,也让我有了更多的思考。让我们随着作者的思绪一起听一下这篇分享。
Postgres vs. page sizes
页结构
前面主要介绍了 PostgreSQL 读取数据页的大致流程
默认8KB的页面,表/索引等数据库对象,以及WAL在底层存储上都是按照8KB的页面进行划分。但是由于一 IO会经过层层关卡,并且由于使用的是Buffer IO,还会涉及到操作系统页缓存 (此处做了简化,没有去考虑什么驱动层、IO调度层这些东西)。
可以看到,PostgreSQL虽然默认使用8KB的页面,并且由于是Buffer IO,那么以x86为例,page cache和文件系统通常默认是4KB,然后就是存储层,老式HDD扇区通常是512字节,新一点的可能是4KB。SSD的话会更加复杂一点,通常在4KB ~ 16KB。因此整个IO流程就变得有点复杂了,一个数据页可能映射到多个文件系统页,而每个文件系统页又可能映射到多个存储"页",这也侧面导致了"放大",WAL的机制类似,但相对要简单一点,因为都是顺序写。
SSD vs. HDD
作者也特意介绍了一下SSD和HDD的区别,因为这对后文的测试也很重要。
HDD使用磁盘,即磁性介质作为数据存储介质,在数据读取和写入上,使用磁头+马达的方式进行机械寻址。因为机械硬盘靠机械驱动读写数据的限制,导致机械硬盘的性能提升遇到了瓶颈,特别是随机读写能力。SSD使用Flash作为存储介质,数据读取写入通过SSD控制器进行寻址,不需要机械操作,有着优秀的随机访问能力。
与HDD不同的是,SSD总是将数据写入新位置(Program),同时不断擦除(Erase)老数据,完全擦写一次可以称为1次P/E,因此SSD的寿命以P/E为单位。page是单次读写单位,大小一般为4KB ~ 16KB,写操作只能写到空的page,而擦除是以块为单位,块通常是64~128个page,块的擦除次数有寿命限制,超过限制就会变成坏块。
SSD通常都有较大的DRAM缓存,
PostgreSQL总是重新写入整个数据页,使得所有"映射"的SSD页面失效,导致"放大"问题
数据的反复修改会产生大量的无效页,一旦整个块的空间不足以写入数据,SSD会将这个块的数据读入到缓存中,擦除这个块中的页,然后再把缓存中已更新的数据写入进去。这种 read-erase-modify-write过程,就好比写入的数据可能只有一个页4KB,但实际要擦除并且写N个页,称之为写入放大。就好比你写一张纸需要耗费一张纸,你写一行字也会浪费一张草稿纸,这就是写入放大的一个主要原因。
修改块大小
要修改块大小,我们需要在编译的时候修改,并且一经编译,无法动态修改,磁盘上的数据布局与内存中的数据表示完全一致。页面及其元组按原样读入缓冲区缓存中,无需任何转换。这也是为什么数据文件在不同平台之间不兼容的原因,当然还有可能的原因便是字节序、大小端以及对齐的问题。
数据页大小最小支持1KB,最大32KB WAL页大小最小支持1KB,最大64KB
修改很容易,但是代价却不菲,就比如此例中,改了之后你的pgbackrest就摆烂了,其次每个小版本怎么办?另外,AFAIK,CI/CD测试也是默认使用的8KB大小,如果修改了,那么意味着你也要再做一遍测试。其次就是那些插件?那些备份工具?那些上下游生态组件等等?是否都会有影响?这些都是你要考虑的点。
OLTP
tomas vondra 为了验证不同的块大小会对性能产生怎样的性能影响,进行了详尽的测试。他的机器规格如上,一台64GB内存,280GB NVME,另一台是8GB内存,100GB SATA (RAID)。
按照以往我们的既定思维,小的数据块适合OLTP,大的数据块适合OLAP,比如Greenplum默认数据块就是32KB。对于WAL的话,大的数据块可能更有效(顺序写)。
OLTP使用pgbench进行测试,主要调整不同的scalefactor以控制数据集大小,数据集也分为三种情况:
小的数据集适合shared buffers 中等数据集适合内存 大数据集超过内存,也就意味着page cache无法全部缓存,可能需要进行I/O
小数据集
X轴是数据块大小,Y轴是WAL的块大小,左侧是read-only测试结果,右侧是read-write测试结果,颜色越绿代表结果越好,TPS越高,颜色越红代表结果越差,TPS越低。
以8KB的默认参数作为基准数据 (因为大多数情况下我们使用的都是该配置),然后对比不同的数据块和WAL块大小的性能(以百分比显式)。
如果 >100 意味着TPS更高,<100意味着TPS更低。
结果表明,对于小数据集没有太大差异。对于read-write来说,情况类似,使用大的WAL页面显然并没有好处,吞吐量下降了10% ~ 20%。
中等数据集
中等数据集,对于read-write,1~4KB的数据页,大概有10%的性能提升,越大的数据页会有下降,当32KB的时候,下降约35%。
而对于WAL的页大小,影响就要低一点,32 ~ 64KB要稍慢一点。
大数据集
对于超过内存的大数据集与中等数据集的结果类似,但是效果更加明显。1 ~ 4KB的页大小,对于read-write大约可以提升50%左右,这个是个很可观的数字,对于read-only,也有20%左右的性能提升。但是较大的WAL页面带来的好处并不明显。
FPI 的影响
接下来是每个事务产生的WAL大小,可以看到,对于越小的数据块,产生的WAL量越小,不难理解,数据页越小,那么由于FPI记录的整页大小也就越小,WAL的量也随之越小。大数据集的行为也是类似,越小的数据块,WAL的量大约可以减小20%~30%左右。
作者也测试了一下,将full_page_write设为off之后,基线从42K的TPS提升到了58K,提升也很可观,如果改为4KB,也是从57K提升到了70K。
SATA
X轴是请求的大小 (size of request in kilobytes),Y轴是IOPS。
对于SSD,请求大小和IOPS的对比会非常明显,右侧的图显式:请求大小越大,IOPS越小,因此具体存储的行为和访问数据的方式都至关重要。
如果是SATA,结果类似,4KB大约提升40%(read-only 提升30%左右)。
结论
对于至强处理器,对于小的数据块,2~4KB,提升大约50%;WAL的块大小无关紧要 对于i5处理器,对于小的数据块,2~4KB,提升大约40%;小的WAL块大小表现要好点
OLAP
OLAP采用TPC-H 模型,不过作者只运行了初始数据加载和查询,没有进行任何数据刷新等操作。
数据加载
数据加载包括COPY,创建PK/FK/INDEXs,VACUUM等,此处的指标是耗时时长。
可以看到,数据块越大,耗时越少,8KB和32KB大约20%左右的差距。WAL块大小也是类似,越大,时间也少,但相较于数据块的影响就要小得多。
动作拆解
将5个动作拆解开之后,行为类似,1KB的性能最差,大数据块性能越好。但是外键有个例外,32KB时有个尖刺。
查询
查询是跑完每组查询的总耗时,2~16KB的数据类似,但是为1KB和32KB的差距这么明显?
QUERY 17
随着数据块的增大,耗时逐渐降低,在16KB的时候,达到最佳状态,32KB的效果并没有8KB的好。
QUERY 8
QUERY 8表现不同
QUERY 7&9
7和9就表现更奇葩了,时好时坏,作者的解释是
更大的页面意味着更少的页面数量,从而导致不同的成本估算。这可能是成本模型的一个限制或问题,只是简单地使用seq_page_cost和random_page_cost,而不考虑页面大小。此外,查询中的不同操作可能对页面大小的敏感度不同,并在不同的页面大小下“切换”。
查询计划与数据块大小之间有这明显的相关性,更大的页面意味着更少的页面数量,从而导致不同的成本估算。
结论
最终结论
不同的页大小对于性能可能会产生可观的影响 WAL的页大小影响并不大 对于OLTP类的应用,小数据页更佳 (SSD) 对于OLAP类的应用,越大的数据页和WAL页大小,加载越快 对于OLAP类的查询,则较难调整,需要根据实际情况和测试数据,相应调整
小结
确实,优化器只考虑了随机IO和顺序IO的比值,而没有考虑数据块的大小,比如FPI,数据块的大小就会有显著影响;其次,不同存储之间的行为差异等等,另外,数据块越大,意味着数据页越少,那么进而影响到relpages,影响到执行计划的选择 (cost的计算)等等,再者,统计信息的收集是基于采样算法,默认是3W个数据块里取3W行数据来进行采样,数据块的多少也影响到数据统计信息的采集,如果有4W个,则采样3W个,如果29000个,则全部采集...
但是,一个大致可行的想法是:越大的数据块对于批次操作有益,但是不利于随机操作。TP场景下,对于SSD,默认4KB的页大小,可以考虑将PostgreSQL的blocksize设置为4KB,HDD也可以考虑设置为4KB。
参考
https://www.youtube.com/watch?v=mVKpoQxtCXk
https://www.zhihu.com/question/31024021