PostgreSQL 的 max_wal_size 设置如何影响写密集型系统的磁盘写入量
来源标题:Postgres Max_wal_size
PostgreSQL 在高写入负载下,max_wal_size 设置会导致大量可避免的重复磁盘写入。默认值为 1 GB,繁忙服务器可能不到一分钟就写满,触发频繁的 WAL 检查点。这一设置直接影响事务日志的写入效率和系统开销。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
每台繁忙的 PostgreSQL 服务器写入磁盘的数据量都超过应用程序请求的量。其中一部分额外工作是正常且健康的。但在写入密集的系统上,其中一部分可能是重复的、可避免的工作——而这类工作有多少,很大程度上受一个设置影响:max_wal_size。日常运维中没有任何迹象指向它,而网上容易找到的建议都是泛泛之谈——一个固定数值,或者一个看不到你实际负载的计算器。PostgreSQL 上游给出的通用默认值是 1 GB——项目方无法预知服务器会有多大——而一台真正繁忙的服务器不到一分钟就能产生这么多事务日志。
要理解这为什么重要,需要两个概念。两者都比听起来简单。
在 PostgreSQL 改动数据文件(磁盘上存放表和索引的文件)中的任何内容之前,它首先会把这次变更的描述写入事务日志(用 PostgreSQL 的话说:WAL,即预写式日志)。日志是数据库应对崩溃的保险。如果服务器在工作进行中掉电,PostgreSQL 会在启动时重放日志,已提交的数据不会丢失。
日志不能无限增长。因此 PostgreSQL 会不时保存进度:它把内存中所有已修改的数据页——PostgreSQL 存储表所用的 8 KB 块——写入数据文件。这就是检查点。检查点完成后,在它之前写入的日志就不再需要用于崩溃恢复,PostgreSQL 可以复用其磁盘空间——前提是备份和复制也不再需要那部分日志。
正常运行时,检查点会因以下两个原因之一自动启动,以先到者为准:
在空闲服务器上,计时器总是先触发,没有什么需要调优。在繁忙服务器上,1 GB 很快就会被填满——真正的代价正是从这里开始。
下面是不那么显而易见的部分。每次检查点之后,对任何数据页的第一次修改都会被完整记录:在 PostgreSQL 默认的 full_page_writes 设置下,整个 8 KB 页都会被复制进事务日志,而不只是那几字节的变更。这是有意为之——它保护你免受服务器在写入过程中崩溃时出现部分写入页的影响。
现在把两个机制结合起来。每次检查点都会重置这条规则:之后对每个页面的第一次修改又会完整记录。当检查点每隔几秒就运行时,频繁修改的页面就会被一次又一次地完整复制进日志。膨胀的日志更快达到 1 GB 的预算,从而迫使下一次检查点更早到来。服务器最终反复把相同的繁忙页面写入磁盘,记录的日志量也超过工作负载真正所需的量。
PostgreSQL 确实会警告你:它会在服务器日志中打印 "checkpoints are occurring too frequently"。除非你已经在关注服务器日志,否则很容易错过。而且这个警告有一个盲区:默认情况下,它只在检查点间隔小于 30 秒时才出现。每 40 秒一次检查点仍然比五分钟的调度频繁许多倍,但日志却保持沉默。
在信任任何 Guardioz 调优规则用于任何人的数据库之前,我们都会在内部基准测试环境中进行测试。下面是其中一次实验:同样的 30 分钟 pgbench 运行——pgbench 是 PostgreSQL 自带的基准测试工具——使用其默认场景(大致基于 TPC-B,大多数语句是写入),在 PostgreSQL 18 上以四种不同的 max_wal_size 值重复进行。该实验比较了 PostgreSQL 自身的事务日志量、数据文件写入量和在计时器之前启动的检查点(PostgreSQL 将其计为 "requested" 检查点)计数器。这不是存储性能比较。而且由于每次运行完成的事务数不同,写入量按已完成事务显示,以便公平比较各项设置。
当 max_wal_size 为 1 GB 时,服务器每 30 分钟运行记录到的 requested 检查点中位数为 189 次——在这台隔离的基准测试服务器上,全部都是日志增长所要求的:大约每十秒一次,而不是每五分钟一次。设为 32 GB 时,中位数降至零,三次运行中只有一次出现单个 requested 检查点。在按已完成工作量归一化后,PostgreSQL 生成的事务日志少了 5.3%,每事务记录的数据文件写入量少了 6.9%。
五分钟示意图根据上述检查点速率中位数推导;它不是某次单独运行的追踪记录。
将设置从 32 GB 提高到 128 GB 对这些计数器只带来了轻微变化。一旦频繁的 requested 检查点消失,在这个实验中更大的上限几乎没有带来额外收益,同时却增加了磁盘使用和恢复时间的最坏情况。目标是足够,而不是最大。
更大的 max_wal_size 有实际代价。磁盘空间:PostgreSQL 不会预先保留空间,但在重写入负载下,日志确实可能增长到你设置的上限——所以要为此做好规划。恢复时间:崩溃后,PostgreSQL 会重放自上次检查点以来写入的日志,因此检查点间隔更远可能使重启耗时更长。对大多数系统来说这是可接受的权衡——但要有意做出选择,而不是无意为之。
还要知道 max_wal_size 是一个软限制。日志可以增长到超过它:在重负载下、当事务日志归档(将事务日志复制到备份存储)失败时,或者当服务器被要求为落后或消失的副本保留日志时(复制槽,或较高的 wal_keep_size)。绝不要将其设置得接近磁盘容量,以至于超出限制可能填满磁盘。
PostgreSQL 为计时器调度的检查点和因其他原因请求的检查点分别提供了计数器——比较它们是诊断的起点。用 psql 或你通常使用的任何其他 SQL 客户端连接到数据库,然后运行:
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。