oom杀掉mysqld需用dmesg -t | grep "killed process.*mysqld"确认,再结合free -h的available列(≤200mb极危险)和/proc/$(pgrep mysqld)/status中的vmrss验证;innodb_buffer_pool_size减小必须重启生效,且须满足热数据×1.2下限、os预留≥1.8gb上限、chunk_size×instances整数倍约束。

从库OOM和主库配置完全无关,问题一定出在从库本地内存分配失控——innodb_buffer_pool_size设高了、没给OS留够余量、SQL线程重放又触发额外内存开销,三者叠加直接撑爆MemAvailable。
怎么确认真是OOM杀掉了mysqld
别猜,看证据:
- 执行
dmesg -T | grep -i "killed process.*mysqld",有输出且时间戳匹配MySQL崩溃时刻,就是OOM实锤 - 再跑
free -h,重点盯available列——长期低于500MB就已危险;稳定 ≤200MB 基本就是SQL线程卡死前夜 -
cat /proc/$(pgrep mysqld)/status | grep VmRSS查真实驻留内存,比SHOW VARIABLES LIKE 'innodb_buffer_pool_size'更准
为什么调小innodb_buffer_pool_size必须重启才生效
MySQL 5.7+虽支持 SET GLOBAL innodb_buffer_pool_size 动态调整,但只允许“增大”且需满足整数倍约束;而OOM场景下几乎全是需要“减小”,该操作不被允许:
- 填个
600M进去,MySQL 8.0 会静默向下取整成512M(因默认innodb_buffer_pool_chunk_size=128M×instances=4) - 真正生效值必须是
chunk_size × instances的整数倍,常见安全值只有:512M、640M、768M、896M… - 改完
/etc/my.cnf后必须sudo systemctl restart mysqld,否则配置不落地
调小buffer pool要守住哪三个硬约束
不是随便砍掉20%就行,得同时满足:
- 下限:大于热数据总量 × 1.2 —— 用
SELECT SUM(data_length + index_length) FROM information_schema.tables WHERE engine='InnoDB' AND table_schema NOT IN ('mysql','performance_schema','information_schema');算出后乘1.2 - 上限:给OS留足 ≥1.8GB —— 以
cat /proc/meminfo | grep MemAvailable为准,减去2–4GB才是安全边界 - 倍数:必须是
innodb_buffer_pool_chunk_size × innodb_buffer_pool_instances的整数倍;云上2GB从库推荐配innodb_buffer_pool_size=768M、innodb_buffer_pool_instances=6
容易被忽略的隐性内存杀手
光压 innodb_buffer_pool_size 不够,这些配角常在后台悄悄吃掉1–2GB:
-
performance_schema开着且启用了memory/% = COUNTED,重放带触发器的表时,memory/sql/sp_head::main_mem_root可能单次飙升几GB -
table_open_cache_instances过大(如设成16),配合大量表数量,会显著抬高数据字典内存占用 - SQL线程虽是单连接,但重放大事务时会浮动使用
sort_buffer_size、join_buffer_size,这些是“每事务”而非“每连接”分配 - 监控 agent、备份脚本、同机 Redis 或 Java 进程,它们不走 MySQL 内存统计,但共享物理内存
真正麻烦的是变量叠加:热数据每天涨一点、OS page cache波动、agent版本升级多占几百MB——这些细微变化不会立刻报错,但会让原本刚好的配置,在某次大事务重放时突然越过临界点。调参后务必盯三天 free -h 的 available 走势,而不是只看重启那一刻是否回升。











