错误1205是锁等待超时(默认50秒),非死锁,主因是从库sql thread被未提交事务或长查询持锁阻塞;需通过innodb_status、sys.innodb_lock_waits交叉定位阻塞源并kill释放锁,根本解法是启用read_only、super_read_only并优化索引与隔离级别。

SQL线程卡在 Lock wait timeout exceeded(错误 1205)
这不是“复制失败”,而是从库 SQL Thread 在重放主库事务时,被另一个连接(比如报表查询、监控脚本或误连的应用)持锁阻塞,等不到锁就超时退出。错误日志里 Last_SQL_Error 明确写着 Error 'Lock wait timeout exceeded; try restarting transaction',Last_SQL_Errno 是 1205。
常见诱因:
- 从库没设
read_only = ON,应用或定时任务直接往GC_Price_Archive这类表写数据,和 SQL Thread 更新同一张表形成锁竞争 - 执行长事务的
SELECT ... FOR UPDATE或未提交的UPDATE持有行锁或间隙锁,SQL Thread 回放 INSERT/UPDATE 时申请不到锁 - 主库发来的大事务(如批量 INSERT),在从库执行耗时长,期间更容易撞上其他连接的锁
怎么确认是哪个连接在阻塞 SQL Thread
不能只看 SHOW PROCESSLIST 里运行时间最长的那个——它可能是被阻塞者,不是阻塞者。真正要找的是“持有锁却不释放”的那个连接。
分三步交叉验证:
- 查
SHOW ENGINE INNODB STATUS\G,翻到LATEST DETECTED DEADLOCK或TRANSACTIONS区块,找TRX_WAITING的事务,记下它的trx_mysql_thread_id;再查INFORMATION_SCHEMA.PROCESSLIST中对应ID的INFO字段,看它在跑什么 SQL - 运行
SELECT * FROM sys.innodb_lock_waits;,直接看到blocking_trx_id和blocking_pid,后者就是阻塞线程 ID - 如果
sys.innodb_lock_waits为空但同步仍卡住,说明锁已释放、等待已超时,此时重点看SHOW SLAVE STATUS\G中的Seconds_Behind_Master是否突增,再结合错误日志时间戳反推当时活跃连接
为什么 kill 掉阻塞连接后同步就恢复了
因为 InnoDB 锁是事务级的:只要阻塞方事务没提交或回滚,它持有的锁就不会释放。SQL Thread 一直在等那个锁,直到 innodb_lock_wait_timeout(默认 50 秒)超时,触发 1205 错误并中止当前事务回放。
KILL 操作强制终止阻塞连接的会话,InnoDB 自动回滚其未提交事务,释放所有锁,SQL Thread 下次重试就能立刻拿到锁继续执行。
注意:KILL 只解决“当下卡住”,不防复发。如果每天都有报表查询在 GC_Price_Archive 上跑 COUNT(DISTINCT ...),就得把这类查询挪到专用只读从库,或加 READ COMMITTED 隔离级别降低锁粒度。
slave_transaction_retries 不是万能解药
设 SET GLOBAL slave_transaction_retries = 100 确实能让 SQL Thread 失败后自动重试 100 次,但它只是“多撞几次门”,不解决门被谁堵着的问题。
副作用很明显:
- 重试期间
Seconds_Behind_Master持续上涨,延迟不可控 - 每次重试都重新解析、加锁、执行,加重从库 CPU 和 IO 压力
- 若根本原因是索引缺失(比如
WHERE non_unique_index = ?触发全表扫描+大量间隙锁),重试只会反复死锁
真正要动的配置只有三个:read_only = ON、super_read_only = ON、innodb_print_all_deadlocks = ON。前两个封死写入口,最后一个确保下次死锁能留下完整现场——没这个,你永远不知道锁是怎么缠上的。











