mysql内存oom主因是innodb_buffer_pool_size超可用内存且未预留os/agent/脚本空间,叠加performance_schema、连接线程等隐性消耗;需以memavailable为准校准buffer pool,并监控innodb_buffer_pool_wait_free及命中率。

直接调大 innodb_buffer_pool_size 是最常见、也最容易翻车的 OOM 根源——它不是“按需分配”,而是 MySQL 启动时就向系统硬占一块内存,不释放、不收缩,哪怕你只用了 10%。
怎么看 MySQL 真实吃了多少内存,而不是被 free -h 骗了
Linux 的 free -h 输出里 used 包含大量可回收的 buff/cache,不能当真。真正要盯的是:
-
available列:这是系统当前能分给新进程的内存,必须 >innodb_buffer_pool_size+ 其他服务预留(至少留 2GB 给 OS、宝塔面板、agent) -
mysqld进程的RES(常驻内存):用ps aux --sort=-%mem | head -n 10查,如果 RES 接近总物理内存且available持续低于 1GB,基本就是 buffer pool 抢过头了 - 确认 InnoDB 实际分配量:
mysql -e "SELECT @@innodb_buffer_pool_size/1024/1024/1024 AS gb;",再和available对比
为什么改了 my.cnf 的 innodb_buffer_pool_size 还是启动失败
MySQL 8.0 对这个值有硬性校验,不是随便填个数字就能生效:
- 必须是
innodb_buffer_pool_chunk_size×innodb_buffer_pool_instances的整数倍(默认 chunk=128MB,instances=8 → 最小合法值是 1GB) - 如果原值是 2GB,想改成 1.5GB,MySQL 会报错
Invalid value for innodb_buffer_pool_size,必须同步调整instances或chunk_size - 改完后不删旧日志文件会卡住启动:
rm -f /www/server/data/ib_logfile0 /www/server/data/ib_logfile1(宝塔路径)
哪些配置关掉能立刻省下几百 MB 到 2GB 内存
单机环境(无主从、无审计需求)下,这些开关不关,就是在给 OOM 加速:
-
skip-log-bin:加在[mysqld]段,不是log_bin=OFF;关掉后删光/www/server/data/mysql-bin.* -
skip-performance-schema:MySQL 8.0 默认开启,实测空载占用 300–800MB,关掉后内存回落立竿见影 -
skip-audit-plugin:宝塔默认不装,但若手动启用过,务必关掉 -
tmp_table_size和max_heap_table_size:别设太高(如 512MB),否则一个大GROUP BY就可能在内存里撑出几百 MB 临时表
连接数暴增时,per-connection 缓冲区才是隐形杀手
每个连接独占 sort_buffer_size、join_buffer_size、read_rnd_buffer_size,默认加起来约 1.5–2MB。如果 max_connections=500,理论峰值就是 1GB;而实际中 Threads_connected 长期 >300 且大量 Sleep,说明应用没复用连接,缓冲区全在空转。
- 查真实并发:
SHOW STATUS LIKE 'Threads_connected'; - 调低 per-connection 参数(比如
sort_buffer_size=512K),比盲目调大buffer_pool更有效 - 监控关键指标:
Innodb_buffer_pool_wait_free> 0 表示缓冲池太小,频繁等待刷脏页;但available不足时,优先降 buffer pool 而不是升它
最常被忽略的一点:innodb_buffer_pool_size 是静态预分配的,而 performance_schema 和 binlog 是运行时动态增长的。前者你改完重启就定死了,后两者会在业务高峰悄悄吃掉最后一点 available——所以调参前,先看 dmesg -T | grep -i "killed process" 确认是不是 mysqld 被杀,再查 free -h 的 available 值,别一上来就改配置文件。











