必须先终止trx_state='RUNNING'且trx_query IS NULL的最老事务,否则所有调参和清理均无效;需用INNODB_TRX按trx_started排序定位,关联PROCESSLIST确认异常连接,再分状态KILL,最后加大innodb_purge_batch_size并启用undo截断。

怎么确认undo真被卡住了
别先看磁盘空间或df -h,先查数据库内部是否已积压:运行SHOW ENGINE INNODB STATUS\G,找到HISTORY LIST LENGTH行——超过10万基本就是purge被阻塞。再执行:SELECT trx_id, trx_started, trx_state, trx_mysql_thread_id, TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) AS duration_sec, trx_query FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 60;
重点盯住duration_sec高、trx_state = 'RUNNING'且trx_query IS NULL的记录,这才是真正钉死purge的“空闲未提交”事务。
kill前必须分状态操作,乱杀会更糟
KILL不是万能解药,错杀会让实例雪上加霜:
-
trx_state = 'RUNNING'且trx_query IS NULL:大概率是应用忘了COMMIT或ROLLBACK,可安全KILL -
trx_state = 'LOCK WAIT':它正被别的事务堵着,先查INNODB_LOCK_WAITS找出blocking_trx_id,优先干掉上游事务 -
trx_state = 'ROLLING BACK':别动,此时KILL只会拉长回滚时间、加重IO压力,甚至拖垮purge线程 - 若
trx_rows_modified > 100000,回滚可能持续数分钟,KILL前务必评估业务影响
清理后undo文件不缩容?这是InnoDB的正常行为
即使杀光所有长事务,ibdata1或undo_001也不会自动收缩——InnoDB不支持online shrink undo表空间:
- 用共享表空间(
innodb_undo_tablespaces = 0):唯一办法是mysqldump全库重建,停机时间长、风险高 - 用独立undo表空间(
innodb_undo_tablespaces > 0):先确保HISTORY LIST LENGTH已降到很低,再执行SET GLOBAL innodb_undo_log_truncate = ON; - 验证是否生效:
SHOW VARIABLES LIKE 'innodb_undo_log_truncate';和SHOW STATUS LIKE 'Innodb_undo_log_truncated';
MySQL 5.7启用undo截断要改配置+两次重启
想让undo_001真正释放空间,不能只靠SET GLOBAL:
- 在
my.cnf中设innodb_undo_tablespaces = 3(必须≥3)、innodb_max_undo_log_size = 1G、innodb_undo_log_truncate = ON - 第一次重启:MySQL新建
undo_002/undo_003,老undo_001被标记为inactive - 等purge清空后(观察error log里
Truncate undo tablespace 1 completed),再第二次重启:MySQL自动删除空的undo_001 - 全程必须先杀掉所有
trx_state = 'RUNNING'且trx_query IS NULL的事务,否则purge无法推进
trx_query IS NULL却挂着不动的连接——它像一根刺,扎在最老read view上,让所有后续undo都动不了。参数调得再激进,也绕不开这根刺。


















