mysql如何理解只读事务的优化_mysql内部只读事务标记

2次阅读

set transaction read only 通过跳过undo log分配、锁子系统初始化等写路径开销提升只读事务性能;mysql 5.7+默认懒升级,满足全条件才启用优化,需用show engine innodb status或performance_schema验证read_only=1。

mysql如何理解只读事务的优化_mysql内部只读事务标记

SET TRANSACTION READ ONLY 不是“加个提示就变快”,而是让 InnoDB 从事务一开始就能跳过大量写路径的初始化开销。

MySQL 5.6 起,只读事务会绕过事务系统中很多为写操作预留的结构(比如 undo log 分配、锁子系统初始化、活跃事务链表注册等);到 5.7+,InnoDB 更进一步——所有事务默认以只读模式启动,直到执行第一条修改语句或显式加锁查询(如 select ... FOR UPDATE)才升级为读写事务。这个“懒升级”机制本身就在帮你省资源。


怎么判断一个事务真的被识别为只读?

InnoDB 只在满足全部条件时才启用只读优化:

AUTOCOMMIT = 1 且事务仅含一条 SELECT(不含 FOR UPDATELOCK IN SHARE MODE
• 或显式用 START TRANSACTION READ ONLY / SET TRANSACTION READ ONLY 声明
• 事务中没执行过任何 DML(INSERT/UPDATE/delete)、DDL、临时表写入、用户变量赋值(@var := ...
• 没调用任何会隐式写入的函数,比如 UUID()(生成新 UUID 会触发内部写)、NOW() 在某些 binlog 格式下也可能触发

常见误判:以为只要没 UPDATE 就是只读——但 SELECT count(*) FROM t WHERE id > (SELECT MAX(id) FROM log) 这种子查询若涉及写表,整个事务仍会被标记为读写。


READ ONLYautocommit=1 的区别在哪?

两者都能触发只读优化,但行为不同:

autocommit = 1:每个 SELECT 自成一个单语句事务,InnoDB 确保它不带锁、不分配 undo、不进 trx list——最轻量,适合报表、监控类查询
START TRANSACTION READ ONLY:显式开启多语句只读事务,可用于跨多个 SELECT 复用一致性快照(比如先查总数再分页),但需注意:一旦执行了任何写操作,事务会立即报错 Error 1792 (HY000): Cannot execute statement in a READ ONLY transaction

容易踩的坑:SET TRANSACTION READ ONLY 必须在 START TRANSACTION 之前执行,否则无效;而 SET session TRANSACTION READ ONLY 是会话级默认,对后续所有事务生效——这在连接池场景下可能污染其他请求。


为什么有时候加了 READ ONLY 却没提速?

根本原因通常是“只读声明”和“实际执行路径”不匹配:

• 查询走了全表扫描,CPU 和 IO 耗在磁盘读取上,事务标记再干净也救不了慢查询本身
• 表上有 UNIQUE 索引但查询用了函数,比如 WHERE UPPER(name) = 'ABC',导致无法走索引,锁范围扩大,InnoDB 仍要扫描并加间隙锁
• 使用了 INFORMATION_SCHEMA 或性能视图(如 sys.schema_table_statistics),这些表底层访问可能触发内部写或元数据锁,强制事务降级为读写
• MySQL 8.0+ 开启了 persisted_globals_load=ON,某些全局状态变更会让只读事务悄悄“破功”

实操建议:用 SHOW ENGINE INNODB STATUSG 查看 TRANSACTIONS 部分,找你的事务 ID,确认字段 read_only 是否为 1;再配合 performance_schema.events_transactions_current 表查 ACCESS_MODE 字段值。

真正起效的只读优化,往往藏在高并发简单查询里——比如每秒几千次的配置拉取、权限校验、状态轮询。这时候少掉几个内存分配、少一次 trx list 插入,积少成多就是毫秒级延迟下降。别指望它拯救一条 2s 的慢 JOIN

text=ZqhQzanResources