MySQL RC事务隔离的实现原理:ReadView与Undo Log

蓝鲸
蓝鲸
发布时间:2026-10-16 | 4人浏览过
原创

MySQL的RC(Read Committed)隔离级别核心在于“每次查询都重新生成ReadView”。本文将深入解析ReadView机制如何利用Undo Log版本链条判断数据可见性,以及这一机制如何导致RC级别下可读到其他已提交事务的修改,却依然存在不可重复读现象。

RC隔离级别的核心机制:ReadView与Undo Log

在RC(读已提交)事务运行期间,只要别的事务修改数据并提交,当前事务即可读到对方修改的数据。这意味着RC级别下,同一事务内多次读取同一记录可能得到不同的值,即存在不可重复读和幻读问题。

其背后的实现基础是ReadView机制,该机制基于undo log版本链条实现。当事务发起查询时,系统会生成一个ReadView对象,用于定义当前时刻的可见性范围。具体规则如下:

  • 若读取的是事务自己更新的数据,直接可见。
  • 若在生成ReadView之前,已有事务提交了修改,则该修改可见。
  • 若在生成ReadView时,某个事务已经处于活跃状态(未提交),即使它后续提交了,在本次查询中也不可见。
  • 若在生成ReadView之后才开启的新事务,其修改在当前查询中不可见。

RC与RR(Repeatable Read)的关键区别在于ReadView的生成时机:RC级别下,事务在每次发起查询时都会重新生成一个新的ReadView;而RR级别通常在第一次查询时生成一次,整个事务期间复用。

工作原理演示:从插入到提交的完整过程

为了清晰理解这一机制,我们模拟以下场景:数据库表中有一行原始数据,由事务id=50的事务很久以前插入并提交了。此时,数据库中存在两个活跃事务:事务A(id=60)和事务B(id=70)。

第一步:事务B更新数据并生成版本链

假设现在事务B发起update操作,将这条数据修改为值“b”。此时会发生两件事:

  1. 数据的最新trx_id被标记为事务B的id,即70。
  2. 系统在undo log中生成一条历史版本记录,保留旧数据及其对应的trx_id=50。

第二步:事务A首次查询(ReadView生效)

此时,事务A发起一次查询操作。系统会为事务A生成一个新的ReadView。

事务A查询发现,当前数据的trx_id=70。根据ReadView的规则:

  • 检查m_ids(活跃事务ID列表),其中包含[60, 70]。
  • 因为70存在于活跃事务列表中,说明该数据是在生成ReadView时已经存在但未提交的事务所修改的。
  • 由于事务B尚未提交,事务A无法读到事务B修改的值“b”。

接着,MySQL顺着undo log版本链条往下查找,找到一个原始值,其trx_id为50。因为50小于ReadView的min_trx_id,说明这是在读视图生成前就已提交的旧数据,因此事务A可以查到这个原始值。

第三步:事务B提交后,事务A再次查询

当事务B执行commit操作后,事务B不再被视为活跃事务。如果事务A再次发起查询:

  1. 生成新的ReadView:由于是RC级别,事务A会重新生成ReadView。此时活跃事务列表中仅包含事务A自身(id=60)。因此:

    • min_trx_id设为 60。
    • max_trx_id(或m_exclusive_limit)设为 71(下一个即将分配的ID)。
    • m_ids列表中只包含 [60],事务B的id=70不再出现在列表中。
  2. 判断可见性:事务A再次查询时发现数据trx_id=70。虽然70位于min_trx_id和max_trx_id之间,但因为它不在m_ids活跃列表中,这意味着事务B在生成本次ReadView之前就已经提交了。

  3. 结果:事务A可以读到事务B修改过的值“b”。

这就是RC隔离级别实现“读到已提交数据”的核心逻辑:通过每次查询刷新ReadView来识别哪些活跃事务已变为已提交状态。

总结

RC隔离级别通过更频繁的ReadView生成,实现了比RR更宽松的一致性要求。它解决了某些场景下的并发读取阻塞问题,但也正因为视图频繁变化,无法保证事务内多次读取结果一致。以上就是关于MySQL RC事务隔离的实现原理的详细内容,更多关于MySQL事务隔离级别对比的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表