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

13.4.1. 使用可序列化事务强制实现一致性

13.4.2. 使用显式阻塞锁强制实现一致性

使用读提交事务执行有关数据完整性的业务规则是非常困难的,因为数据的视图随着每条语句的变化而变化,如果发生写冲突,甚至一条语句也可能不会将自身限制在语句的快照中。

虽然可重复读取事务在整个执行过程中都有一个稳定的数据视图,但使用MVCC快照进行数据一致性检查存在一个微妙的问题,这涉及到读/写冲突。如果一个事务写入数据,而一个并发事务尝试读取相同的数据(无论是在写入之前还是之后),则它无法看到另一个事务的工作。然后,无论哪个先启动,哪个先提交,读卡器似乎都是先执行的。如果仅此而已,则没有问题,但如果读卡器也写入由并发事务读取的数据,则现在有一个事务似乎在前面提到的任何一个事务之前运行。如果似乎最后执行的事务实际上是先提交的,那么在事务执行顺序图中很容易出现一个循环。如果出现这种循环,完整性检查将无法在没有帮助的情况下正常工作。

如中所述第13.2.3节,可序列化事务只是可重复的读事务,它为危险的读/写冲突模式添加了非阻塞监控。当检测到一种模式,这种模式可能导致一个明显的执行顺序循环时,所涉及的一个事务被回滚以打破循环。

# 13.4.1.使用可序列化事务强制实现一致性

如果Serializable transaction isolation level用于所有需要一致数据视图的写入和读取,则无需其他工作来确保一致性。在PostgreSQL中,为确保一致性而编写的使用可序列化事务的其他环境中的软件在这方面应该“正常工作”。

当使用此技术时,如果应用程序软件通过一个框架自动重试序列化失败后回滚的事务,则可以避免给应用程序程序员造成不必要的负担。这也许是个好主意默认事务隔离可序列化。采取一些措施,通过检查触发器中的事务隔离级别,确保不会无意中使用其他事务隔离级别,或破坏完整性检查,这也是明智的。

看见第13.2.3节获取性能建议。

# 警告

使用可序列化事务的这种完整性保护级别尚未扩展到热备用模式(第27.4节).因此,使用热备用的用户可能希望在主服务器上使用可重复读取和显式锁定。

# 13.4.2.使用显式阻塞锁强制实现一致性

当可以进行不可序列化的写入时,为了确保行的当前有效性并防止并发更新,必须使用选择更新, 选择共享,或适当的锁桌陈述(选择更新选择共享只锁定返回的行以防并发更新,而锁桌锁上整张桌子。)在将应用程序从其他环境移植到PostgreSQL时,应该考虑到这一点。

对于那些从其他环境转换过来的人来说,同样值得注意的是选择更新不确保并发事务不会更新或删除所选行。要在PostgreSQL中实现这一点,您必须实际更新行,即使不需要更改任何值。选择更新 临时封锁获取同一锁或执行使现代化删去这将影响被锁定的行,但一旦持有该锁的事务提交或回滚,被阻止的事务将继续执行冲突操作,除非实际发生冲突使现代化在保持锁定的同时执行该行的。

在不可序列化的MVCC下,全局有效性检查需要额外考虑。例如,当两个表都在积极更新时,银行应用程序可能希望检查一个表中所有贷方的总和是否等于另一个表中借方的总和。比较两次连续试验的结果选择sum(…)命令在读提交模式下无法可靠工作,因为第二个查询可能包含第一个查询未统计的事务的结果。在一个可重复读取事务中进行这两个求和,只会准确地描述在可重复读取事务开始之前提交的事务的影响,但有人可能会合理地怀疑,答案在交付时是否仍然相关。如果可重复读取事务本身在尝试进行一致性检查之前应用了一些更改,那么检查的有用性将变得更具争议,因为现在它包括一些但不是所有事务后启动更改。在这种情况下,一个细心的人可能希望锁定检查所需的所有表格,以便对当前现实有一个无可争议的了解。A.共有模式锁(或更高级别的锁)保证锁定的表中没有未提交的更改,当前事务的更改除外。

还请注意,如果依赖显式锁定来防止并发更改,则应该使用读取提交模式,或者在可重复读取模式下,在执行查询之前,小心获取锁。可重复读取事务获得的锁保证没有其他修改表的事务仍在运行,但如果事务看到的快照早于获得锁,则它可能早于表中现在提交的某些更改。可重复读取事务的快照实际上在其第一个查询或数据修改命令开始时被冻结(选择, 插入, 使现代化删去),因此可以在冻结快照之前显式获取锁。