rc下“读不到最新提交数据”是正常行为,因每次select在执行开始时生成readview,若其他事务在其后才提交,则该数据对本次查询不可见。

RC级别下“读不到最新提交的数据”其实是假象
这不是Bug,也不是配置错误,而是Read Committed隔离级别下MVCC可见性算法的正常行为。关键在于:**每次SELECT都会生成新ReadView,但这个ReadView只对本次查询生效,且生成时机在语句执行「开始时」**。如果另一个事务在你SELECT启动后、实际扫描数据前才提交,那这条记录的TRX_ID仍会落在当前ReadView的m_up_limit_id及以上范围,从而被判定为「不可见」。
如何验证当前事务的ReadView内容
MySQL不直接暴露ReadView结构,但可通过组合查询间接推断其边界:
-
SELECT * FROM information_schema.INNODB_TRX查看当前活跃事务ID列表(对应m_ids) -
SELECT TRX_ID, TRX_STATE, TRX_STARTED FROM information_schema.INNODB_TRX ORDER BY TRX_STARTED找出最早未提交事务的TRX_STARTED时间,它大致对应m_low_limit_id的下界 - 结合
SHOW VARIABLES LIKE 'transaction_isolation'确认当前会话确实是READ-COMMITTED
注意:m_up_limit_id通常等于当前系统最大已分配事务ID+1,无法直接查,但可通过高并发插入后观察TRX_ID增长趋势反推。
RC与RR在可见性判断上的核心差异
二者都用同一套可见性规则(比较TRX_ID与m_creator_trx_id、m_low_limit_id、m_up_limit_id、m_ids),但触发点不同:
-
RC:每个SELECT语句执行前,立刻构建新ReadView;其他事务只要在该语句「开始前」已提交,就可见 -
RR:仅在事务内**第一次快照读**(如无锁SELECT)时创建ReadView,后续所有快照读复用它;即使其他事务中途提交,也始终不可见 - 两者对「当前事务自身修改」的处理一致:
TRX_ID == m_creator_trx_id→ 总是可见
所以如果你在RC下连续执行两次SELECT,中间夹着别人COMMIT,第二次一定能读到——但第一次未必能,取决于那个COMMIT是否发生在第一次SELECT构造ReadView之后。
容易被忽略的执行时机陷阱
开发中最常踩的坑,是把「语句执行开始」误解为「客户端发包时刻」或「SQL解析完成时刻」。实际上:
- InnoDB在真正访问聚簇索引页、准备读取记录行时,才确定要构造
ReadView - 如果表上有大范围
WHERE条件、需要遍历大量索引页,从语句发出到ReadView生成之间可能有毫秒级延迟 - 此时另一个事务恰好
COMMIT,就会导致「我刚看到别人提交了,怎么SELECT还读不到?」 - 用
SELECT ... LOCK IN SHARE MODE或SELECT ... FOR UPDATE会跳过快照读,直接走当前读,绕过ReadView机制——但这会加锁,影响并发
真正稳定的方案不是强行“读到最新”,而是根据业务语义选择:需要强一致性就用RR + 明确的锁策略;接受短暂延迟就接受RC的天然行为,避免在应用层做竞态重试。











