MySQL 8.0中SELECT默认不加锁,仅显式FOR UPDATE或LOCK IN SHARE MODE才加锁;LOCK TABLES基本失效;MDL锁易阻塞DDL;innodb_deadlock_detect默认开启但高并发下可考虑关闭。

MySQL 8.0 的行锁默认行为变了,SELECT 不再隐式加锁
MySQL 8.0 默认启用了 innodb_lock_wait_timeout 和更严格的隔离控制,但最关键的变动是:在 READ-COMMITTED 隔离级别下,普通 SELECT 语句彻底不加任何锁(包括 gap lock),除非显式写成 SELECT ... FOR UPDATE 或 SELECT ... LOCK IN SHARE MODE。这和 5.7 表现不同,很多依赖“读即锁”逻辑的业务会突然出现幻读或更新丢失。
实操建议:
- 检查当前会话隔离级别:
SELECT @@transaction_isolation,确认是否为READ-COMMITTED(8.0 默认) - 若需兼容旧逻辑,临时改用
REPEATABLE-READ:SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ - 不要靠“没写
FOR UPDATE就安全”做并发控制——该加锁的地方必须显式加
LOCK TABLES 在 MySQL 8.0 中基本失效,别再用它做并发协调
MySQL 8.0 彻底移除了对 LOCK TABLES 的 DML 支持(如 INSERT、UPDATE),仅保留对 SELECT 的只读表锁能力。更关键的是,它与事务、MDL(metadata lock)机制冲突严重,容易触发 ER_LOCK_WAIT_TIMEOUT 或阻塞 DDL。
常见错误现象:
- 执行
LOCK TABLES t1 WRITE后,另一个会话对t1做ALTER TABLE直接卡住 -
UNLOCK TABLES后发现事务里之前的UPDATE没生效——因为表锁强制提交了隐式事务
替代方案:
- 用
SELECT ... FOR UPDATE控制行级并发,而不是锁整张表 - DDL 操作统一走
pt-online-schema-change或gh-ost,避开手动锁表 - 配置
lock_wait_timeout=60防止无限等待(默认 31536000 秒!)
MDL 锁升级导致 ALTER TABLE 卡住整个库,怎么查和解
MySQL 8.0 强化了 MDL(Metadata Lock)的粒度和持有逻辑。一个慢查询正在读某张表,就可能让后续对该表的 ALTER TABLE 一直等在 Waiting for table metadata lock 状态,连 SHOW PROCESSLIST 都看不到明显阻塞源。
快速定位方法:
- 查正在等待 MDL 的线程:
SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA = 'db' AND OBJECT_NAME = 't1' AND LOCK_STATUS = 'PENDING' - 反查谁持有锁:
SELECT * FROM performance_schema.metadata_locks WHERE LOCK_STATUS = 'GRANTED',结合THREAD_ID关联threads表找 SQL - 紧急时可 kill 掉长时间运行的
SELECT线程(注意:不是 kill DDL 线程)
预防要点:
- 避免在业务高峰期跑大范围
SELECT或未加 LIMIT 的统计查询 - 给所有长事务设置
max_execution_time(如SET SESSION max_execution_time = 30000) - 监控
performance_schema.events_statements_current中LOCK_TIME异常高的语句
innodb_deadlock_detect 开关影响比你想的大
MySQL 8.0 默认开启 innodb_deadlock_detect = ON,每笔写入都会做死锁检测。好处是能快速报错 Deadlock found when trying to get lock;坏处是高并发小事务场景下,检测开销可能吃掉 10%+ CPU。
什么时候可以关?
- 确定业务无循环依赖(比如固定按
user_id顺序更新多行),且已做好重试逻辑 - 写入量极大(>5k TPS)、单事务极短(innodb_lock_wait_timeout=10)
关了之后的表现:
- 死锁不会被主动发现,而是等到锁超时才释放,表现为
Lock wait timeout exceeded - 必须确保应用层捕获该错误并重试,否则数据会不一致
- 关之前务必压测验证重试逻辑是否真能收敛
真正难处理的从来不是锁本身,而是那些没出现在 SHOW ENGINE INNODB STATUS 里的隐式锁等待——比如唯一索引冲突引发的插入意向锁(insert intention lock)卡在间隙里,日志里只显示 waiting for an update lock,实际得翻 INFORMATION_SCHEMA.INNODB_TRX 和 INNODB_LOCK_WAITS 对着看。










