30.3. 异步提交

异步提交是一个选项,它使事务可以更快地完成,但如果数据库崩溃,最新的事务可能会丢失。在许多应用中,这是一个可以接受的折衷方案。

如上一节所述,事务提交通常是“同步的”:服务器在将成功指示返回给 Client 端之前,await 事务的 WAL 记录刷新到永久存储。因此,即使在此后立即发生服务器崩溃的情况下,也可以保证 Client 端将保留报告为已提交的事务。但是,对于短 Transaction,此延迟是总 Transaction 时间的主要组成部分。选择异步提交模式意味着在逻辑上完成事务之后,服务器将在生成的 WAL 记录实际进入磁盘之前返回成功。这可以大大提高小事务的吞吐量。

异步提交会带来数据丢失的风险。在向 Client 端提交事务完成报告与 true 提交事务的时间之间存在很短的时间窗口(也就是说,如果服务器崩溃,可以保证不会丢失)。因此,如果 Client 端将依赖于将记住该事务的假设来执行外部操作,则不应使用异步提交。例如,银行肯定不会在记录 ATM 现金分配的 Transaction 中使用异步提交。但是在许多情况下,例如事件日志记录,不需要这种强大的保证。

使用异步提交承担的风险是数据丢失,而不是数据损坏。如果数据库崩溃,它将通过重播 WAL 直到刷新的最后一条记录来恢复。因此,数据库将还原到一个自洽状态,但是尚未刷新到磁盘的任何事务都不会反映在该状态下。因此,最终结果是最后几笔 Transaction 的损失。因为事务是按提交 Sequences 重播的,所以不会引起不一致的情况-例如,如果事务 B 依赖于先前事务 A 的影响进行了更改,则在保留 B 的影响的同时 A 的影响也不会丢失。

用户可以选择每个事务的提交模式,以便可以同时运行同步和异步提交事务。这允许在性能和 Transaction 持久性的确定性之间进行灵活的权衡。提交模式由用户可设置的参数synchronous_commit控制,该参数可以通过设置配置参数的任何方式进行更改。事务提交开始时,任何一个事务使用的模式取决于synchronous_commit的值。

无论synchronous_commit的设置如何,某些 Util 命令(例如DROP TABLE)都被强制同步提交。这是为了确保服务器的文件系统和数据库的逻辑状态之间的一致性。支持两阶段提交的命令(例如PREPARE TRANSACTION)也始终是同步的。

如果数据库在异步提交和写入事务的 WAL 记录之间的风险窗口内崩溃,那么在该事务期间所做的更改将*丢失。由于后台进程(“ WAL 编写器”)每wal_writer_delay毫秒将未写入的 WAL 记录刷新到磁盘,因此风险窗口的持续时间受到限制。风险窗口的实际最大持续时间是wal_writer_delay的三倍,因为 WAL 编写器旨在在忙碌的时间内一次写入整个页面。

Caution

立即模式关闭等效于服务器崩溃,因此将导致丢失所有未刷新的异步提交。

异步提交提供的行为不同于设置fsync =关。 fsync是服务器范围的设置,它将更改所有事务的行为。它会禁用 PostgreSQL 中试图将写入同步到数据库不同部分的所有逻辑,因此系统崩溃(即,硬件或 os 崩溃,而不是 PostgreSQL 本身的故障)可能会导致数据库的任意严重损坏 State。在许多情况下,异步提交提供了大多数性能改进,可以通过关闭fsync来获得,但没有数据损坏的风险。

commit_delay听起来与异步提交非常相似,但实际上是一个同步提交方法(实际上,commit_delay在异步提交期间会被忽略)。 commit_delay会在事务将 WAL 刷新到磁盘之前引起延迟,希望一个这样的事务执行的单个刷新也可以服务于几乎同时提交的其他事务。可以将该设置视为增加时间窗口的一种方式,在该时间窗口中,事务可以加入要参与单个冲洗的组,以分摊多个事务之间的冲洗成本。