长事务会卡死从库sql线程,因其需完整执行完超大事务才能推进位点,导致seconds_behind_master飙升而exec_master_log_pos几乎不动;row格式下每行变更生成独立event,进一步加剧复制压力。

长事务会卡死从库 SQL 线程
MySQL 从库默认用单个 SQL 线程回放 relay log,而长事务在 binlog 中表现为一个超大事务事件(可能跨多个 binlog 文件、持续数分钟甚至小时)。只要这个事务没执行完,SQL 线程就无法推进到后续事务——它必须等完整执行完这个事务,才能处理下一个 GTID 或位点。这不是“慢”,是“阻塞”。
ROW 格式下长事务放大复制压力
当 binlog_format=ROW(生产环境主流配置)时,一个长事务中每行变更都会被记录为独立的 binlog event。例如:主库执行一条 UPDATE t1 SET status=1 WHERE create_time 影响 500 万行,binlog 就会生成约 500 万个 <code>Write_rows_log_event。从库 SQL 线程要逐条解析、定位、更新,期间还可能触发大量二级索引维护、唯一键检查、外键约束校验——CPU 和 IO 压力陡增。
事务内锁等待在从库被“复现”
主库长事务中若涉及 SELECT ... FOR UPDATE 或隐式锁(如唯一索引冲突),这些锁在从库回放时也会被重建。但注意:从库没有事务并发上下文,所有锁都由单个 SQL 线程串行申请。一旦遇到行锁冲突(比如另一条已回放的事务还没提交、或从库自身有慢查询持有 MDL 锁),SQL 线程就会卡在 Waiting for table metadata lock 或 Waiting for row lock 状态,Seconds_Behind_Master 可能显示为 0(因事务未提交,时间戳未更新),但实际 relay log 已堆积数 GB。
GTID 模式下长事务导致 Executed_Gtid_Set 滞后
使用 GTID 复制时,Executed_Gtid_Set 只在事务真正提交后才更新。长事务未提交前,其 GTID 不会进入该集合,下游监控脚本(如对比 Retrieved_Gtid_Set 和 Executed_Gtid_Set)会误判为“只差 1 个事务”,掩盖真实延迟量。更麻烦的是:如果该长事务中途失败(如从库磁盘满),整个 GTID 链会中断,需人工干预跳过,风险极高。
真正棘手的不是“大”,而是“长”——哪怕只改一行,只要事务保持打开超过 10 分钟,就能让从库复制停滞。线上最稳妥的做法,是业务层禁止无超时控制的事务,并在从库开启 innodb_lock_wait_timeout 严格限制锁等待时长,避免无限期挂起。











