异步提交是一项可选功能,它允许事务更快完成,但代价是 如果数据库发生崩溃,最近的事务可能会丢失。在许多应用中,这是可以接受的权衡。
如前一节所述,事务提交通常是 同步的:服务器要等到该 事务的 WAL 记录被刷入持久存储后,才向客户端返回成功 指示。因此,客户端可以确信那些已被报告为提交成功的事务一定会被保留,即使 服务器紧接着立刻崩溃也是如此。然而,对于短事务而言,这种等待是总事务时间的 主要组成部分。选择异步提交模式意味着服务器会在事务从逻辑上完成后就立刻返回 成功,而不必等到它生成的 WAL 记录真正到达磁盘。这会显著 提升小事务的吞吐量。
异步提交引入了数据丢失风险。在向客户端报告事务完成与事务真正被提交(也就是 说,即使服务器崩溃也保证不会丢失)之间,存在一个很短的时间窗口。因此,如果 客户端会基于“系统一定会记住这个事务”这一假设采取外部动作,就不应使用异步提 交。例如,银行显然不会对记录 ATM 吐钞的事务使用异步提交。但在很多场景中, 例如事件日志记录,并不需要这种强保证。
使用异步提交所承担的风险是数据丢失,而不是数据损坏。如果数据库发生崩溃,它会 通过重放 WAL 直到最后一条已刷写记录来恢复。因此,数据库 会被恢复到自洽状态,但任何尚未刷到磁盘的事务都不会反映在该状态中。其最终 效果就是丢失最后几个事务。由于事务按提交顺序重放,所以不会引入任何不一致性 — 例如,如果事务 B 做出的更改依赖于更早的事务 A 的效果,就不可能出现 A 的效果丢失而 B 的效果却被保留的情况。
用户可以为每个事务选择提交模式,因此同步提交事务和异步提交事务可以并发运行。 这使得在性能与事务持久性的确定性之间能够灵活权衡。提交模式由用户可设置的 参数 synchronous_commit 控制,而该参数可以用任何 设置配置参数的方式来更改。任一事务实际使用的模式取决于事务提交开始时 synchronous_commit 的值。
某些实用命令,例如 DROP TABLE,无论 synchronous_commit 如何设置,都会强制同步提交。这是为了 保证服务器文件系统与数据库逻辑状态之间的一致性。支持两阶段提交的命令,例如 PREPARE TRANSACTION,也始终是同步的。
如果数据库在异步提交与写出该事务 WAL 记录之间的风险窗口内 崩溃,那么该事务所做的更改将丢失。该风险窗口的时长是 受限的,因为一个后台进程(“WAL 写入器”)会将尚未写出的 WAL 记录每隔 wal_writer_delay 毫秒刷到磁盘。风险窗口的实际最大时长是 wal_writer_delay 的三倍,因为 WAL 写入器在系统繁忙时被 设计为倾向于一次写出整页。
立即模式关闭等同于服务器崩溃,因此也会造成任何未刷写的异步提交丢失。
异步提交的行为不同于将 fsync 设为 off。 fsync 是影响所有事务的全服务器设置。它会禁用 PostgreSQL 内部所有试图同步数据库不同部分写入的 逻辑,因此系统崩溃(即硬件或操作系统崩溃,而不是 PostgreSQL 本身故障)可能导致数据库状态遭受任意 严重的损坏。在许多场景中,异步提交能带来关闭 fsync 所能 获得的大部分性能提升,却不承担数据损坏的风险。
commit_delay 听起来也很像异步提交,但它实际上是一种同步 提交方法(事实上,在异步提交期间 commit_delay 会被忽略)。 commit_delay 会在事务把 WAL 刷到磁盘 之前引入一段延迟,希望某个事务执行的一次刷盘也能服务于其他差不多同时提交的 事务。可以把这个设置看成是扩大一个时间窗口,让事务得以加入即将参与同一次刷盘 的组,从而在多个事务之间摊销这次刷盘的代价。
如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。