快速定位从库锁等待需查performance_schema.data_lock_waits,用blocking_engine_lock_id关联data_locks查阻塞方锁类型和范围;从库执行计划差异源于单线程回放+read-committed隔离级导致优化器选错索引;“lock wait timeout exceeded”不报错因mysql自动重试10次才写日志;大事务未分批、ddl操作、显式加锁查询会加剧从库锁问题。

怎么快速定位从库正在等哪个锁
从库出现大量锁等待,第一反应不是杀进程,而是看它卡在哪儿。MySQL 5.7+ 的 performance_schema.data_locks 和 data_lock_waits 是唯一直接反映当前行级锁阻塞关系的来源,information_schema.INNODB_TRX 只能告诉你“有事务在跑”,但不知道它被谁拦着。
实操建议:
- 先查
SELECT * FROM performance_schema.data_lock_waits,重点关注BLOCKING_ENGINE_LOCK_ID和BLOCKED_ENGINE_LOCK_ID,它们是锁的唯一标识,能反查到具体表、索引、甚至记录主键值 - 用
BLOCKING_ENGINE_LOCK_ID去data_locks表里 JOIN,就能看到阻塞方持有的锁类型(LOCK_MODE)、锁范围(LOCK_DATA,比如主键值或间隙值) - 注意:默认
performance_schema可能没开全,确认setup_instruments中wait/lock/innoDB和transaction类已启用,否则查出来是空的 - 别依赖
SHOW PROCESSLIST里的State字段,像Waiting for table metadata lock或Updating这类描述太模糊,无法区分是 DDL 锁还是行锁冲突
为什么从库执行计划经常和主库不一致
从库 SQL 线程回放时用的是单线程(除非开了 MTS),且默认以 READ-COMMITTED 隔离级别运行(即使主库是 REPEATABLE-READ),这会导致优化器对同一语句选择不同索引,尤其是当 WHERE 条件涉及非主键字段、又存在多个可选索引时。
常见错误现象:主库走 idx_status_created,从库却走 PRIMARY 扫全表,导致延迟飙升 + 锁等待加剧。
实操建议:
- 在从库上对慢查询执行
EXPLAIN FOR CONNECTION <sql_thread_id></sql_thread_id>(需先从INNODB_TRX查到 SQL 线程的TRX_MYSQL_THREAD_ID) - 对比主库同语句的
EXPLAIN,重点看key、rows、Extra(特别是Using index condition是否一致) - 如果发现从库走了全表或错误索引,优先检查该表在从库上是否缺失统计信息——执行
ANALYZE TABLE <table_name></table_name>,不要等自动更新 - 避免在从库上手动加
FORCE INDEX,SQL 线程不认这个 hint;真要干预,只能改主库原 SQL 或调整索引设计
从库锁日志里看到 “Lock wait timeout exceeded” 但没报错
这是最误导人的现象:从库 SQL 线程卡住,监控看到 Seconds_Behind_Master 持续上涨,但错误日志里没有 Lock wait timeout exceeded 报错。原因在于,MySQL 默认把这种超时当成“重试场景”而非“失败事件”——它会自动回滚当前事务,然后重试,最多 10 次(由 innodb_lock_wait_timeout 控制),只有全部失败才写日志。
实操建议:
- 调低
innodb_lock_wait_timeout(比如设为 3 秒),让失败更快暴露,便于抓取真实错误上下文 - 开启
log_error_verbosity = 3并确保innodb_print_all_deadlocks = ON,否则死锁检测日志会被静默丢弃 - 别只盯
error.log,用SELECT * FROM performance_schema.events_statements_history_long WHERE SQL_TEXT LIKE '%UPDATE%' ORDER BY TIMER_START DESC LIMIT 10查最近执行失败的语句 - 注意:从库的
innodb_lock_wait_timeout值应与主库一致,否则主库能过的语句,从库可能反复超时
哪些操作会让从库锁问题雪上加霜
有些看似无害的操作,在从库单线程回放模型下会放大锁竞争,比如大事务拆分、批量更新顺序、甚至建索引方式。
实操建议:
- 主库执行大 UPDATE 时,务必用主键分批次(如
WHERE id BETWEEN ? AND ?),避免从库单次回放锁住整个二级索引范围 - DDL 操作(尤其是
ADD INDEX)在从库上会阻塞后续所有 SQL 线程,且无法中断;必须用pt-online-schema-change或 MySQL 8.0+ 的ALGORITHM=INSTANT(仅限部分操作) - 从库上禁止执行
SELECT ... FOR UPDATE或任何显式加锁查询,哪怕只是临时排查——它会和 SQL 线程争抢同一行锁,且没有优先级 - 监控
Slave_SQL_Running_State时,留意是否长期停留在Reading event from the relay log,这往往意味着前一条语句还没释放锁,下一条已在排队
真正难处理的从来不是锁本身,而是从库 SQL 线程那条看不见的串行链路——它让所有并发问题都变成时间问题,而时间问题最容易被误判成性能问题。











