服务器内存占用过高怎么办?从排查到优化的完整指南

摘要:服务器内存占用居高不下,轻则网站响应变慢,重则 OOM Killer 直接杀掉关键进程导致服务宕机。本文从「读懂内存数据」开始,手把手带你定位内存吃光的元凶,并给出 8 种经过验证的优化方案,适用于 WordPress、PHP、MySQL、Node.js 等主流技术栈。


一、先读懂内存数据:free -h 各字段含义详解

很多人看到内存使用率 80% 就开始慌,其实 Linux 的内存管理机制和 Windows 完全不同,未必需要立刻处理。先学会正确解读数据。

执行 free -h,输出结果类似:

              total        used        free      shared  buff/cache   available
Mem:           3.8G        2.9G        156M        52M        780M        680M
Swap:          1.0G        320M        704M
字段含义关注重点
total服务器总物理内存硬件上限
used已使用内存(含 buff/cache)不能只看这个
free完全空闲的内存通常很小,属正常
buff/cache系统用于缓存的内存,可随时释放不算"真正占用"
available真正可用内存(free + 可释放 cache)这才是关键指标
Swap used已使用的交换空间有值说明物理内存已紧张
💡 关键原则:Linux 会尽量把空闲内存用作 buff/cache 来提升性能,这是正常行为。真正需要关注的是 available 字段。当 available 低于总内存的 10%,才需要认真排查。

判断内存状态的三个级别:

available 占总内存比例状态建议
> 30%正常无需处理
10%–30%需关注观察趋势,排查占用高的进程
< 10%危险立即排查,OOM 风险高
Swap 有大量占用严重物理内存不足,需优化或扩容

二、快速定位内存占用最高的进程

1 用 top / htop 实时查看

# 启动 top,按 M 键按内存排序
top
# 进入后按 Shift+M 按内存使用率排序

# htop 更直观(需先安装)
apt install htop -y   # Debian/Ubuntu
yum install htop -y   # CentOS
htop
# F6 选择排序字段,选 MEM% 按内存排序

2 用 ps 命令输出内存排行榜

# 按内存占用降序列出前 15 个进程
ps aux --sort=-%mem | head -15

# 输出格式:USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
# RSS = 实际占用物理内存(单位 KB),这是最重要的列

# 按进程名汇总内存(适合多进程服务如 PHP-FPM)
ps aux | awk '{arr[$11]+=$6} END {for (i in arr) print arr[i], i}' | sort -rn | head -15

3 用 smem 精准统计共享内存

# smem 能区分共享内存,统计更准确
apt install smem -y
smem -r -k | head -15

# 按进程名汇总
smem -r -k -t -P nginx
💡 重点关注以下进程:MySQL / MariaDB、PHP-FPM、Nginx / Apache、Node.js、Java 应用、Redis。这些是服务器内存的主要消耗者。

三、判断是内存泄漏还是正常使用

内存占用高分两种情况,处理方式完全不同:

类型特征处理方式
正常使用内存随业务量增长,业务低谷期自动回落;重启服务后恢复正常优化配置参数或升级内存
内存泄漏内存单调递增,即使业务量低也不回落;持续运行时间越长占用越高;重启后暂时恢复排查代码 bug,更新软件版本

用以下命令监控进程内存变化趋势:

# 每 5 秒打印一次指定进程(如 php-fpm)的内存使用
watch -n 5 "ps aux | grep php-fpm | grep -v grep | awk '{sum += \$6} END {print sum/1024 \" MB\"}'"

# 记录 MySQL 内存占用历史(每分钟一次,记录 60 分钟)
for i in $(seq 1 60); do
  echo "$(date): $(ps aux | grep mysqld | grep -v grep | awk '{print $6/1024 " MB"}')"
  sleep 60
done
⚠️ 内存泄漏信号:如果某个进程每小时增长超过 50MB 且不回落,基本可以确认存在内存泄漏。临时解决方案是定时重启该服务,根本解决需要修复代码或升级到修复版本。

四、8 种内存优化方案(按场景分类)

1 优化 MySQL / MariaDB 内存配置

MySQL 是服务器内存的头号消耗者,默认配置往往针对大内存服务器,在 1–4G 内存的 VPS 上需要大幅调低。

# 查看当前 MySQL 内存配置
mysql -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"

# 编辑 MySQL 配置文件
nano /etc/mysql/mysql.conf.d/mysqld.cnf   # Ubuntu
nano /etc/my.cnf                           # CentOS

针对不同内存规格的推荐配置:

服务器内存innodb_buffer_pool_size其他关键参数
1G128Mkey_buffer_size=16M
2G256Mkey_buffer_size=32M
4G512M–1Gkey_buffer_size=64M
8G2G–4Gkey_buffer_size=128M
# 推荐配置示例(2G 内存 VPS)
[mysqld]
innodb_buffer_pool_size = 256M
key_buffer_size = 32M
tmp_table_size = 32M
max_heap_table_size = 32M
max_connections = 100
thread_cache_size = 8

