PostgreSQL 教程: 优化 commit_delay 设置

二月 13, 2025

摘要:在本教程中,您将学习如何在 PostgreSQL 中优化commit_delay设置。

目录

在某些情况下,可以通过关闭参数synchronous_commit,对事务型业务负载进行调优。很容易证明该措施的惊人效果,但在操作系统崩溃期间会丢失已提交事务的可能性,使其对于许多应用程序都不适用。在这种情况下,其实可以尝试调整commit_delaycommit_siblings参数。

WAL 刷写是事务型业务负载的瓶颈

为了确保提交的事务不会丢失,PostgreSQL 必须确保在向客户端报告成功之前,事务的 WAL 已刷写到磁盘。如果数据库业务负载以少量数据的修改为主,则这些事务产生的 IOPS 可能会使磁盘饱和,即使写入的数据量适中也是如此。

调整参数commit_delaycommit_siblings可以减少这些 WAL 刷写所需的 IOPS 数量,以缓解刷写的瓶颈。

commit_delay 和 commit_siblings 的工作原理

您可以通过将commit_delay设置为大于零的值,来启用该特性。每当一个事务在提交期间到达将 WAL 刷写到磁盘的点时,它首先会检查当前有多少其他事务处于活动状态。如果至少还有commit_siblings个其他的事务打开并且没有等待锁定,则 PostgreSQL 不会立即刷写 WAL,而是等待commit_delay微秒的时间。在该延迟之后,其他一些事务可能已经到了准备刷写 WAL 的点。然后,所有这些后端都可以在单次 I/O 操作中来执行其 WAL 的刷写。

调整commit_delay并不容易,因为延迟会使事务花费更长的时间。另一方面,如果您选择的值太低,则在延迟过去时可能没有其他事务准备就绪,这样就无法减少执行的 IOPS 数量。处理由 WAL 刷写引起的 I/O 负载问题的另一种方法是调整 synchronous_commit。设置该参数为off,会比调整commit_delaycommit_siblings更有效。但是,关闭synchronous_commit意味着在崩溃后可能会丢失一些已提交的事务。使用commit_delaycommit_siblings的最大优点是事务持久性不受影响:您不能丢失提交的事务,因为COMMIT会始终等待 WAL 刷写完成。

为 commit_delay 基准测试进行设置

进行基准测试的机器带有本地 NVME 磁盘、8 个 CPU 内核和 16GB 内存。这里设置了shared_buffers = 3GBmax_wal_size = 100GBcheckpoint_timeout = 15min。然后,我们使用比例因子 100 初始化标准的 pgbench 数据库。我们使用 pg_prewarm 将所有pgbench的表和索引加载到共享缓冲区中。这样,就不应该会有读取 I/O,并且除了检查点之外,唯一的 I/O 将是 WAL 写入。

这里使用的pgbench命令是

pgbench -b simple-update -c 10 -T 1200

限制磁盘

因为机器上内置的 NVME 表现太好,以至于很难用pgbench让其处于饱和状态。因此,这里决定使用 Linux 控制组将设备限制为 1000 IOPS。在测试的 Linux 系统上,必须为 systemd 切片启用 I/O 控制:

echo '+memory +pids +io' > /sys/fs/cgroup/system.slice/cgroup.subtree_control

然后,我们可以在 NVME 上设置 IOPS 限制,以让 PostgreSQL v17 服务写入数据:

echo '259:0 wiops=1000' > /sys/fs/cgroup/system.slice/postgresql-17.service/io.max

你可能会怀疑,这让测试过程受到了人为影响。但是,在公有云上托管数据库的人,也会受到同样的限制。然后,无论如何,您永远无法将基准测试的结果直接应用于不同的系统和业务负载。

commit_delay 基准测试的结果

基准测试结果:

commit_delay 每秒事务数 IOPS
0 μs 1576 1000
10 μs 1703 1000
30 μs 1715 1000
50 μs 1778 1000
100 μs 1837 1000
200 μs 1933 1000
500 μs 2183 1000
750 μs 2583 900
1000 μs 2738 600
1250 μs 2508 510
1500 μs 2397 480
2000 μs 2051 430

commit_delay为 1000 μs 时,我们实现了最佳性能。使用该设置时,pgbench每秒执行的事务数略小于未启用commit_delay时的两倍。有趣的是,在最佳状态下,磁盘远未饱和,因此还有可能获得更好的结果。

结论

虽然commit_delay不能以同synchronous_commit = off一样的方式提升事务型业务负载的性能,但我们仍然能够实现实质性的性能提升。如果您无法承受在操作系统崩溃后丢失事务,那么调整commit_delay是加快由短事务组成的业务负载的最佳方法。

了解更多

PostgreSQL 优化