异步提交是一个选项,它允许事务更快地完成,但代价是,如果数据库崩溃,则最新的事务可能会丢失。在许多应用程序中,这是一个可以接受的权衡。
如上一节所述,事务提交通常是同步的:服务器等待事务的WAL记录在向客户端返回成功的指示前需要刷新到永久存储中。因此,即使在服务器在之后立即崩溃的情况下,客户端也可保证事务会报告为已落实。但是,对于短事务,此延迟是事务总时间的关键部分。选择异步落实模式意味着服务器在事务在WAL记录其生成内容实际已写入磁盘之前就立即返回成功。这可为小型事务提供显着的吞吐量提升。
异步落实会引入数据丢失的风险。从向客户端报告事务完成到事务被真正落实(即如果服务器崩溃,则保证事务不会丢失)之间存在一个短暂的时间窗口。因此,如果客户端将基于事务会被记住的假设采取外部操作,则不应使用异步落实。例如,银行肯定不会对记录 ATM 机发放现金的事务使用异步落实。但在许多场景中,例如事件日志,无需这种类型的强力保证。
使用异步落实所承担的风险是数据丢失,而非数据损坏。如果数据库崩溃,它将通过重播WAL直至刷新过的最新记录来进行恢复。因此,数据库将恢复到自我一致的状态,但任何尚未刷新到磁盘的事务都不会反映在那状态中。因此,净效果是丢失最近的一些事务。由于事务以落实顺序重播,因此不会引入任何不一致性 — 例如,如果事务 B 根据前一个事务 A 的作用进行更改,则不可能丢失 A 的作用而保留 B 的作用。
用户可以选择每个事务的落实模式,以便可以同时运行同步和异步落实事务。这使得性能和事务耐用性的确定性之间能够进行灵活的权衡。落实模式由用户可设置的参数 synchronous_commit 来控制,该模式修改方式与设置配置参数的任何方式相同。任何一个事务所用的模式在于事务落实开始时 synchronous_commit
的值。
某些实用程序命令(例如 DROP TABLE
),强制无论 synchronous_commit
的设置如何,都进行同步落实。这是为了确保服务器的文件系统和数据库的逻辑状态之间的一致性。支持两阶段落实的命令(例如 PREPARE TRANSACTION
),也总是非同步的。
如果在异步落实和写入事务的WAL记录,那么期间做出的更改 将被丢失。风险窗口的持续时间是有限的,因为后台进程(“WAL writer”)将未写入的WAL记录刷新到硬盘上,时间为每次的 wal_writer_delay 毫秒。风险窗口的实际最大持续时间是 wal_writer_delay
的三倍,这是因为 WAL writer 在繁忙时期倾向于一次写入完整页面。
即时模式关机等效于服务器崩溃,因此将导致丢失所有未刷新的异步提交。
异步提交提供不同于设置 fsync = off 的行为。 fsync
是服务器范围的设置,将改变所有事务的行为。这会禁用 PostgreSQL 中所有尝试同步将写入到数据库不同部分的逻辑,因此系统崩溃(即硬件或操作系统的崩溃,而不是 PostgreSQL 本身的故障)可能导致数据库状态出现任意严重的损坏。在许多情况下,异步提交提供了大部分可通过关闭 fsync
获得的性能改进,但没有数据损坏的风险。
commit_delay 听起来也与异步提交非常相似,但实际上它是一种同步提交方法(实际上,在异步提交期间会忽略 commit_delay
)。 commit_delay
会在事务刷新WAL到硬盘上之前稍作延迟,希望这样由一个事务执行的单个刷新还可以同时为在同一时间提交的其他事务服务。此设置可以被认为是在增加事务可以加入准备参与单个刷新的组的时间窗口的一种方式,以便在多个事务之间摊销刷新的成本。