错误日志中“over 4/5 of the buffer pool is occupied by lock heaps”表明innodb_buffer_pool_size严重不足,超80%缓冲池被锁结构和自适应哈希索引占用,导致数据页缓存空间匮乏,引发查询变慢、连接堆积、SHOW ENGINE INNODB STATUS卡顿等问题。

这不是锁“太多”,而是锁结构没地方存了——innodb_buffer_pool_size太小,导致 InnoDB 没法为锁分配足够内存。
锁结构内存来自 buffer pool,不是独立内存池
InnoDB 的锁信息(行锁、间隙锁、意向锁等)不单独划内存,而是从 innodb_buffer_pool_size 里动态分配。每个锁结构约占用 100–200 字节,但背后依赖 buffer pool 的页管理机制:锁元数据被当作“非数据页”存在 buffer pool 中。当事务锁住大量行(比如全表 UPDATE 或无索引 SELECT ... FOR UPDATE),锁结构持续增长,buffer pool 中的 misc 类页(非数据页)占比就会飙升。
一旦 misc 页使用率超过 buffer pool 总量的 75%,InnoDB 就拒绝再分配新锁结构,直接报 ERROR 1206 (HY000)。
- 查当前 buffer pool 使用情况:
SHOW ENGINE INNODB STATUS\G,搜Buffer pool段,看misc行占比 - 查锁相关内存消耗:
SELECT * FROM information_schema.INNODB_TRX WHERE trx_rows_locked > 10000 - 默认
innodb_buffer_pool_size = 8M时,misc很快就顶到 75% 上限,哪怕物理内存有 16G 也照样崩
为什么调大 innodb_lock_table_size 却没用?
innodb_lock_table_size 看似是“锁表大小”,实则是锁哈希桶数量上限,单位是槽位(slot),不是字节。但它能生效的前提是:底层有足够 buffer pool 内存来承载这些锁结构。如果 innodb_buffer_pool_size 只有 128M,就算把 innodb_lock_table_size 设成 5000000,InnoDB 启动时也会静默截断——因为硬限制是 innodb_buffer_pool_size / 16。
解析 MySQL 8.0 的 DDL 脚本,并将表结构导出为 Excel 格式。适用于用户需将 MySQL 的 `CREATE TABLE` 语句转换为结构化的 Excel 文件场景,导出内容包含字段名、数据类型、长度及精度等信息。支持多张表的批量处理,每张表对应 Excel 中的一个独立工作表。适用于数据库文档编写、数据字典生成及模式分析等任务。
- 运行时设值:
SET GLOBAL innodb_lock_table_size = 2000000,之后必须立刻查SELECT @@innodb_lock_table_size确认是否真生效 - 若返回值远小于设定值(如设了 200 万却只显示 10 万),说明
innodb_buffer_pool_size不够,或未重启服务(Windows 下 my.ini 修改必须重启) - 别碰
innodb_rollback_segments或innodb_undo_log_truncate:它们管 Undo,和 ERROR 1206 完全无关
还原 SQL 文件时卡死或 OOM,本质是同一问题
用 mysql -u root db 导入大文件,若其中含未分块的 <code>INSERT INTO ... VALUES (...),(...),... 或单条超长事务,MySQL 服务端会在解析+执行过程中不断申请锁内存。此时 innodb_buffer_pool_size 不足 + 大量脏页 + undo 日志堆积 → buffer pool 几乎全被占满 → 触发 OOM Killer 杀进程,或卡在 Waiting for table metadata lock。
- 导入前先检查:
df -h /tmp(很多 MySQL 临时排序/建表走/tmp,tmpfs 耗尽会伪装成磁盘满) - 改临时路径:
mysql --tmpdir=/data/tmp -u root db - 强制分事务:用
sed -i 's/;$/; COMMIT;/g' dump.sql(慎用)或用mysqldump --skip-extended-insert生成每行一条 INSERT 的文件,再配合mysql --max-allowed-packet=512M
真正该做的三件事
参数只是兜底,业务逻辑不改,调再大也迟早爆。
- 所有
UPDATE/DELETE必须带WHERE且命中索引,否则 InnoDB 升级为表锁,锁数量指数级增长 - 批量操作拆成 1000–5000 行/批,每批后
COMMIT,释放锁+刷脏页 - 避免在事务里做网络请求、文件读写等长耗时操作——锁持有时间越长,其他事务排队越久,锁结构堆积越猛
最容易被忽略的是:buffer pool 不是越大越好,但太小一定出事;而锁结构一旦开始挤占 misc 页,系统反应会越来越慢,直到某次操作突然报错,其实早已在临界点徘徊多时。

















