MySQL 8.0内存飙升主因是innodb_buffer_pool_size对齐放大、performance_schema预占内存及TEMPTABLE引擎默认1MB内存限制三者叠加;需同步调整chunk_size/instances、关闭非必要PS表、设temptable_max_ram并重启,单改一参数无效。

MySQL 8.0 内存持续飙升,95% 以上不是泄漏,而是 innodb_buffer_pool_size 自动对齐放大 + performance_schema 预占内存 + 临时表引擎切换三者叠加导致的——改错一个参数,其他全白调。
innodb_buffer_pool_size 实际分配远超配置值
SHOW VARIABLES 显示 innodb_buffer_pool_size 是 4G,但 SHOW ENGINE INNODB STATUS\G 里 “Buffer pool size” 显示 5242880 × 16KB ≈ 80GB,这是典型对齐放大。MySQL 8.0 启动时会按 innodb_buffer_pool_chunk_size × innodb_buffer_pool_instances 向上取整,而默认 chunk 是 128MB、instances 是 8 → 最小合法单位是 1G。如果你设了 1.5G,它会自动跳到 2G;设了 3.8G,实际分到 4G;设了 40G,可能因对齐变成 48G 或 64G。
容易踩的坑:
- 只改
innodb_buffer_pool_size,不配innodb_buffer_pool_chunk_size和innodb_buffer_pool_instances,启动日志会报 warning:“Buffer pool size rounded up to …” - 改完没删旧的
ib_logfile0和ib_logfile1,MySQL 启动卡死在InnoDB: Error: log file ib_logfile0 is of different size - 误以为
innodb_dedicated_server=ON很智能,其实它在混部环境(比如宝塔+PHP+Nginx)下会把 buffer pool 推到物理内存的 75%,直接挤爆系统
performance_schema 启动即吃掉 300–500MB
MySQL 8.0 默认全开 performance_schema,其中 events_statements_history_long 和 events_transactions_history_long 这两个表一启动就各自预分配 128MB+,不查也占着。它们属于“静态内存池”,不会随连接数增长,但也不会归还给 OS。
验证方式:
- 运行
SELECT * FROM performance_schema.memory_summary_global_by_event_name WHERE event_name LIKE 'memory/performance_schema/%' ORDER BY SUM_ALLOCATED DESC LIMIT 5;,看前几行是不是都带history_long - 如果
sys.memory_global_total里performance_schema占比超 40%,基本就是它
实操建议:
- 关掉非必要 consumer:
UPDATE performance_schema.setup_consumers SET ENABLED = 'NO' WHERE NAME IN ('events_statements_history_long', 'events_transactions_history_long'); - 限制大小(写进 my.cnf):
performance_schema_events_statements_history_long_size = 10000(默认 20000) - 确认 MySQL 真读的是哪个配置文件:用
ps aux | grep mysqld | grep -o '\-\-defaults-file=[^ ]*'查,宝塔用户常改/www/server/mysql/my.cnf,但 MySQL 默认只认/etc/my.cnf
TEMPTABLE 引擎让 ibtmp1 持续暴涨
升级后 ibtmp1 文件不断变大,lsof +D /tmp 却查不到对应文件,SHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables' 飙升但 Created_tmp_tables 更猛——说明大量临时表在内存阶段就失败,直接落盘到 InnoDB 临时表空间,根本没走 /tmp。
根因是 MySQL 8.0.16+ 默认启用 internal_tmp_mem_storage_engine = TEMPTABLE,它不再受 tmp_table_size 控制,而是独占一个内存池:temptable_max_ram。该值默认只有 1MB,超限就直写 ibtmp1。
关键限制:
-
temptable_max_ram不支持SET GLOBAL,必须写进配置文件并重启 - 不能只调
tmp_table_size,那个对 TEMPTABLE 引擎完全无效 - 必须同步设好
tmp_table_size和max_heap_table_size,且二者必须严格相等(比如都为268435456),否则老式 MEMORY fallback 场景仍会出问题
buffer pool 预热和 binlog 日志堆积加剧内存压力
MySQL 8.0 默认开启 innodb_buffer_pool_dump_at_shutdown 和 innodb_buffer_pool_load_at_startup。如果上次 shutdown 前 buffer pool 是满的,启动时会快速加载 dump 文件(如 ib_buffer_pool),表现为 RSS 在几秒内陡增 —— 这不是泄漏,是预热行为,但容易被 OOM Killer 误杀。
binlog 也是隐形推手:即使没主从,log_bin=ON 也会让每个事务多一次写入缓冲、多一次 fsync,间接抬高内存水位。宝塔单机环境几乎不需要它。
安全操作清单:
- 关预热:
SET GLOBAL innodb_buffer_pool_load_at_startup = OFF;,并在 my.cnf 中加innodb_buffer_pool_dump_at_shutdown = OFF - 关 binlog:
skip-log-bin(注意不是log_bin=OFF),删掉所有mysql-bin.*文件 - 重启前执行:
SET GLOBAL innodb_fast_shutdown = 0;,再SHUTDOWN;,确保旧ibtmp1能被安全删除
真正难处理的,是多个参数之间隐含的依赖关系:比如调小 innodb_buffer_pool_size 后,如果 max_connections 还很高,会话缓冲区总占用可能反超;又比如关了 performance_schema,但忘了关 audit_log 插件,照样吃内存。调参不是单点动作,得看组合效应。











