“undo log record is too big”错误源于单条undo记录超1mb硬限制,非事务大小所致,主因是update/insert超宽字段或触发器内联生成大数据,须定位并拆分问题sql而非杀事务或调参。

“undo log record is too big”不是事务太大导致的,而是事务在执行过程中生成了单条超长 undo 记录——通常是因为某次 UPDATE/INSERT 修改了超宽字段(如超长 JSON、TEXT、BLOB),或触发器/函数内联写了大量数据。必须先定位并拆分这类语句,而不是直接 kill 事务或调参数。
为什么会出现 “undo log record is too big” 错误
这个错误由 InnoDB 内核抛出,触发条件是单条 undo log record 超过 UNDO_LOG_RECORD_MAX_SIZE(内部硬限制,约 1MB)。它和事务总大小、运行时长无关,只和**单次修改涉及的数据宽度**有关:
- UPDATE 一行,其中某个
TEXT字段从空更新为 2MB JSON —— 会写入一条 2MB 的 undo 记录,直接触发错误 - INSERT 时用
CONCAT(REPEAT('x', 1000000), ...)拼接超长值,undo 需存原始值快照 - 触发器里隐式拼接、加密、序列化等操作,放大了实际写入宽度
- MySQL 8.0+ 对大字段 undo 做了更严格校验,5.7 可能静默截断,8.0 直接报错
如何快速定位触发该错误的具体 SQL
错误日志里一般只含事务 ID 和错误码,需结合 INNODB_TRX 和 PROCESSLIST 追溯:
- 查当前最可疑事务:
SELECT trx_id, trx_mysql_thread_id, trx_started, trx_state, trx_query FROM information_schema.INNODB_TRX WHERE trx_state = 'RUNNING' ORDER BY trx_started DESC LIMIT 1; - 用
trx_mysql_thread_id关联:SELECT ID, USER, HOST, COMMAND, INFO FROM information_schema.PROCESSLIST WHERE ID = ?;——INFO字段常含完整语句(若未被截断) - 重点检查
INFO中是否含UPDATE ... SET blob_col =、INSERT ... VALUES (...)后跟超长字符串或函数调用 - 若
INFO为空,说明连接已空闲但事务未提交,此时需查应用日志或监控链路追踪(如 OpenTelemetry 的 span 名)
怎么安全修复而不中断业务
不能直接 KILL,尤其当事务已部分执行、且涉及关键业务逻辑时。应优先改写语句结构:
- 把单次超大更新拆成多批次:例如
UPDATE t SET json_col = ? WHERE id BETWEEN 1 AND 1000→ 改为循环 100 行一批 - 避免在 SQL 层拼接超长内容:把
CONCAT(...)移到应用层生成,再以参数传入 - 对大字段单独处理:先
UPDATE t SET flag = 1 WHERE id = ?,再另起事务异步填充json_col - 确认触发器是否冗余:禁用后重试,看错误是否消失;若必须保留,改用存储过程分步写入
- 临时绕过(仅调试):
SET SESSION innodb_strict_mode = OFF可让部分场景降级为 warning,但不解决根本问题,且 8.0 默认强制开启
后续必须检查的配置与监控点
该错误暴露的是应用层数据模型或 SQL 写法风险,参数调优无效,但以下配置可辅助预防:
- 启用
innodb_rollback_on_timeout = ON:防止事务卡住后持续占用 undo 空间 - 设置
max_allowed_packet合理上限(如 64M),避免客户端发超长语句被服务端截断后引发不一致 - 监控
INFORMATION_SCHEMA.INNODB_METRICS中的innodb_undo_log_written和innodb_undo_truncations,突增即预警 - 在应用 ORM 层加字段长度校验(如 MyBatis 的
@Size(max=1048576)),拦截 >1MB 的 JSON/BLOB 写入
真正麻烦的不是这行报错本身,而是它背后藏着一个没做分片的大字段更新逻辑——这类语句在线上跑一次,可能同时拖垮 purge、撑爆磁盘、卡死 MVCC 快照。别只盯着日志删错误,得顺藤摸到那条 SQL,把它切成小块再放回去。











