使用读已提交事务来强制执行有关数据完整性的业务规则非常困难,因为数据的视图在每个语句中都在变化,即使是单个语句,如果发生写冲突,也可能不会限制在语句的快照中。
虽然可重复读事务在其执行期间拥有稳定的数据视图,但使用MVCC快照进行数据一致性检查存在一个微妙的问题,涉及到称为读写冲突的东西。如果一个事务写入数据,而一个并发事务尝试读取相同的数据(无论是在写入之前还是之后),它都无法看到另一个事务的工作。然后,读取器似乎首先执行,无论哪个事务先开始或哪个事务先提交。如果仅此而已,则没有问题,但如果读取器也写入由并发事务读取的数据,那么现在就有一个事务似乎在前面提到的两个事务之前运行。如果似乎最后执行的事务实际上先提交,则在事务执行顺序图中很容易出现循环。当出现此类循环时,完整性检查将无法正常工作,除非有一些帮助。
如第 13.2.3 节所述,可串行化事务只是添加了对危险的读写冲突模式进行非阻塞监控的可重复读事务。当检测到可能导致执行顺序中出现循环的模式时,将回滚其中一个相关事务以打破循环。
如果对所有写入以及所有需要一致数据视图的读取都使用可串行化事务隔离级别,则无需其他任何努力即可确保一致性。从其他环境中编写的软件,如果使用可串行化事务来确保一致性,则在这方面应该在PostgreSQL中“正常工作”。
使用此技术时,如果应用程序软件通过一个自动重试因序列化失败而回滚的事务的框架,则可以避免为应用程序程序员造成不必要的负担。设置default_transaction_isolation
为serializable
可能是一个好主意。明智的做法是采取一些措施,以确保不会无意中或通过在触发器中检查事务隔离级别来破坏完整性检查而使用其他事务隔离级别。
有关性能建议,请参阅第 13.2.3 节。
使用可串行化事务的这种完整性保护级别尚未扩展到热备模式(第 26.4 节)或逻辑副本。因此,使用热备或逻辑复制的用户可能希望在主服务器上使用可重复读和显式锁定。
当非可串行化写入可能时,要确保行的当前有效性并保护它免受并发更新的影响,必须使用SELECT FOR UPDATE
、SELECT FOR SHARE
或适当的LOCK TABLE
语句。(SELECT FOR UPDATE
和SELECT FOR SHARE
仅锁定返回的行以防止并发更新,而LOCK TABLE
锁定整个表。)在将应用程序从其他环境移植到PostgreSQL时,应考虑这一点。
同样值得注意的是,那些从其他环境转换过来的人需要注意,SELECT FOR UPDATE
不能确保并发事务不会更新或删除选定的行。在PostgreSQL中执行此操作,必须实际更新该行,即使不需要更改任何值。SELECT FOR UPDATE
临时阻止其他事务获取相同的锁或执行会影响锁定行的UPDATE
或DELETE
,但一旦持有此锁的事务提交或回滚,被阻塞的事务将继续执行冲突操作,除非在持有锁时执行了该行的实际UPDATE
。
全局有效性检查在非可串行化下需要额外的考虑MVCC。例如,银行应用程序可能希望检查一个表中所有贷方的总和是否等于另一个表中借方的总和,当这两个表都正在被积极更新时。比较两个连续的SELECT sum(...)
命令的结果在读已提交模式下不可靠,因为第二个查询可能会包含第一个查询未计算的事务的结果。在一个可重复读事务中执行这两个求和将仅准确反映在可重复读事务开始之前提交的事务的影响——但人们可能会合理地怀疑,在交付答案时,答案是否仍然相关。如果可重复读事务本身在尝试进行一致性检查之前应用了一些更改,则检查的有用性变得更加值得怀疑,因为现在它包含了一些但并非所有事务开始后的更改。在这种情况下,谨慎的人可能希望锁定检查所需的所有表,以便获得当前现实的明确视图。SHARE
模式(或更高模式)的锁保证锁定的表中没有未提交的更改,除了当前事务的更改。
另请注意,如果依赖显式锁定来防止并发更改,则应使用读已提交模式,或者在可重复读模式下小心地在执行查询之前获取锁。可重复读事务获取的锁保证没有其他修改该表的事务仍在运行,但如果事务看到的快照早于获取锁,则它可能早于该表中某些现在已提交的更改。可重复读事务的快照实际上在其第一个查询或数据修改命令(SELECT
、INSERT
、UPDATE
、DELETE
或MERGE
)开始时冻结,因此可以在快照冻结之前显式获取锁。
如果您在文档中发现任何不正确的内容、与您对特定功能的体验不符的内容或需要进一步澄清的内容,请使用此表单报告文档问题。