# 重启 MySQL 使配置生效
systemctl restart mysql

2 优化 PHP-FPM 进程数

PHP-FPM 采用进程池模型,每个 worker 进程占用约 20–40MB 内存。进程数过多是 WordPress / PHP 网站内存超标的主要原因。

# 查看当前 PHP-FPM 进程数和内存占用
ps aux | grep php-fpm | grep -v grep | wc -l
ps aux | grep php-fpm | grep -v grep | awk '{sum+=$6} END {print sum/1024 " MB"}'

# 编辑 PHP-FPM 配置
nano /etc/php/8.1/fpm/pool.d/www.conf

根据内存调整进程数公式:最大进程数 = (可用内存 × 0.7) ÷ 单进程内存

服务器可用内存推荐 pm.max_childrenpm 模式
512MB5–8ondemand
1GB10–15ondemand
2GB20–30dynamic
4GB40–60dynamic
# 小内存 VPS 推荐配置(1G 内存)
pm = ondemand
pm.max_children = 10
pm.process_idle_timeout = 10s
pm.max_requests = 200

# 重启 PHP-FPM
systemctl restart php8.1-fpm

3 优化 Nginx worker 进程

# 编辑 Nginx 主配置
nano /etc/nginx/nginx.conf

# 关键参数优化
worker_processes auto;          # 自动匹配 CPU 核数,不要设置过大
worker_connections 1024;        # 每个 worker 最大连接数
keepalive_timeout 30;           # 降低长连接超时,释放内存
client_body_buffer_size 16k;    # 限制请求体缓冲
large_client_header_buffers 2 8k;

4 关闭不必要的系统服务

# 查看所有开机启动的服务
systemctl list-units --type=service --state=running

# 常见可安全关闭的服务(根据实际需要判断)
systemctl disable --now postfix    # 本地邮件服务(不发邮件可关)
systemctl disable --now cups       # 打印服务(服务器不需要)
systemctl disable --now bluetooth  # 蓝牙服务(服务器不需要)
systemctl disable --now avahi-daemon # mDNS 服务(通常不需要)
⚠️ 注意:关闭服务前确认其用途,不要关闭 sshd、network、cron 等核心服务。

5 优化 Redis 内存上限

# 编辑 Redis 配置,设置最大内存和淘汰策略
nano /etc/redis/redis.conf

# 添加或修改以下配置
maxmemory 256mb               # 根据服务器内存设置上限
maxmemory-policy allkeys-lru  # 内存满时淘汰最久未使用的 key

systemctl restart redis

6 清理系统 buff/cache 缓存

注意:这只是临时释放缓存,不解决根本问题,且可能短暂影响 I/O 性能。紧急情况下使用。

# 清理 PageCache(最安全)
sync && echo 1 > /proc/sys/vm/drop_caches

# 清理 dentries 和 inodes
sync && echo 2 > /proc/sys/vm/drop_caches

# 清理全部缓存
sync && echo 3 > /proc/sys/vm/drop_caches

# 释放后立即查看效果
free -h

7 优化 Node.js 内存限制

# Node.js 默认堆内存上限约 1.5GB,可根据服务器内存调整
# 启动时指定最大内存(单位 MB)
node --max-old-space-size=512 app.js   # 限制为 512MB

# PM2 管理的应用,在 ecosystem.config.js 中设置
module.exports = {
  apps: [{
    name: 'myapp',
    script: 'app.js',
    node_args: '--max-old-space-size=512',
    max_memory_restart: '600M'  // 超过 600M 自动重启
  }]
}

8 使用 OOM Score 保护关键进程

当内存耗尽时,Linux OOM Killer 会自动杀死进程。通过调整 oom_score_adj 可以控制哪些进程优先被杀,保护关键服务。

# 值范围:-1000(永不被杀)到 1000(优先被杀)
# 保护 Nginx 不被 OOM 杀死
echo -500 > /proc/$(pgrep -f nginx | head -1)/oom_score_adj

# 保护 MySQL
echo -800 > /proc/$(pgrep mysqld)/oom_score_adj

# 查看某进程当前的 oom_score
cat /proc/$(pgrep mysqld)/oom_score

五、配置 Swap 交换空间作为应急缓冲

Swap 是在磁盘上划出一块空间模拟内存,当物理内存不足时系统会将部分数据转移到 Swap。速度比物理内存慢很多,但能防止 OOM 崩溃。很多云服务器默认没有开启 Swap,建议手动添加。

# 检查是否已有 Swap
swapon --show
free -h

