服务器内存不足OOM killed进程?排查服务器内存不足优化实战
- 2026年7月2日
- 89 次
昨晚12点收到服务器告警,PHP-FPM进程被OOM Killer干掉了,网站直接502。排查发现是Redis缓存没设置上限,疯狂涨内存。这次把完整的排查和优化流程写下来。
\n
\n
什么是OOM Killer?
OOM是"Out Of Memory"的缩写。当Linux服务器内存用完时,内核会启动OOM Killer机制,选择一些进程杀掉释放内存。被杀掉的进程通常是没有"保护"的应用程序(比如PHP-FPM、MySQL、Java等),而系统进程(如init、sshd)一般不会被杀。
现象: 服务器突然变卡,某个进程突然消失,查看系统日志能看到"Out of memory: Kill process"字样。
排查第一步:查看内存状态
# 查看整体内存使用
free -h
# 输出示例:
# total used free shared buff/cache available
# Mem: 3.9G 3.6G 120M 88M 200M 250M
# Swap: 2.0G 1.8G 200M
关注几个指标:
- used:已使用的内存
- buff/cache:系统缓存(可释放)
- available:实际可用的内存(这才是关键)
- Swap:如果used接近total,看Swap用了多少
注意: "available"才是真正的可用内存。buff/cache占的内存虽然算used,但系统需要时可以快速释放。
排查第二步:找出谁吃掉了内存
# 按内存使用排序,查看前10个进程
ps aux --sort=-%mem | head -15
# 输出示例:
# USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
# www 1234 2.5 15.3 2100000 600000 ? Sl 昨 12:34 php-fpm: pool www
# mysql 5678 1.2 8.7 1800000 340000 ? Sl 昨 08:22 mysqld
# redis 9012 0.8 5.2 900000 200000 ? Sl 昨 14:10 redis-server
重点关注 RSS 列(实际占用物理内存),看看哪个进程占得最多。
### 常见内存杀手
1. PHP-FPM:子进程过多或某个PHP脚本内存泄漏
2. MySQL:查询缓存过大或连接数过多
3. Redis:缓存数据过大且没有设置maxmemory
4. Java应用:JVM heap设置过大
5. Nginx:一般不吃内存,但keepalive连接过多会累积
排查第三步:分析具体进程
### PHP-FPM内存分析
# 查看PHP-FPM各进程内存占用
ps aux | grep php-fpm | awk '{printf "%s %.1fMB %s\n", $11, $6/1024, $1}'
# 查看PHP-FPM配置
php -i | grep -E "memory_limit|max_execution_time"
如果某个PHP脚本占用内存异常高(比如超过200M),很可能是有内存泄漏或大数组操作。
排查方法:
- 查看error_log中是否有OOM相关错误
- 用xdebug或blackfire分析脚本内存消耗
### MySQL内存分析
# 查看MySQL当前连接数和内存使用
mysql -e "SHOW PROCESSLIST;"
mysql -e "SHOW STATUS LIKE 'Threads_connected';"
# 查看MySQL配置推荐的内存分配
mysql -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"
# innodb_buffer_pool_size 应该占物理内存的50-70%
如果innodb_buffer_pool_size设得太大,MySQL会吃掉大部分内存。
### Redis内存分析
# 查看Redis内存使用
redis-cli info memory
# 关键字段:
# used_memory_human: 已使用内存(人类可读)
# used_memory_peak_human: 峰值内存
# maxmemory_human: 最大内存限制(0表示无限制)
# mem_fragmentation_ratio: 内存碎片率
如果maxmemory_human显示0,说明Redis没有设置上限,这是最常见的OOM原因。
修复:
# 设置Redis最大内存限制
redis-cli config set maxmemory 512mb
redis-cli config set maxmemory-policy allkeys-lru
allkeys-lru策略表示当内存满时,淘汰最近最少使用的键。这是Redis生产环境的推荐配置。
优化方案
### 方案1:增加Swap分区
如果物理内存不够,加Swap是最经济的方式:
# 创建2G swap文件
dd if=/dev/zero of=/swapfile bs=1M count=2048
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
# 写入fstab确保重启生效
echo '/swapfile none swap sw 0 0' >> /etc/fstab
# 调整swappiness(越小越倾向使用物理内存)
sysctl vm.swappiness=10
建议: swappiness设为10而不是0(0在部分内核有bug),这样系统会优先用物理内存,满了再切swap。
### 方案2:调整PHP-FPM进程数
# 进入PHP-FPM配置文件
nano /etc/php/7.4/fpm/pool.d/www.conf
# 调整以下参数:
pm.max_children = 20 # 最大子进程数
pm.start_servers = 5 # 启动时子进程数
pm.min_spare_servers = 5 # 最小空闲进程数
pm.max_spare_servers = 10 # 最大空闲进程数
# 重启PHP-FPM
/etc/init.d/php-fpm-74 restart
计算公式: max_children = (总内存 - 系统使用 - MySQL使用) / 每个PHP进程内存
一般每个PHP-FPM进程占用30-80M内存。2G服务器建议设10-15,4G服务器设20-30。
### 方案3:调整MySQL innodb_buffer_pool_size
# 查看当前配置
mysql -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"
# 如果是默认值(128M),对于有2G+内存的服务器来说太小
# 对于4G服务器,建议设置为2G(物理内存的50%)
# 修改my.cnf
nano /etc/my.cnf
# 添加或修改:
[mysqld]
innodb_buffer_pool_size = 2G
# 重启MySQL
/etc/init.d/mysqld restart
### 方案4:清理Redis缓存
# 查看Redis大key(帮助定位谁占了最多内存)
redis-cli --bigkeys
# 删除特定key(谨慎操作)
redis-cli del "cache:article:page:*"
# 或者清理所有过期key(不会阻塞)
redis-cli expireall 0
监控和告警
设置监控防止OOM再发生:
#!/bin/bash
# monitor_memory.sh - 内存监控脚本
THRESHOLD=80
# 获取内存使用率(百分比)
used_mem=$(free | awk '/Mem:/ {printf "%.0f", $3/$2 * 100}')
if [ "$used_mem" -gt "$THRESHOLD" ]; then
echo "$(date): 内存使用率 ${used_mem}% 超过阈值 ${THRESHOLD}%" >> /var/log/memory_monitor.log
# 这里可以加邮件/钉钉告警
# 自动清理缓存
sync; echo 3 > /proc/sys/vm/drop_caches
echo "$(date): 已清理缓存" >> /var/log/memory_monitor.log
fi
加到crontab:
crontab -e
# 每5分钟检查一次
*/5 * * * * /path/to/monitor_memory.sh
总结
排查OOM问题的核心思路:
1. free -h 看整体:确认内存确实不够用
2. ps aux 找元凶:哪个进程吃内存最多
3. 针对性优化:调小进程数、设上限、加swap
4. 持续监控:设置告警防止再次发生
最常见的OOM原因:Redis没设maxmemory + PHP-FPM子进程过多 + 物理内存太小。三个原因叠加,服务器不爆才怪。
若非作者标注皆为李涛自留地原创文章,转载或复制请以超链接形式并注明出处。
- 十一月你好 十一我的小棉袄你好 2022-11-06
- WordPress设置伪静态后百度仍然收录带?p链接怎么办? 2022-06-02
- PHP合并数组函数array_combine()与array_merge()用法与区别 2022-10-27
- Phpcms v9如何修改附件上传数量? Phpcms解除附件上传数量限制 2022-02-05
- PHP 数组查找函数 in_array()、array_search()、array_keys() 实战对比 2026-07-02
- PHP static静态变量方法如何使用 PHP static静态变量方法实例代码 2022-12-08
