可重复读隔离级别下,PostgreSQL是如何避免不可重复读问题的?

在可重复读(Repeatable Read)隔离级别下,PostgreSQL 主要通过多版本并发控制(MVCC,Multi - Version Concurrency Control)机制来避免不可重复读问题,同时结合锁机制来辅助实现。
  1. 多版本并发控制(MVCC)
    • 数据版本管理:PostgreSQL 为数据库中的每一行数据维护多个版本。当数据被修改时,不会直接覆盖原数据,而是创建一个新的数据版本,并记录修改的事务信息。例如,假设存在一张users表,有一条记录(id=1, name='Alice') ,当事务T1将其修改为(id=1, name='Bob')时,数据库不会直接修改原记录,而是生成一条新版本记录,原记录仍然保留,并且标记为旧版本。
    • 事务快照:在可重复读隔离级别下,事务开始时会创建一个全局的事务快照(Snapshot)。这个快照记录了事务开始瞬间数据库中所有已提交事务的状态。事务在执行过程中的读取操作,都基于这个快照进行。例如,事务T2在可重复读隔离级别下开始执行,它的快照记录了此时数据库中所有已提交事务的情况。在事务执行期间,无论其他事务对数据进行了何种修改,T2始终读取的是快照中的数据版本。
    • 版本可见性判断:对于每个数据版本,PostgreSQL 会根据事务快照判断其可见性。如果一个数据版本是在事务快照创建之前提交的,那么该版本对当前事务是可见的;如果是在快照创建之后提交的,并且该版本所在事务在快照创建时还未提交,那么这个版本对当前事务不可见。例如,事务T1修改了users表中id=1的记录并提交,事务T2T1提交之后开始(可重复读隔离级别),T2的快照创建时T1已经提交,那么T2在读取users表时,不会看到T1修改后的数据,而是看到T1修改前的数据版本,因为修改后的数据版本是在T2快照创建之后提交的。
  2. 锁机制辅助
    • 行级锁的使用:虽然 MVCC 是避免不可重复读的核心机制,但锁机制也起到辅助作用。在可重复读隔离级别下,当事务执行UPDATEDELETE等修改操作时,会获取行级排他锁。这可以防止其他事务在当前事务执行期间修改同一行数据,从而避免因数据修改导致的不可重复读问题。例如,事务T1users表中id=1的记录执行UPDATE操作,它会获取该行的排他锁,在T1事务结束前,其他事务无法修改该行数据,保证了T1在事务内多次读取该行数据时结果的一致性。
    • 防止幻影读相关的锁:虽然可重复读隔离级别不能完全避免幻影读,但 PostgreSQL 会通过一些锁机制来降低幻影读的发生概率。例如,在执行范围查询(如SELECT * FROM users WHERE age > 18)时,会获取一种特殊的锁(如谓词锁),防止其他事务在查询范围内插入新的数据,以保证在同一事务内多次执行相同范围查询时结果的一致性。

posted on 2025-04-06 20:28  阿陶学长  阅读(141)  评论(0)    收藏  举报