服务器内存不够用怎么办?SWAP配置与内存泄漏排查实战

服务器内存不够用了怎么解决?SWAP 配置与内存泄漏排查实战

服务器内存不足时,Linux 内核会启动 OOM Killer(Out-of-Memory Killer) 机制,强制杀掉内存占用最大的进程——很多时候,被杀的恰好是你的 MySQL、Nginx 或核心应用。本文从现象判断到根因处理,提供一套完整的内存问题解决方案。


一、快速判断:内存是否真的不足?

free -h

典型输出解读:

              total        used        free      shared  buff/cache   available
Mem:          3.8Gi       2.9Gi       156Mi        45Mi       743Mi       651Mi
Swap:         1.0Gi       891Mi       133Mi

关键指标说明:

  • available(651Mi):真正可供应用使用的内存,这个值才是判断内存是否紧张的关键,而不是 free
  • buff/cache(743Mi):Linux 用于文件缓存的内存,在应用需要时可被回收,不代表内存浪费
  • Swap used(891Mi):SWAP 使用率高说明物理内存已经不够用,系统正在用磁盘补充内存,性能会显著下降

判断标准:available 持续低于总内存的 10%,或 SWAP 使用率持续超过 50%,说明内存确实不足,需要处理。


二、查看 OOM Killer 是否已经杀过进程

# 检查内核日志中的 OOM 记录
sudo dmesg | grep -i "oom\|killed"

# 或查看系统日志
sudo grep -i "out of memory\|oom_kill" /var/log/syslog | tail -20

如果看到类似以下输出,说明 OOM Killer 已经工作过:

kernel: Out of memory: Kill process 1234 (mysql) score 892 or sacrifice child
kernel: Killed process 1234 (mysql) total-vm:1048576kB, anon-rss:524288kB

三、定位内存使用最多的进程

# 按内存使用量降序排列进程(%MEM 列)
ps aux --sort=-%mem | head -15

# 或在 top 中按 M 键按内存排序
top   # 进入后按 M

常见内存消耗大户:

  • MySQL / MariaDB:innodb_buffer_pool_size 设置过大
  • PHP-FPM:进程数过多,每个进程约占 30~80MB
  • Redis:未设置 maxmemory 导致无限增长
  • Node.js 应用:内存泄漏最常见的运行时之一
  • Java 应用:JVM 堆内存配置过大或泄漏

四、临时方案:配置 SWAP 虚拟内存

SWAP 是在磁盘上划出一块空间模拟内存,当物理内存不足时系统可以将不活跃的内存页交换到 SWAP,避免 OOM Killer 杀进程。

⚠️ 重要说明:SWAP 的读写速度远低于物理内存(磁盘 vs 内存),频繁使用 SWAP 会导致系统明显变慢。SWAP 是应急缓冲,不能替代充足的物理内存。

Step 1:创建 SWAP 文件

# 创建 2GB 的 SWAP 文件(推荐大小为物理内存的 1~2 倍,最大 8GB)
sudo fallocate -l 2G /swapfile

# 设置正确的权限(SWAP 文件不应被其他用户读取)
sudo chmod 600 /swapfile

# 将文件格式化为 SWAP 格式
sudo mkswap /swapfile

Step 2:启用 SWAP

# 立即启用 SWAP(重启后失效)
sudo swapon /swapfile

# 验证 SWAP 是否生效
free -h   # Swap 行应显示 2GB 总量

Step 3:设置开机自动挂载

# 写入 /etc/fstab,开机自动挂载
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

Step 4:调整 swappiness(SWAP 使用倾向)

# 查看当前 swappiness 值(默认60)
cat /proc/sys/vm/swappiness

# 设置为 10(更倾向于使用物理内存,减少不必要的 SWAP 使用)
sudo sysctl vm.swappiness=10

# 永久生效
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf

swappiness 取值 0~100:值越小,系统越倾向于保留物理内存不换出;服务器环境通常建议设置为 10~20。


五、减少各服务内存占用的实用配置

MySQL:限制 InnoDB 缓冲池

