受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发版本: devel

13.4. 应用级别的数据一致性检查 #

在读已提交事务下,想要通过业务规则强制数据一致性非常困难,因为数据视图会随每条语句而变化, 而且一旦发生写冲突,即使是单条语句也未必局限于该语句自己的快照。

虽然可重复读事务在整个执行期间都具有稳定的数据视图,但在使用 MVCC 快照进行数据一致性检查时,还存在一个微妙问题,即所谓的读/写冲突。如果一个事务写入数据,而并发事务试图读取相同的数据(无论是在写入之前还是之后),它都看不到另一个事务的工作。于是,读取者看起来就像是先执行的一方,而不管究竟是谁先启动,也不管谁先提交。如果事情只停留在这一步,就没有问题;但如果读取者还写入了数据,而这些数据又被另一个并发事务读取,那么现在就会有一个事务看起来像是在前面提到的任一事务之前执行。如果看起来最后执行的事务实际上最先提交,就很容易在事务执行顺序图中形成一个环。一旦出现这样的环,不借助额外机制,一致性检查就无法正确工作。

正如 Section 13.2.3 中提到的,可串行化事务其实就是在可重复读事务的基础上,增加了对危险读/写冲突模式的非阻塞监控。当检测到某种模式可能在表面执行顺序中形成一个环时,其中一个相关事务就会被回滚,以打破这个环。

13.4.1. 用可串行化事务强制一致性 #

如果对所有写操作,以及所有需要一致数据视图的读操作都使用可串行化事务隔离级别, 那么不需要额外工作就能确保一致性。从其他环境移植而来、按使用可串行化事务来保证一致性而编写的软件, 在这方面应当能够在 PostgreSQL正常工作

使用这种技术时,如果应用软件通过某个框架运行,并由该框架自动重试因串行化错误而回滚的事务,就可以避免给应用程序员带来不必要的负担。把 default_transaction_isolation 设置为 serializable 可能是个好主意。通过在触发器中检查事务隔离级别来采取某些措施,以确保不会因为疏忽或为了绕过完整性检查而使用其他事务隔离级别,也是明智的。

性能方面的建议见 Section 13.2.3

Warning

利用可串行化事务提供的这一层完整性保护,尚未扩展到热备模式(Section 26.4)。 因此,使用热备的用户可能希望在主库上使用可重复读和显式锁定。

13.4.2. 使用显式锁定强制一致性 #

当存在非可串行化写入时,要确保某一行当前仍然有效,并保护它不受并发更新影响, 就必须使用 SELECT FOR UPDATESELECT FOR SHARE 或适当的 LOCK TABLE 语句。 (SELECT FOR UPDATESELECT FOR SHARE 只锁定返回的行以防止并发更新,而 LOCK TABLE 会锁住整张表。) 从其他环境向 PostgreSQL 迁移应用时,应当考虑这一点。

对于从其他环境迁移而来的用户,还需要注意:SELECT FOR UPDATE 并不能保证并发事务不会更新或删除被选中的行。要在 PostgreSQL 中做到这一点,必须真正去更新该行,即使没有任何值需要改变。 SELECT FOR UPDATE 只是临时阻塞其他事务, 使它们不能获取同样的锁,也不能执行会影响被锁定行的 UPDATEDELETE;但是一旦持有该锁的事务提交或回滚,被阻塞的事务就会继续执行冲突操作, 除非在持锁期间已经对该行执行了实际的 UPDATE

在非可串行化的 MVCC 环境下,全局有效性检查需要额外考虑。 例如,一个银行应用可能希望检查一个表中的所有收入总和等于另一个表中的所有支出总和, 而这两个表都在被活跃更新。在读已提交模式下,比较两个连续的 SELECT sum(...) 命令的结果并不可靠,因为第二个查询很可能会包含第一个查询没有统计到的事务提交结果。 在单个可重复读事务中完成这两次求和,只能准确反映在该可重复读事务开始之前已提交事务的效果 — 但等到结果交付时,人们完全可能合理地怀疑这个答案是否仍然相关。 如果可重复读事务本身在尝试进行一致性检查之前已经应用了某些更改,那么这种检查的意义就更加值得商榷, 因为此时它包含了事务开始后的一部分而不是全部更改。在这种情况下,谨慎的做法可能是锁定执行检查所需的所有表, 以获得当前真实状态的无可争议图景。SHARE 模式(或更高)的锁能够保证, 在被锁定的表中,除了当前事务自身的更改之外,没有其他未提交更改。

注意,如果依赖显式锁定来防止并发更改,就应当使用读已提交模式, 或者在可重复读模式下小心地在执行查询之前先获取锁。 可重复读事务获得的锁能够保证没有其他修改该表的事务仍在运行, 但如果该事务看到的快照早于获取锁的时刻,那么它看到的快照也可能早于表中某些现在已经提交的更改。 可重复读事务的快照实际上是在其第一条查询或数据修改命令 (SELECTINSERTUPDATEDELETEMERGE)开始时被冻结的, 因此可以在快照冻结之前显式获取锁。

提交更正

如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。