MySQL只读事务不分配事务ID,是因无需参与MVCC可见性判断;仅显式使用START TRANSACTION READ ONLY时,InnoDB才跳过ID分配、不注册活跃链表、不分配undo段,而BEGIN后SELECT仍走完整事务流程。

MySQL 在只读事务下不分配事务 ID,不是省略步骤,而是明确跳过——因为事务 ID 的本质作用是参与 MVCC 可见性判断和 undo 版本链标记,而纯只读事务既不生成新版本,也不影响其他事务的可见性检查。
START TRANSACTION READ ONLY 真正触发只读优化
只有显式使用 START TRANSACTION READ ONLY 开启的事务,InnoDB 才会标记 trx->read_only = true 并跳过事务 ID 分配、不注册到活跃事务链表、不分配 undo 段。这和 BEGIN 或 START TRANSACTION 后跟 SELECT 完全不同:
-
BEGIN; SELECT ...仍走完整事务生命周期:分配事务 ID、创建 read view、加入trx_sys->rw_trx_list -
autocommit=1下单条非锁定SELECT(无FOR UPDATE/LOCK IN SHARE MODE)会被识别为 auto-commit read-only,但不会复用全局只读 ID,仅避免部分开销 -
SET TRANSACTION READ ONLY只影响后续事务,对当前已BEGIN的事务无效
事务 ID 缺失在 SHOW ENGINE INNODB STATUS 中的表现
当你看到 TRX_ID: 0 或极大值(如 4294967295)时,别急着怀疑配置错误——这是 MySQL 8.0+ 对只读事务的正常处理:
-
TRX_ID: 0常见于autocommit=1的单条SELECT,它被归类为 AC-NL-RO-SELECT(Auto-Commit Non-Locking Read-Only Select) - 显式
START TRANSACTION READ ONLY事务在INFORMATION_SCHEMA.INNODB_TRX中可能显示TRX_ID为空或为占位符值,因未真正分配 -
SHOW ENGINE INNODB STATUS不再列出只读事务的完整上下文,需查INNODB_TRX表并过滤TRX_ISOLATION_LEVEL = 'REPEATABLE READ'且TRX_ROWS_LOCKED = 0来定位
混入写操作会让只读事务立即“降级”
一旦事务中执行了任何写语句(哪怕只是 INSERT INTO ... SELECT、SELECT ... INTO OUTFILE、调用含写逻辑的存储函数),MySQL 会立刻报错 ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION (1792),并终止该事务的只读状态:
- 错误发生在 Server 层,不进入 InnoDB 引擎,所以不会触发回滚段分配或事务 ID 补发
- 临时表(
CREATE TEMPORARY TABLE)操作例外:允许在只读事务中增删改,因其变更对其他事务不可见 -
SELECT FOR UPDATE或LOCK IN SHARE MODE会直接让事务变为读写模式,即使此前声明为READ ONLY
真正容易被忽略的是:只读事务的优化效果高度依赖开启方式与语句洁癖——少一个 READ ONLY 关键字,或者多一次隐式锁读,整个事务就退回高开销路径。运维排查时若发现大量 TRX_STATE: RUNNING 但无锁无写,大概率是忘了加 READ ONLY 声明。


















