摘要: 每个运维工程师都经历过这样的时刻:凌晨被告警电话叫醒,核心数据库进程"消失"了,业务全线瘫痪。这篇文章不是教科书,是我这些年处理 OOM 故障的实战手册——从发现问题的第一秒,到彻底解决,每一步都经过实战检验。
凌晨4点17分,手机震动。
打开监控一看,MySQL 进程"没了"。
不是挂了,是"消失"了——进程列表里找不到,端口也监听不到。
那一刻,我相信每个运维人都懂——心跳加速,手心冒汗,脑子里闪过无数个"完了完了"。
但别慌。
这些年,我处理过不下五十次类似的故障。今天把这套 OOM 应急处置流程分享给你,不是教科书那种理论,是真正能救命的实战经验。
01 第一步:确认是不是 OOM Killer 干的
很多人一看到进程"消失",第一反应是"程序崩了"或者"被黑客攻击了"。
先别急着下结论。
Linux 系统有个机制叫 OOM Killer(Out Of Memory Killer),当系统内存耗尽时,内核会自动选择占用内存最多的进程"杀掉",以保护系统不崩溃。
怎么确认是不是 OOM Killer 干的?
方法1:查看内核日志
dmesg | grep -i "oom\|killed process"
如果你看到类似这样的输出:
[12345.678] Out of memory: Kill process 12345 (mysqld) score 900 or sacrifice child[12345.679] Killed process 12345 (mysqld) total-vm:8192000kB, anon-rss:7340032kB
实锤了,就是 OOM Killer 干的。
方法2:查看系统日志
grep -i "oom\|killed process" /var/log/messages# 或者grep -i "oom\|killed process" /var/log/syslog
方法3:查看 journalctl(systemd 系统)
journalctl -k | grep -i "oom\|killed process"
记住:先确认问题,再解决问题。
02 第二步:紧急恢复服务
确认是 OOM 后,第一件事不是找原因,而是恢复服务。
业务不能等,用户不能等。
1. 重启被杀的进程
systemctl restart mysqld# 或者service mysql restart
2. 检查服务状态
systemctl status mysqld
3. 验证业务恢复
# 检查端口是否监听netstat -tlnp | grep 3306# 检查进程是否存在ps aux | grep mysqld# 测试连接mysql -u root -p -e "SELECT 1"
如果服务起不来,别死磕,先看看日志:
tail -f /var/log/mysqld.log
记住:紧急情况下,先恢复,再排查。
03 第三步:分析内存使用情况
服务恢复后,别松口气,问题还没解决。
现在要搞清楚:为什么会 OOM?
1. 查看当前内存使用情况
free -h
输出示例:
total used free shared buff/cache availableMem: 15Gi 8.2Gi 1.2Gi 234Mi 6.1Gi 6.5GiSwap: 2.0Gi 2.0Gi 0B
重点看三个指标:
- buff/cache:被内核用于缓冲和缓存的内存(这部分可以回收)
- available:应用程序实际可用的内存(这才是关键)
如果 available 很低(比如低于 500MB),说明内存确实紧张。
2. 查看 Swap 使用情况
swapon --show
如果 Swap 使用率接近 100%,说明物理内存已经不够用了,系统在频繁使用虚拟内存。
Swap 不是万能的。 当系统频繁在物理内存和 Swap 之间倒腾数据时,性能会急剧下降,这叫"抖动"(thrashing)。
3. 查看内存占用前10的进程
ps aux --sort=-%mem | head -11
输出示例:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMANDmysql 12345 5.2 45.3 8192000 7340032 ? Sl 03:15 12:34 /usr/sbin/mysqldjava 23456 12.5 23.1 4096000 3670016 ? Sl 03:20 8:45 java -jar app.jarroot 34567 2.1 8.7 2048000 1409024 ? Ss 03:10 5:23 /usr/bin/dockerd
重点看 %MEM 列和 RSS 列:
- RSS:进程实际使用的物理内存(Resident Set Size)
找到那个占用内存最多的进程,它就是"罪魁祸首"。
04 第四步:深入分析根因
找到内存占用最高的进程后,要搞清楚:它为什么占用这么多内存?
情况1:内存泄漏
如果进程的内存占用在持续增长,从来不下降,很可能是内存泄漏。
怎么确认?
# 每隔10秒查看一次进程的内存占用watch -n 10 "ps -p 12345 -o pid,rss,vsz,comm"
如果 RSS 值在持续增加,说明有内存泄漏。
对于 Java 应用,可以抓取堆转储:
jmap -dump:format=b,file=heap.hprof 12345
然后用 MAT(Memory Analyzer Tool)或者 JProfiler 分析堆转储文件,找出哪些对象没有被释放。
情况2:配置不当
有时候不是程序有问题,而是配置不合理。
比如 MySQL:
# 查看 MySQL 的内存相关配置mysql -u root -p -e "SHOW VARIABLES LIKE '%buffer%';"
如果 innodb_buffer_pool_size 设置得太大(比如超过物理内存的 70%),就可能导致 OOM。
合理的配置:
innodb_buffer_pool_size:物理内存的 50%-70%max_connections:根据实际需求设置,不要盲目设大tmp_table_size 和 max_heap_table_size:不要设置过大
情况3:突发流量
有时候内存突然飙升,是因为突发流量。
比如:
怎么排查?
# 查看 MySQL 的慢查询日志tail -f /var/log/mysql/slow.log# 查看当前正在执行的查询mysql -u root -p -e "SHOW PROCESSLIST;"
05 第五步:制定解决方案
根据根因分析的结果,制定对应的解决方案。
方案1:修复内存泄漏
如果是代码问题导致的内存泄漏:
方案2:调整配置
如果是配置不当:
# 编辑 MySQL 配置文件vim /etc/my.cnf# 调整 buffer pool 大小innodb_buffer_pool_size = 4G # 假设物理内存是 8G# 重启 MySQL 生效systemctl restart mysqld
方案3:优化查询
如果是慢查询导致的内存飙升:
-- 查看慢查询SHOWGLOBAL STATUS LIKE'Slow_queries';-- 开启慢查询日志SETGLOBAL slow_query_log ='ON';SETGLOBAL long_query_time =1; -- 超过1秒的查询记录为慢查询
然后找开发优化 SQL,加索引,避免全表扫描。
方案4:扩容
如果业务确实在增长,服务器扛不住了:
记住:能用钱解决的问题,都不是问题。关键是你要能证明"确实不够用了",而不是"配置太烂了"。
06 第六步:建立防线,预防再次发生
故障处理完了,但工作还没结束。
1. 设置内存告警
别等 OOM 了才发现问题。设置多级告警:
2. 配置 OOM Score
Linux 允许你为每个进程设置 OOM Score,告诉 OOM Killer 在内存紧张时优先杀哪些进程。
# 查看进程的 OOM Scorecat /proc/12345/oom_score# 调整 OOM Score(-1000 到 1000)echo -1000 > /proc/12345/oom_score_adj # 降低被杀的概率
关键进程(如数据库)设置负值,非关键进程设置正值。
3. 定期巡检
每天看一眼这些命令的输出:
# 查看内存使用情况free -h# 查看内存占用前10的进程ps aux --sort=-%mem | head -11# 查看 Swap 使用情况swapon --show# 查看内核日志中的 OOM 记录dmesg | grep -i "oom\|killed process"
4. 建立故障知识库
每次故障都记录下来:
我的经验是:同一个问题,第二次出现时,处理时间能缩短80%。
写在最后
写这篇文章的时候,我又想起了那些凌晨4点被告警电话叫醒的夜晚。
说实话,运维这个工作,很多时候是在"救火"。但每一次救火,都是一次学习的机会。
从手忙脚乱到从容应对,从被动救火到主动预防——这就是成长。
OOM 不可怕,可怕的是不知道为什么会 OOM,不知道怎么预防。
如果你现在正面临 OOM 的问题,别慌,按这篇文章的步骤一步步来。
如果你还没遇到问题,那更好,把这些知识存起来,早晚用得上。
最后送大家一句话:
最好的运维,不是能多快解决故障,而是让故障不发生。
共勉。
你遇到过最棘手的 OOM 故障是什么?是怎么解决的?欢迎留言分享你的实战经验。