只读事务不分配真实事务ID,仅生成伪造trx_id用于结构统一,不影响MVCC可见性判断,但会导致INFORMATION_SCHEMA.INNODB_TRX中误判长事务;其trx_weight为0、trx_id为大数值、无undo日志、不入活跃事务列表。

只读事务不分配真实事务ID,但会生成一个伪造的 trx_id;它不影响 MVCC 的可见性判断逻辑,但会影响 INFORMATION_SCHEMA.INNODB_TRX 中的显示和部分监控行为。
只读事务分配的是假 trx_id,不是真事务ID
MySQL InnoDB 对只读事务(START TRANSACTION READ ONLY 或普通事务中只执行 SELECT)不做真正的事务ID分配。它不会从全局 max_trx_id 计数器取值,也不会让该计数器自增。
但为了内部结构统一(比如事务对象必须有 trx_id 字段),InnoDB 会用当前事务结构体的内存地址做变换,加上 2^48 得到一个很大的伪造值(如 281475252293752)。这个值在 INFORMATION_SCHEMA.INNODB_TRX 中可见,但它:
- 不参与 MVCC 版本可见性判断(因为没写操作,没产生新版本)
- 不被写入 undo log
- 不进入活跃事务数组(
m_ids) - 多个只读事务可能共享同一个伪造
trx_id(如果复用相同内存地址)
为什么 SELECT 也会触发假 trx_id 生成?
事务真正“启动”不是靠 START TRANSACTION,而是靠第一条语句执行。即使只是 SELECT,InnoDB 也需要构建事务上下文来支持快照读——包括初始化 trx_t 结构、准备后续可能的锁或 MVCC 查找。
所以:
-
START TRANSACTION;→ 无trx_id,事务处于“未激活”状态 -
SELECT * FROM t1;→ 分配假trx_id,生成第一个ReadView(RR 级别下) -
UPDATE t1 SET x=1;→ 才分配真实trx_id(来自max_trx_id),并开始写 undo log
注意:RC 隔离级别下,每次 SELECT 都会新建 ReadView,但依然只用假 trx_id,除非后续有更新。
对 MVCC 可见性判断完全无影响
MVCC 判断一行是否可见,核心是比对记录上的 trx_id(即最后修改它的事务的真实 ID)与当前事务的 ReadView 中的 m_ids(活跃事务列表)、min_trx_id、max_trx_id。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
只读事务没有真实 trx_id,意味着它:
- 不会出现在任何其他事务的
ReadView.m_ids中(因为它不修改数据,也不被锁机制视为“活跃写事务”) - 不会阻塞其他事务的
UPDATE或DELETE - 其自身的
ReadView构建只依赖当时已提交/未提交的写事务,与自己的假trx_id无关
换句话说:SELECT 能看到什么,取决于它执行时系统里有哪些**真实写事务**在运行,而不是它自己那个伪造的 trx_id 是多少。
容易忽略的监控陷阱
运维或排查时直接查 INFORMATION_SCHEMA.INNODB_TRX,看到一堆大数字 trx_id,容易误判为“大量长事务正在运行”。其实它们很可能是只读连接反复执行查询产生的假 ID。
更可靠的判断方式是结合以下字段:
-
trx_state = 'RUNNING'且trx_operation_state IS NULL→ 大概率是只读事务 -
trx_started时间早,但trx_weight = 0(表示没修改任何行)→ 基本可确认为只读 -
trx_mysql_thread_id对应的应用连接是否长期只发SELECT
真实写事务一定有非零 trx_weight,且 trx_id 是单调递增的小整数(如 48245 这类)。










