能,但仅当显式声明 start transaction read only 且全程无隐性写操作(如 select for update、临时表、会话变量赋值等)时生效;mysql 5.6+ 支持,5.7+ 进一步优化 mvcc 和锁处理。

Read-Only 事务在 MySQL 中真能提升性能?
能,但只在特定条件下生效——核心前提是:事务**全程不触发写操作、不访问未提交的写数据、且显式声明为 START TRANSACTION READ ONLY**。MySQL 5.6+ 才支持该语法,5.7+ 进一步优化了只读事务的锁和 MVCC 处理逻辑。如果只是“没写”,但没加 READ ONLY 声明,优化基本不会触发。
为什么加了 READ ONLY 还没效果?
常见原因不是语法错,而是隐性写行为或环境限制:
-
SELECT FOR UPDATE或SELECT LOCK IN SHARE MODE会强制升级为可写事务,即使没UPDATE - 查询中调用了带副作用的函数,比如
NOW()、UUID()、USER()(部分版本下会禁用只读优化) - 事务内执行了
SET语句修改会话变量(如SET @x = 1),某些 MySQL 版本会标记事务为“潜在可写” - 使用了临时表(
CREATE TEMPORARY TABLE),哪怕只读查询也触发写路径 - 连接启用了
autocommit = 0但没显式START TRANSACTION READ ONLY,而是靠隐式事务启动,MySQL 不识别其只读意图
START TRANSACTION READ ONLY 和 SET SESSION transaction_read_only = ON 区别在哪?
前者是事务级控制,后者是会话级开关,二者行为差异明显:
-
START TRANSACTION READ ONLY:仅对该事务生效;事务结束后自动恢复;允许后续再开可写事务;推荐用于明确、短生命周期的只读查询 -
SET SESSION transaction_read_only = ON:整个会话后续所有事务都强制只读(除非显式关掉);若尝试写操作,会直接报错ERROR 1792 (HY000): Cannot execute statement in a READ ONLY transaction - 注意:
transaction_read_only是动态变量,但不能在事务中设置;且它不影响当前已开启的事务,只影响后续BEGIN或START TRANSACTION
只读事务优化实际体现在哪些地方?
不是“快一点”,而是绕过几类开销较大的机制:
- 跳过事务 ID 分配(
trx_id不分配),减少 InnoDB 内存和锁竞争 - 避免在
read_view构建时扫描活跃事务链表(尤其高并发写场景下显著降低开销) - 不参与全局
max_trx_id更新,减少对dict_sys->mutex的争用 - 某些版本中,只读事务可复用更老的
read_view,提升一致性读效率
不过这些优化对单条简单查询几乎不可测;只有在高并发、长生命周期、大量只读事务共存时,CPU 和锁等待才会有可观下降。
容易被忽略的一点:只读事务仍会持有 LOCK_S(共享锁)去读二级索引,如果同时有大量 INSERT ... ON DUPLICATE KEY UPDATE 或 REPLACE,可能因索引锁冲突反而变慢——优化不是万能的,得看锁竞争热点在哪。










