二月 13, 2025
摘要:在本教程中,您将学习如何在 PostgreSQL 中优化commit_delay
设置。
目录
在某些情况下,可以通过关闭参数synchronous_commit
,对事务型业务负载进行调优。很容易证明该措施的惊人效果,但在操作系统崩溃期间会丢失已提交事务的可能性,使其对于许多应用程序都不适用。在这种情况下,其实可以尝试调整commit_delay
和commit_siblings
参数。
WAL 刷写是事务型业务负载的瓶颈
为了确保提交的事务不会丢失,PostgreSQL 必须确保在向客户端报告成功之前,事务的 WAL 已刷写到磁盘。如果数据库业务负载以少量数据的修改为主,则这些事务产生的 IOPS 可能会使磁盘饱和,即使写入的数据量适中也是如此。
调整参数commit_delay
和commit_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_delay
和commit_siblings
更有效。但是,关闭synchronous_commit
意味着在崩溃后可能会丢失一些已提交的事务。使用commit_delay
和commit_siblings
的最大优点是事务持久性不受影响:您不能丢失提交的事务,因为COMMIT
会始终等待 WAL 刷写完成。
为 commit_delay 基准测试进行设置
进行基准测试的机器带有本地 NVME 磁盘、8 个 CPU 内核和 16GB 内存。这里设置了shared_buffers = 3GB
、max_wal_size = 100GB
和checkpoint_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
是加快由短事务组成的业务负载的最佳方法。