大事务会让从库卡住是因为主库提交的耗时大事务(如trx_rows_modified超1万、trx_started早于当前30秒)在row格式binlog下被完整重放,导致从库sql线程长时间阻塞;需按时间/主键分片、游标分页、禁用offset、使用pt-online-schema-change等工具拆分,并统一“大事务”定义以实现有效监控。

大事务为什么会让从库卡住?
主库提交一个耗时几十秒的大事务,比如 DELETE FROM orders WHERE created_at ,Binlog 里会记录成一条完整语句(STATEMENT)或上万行变更(ROW)。从库的 <code>SQL Thread 必须串行执行完所有变更才能推进位点——这期间 Seconds_Behind_Master 会直线飙升,且无法被并行复制加速,因为整个事务被当作一个原子单元处理。
怎么识别主库上的大事务?
别等从库报警才查。直接在主库执行:
SELECT trx_id, trx_started, trx_state, trx_rows_modified,
SUBSTRING(trx_query, 1, 50) AS short_query
FROM information_schema.INNODB_TRX
WHERE trx_rows_modified > 10000
ORDER BY trx_started LIMIT 5;
重点关注 trx_rows_modified 超过 1 万、trx_started 时间早于当前 30 秒以上的事务。这类事务大概率是定时清理、批量导入或未分页的全表更新。
- 如果
trx_query是空,说明是隐式事务(比如 autocommit=0 下手动 BEGIN 后没 COMMIT) - 用
SHOW ENGINE INNODB STATUS\G查看 “TRANSACTIONS” 部分,确认是否锁等待拖长了事务生命周期
拆分大事务的实操要点
不是简单加个 LIMIT 就行,得保证语义正确、不漏不重、不影响线上业务:
- 用时间/自增主键分片:比如按
id BETWEEN ? AND ?或created_at BETWEEN ? AND ?拆成每批 1000~5000 行,每次提交后SLEEP(0.1)降低主库压力 - 避免使用
OFFSET分页:它会随删除推进越来越慢,改用游标式分页(WHERE id > last_id ORDER BY id LIMIT 1000) - DDL 大操作必须用
pt-online-schema-change或gh-ost,原生ALTER TABLE在 5.7+ 仍会阻塞写入并生成巨型 Binlog 事件 - 确认 binlog_format 是
ROW:STATEMENT 模式下,即使拆分,函数、临时表等仍可能被重放为单条语句,失去拆分意义
拆分后还要防“伪大事务”
有些代码看似小事务,但循环里反复 INSERT/UPDATE 不提交,实际凑成了隐式大事务。检查应用层:
- ORM 批量操作(如 Django 的
bulk_create、MyBatis 的foreach)默认不自动分批次,需显式指定batch_size=1000 - ETL 工具(DataX、Flink CDC)配置中
batchSize和maxFetchSize必须对齐,否则一次拉 10 万行再逐条发 INSERT,Binlog 日志体积翻倍 - 禁用
autocommit=0的长连接:这类连接容易被遗忘,变成“永远不提交”的悬挂事务
真正难的不是拆,而是让开发和 DBA 对齐“什么算大事务”——比如 500 行 UPDATE 算不算?取决于单行平均大小和从库磁盘 IOPS。没有统一标准,监控和告警就形同虚设。











