PostgreSQL 中选择性异步提交:平衡持久性和性能
/ Shayon Mukherjee / blog
PostgreSQL 中选择性异步提交:平衡持久性和性能
2025 年 3 月 16 日 ~ 3 分钟
最近,我研究了一些在高写入代码路径上产生大量 I/O 和 CPU 竞争的工作负载,并发现了 synchronous_commit
(https://www.postgresql.org/docs/current/wal-async-commit.html)。 全局关闭此设置可能非常诱人,因为在 I/O、CPU 和 TPS(每秒事务数)方面的性能提升非常显著。我注意到 I/O 完全消失,CPU 下降 20%(在峰值时),TPS 增加 30%。但是,这带来了一些重要的权衡,值得记住。
什么是同步提交?
默认情况下,PostgreSQL 使用同步提交,这意味着当您的应用程序提交事务时,PostgreSQL 会:
- 将事务的更改写入预写日志 (WAL)
- 将这些 WAL 记录刷新到永久存储
- 仅在此时 向客户端确认成功
这确保了持久性 - 如果数据库在提交后立即崩溃,您的事务仍然安全。但是,此磁盘 I/O 操作通常是事务吞吐量的瓶颈,尤其是对于小型、频繁的事务。
进入异步提交
异步提交改变了这种行为。启用后,PostgreSQL 在事务逻辑上完成后立即确认事务成功,在 WAL 记录刷新到磁盘之前。
-- 启用异步提交
SET synchronous_commit = off;
-- 恢复默认(同步)
SET synchronous_commit = on;
结果是?事务吞吐量显着提高,I/O 压力降低。在我的测试中,这个简单的更改使每秒事务数增加了 30%,尤其是在 I/O 受限的系统上。
权衡:理解风险窗口
性能提升伴随着权衡:在事务报告为已提交和实际写入磁盘之间存在一个小的“风险窗口”。如果数据库服务器在此窗口期间崩溃,则最近的事务可能会丢失,这就是全局启用此设置感觉不合适的原因。 这里的风险是数据丢失,而不是数据损坏。PostgreSQL 文档以非常简单的术语很好地解释了这一点:https://www.postgresql.org/docs/current/wal-async-commit.html
选择性实施
即使在测试了 synchronous_commit
的其他设置之后,我发现此功能的优点在于您不必做出全局的“全有或全无”的选择。 您可以切换它:
- 每个会话
- 每个事务
- 对于特定操作
这允许采用细致入微的方法,其中关键事务保持完全持久,而不太关键的操作获得性能提升。
在 Ruby on Rails 应用程序中,可以像这样简单地操作:
defwith_synchronous_commit_off(&block)
ActiveRecord::Base.connection.exec_query("SET synchronous_commit = off")
yield
ensure
ActiveRecord::Base.connection.exec_query("SET synchronous_commit = on")
end
with_synchronous_commit_off do
# 在此处执行非关键批量操作
# 例如,分析数据、日志或后台处理
end
中间设置
PostgreSQL 为 synchronous_commit
提供了不仅仅是开/关选项。 存在提供不同性能和持久性平衡的中间设置:
-- 从最强的保证到最高的性能的选项:
SET synchronous_commit = 'remote_apply'; -- 最强的保证(对于副本)
SET synchronous_commit = 'remote_write'; -- 强大但更快
SET synchronous_commit = 'local'; -- 仅本地持久性
SET synchronous_commit = 'off'; -- 最大性能
但是,至少在 Aurora PostgreSQL 上,我发现将其设置为 OFF
的好处最大。 我认为,由于 Aurora 的工作方式以及它要求 6 个节点中的 4 个需要确认提交 (https://aws.amazon.com/blogs/database/amazon-aurora-postgresql-parameters-part-2-replication-security-and-logging/),其余设置可能没有做太多,或者它们的优点可能会被摊销。
总结
我意识到这对于经验丰富的 PostgreSQL 用户来说可能是一个非常众所周知的设置。 也就是说,我希望您发现这篇文章有用,并且我很乐意听到您在生产中使用 synchronous_commit
的经验。