Read-View 是 InnoDB 在 SELECT 时动态生成的可见性判断元数据,含 m_ids、min_trx_id、max_trx_id;它不锁行、不改数据、不持久化,RR 下首次 SELECT 创建并复用,RC 下每次 SELECT 重建。

Read-View 是什么,不是什么
Read-View 不是 MySQL 自动创建的快照表,也不是某个事务专属的“副本”。它是 InnoDB 在事务执行 SELECT 时动态生成的一组元数据,核心就三样:m_ids(当前活跃事务 ID 列表)、min_trx_id(其中最小值)、max_trx_id(下一个将分配的事务 ID)。它只决定“这一条 SELECT 能看到哪些版本”,不锁行、不改数据、不持久化。
常见误解是以为 Read-View 一建就固定不变——其实它在可重复读(RR)下复用,在读已提交(RC)下每次 SELECT 都新建。这点直接影响可见性结果,后面会踩坑。
RR 和 RC 下 Read-View 创建时机差异
可重复读(RR)下,事务第一次执行 SELECT 时生成 Read-View,并在整个事务中复用;读已提交(RC)下,每次 SELECT 都重新生成 Read-View。
- RR:事务 A 开启后查一次,再等事务 B 提交一行新数据,A 再查——还是看不到,因为用的是老 Read-View
- RC:同样场景,第二次
SELECT会拿到新 Read-View,m_ids里不含 B 的 ID(B 已提交),所以能看到 - 注意:
BEGIN不触发 Read-View 创建,真正执行第一个SELECT才创建 -
START TRANSACTION WITH CONSISTENT SNAPSHOT会立即建 Read-View,哪怕没SELECT
判断某行记录是否可见的逻辑链
InnoDB 拿到一条聚簇索引记录后,检查其 DB_TRX_ID(最后修改该行的事务 ID),再套进 Read-View 规则比对:
- 如果
DB_TRX_ID== 当前事务 ID → 自己改的,可见 - 如果
DB_TRX_IDmin_trx_id → 修改者在本 Read-View 创建前已提交,可见 - 如果
DB_TRX_ID>=max_trx_id→ 修改者在本 Read-View 创建后才启动,不可见 - 如果
min_trx_idDB_TRX_ID max_trx_id → 查m_ids列表:若存在,说明该事务还活跃,不可见;若不存在,说明已提交,可见
关键点:这个判断是逐行做的,不是整张表统一过滤。所以即使同一事务中,不同 SELECT 可能因索引扫描路径不同,看到不同版本的“同一逻辑行”(比如走二级索引回表时,主键记录版本可能被其他事务覆盖过)。
容易被忽略的三个实际影响
第一,长事务会把 m_ids 拉得非常长,导致后续所有新事务的 Read-View 构建变慢,尤其在高并发小事务场景下,SELECT 延迟肉眼可见上升。
第二,DELETE 或 UPDATE 语句虽然不查数据,但内部仍要判断“哪些行满足 WHERE 条件”,这个判断也依赖 Read-View —— 所以 RR 下的 UPDATE 可能“看不见”RC 下能看到的行,导致更新零行却不报错。
第三,唯一键冲突检测(如插入重复主键)用的是最新版本,不走 Read-View 可见性规则,所以即使你 SELECT 看不到某行,INSERT 仍可能因那行存在而报 Duplicate entry 错误。










