这类问题我又碰上了一次。十六 G 内存的机器上跑着 PHP-FPM 和 MySQL,内核日志里 oom-kill 挑中的是 RSS 只有两百兆的 php-fpm 子进程,MySQL 一点事没有,业务每次都要重启进程池才恢复,free 显示当时还有一 G 可用。
第一反应是「RSS 小不代表占得少,共享内存和页表没算在 RSS 里」,验证下来不成立。
真正麻烦的地方在于,几个原因的现象几乎一样。这个进程的 oom_score_adj 被设成了 1000,内核会优先挑它;systemd 的 OOMScoreAdjust 写在 unit 里,重启之后一直生效;内存是慢慢耗尽的,被杀那一刻可用内存已经被压得很低。
上来先确认被杀的是谁、当时还有多少内存
grep -i 'oom-kill' /var/log/messages | tail -5
接着看进程当前的分数和调整值
cat /proc/$(pgrep -f php-fpm | head -1)/oom_score_adj
然后把关键服务的保护值调低
systemctl set-property php-fpm.service OOMScoreAdjust=-500
给数据库单独加一层保护
echo -900 > /proc/$(pgrep -x mysqld)/oom_score_adj
恢复的判定其实很直接。再压一次时 oom-kill 挑中的是压测进程本身,关键服务没有被杀,oom_score_adj 的值重启之后仍然保持。
这里还有个坑。score 的范围是负一千到一千,负值只降低被选中的概率,不能保证不被杀;真正的解法是给关键服务设 MemoryMin 或者把内存算够,靠调分数兜底迟早翻车。监控比日志更早给出信号,事后回看经常能提前半小时发现异常。
