改 mysqld.service 比调 mysql 参数更关键,因为 oom killer 依据整个 mysqld 进程 rss 内存触发,而非 innodb_buffer_pool_size;需通过 systemd 的 memorymax 等参数强制限制总内存并配置重启策略。

为什么改 mysqld.service 比调 MySQL 参数更关键
因为 OOM Killer 杀进程看的是整个 mysqld 进程的 RSS 内存,不是 MySQL 自己报告的 innodb_buffer_pool_size。哪怕你把 MySQL 配置压到 384M,JVM、临时表、performance_schema、线程栈加起来照样可能冲到 900M+——而 systemd 默认对这个进程组不设上限,内核一见 RSS 超过 MemAvailable 就直接开刀。
所以第一步不是改 my.cnf,而是让 systemd 明确知道:“这个服务最多只准吃这么多内存”,逼它在触线前主动限流或重启,而不是等内核拔刀。
在 mysqld.service.d/oom.conf 中加这三行
不要动默认 service 文件,用 drop-in 方式覆盖:
[Service] MemoryMax=650M OOMScoreAdjust=-500 Restart=on-failure RestartSec=10
MemoryMax=650M 是硬边界:systemd 会通过 cgroups-v2 强制限制该服务所有子进程总 RSS 不得超过此值;超限后进程会被 SIGKILL(比 OOM Killer 更可控)。
OOMScoreAdjust=-500 是保险丝:即使 MemoryMax 失效(比如 cgroups-v1 环境),也能大幅降低 mysqld 被内核 OOM Killer 选中的概率。
Restart=on-failure 很重要:MySQL 因内存超限被 kill 后,systemd 会自动拉起,避免服务静默中断;但注意别配成 always,否则配置错误导致反复崩溃会陷入循环。
别踩这些 systemd 内存参数坑
-
MemoryLimit已废弃,只在旧版 systemd(MemoryMax -
MemoryLow=128M对 MySQL 效果有限:它只提示内核“可回收”,但 mysqld 的 buffer pool 是 mmap 锁住的,内核没法轻易回收 - 不要同时设
MemoryMax和LimitMEMLOCK:后者限制 mlock() 锁内存页,和前者逻辑冲突,可能导致启动失败 - 如果用了
tmpfs做临时目录(如/tmp/mysql_tmp),记得在MemoryMax里预留空间——tmpfs 占用算进进程 RSS
验证配置是否生效
改完必须 reload 并检查:
sudo systemctl daemon-reload && sudo systemctl restart mysqld
确认 cgroups 限制已加载:
cat /sys/fs/cgroup/system.slice/mysqld.service/memory.max → 应输出 681574400(650M 字节)
确认 OOM 优先级已写入:
cat /proc/$(pgrep mysqld)/oom_score_adj → 应为 -500
最后压测一把:跑一条大 GROUP BY 查询,观察 systemctl status mysqld 是否出现 “Killed” 或 “OOM” 提示——如果看到的是 “memory.max exceeded”,说明 systemd 拦住了,没轮到内核出手。
真正容易被忽略的是:systemd 的 MemoryMax 限制对子进程(比如 mysqlbackup、自定义脚本调用的 mysqldump)同样生效。如果备份时爆内存,得单独给备份服务建一个 mysqld-backup.service 并配独立 memory limit。











