mysql 5.6+ 支持 start transaction read only,5.7/8.0 才真正生效并优化性能;需显式声明,autocommit=1 下无效;执行写操作会报错;与全局 read_only=on 无关;不保证强一致性,仅减少事务开销。

MySQL 5.6+ 才支持 START TRANSACTION READ ONLY
低于 5.6 的版本执行会直接报错:ERROR 1290 (HY000): The MySQL server is running with the --read-only option so it cannot execute this statement(注意:这错误名有误导性,实际是语法不识别,不是权限问题)。5.6 引入只读事务语法,8.0 进一步优化了只读事务的内部处理路径——跳过事务 ID 分配、不写 undo log、减少锁竞争。如果你用的是 5.7 或 8.0,这个语法才真正生效;否则它会被降级为普通事务,徒增误解。
检查版本:SELECT VERSION();
只读事务必须显式声明,AUTOCOMMIT=1 下无效
很多人以为只要不写 UPDATE/INSERT,查询自动就是只读事务——不是。MySQL 不会自动推断事务读写属性,必须靠 START TRANSACTION READ ONLY 显式开启。即使你在 AUTOCOMMIT=0 下先 BEGIN,再执行 SELECT,也还是可写事务(只是没写而已),无法触发只读优化。
-
SET AUTOCOMMIT = 0;→SELECT ...→ 仍是可写事务 -
START TRANSACTION READ ONLY;→SELECT ...→ 触发只读路径 - 一旦在只读事务里执行了
INSERT、UPDATE、CREATE TEMPORARY TABLE等写操作,MySQL 会立刻报错:ERROR 1792 (25006): Cannot execute statement in a READ ONLY transaction.
只读事务和全局 read_only=ON 是两回事
全局 read_only=ON 是服务器级开关,禁止除 super 用户外的所有写操作;而 START TRANSACTION READ ONLY 是会话级、事务级控制,对用户权限无额外要求,也不影响其他连接。两者可共存,但目的不同:
- 你设了
read_only=ON,普通用户连BEGIN后的INSERT都不允许——这是权限锁死 - 你用
START TRANSACTION READ ONLY,是在告诉 InnoDB:“这次我真不写,你少干点活”,属于性能提示 - 如果同时开了全局
read_only,再开只读事务也没问题,但后者带来的性能收益基本被前者覆盖了
只读事务不能跨连接复用,且不保证强一致性
只读事务的“只读”仅限当前事务生命周期,它不会自动加 FOR UPDATE 或 LOCK IN SHARE MODE,所以默认走一致性非锁定读(consistent nonlocking read)。这意味着:
- 同一事务内多次
SELECT可能读到不同快照(取决于隔离级别和 MVCC 版本链) - 它不阻止其他连接修改数据,也不提供“查询期间数据冻结”的语义
- 想实现可重复读,得配合
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ使用,而不是依赖READ ONLY - 连接池场景下,别指望拿一个只读事务对象反复执行多条
SELECT——事务结束即释放,下次要重开
真正容易被忽略的是:只读事务不是银弹。它省的是事务管理开销,不是 SQL 执行本身;如果查询本身慢、索引没走、结果集巨大,加了 READ ONLY 也快不起来。











