29.3. 异步提交

异步提交是一个允许事务更快完成的选项,付出的代价是,如果数据库崩溃时,最新的事务可能会丢失。 在大多数应用中这个是可以接受的开销。

如前一节所述,事务提交通常是同步的:服务器等待事务的WAL 记录刷写到永久存储区,然后返回一个成功提示到客户端。客户端因此保证报告提交的事务将被保存, 即使服务器立即崩溃。然而,对于短事务,延迟是总事务时间的主要部分。选择异步提交模式意味着, 在它产生的WAL记录实际转移到磁盘之前,一旦事务在逻辑上完成之后服务器就返回成功。 这样在小事务的吞吐量上可以提供一个显著的推动作用。

异步提交引进了数据丢失的风险。在向客户端报告事务完成和事务实际完成的时间点之间有一个很小的时间窗口 (也就是,保证如果服务器崩溃时不会丢失)。因此如果客户端采取外部动作(这些动作假设事务会被记下), 此时不应使用异步提交。例如,一个银行肯定不会为一个ATM分配现金的事务使用异步提交。但在多数情况下, 如事件日志记录,不需要这种强力保证。

使用异步提交的风险在于数据丢失,而不是数据损坏。如果数据库崩溃,那么它会根据刷入的最新的 WAL记录来进行恢复。因此数据库会被转储到一个一致的状态, 但任何没有写入到磁盘的事务不能反映在这个状态。因此最后的结果是丢失最新的几个事务。 因为事务是以提交的顺序进行重放,不会引入非一致状态;例如,如果事务B所做的更改依赖于前一个事务A, 那么丢失A的效果,而B的效果被保留是不可能的。

用户可以为每个事务选择提交模式,因此可以同时使用同步和异步提交事务。 这样就允许在性能和事务持久性之间做一个灵活的权衡。提交模式是由用户可设定的参数 synchronous_commit控制的,可以用任意配置参数可以设置的方式改变。 任意一个事务的提交模式依赖于事务提交开始时synchronous_commit的值。

某些实用命令,如DROP TABLE,会强制使用同步提交,无论synchronous_commit 参数是怎么设置的。这是为了保证服务器文件系统和数据库逻辑状态之间的一致性。支持两阶段提交的命令, 如PREPARE TRANSACTION,也是使用同步提交。

如果在一个异步提交和写事务的WAL记录中间时发生数据库崩溃, 在事务期间的修改会丢失。风险的持续时间会被限制,因为"WAL writer" 后台进程会每隔wal_writer_delay毫秒就向磁盘刷入未写入的WAL记录。 风险的实际最大持续时间是三倍的wal_writer_delay, 因为WAL写进程被设计为支持在繁忙时一次写入所有块。

小心

IMMEDIATE模式的关闭等同于服务器崩溃,因此会造成那些未刷入的异步提交的数据丢失。

异步提交不同于设置fsync = off。fsync 是一个用于更改所有事物行为的服务器端设置。它会禁用所有PostgreSQL 中尝试向数据库不同部分同步写入的逻辑,因此,一次系统崩溃(硬件或操作系统崩溃, 而不是PostgreSQL本身出问题)会导致数据库状态不可预知的崩溃。 在多数情况下,通过关闭fsync的方式同样可以获得异步提交的性能提升,但异步提交不会有数据崩溃的风险。

看起来,commit_delay与异步提交很相似,但实际上它是一种同步提交方法 (事实上,异步提交时会忽略commit_delay)。一个事务尝试向磁盘刷入 WAL之前,commit_delay会造成延迟, 希望在一个这样的事务中执行一个单独的刷入可以用于同一时间的其他事务提交。 这个设置可以看作增大时间窗口的一种方式,在这个时间窗口内,事务可以加入一个单次刷写的分组, 来分摊多个事务刷写的开销。