# 创建 2G 的 Swap 文件(根据内存大小调整,建议为内存的 1–2 倍)
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile

# 验证 Swap 已启用
free -h

# 设置开机自动挂载(将以下行添加到 /etc/fstab)
echo '/swapfile none swap sw 0 0' >> /etc/fstab

# 调整 swappiness(建议设为 10,减少 Swap 使用频率)
echo 'vm.swappiness=10' >> /etc/sysctl.conf
sysctl -p
💡 swappiness 说明:默认值 60 表示系统会较积极地使用 Swap。对于 SSD 服务器,建议设为 10–20;对于 HDD 服务器,建议设为 5–10,减少频繁读写磁盘带来的性能损耗。

六、设置内存告警,提前预警

出现内存问题时能第一时间收到通知,比事后救火重要得多。以下是一个简单的内存监控脚本:

#!/bin/bash
# 内存告警脚本:当 available 低于 10% 时发送告警
# 保存为 /usr/local/bin/mem_alert.sh,chmod +x 后加入 crontab

THRESHOLD=10  # 告警阈值(百分比)
TOTAL=$(free | awk '/Mem/{print $2}')
AVAILABLE=$(free | awk '/Mem/{print $7}')
PERCENT=$((AVAILABLE * 100 / TOTAL))

if [ $PERCENT -lt $THRESHOLD ]; then
  MSG="⚠️ 服务器内存告警:可用内存仅剩 ${PERCENT}%(${AVAILABLE}KB),请立即检查!"
  # 发送到系统日志
  logger "$MSG"
  # 如已配置 mail,发送邮件
  echo "$MSG" | mail -s "内存告警" admin@yourdomain.com
fi

# 加入 crontab,每 5 分钟检查一次
crontab -e
*/5 * * * * /usr/local/bin/mem_alert.sh
推荐工具:如需更完善的监控方案,可以部署 Prometheus + Grafana 监控面板,或使用 Netdata(轻量级,安装一条命令即可),实时可视化内存、CPU、磁盘等所有指标。

七、各技术栈内存占用参考基准

在优化之前,先了解「正常的内存占用」是多少,避免过度优化或盲目升配:

服务 / 技术栈最小内存需求推荐内存说明
纯静态 Nginx50MB128MB极省内存
WordPress(PHP-FPM + Nginx)256MB512MB–1GB插件越多占用越高
MySQL(小型应用)256MB512MB–1GB取决于 buffer pool 配置
Node.js 应用128MB256MB–512MB取决于应用逻辑
Redis(缓存)64MB128MB–256MB视数据量而定
Java Spring Boot512MB1GB–2GBJVM 开销大
Docker(每个容器)64MB+视应用而定加上 Docker 引擎约 100MB
完整 LNMP 环境512MB1GB–2GB小流量网站最低配置
💡 选型建议:运行完整 LNMP + WordPress 环境,建议至少选择 2核2G 的云服务器,低于此配置内存会非常紧张。如果还需运行 Redis + Elasticsearch,建议升级到 4G 内存。

八、常见问题解答(FAQ)

Q1:内存占用 90% 但服务一切正常,需要处理吗?

需要结合 available 字段判断。如果 available 还有 500MB 以上,且 Swap 没有使用,说明大部分是 buff/cache 缓存,系统运行正常,无需处理。只有 available 持续低于总内存 10% 时才需要介入。

Q2:OOM Killer 杀了我的 MySQL 进程,怎么防止?

有三种方法:一是调低 MySQL 的 innodb_buffer_pool_size 减少内存占用;二是为 MySQL 进程设置负的 oom_score_adj 值降低被杀优先级;三是增加 Swap 空间给系统更多缓冲。从根本上解决需要升级服务器内存或优化应用减少内存使用。

Q3:重启服务器后内存正常,过几天又满了,怎么回事?

这是典型的内存泄漏表现。建议使用 watch 命令每隔一段时间记录各进程内存占用,找出持续增长的进程。常见原因包括:PHP 代码中的循环引用、Node.js 全局变量积累、MySQL 连接未正确释放等。定期重启相关服务是临时方案,彻底解决需要修复代码。

Q4:1G 内存的服务器能跑 WordPress 吗?

可以跑,但需要精细调优。建议:MySQL innodb_buffer_pool_size 设为 128M,PHP-FPM 最大进程数设为 8,开启 2G Swap,并安装 WP Super Cache 等静态缓存插件减少 PHP 请求量。日均 PV 在 3000 以内基本稳定,超过则建议升级到 2G 内存。

Q5:后浪云服务器内存不够用了,如何快速扩容?

后浪云云服务器支持在线升级配置,登录控制面板选择「升级套餐」即可快速扩容内存,停机时间通常在 5 分钟以内。如果当前套餐无法满足需求,也可以迁移到更高配置的独立服务器。

THE END