这种故障特别有迷惑性:半夜网站挂了,ssh上去看,php-fpm或mysqld进程没了,日志里也没有崩溃堆栈,好像人间蒸发。九成的答案是内核OOM Killer干的——内存不够,内核挑个占内存最大的进程杀掉保命。被杀的进程通常正是mysqld,因为它吃内存最多,躺枪专业户。
第一步确认死因。OOM事件内核一定记了日志,用journalctl或者dmesg查关键字。
journalctl -k | grep -i -E "out of memory|oom"
# 或者
dmesg -T | grep -i oom
# 命中会看到类似输出:
# Out of memory: Killed process 1234 (mysqld)
# total-vm:8G, anon-rss:6G
# 关键信息:被杀的进程名、PID、当时吃了多少内存
日志里还会有一张进程内存排行表,按评分高低排序。内核选目标不是看谁当前最大,而是看oom_score,根进程分低,占内存越大的用户进程分越高。mysqld天然高分,所以总是它先死。
第二步找内存去哪了。常见嫌疑:PHP-FPM的pm.max_children乘以单进程内存,超出了机器预算;MySQL的buffer pool加连接缓存配太狠;还有swap没开或被关了,内存一紧连缓冲余地都没有。
# 算PHP-FPM内存预算:
# 单个worker峰值内存(看top里php-fpm的RES列)
# 比如80MB * max_children 100 = 8GB —— 2G的机器直接GG
# 合理算法:留给FPM的内存 / 单进程峰值 = max_children
# 2G内存机器,系统+MySQL占1G,剩1G给PHP
# 1000MB / 80MB ≈ 12,max_children设10~12才安全
# 防mysqld躺枪:降低它的oom_score
echo -1000 > /proc/mysqld进程PID/oom_score_adj
# -1000表示永不被选为OOM目标(代价是极端时整机可能崩)
验证:改完配置压测或等它再出问题,journalctl里不再出现新的OOM记录才算数。同时free -h定期看一眼available还剩多少,长期低于15%就该动手了,别等内核替你动手。
多句嘴:有人图省事给mysqld设了oom_score_adj为-1000,结果极端时内核杀了sshd,人直接ssh不上去,只能靠VNC救援。保护可以有,留一个够用的余量比硬保某个进程更靠谱,内存问题的正解始终是把预算算明白。
A5创业网 版权所有