SQL线程回放时被行锁卡住,是因为从库SQL Thread与其它连接(如报表查询)同时操作同一行,产生锁竞争;常见于从库未设read_only、长事务持有锁、大事务执行慢或并行复制下多个worker争同一行锁。

SQL线程回放时为什么突然被行锁卡住?
不是主库慢,也不是从库弱,而是从库 SQL Thread 在重放 binlog 时,和另一个连接(比如报表查询、监控脚本、误连的应用)同时操作同一张表的同一行——行锁资源被抢占,SQL Thread 只能等。常见现象是 SHOW PROCESSLIST 中看到状态为 Updating 或 Locked,Seconds_Behind_Master 持续上涨但 Exec_Master_Log_Pos 几乎不动。
- 从库未设
read_only = ON或super_read_only = ON,应用直连从库写入,与 SQL Thread 形成真实锁竞争 - 长事务的
SELECT ... FOR UPDATE或未提交的UPDATE持有行锁/间隙锁,SQL Thread 回放后续 DML 时申请不到锁 - 主库发来的大事务(如批量
UPDATE t SET x=1 WHERE y BETWEEN 100 AND 100000),在从库执行耗时长,期间更易撞上其他连接的锁 -
innodb_lock_wait_timeout默认 50 秒,超时即报Error 1205: Lock wait timeout exceeded,SQL Thread 中止当前事务并停摆
为什么 SHOW PROCESSLIST 里找不到真正的阻塞者?
因为“运行时间最长”的那个连接,大概率是被阻塞者,不是持有锁的人。真正要找的是那个“开了事务、拿了锁、但没提交也没回滚”的安静连接。
- 查
SHOW ENGINE INNODB STATUS\G,翻到TRANSACTIONS区块,找TRX_STATE = 'LOCK WAIT'的事务,记下它的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中的Last_SQL_Error_Timestamp
启用并行复制后,行锁争用反而更隐蔽?
开启 slave_parallel_workers > 0 后,并非所有事务都能并发执行。一旦多个 worker 线程修改同一张表的同一行(主键值相同),WRITESET 机制会强制它们串行回放 —— 这时你看到的不是“多线程加速”,而是“多个 worker 都在等同一个行锁”,表现和单线程卡住几乎一样。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 检查
performance_schema.replication_applier_status_by_worker,若多个 worker 的LAST_APPLIED_TRANSACTION停滞在同一个 GTID,且LAST_ERROR_MESSAGE为空,说明它们正排队等锁 - 用
SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA = 'db_name' AND OBJECT_NAME = 'tbl_name'确认是否有其他连接持锁 -
slave_preserve_commit_order = ON是必须的,否则即使并行生效,也可能因事务顺序错乱引发额外锁冲突 - 避免跨业务聚合更新(如一个事务同时更新
orders、inventory、logistics),这类操作天然增加锁范围和等待链长度
为什么 KILL 掉阻塞连接就能恢复,但问题反复出现?
因为 InnoDB 行锁是事务级的:只要阻塞方事务没提交或回滚,锁就一直挂着。KILL 强制终止会话,InnoDB 自动回滚其未提交事务,释放所有锁,SQL Thread 下次重试就能继续。
- 这只是临时解法。若每天都有报表在
sales_summary上跑SELECT COUNT(*),就得把这类查询迁到专用只读从库,或改用READ COMMITTED隔离级别降低锁粒度 - 不要依赖
sql_slave_skip_counter或slave_exec_mode='IDEMPOTENT'来跳过 1205 错误——它跳过的是整个事务,可能造成数据不一致 - 根本解法是收敛写入口:确保从库只接受复制流量,所有业务写操作严格走主库,监控脚本、ETL 工具、DBA 临时连接全部禁止直连从库写入
行锁争用本身不可怕,可怕的是它藏在 Seconds_Behind_Master 背后,看起来像延迟,实则是阻塞;更麻烦的是,它常和 MDL 锁、自增锁、间隙锁交织在一起,单靠 kill 连接只能治标。真正要稳住复制,得从连接管控、事务拆分、隔离级别、监控覆盖这四个点一起动手。

