# /etc/mysql/mysql.conf.d/mysqld.cnf
# 将 buffer pool 设置为可用内存的 50~60%(而非默认的更大值)
innodb_buffer_pool_size = 512M   # 1GB 内存机器建议设 400~512M

# 限制最大连接数(每个连接约占 1~8MB)
max_connections = 50

PHP-FPM:减少进程数

# /etc/php/8.2/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 10      # 内存有限时降低最大进程数
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 5
pm.max_requests = 200     # 每个进程处理200次请求后重启,防止内存泄漏积累

Redis:设置最大内存限制

# /etc/redis/redis.conf
maxmemory 256mb                  # 限制最大使用 256MB
maxmemory-policy allkeys-lru     # 内存满时淘汰最久未使用的 key

Nginx:限制 worker 进程内存

# /etc/nginx/nginx.conf
# worker 进程数设置为 CPU 核心数(而非 auto,内存紧张时不宜过多)
worker_processes 2;

# 每个连接的读写缓冲区减小
client_body_buffer_size 10K;
client_header_buffer_size 1k;
large_client_header_buffers 2 1k;

六、内存泄漏排查:进程内存持续增长怎么办?

内存泄漏的特征是:某个进程的内存占用随时间持续增长,即使业务量没有增加。

监控进程内存随时间的变化

# 每隔5秒打印进程 1234 的内存使用(VmRSS = 实际物理内存)
while true; do
  echo "$(date): $(grep VmRSS /proc/1234/status)"
  sleep 5
done

Node.js 内存泄漏排查

# 启动时开启内存分析
node --expose-gc --inspect app.js

# 或者定期打印内存使用
setInterval(() => {
  const used = process.memoryUsage();
  console.log(`RSS: ${Math.round(used.rss/1024/1024)}MB, Heap: ${Math.round(used.heapUsed/1024/1024)}MB`);
}, 30000);

如果 heapUsed 持续增长而不下降,说明存在内存泄漏。常见原因:事件监听器未移除、全局缓存无上限增长、闭包引用阻止 GC 回收。

防止内存泄漏的应急方案:定时重启

如果无法立即定位泄漏根因,可以临时配置定时重启应用进程(凌晨低峰时段),以释放积累的内存:

# 每天凌晨4点重启 Node 应用(通过 PM2)
0 4 * * * pm2 restart app_name

这是治标不治本的应急措施,需要同步排查根因。


七、预防为主:内存使用监控告警

sudo nano /usr/local/bin/memory_alert.sh
#!/bin/bash
THRESHOLD=90    # 内存使用率告警阈值(90%)

# 计算内存使用率
TOTAL=$(grep MemTotal /proc/meminfo | awk '{print $2}')
AVAILABLE=$(grep MemAvailable /proc/meminfo | awk '{print $2}')
USED_PCT=$(( (TOTAL - AVAILABLE) * 100 / TOTAL ))

if [ "$USED_PCT" -gt "$THRESHOLD" ]; then
    echo "⚠️ 内存告警:$(hostname) 内存使用率已达 ${USED_PCT}%(可用:$((AVAILABLE/1024))MB)" \
    | mail -s "内存空间告警" your@email.com
fi
sudo chmod +x /usr/local/bin/memory_alert.sh
# 每15分钟检查一次
(crontab -l; echo "*/15 * * * * /usr/local/bin/memory_alert.sh") | crontab -

总结

Linux 服务器内存不足的解决思路按优先级排列:

  1. 🔍 确认根因:用 free -h 和 ps aux 定位内存消耗大户
  2. ⚙️ 优化配置:减小 MySQL buffer pool、PHP-FPM 进程数、Redis maxmemory
  3. 💾 配置 SWAP:作为应急缓冲,防止 OOM Killer 突然杀进程
  4. 🔬 排查泄漏:监控进程内存随时间的变化,定位并修复内存泄漏
  5. 📈 升级配置:如果优化后仍不够,考虑升级服务器内存规格

后浪云香港云服务器支持在线升配内存,无需重装系统和迁移数据,在控制台操作即可完成,欢迎按业务需求灵活调整配置。

THE END