sql线程因1213错误停止,因其默认“一次性执行”模式,遇错即停不重试;需手动重放失败事务恢复,而非调大innodb_lock_wait_timeout(该参数仅影响1205锁等待超时)。

SQL 线程停止不是因为“从库发生了死锁”,而是因为从库在重放主库 binlog 时,某条事务执行报了 Deadlock found when trying to get lock; try restarting transaction(错误码 1213),触发了 SQL 线程自动退出。InnoDB 自身不会让死锁卡住整个线程——它已经回滚了那个事务,但复制线程不重试,直接停了。
为什么 SQL 线程会因 1213 停止?
MySQL 复制的 SQL 线程默认是“一次性执行”模式:遇到任何错误(包括 1213)就中断,不再继续。它不会像应用层那样自动重试,也不会跳过这条语句。
-
1213是事务被 InnoDB 主动回滚后的结果,语句本身已失效,但 binlog 里这条事务是“成功提交”的,从库必须重放——所以不能忽略 - 从库的隔离级别、索引状态、数据分布可能和主库不一致,导致同一条 SQL 在从库更容易触发死锁
- 常见诱因:从库有额外查询(如备份脚本加
SELECT ... FOR UPDATE)、缺少索引、或主库写入顺序在从库被并发 DML 干扰
如何快速恢复 SQL 线程而不丢数据?
核心原则:不跳过事件,不重置复制,只让这条出错的事务重试一次。
- 先确认当前错误:
SHOW SLAVE STATUS\G,重点看Seconds_Behind_Master、Slave_SQL_Running_State和Last_SQL_Error - 找到出问题的事务位置:
Relay_Master_Log_File和Exec_Master_Log_Pos指向的就是刚失败那条语句在主库 binlog 中的位置 - 手动重放该事务(推荐):
STOP SLAVE;<br>SET SESSION GTID_NEXT='xxx-xxx-xxx:nnn'; -- 如果是 GTID 模式,填 Last_SQL_Error 中提到的 GTID<br>BEGIN;<br>-- 手动执行出错的那条 SQL(从 Relay log 或 mysqlbinlog 解析出来)<br>COMMIT;<br>SET SESSION GTID_NEXT='AUTOMATIC';<br>START SLAVE;
- 如果非 GTID 模式,用
CHANGE MASTER TO ... RELAY_LOG_FILE='xxx', RELAY_LOG_POS=yyy跳到下一条(不推荐,易丢数据)
innodb_lock_wait_timeout 和死锁无关,别乱调
有人看到 SQL 线程停了,第一反应是把 innodb_lock_wait_timeout 从 50 改成 300——这是无效操作。这个参数控制的是“等锁超时”(1205 错误),不是死锁(1213)。死锁检测是瞬时的,不依赖超时。
-
1213是 InnoDB 主动检测并回滚的结果,和等待时间无关 - 盲目加大
innodb_lock_wait_timeout可能让从库更长时间卡在锁等待上,反而加剧复制延迟 - 真正该查的是:从库是否有长事务、是否缺失索引、是否被外部 DML 干扰
长期避免的关键点往往被忽略
修复一次 SQL 线程只是救火。真正容易被忽略的是:从库的执行环境和主库不等价。
- 主库可能没开
read_only,但从库开了——可一旦被误操作(如 DBA 直连从库执行 DML),就会污染数据并干扰复制重放逻辑 - 从库的索引比主库少(比如忘记同步
ALTER TABLE),导致 UPDATE 走全表扫描,锁范围扩大,死锁概率飙升 - 监控缺失:没开
innodb_print_all_deadlocks = ON,每次死锁都只留下一个1213错误,没有上下文,无法定位是哪类业务引发的 - 应用层重试逻辑只在主库生效,从库复制线程完全不参与——所以“主库能扛住,从库必挂”是典型信号











