只读事务仍可能阻塞DDL,因其首次访问未缓存元数据的表时会加MDL_SHARED_READ锁;SELECT ... FOR UPDATE等语句也会触发额外锁;SET TRANSACTION READ ONLY仅影响下一事务且易失效,而START TRANSACTION READ ONLY从执行起生效并长期持锁。

只读事务本身不写数据,但默认仍会申请 MDL_SHARED_READ 锁——这是它“看似只读、实则锁表”的根本原因。
为什么 START TRANSACTION READ ONLY 仍可能阻塞 DDL
显式声明只读事务后,MySQL 确实跳过了对大多数表的 MDL 升级逻辑,但以下情况仍会触发锁:
- 事务中首次访问某张表时,只要该表尚未被本会话缓存过元数据,就会加
MDL_SHARED_READ锁(哪怕只是SELECT COUNT(*)) - 如果事务里执行了
SELECT ... FOR UPDATE或SELECT ... LOCK IN SHARE MODE,InnoDB 会额外加行级锁 + 意向锁,而这些锁依赖底层表的 MDL 保障结构稳定 - 在 MySQL 5.7 中,即使只读事务没显式访问某表,只要之前执行过涉及该表的语句(比如
SHOW CREATE TABLE),也会残留 MDL 锁
SET TRANSACTION READ ONLY 和 START TRANSACTION READ ONLY 的锁行为差异
两者语义一致,但生效时机不同,直接影响锁持有范围:
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
-
START TRANSACTION READ ONLY:从该语句执行起,后续所有语句都处于只读上下文,MDL 锁按需获取并长期持有,直到事务结束 -
SET TRANSACTION READ ONLY:仅影响**下一个**事务,且必须在事务开始前设置;若已执行过任何语句(哪怕是SELECT 1),该设置会被忽略,退化为普通事务 - 误用
SET方式导致“以为只读,实则可写”,反而因未加锁保护而引发 DDL 冲突或隐式锁升级
如何验证只读事务是否真正在释放 MDL 资源
不能只看事务状态,得查锁的实际持有者:
- 执行
SELECT * FROM performance_schema.metadata_locks WHERE OWNER_THREAD_ID = (SELECT THREAD_ID FROM performance_schema.threads WHERE PROCESSLIST_ID = CONNECTION_ID()),确认LOCK_TYPE是否为SHARED_READ且LOCK_DURATION是TRANSACTION - 对比普通事务:
SELECT * FROM performance_schema.events_statements_current WHERE SQL_TEXT LIKE '%SELECT%',观察NESTING_EVENT_ID是否为空——非空说明该查询被包裹在未显式声明只读的事务中 - 在高并发只读场景下,若
ALTER TABLE仍被卡住,大概率是某些连接漏写了READ ONLY,或用了SET但未生效
真正省锁的关键不在“声明只读”,而在“避免首次访问触发型加锁”——比如提前在连接池初始化时执行一次空 SELECT 触发元数据加载,再开启只读事务;否则,每个新事务第一次 SELECT 都是潜在的锁点。










