[翻译]PostgreSQL中的WAL压缩以及版本15中的改进从以开始就一直在尝试对WAL进行不同级别的压缩。自2016年以来内置功能(wal_compression)就一直存在,几乎所有备份工具都会在传递到备机前对WAL进行压缩。但现在是时候再看看内置的wal_compression功能了。因为PG15提供了更多功能。如果我们将其与PG15中WAL归档的其他重大改进相结合,将会有相当大的收益,如之前贴子PG15中的新WAL归档模块/库:https://www.percona.com/blog/new-wal-archive-module-library-in-postgresql-15/以及PG15中的WAL归档加速:https://www.percona.com/blog/speed-up-of-the-wal-archiving-in-postgresql-15/这里并不打算通过备份工具查看WAL段文件压缩,因为它是一个PG外部工具,主要是备份工具的一个功能,即使我们不使用专门的备份工具,我们仍然可以将WAL段文件作为归档的一部分进行压缩,这可以获得很大回报。先看下PG内部为WAL压缩提供了什么。在将整页写入WAL时启动WAL压缩功能,这将节省大量IO开箱。减小WAL段文件大小在复制和备份方面有进一步的好处,毕竟需要传输的数据更少了嘛。
什么是全页写?
新手用户可能想了解啥是“Full Page Writes”,PG默认使用8KB页面。
postgres=# show block_size ; block_size ------------ 8192 (1 row)
但是主机可能正在处理更小的页面,比如 4k 页面。
$ getconf PAGESIZE 4096
PG将8KB页作为读写的“原子单位”来处理。但由于主机的页面较小,它会拆分8KB页面,并将每个OS页面视为一个原子单位。如果突然中断,会导致问题:8KB页面的一部分可能已保存,而另一部分可能会丢失。因为主机可能不会将后者视为原始部分的一部分。这就是通常说的“部分页面写入”或“撕裂页面”。从数据库的角度来看,这种“损坏的页面”是损坏的。如果存在具有这种撕裂页面的数据文件,PG将失去页面的一致性。当然,这不仅仅是PG的问题,每个数据库软件都需要处理整个问题。例如MySQL/InnoDB使用doublewrite缓冲区处理此问题,恢复时从doublewrite恢复未损坏页面的副本。PG解决这个问题的方法略有不同。该副本在检查点后首次修改时,PG将完整页面的副本写入WAL有日志中。由于WAL周期性同步,并且PG也可以决定在哪个点进行恢复,因此WAL是一个安全的地方用于保存“整页”副本。
全页写的性能损失
如上所述,当数据库页面在检查点之后第一次被修改时,需要将其安全地写入 WAL。因此在崩溃恢复期间,PostgreSQL 可以安全地应用来自 WAL 日志的一致页面。但这会带来相当大的性能影响。现在我们知道在一个检查点之后很有可能会立即进行过多的整页写入。这将在 PostgreSQL性能基准测试中清晰可见,作为Vadim 在他的测试中观察到的“锯齿波”模式:
正如我们所看到的,由于大量 WAL 写入,吞吐量在每个检查点之后突然下降,然后逐渐回升,直到下一个检查点。
全页压缩和改进
PG14及其之前的版本全页更大并且包含所有内容。允许在写入WAL段文件之前压缩那些完整的页面。这个特性在PG9.5就出现了,使用内置的LZ压缩实现,通常称为“pglz”。但由于CPU开销,并不受欢迎。所以并不建议使用。可以使用如下命令启用:
ALTER SYSTEM SET wal_compression=ON; SELECT pg_reload_conf();
现在的压缩算法开始提供更好的压缩,同时占用更少的CPU周期。Lz4就是一个很好的例子。PG15针对wal_compression增加了枚举值:pglz、lz4、zstd。以及向后兼容的on和off。on,true,yes和1等布尔等效于“pglz”。与 PostgreSQL中内置的压缩库 pglz 不同,新的压缩功能由外部库提供。因此需要在编译时启用它们。配置标志–with-lz4和–with-zstd分别用于 lz4 和 zstd。对于repositories中安装的预编译安装包,可以使用下面命令来检查:
/usr/pgsql-15/bin/pg_config | grep "zstd\|lz4" CONFIGURE = '--enable-rpath' '--prefix=/usr/pgsql-15' '--includedir=/usr/pgsql-15/include' '--mandir=/usr/pgsql-15/share/man' '--datadir=/usr/pgsql-15/share' '--libdir=/usr/pgsql-15/lib' '--with-lz4' '--with-extra-version= - Percona Distribution' '--with-zstd' '--with-icu' '--with-llvm' '--with-perl' '--with-python' '--with-tcl' '--with-tclconfig=/usr/lib64' '--with-openssl' '--with-pam' '--with-gssapi' '--with-includes=/usr/include' '--with-libraries=/usr/lib64' '--enable-nls' '--enable-dtrace' '--with-uuid=e2fs' '--with-libxml' '--with-libxslt' '--with-ldap' '--with-selinux' '--with-systemd' '--with-system-tzdata=/usr/share/zoneinfo' '--sysconfdir=/etc/sysconfig/pgsql' '--docdir=/usr/pgsql-15/doc' '--htmldir=/usr/pgsql-15/doc/html' 'CFLAGS=-O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection' 'LDFLAGS=-Wl,--as-needed' 'LLVM_CONFIG=/usr/bin/llvm-config' 'CLANG=/usr/bin/clang' 'PKG_CONFIG_PATH=:/usr/lib64/pkgconfig:/usr/share/pkgconfig' 'PYTHON=/usr/bin/python3' LIBS = -lpgcommon -lpgport -lselinux -lzstd -llz4 -lxslt -lxml2 -lpam -lssl -lcrypto -lgssapi_krb5 -lz -lreadline -lpthread -lrt -ldl -lm
为什么仅压缩全页写?
各个进程产生WAL记录,延迟对于事务来说非常重要。因此压缩所有WAL可能不会增加太多价值。但是如果将来包含这样的功能,那么索引或者批量加载数据等领域可能会从WAL压缩中获得收益。
压缩选项
当谈到压缩时,自然而然地,问题将是关于 I/O 的节省。众所周知,压缩是以 CPU 使用为代价的。我的目标是快速检查当 CPU 利用率已经很高时是否有任何好处,以及是否对整体 TPS 有任何不利(负面)影响。如果有大量整页写入,则可以节省相当多的钱。我可以人为触发检查点以查看对总 WAL 生成的最终影响。
Lz4以更少的CPU成本提供与默认pglz相当的压缩。Zstd可以提供最高的压缩率(比 lz4 多 30%)。在一个已经处于生成过多WAL边缘的系统中,未压缩的WAL可以触发更多的检查点,从而导致生成更多的WAL。
总结
1)老版本中的pglz压缩方法并不高效。使用更多CPU在指定工作负载和机器配置下影响TPS。2)现在的压缩算法和库非常优秀,相比pglz可以做更多的工作。3)测试中看不出对TPS有啥不利影响,相反,启用压缩后,观察到有10-15%的提升,可能因为更少的IO wait。4)如果数据库负载的瓶颈是CPU,那么建议使用lz4,因为该算法相对使用较少的CPU。压缩效果接近gplz但不会造成较高的CPU消耗5)如果服务器负载不受限制,可以使用zstd,可以以更多的CPU利用率为代价,为我们提供更好的压缩6)WAL压缩的一个间接好处是减少了由生成WAL量(max_wal_size)触发checkpoint的几率压缩的实际好处取决于许多因素。配置的检查点频率、WAL 生成触发检查点的机会、存储性能、可接受的 CPU 开销、CPU 架构类型以及许多其他因素。随着新选项的引入,进入壁垒显着降低。
原文
https://www.percona.com/blog/wal-compression-in-postgresql-and-recent-improvements-in-version-15/