上一篇 下一篇 分享链接 返回 返回顶部

如何在Linux服务器中清除RAM缓存、缓冲区和Swap而无需重启?

发布人:Minchunlin 发布时间:2025-08-02 09:08 阅读量:1125

一、实战背景:为什么我要清理内存而不重启?

几个月前,我在维护一台线上 CentOS 7 数据库服务器时遇到一个棘手问题:

  • 系统运行了近 200 天未重启;
  • free -h 显示物理内存占用率接近 95%;
  • 应用层虽然还在运行,但 PostgreSQL 查询偶尔延迟数秒;
  • top 显示主要内存占用并非用户态进程,而是被 cache/buffer 吞掉了,且 swap 使用率达到 60%。

这是一种典型的 内存压力+I/O延迟 场景。由于这是生产环境,重启服务器是不可接受的,于是我开始探索如何在线清理缓存、缓冲区和 Swap。

二、深入理解 Linux 内存管理的核心机制

在动手之前,我必须清楚为什么内存会被“吃满”,以及清理操作可能带来的影响:

  • Linux 内核的缓存机制
  • Page Cache:文件内容的缓存,用于减少磁盘读取。
  • Dentry / Inode Cache:文件目录项和 inode 结构缓存,用于加快文件系统操作。

Linux 会尽量“吃掉所有空闲内存”来做缓存,但这些缓存可被随时回收,并不代表真的内存泄漏。

Swap 使用逻辑

当内存压力大时,内核会根据 vm.swappiness 将冷数据写入 Swap;

如果 Swap 占用过高,意味着内存分配紧张,频繁换入换出可能导致 I/O 抖动。

风险与代价

直接清理缓存可能导致短时 I/O 风暴(因为应用下一次访问需要重新从磁盘加载数据);

清理 Swap 会强制把数据回迁至内存,内存不足时可能触发 OOM(Out of Memory)。

结论:清理动作必须分阶段、可回滚,并严格监控系统负载。

三、分阶段的内存清理策略

1. 清理 Page Cache、Dentry 和 Inode

我首先创建了一个安全执行的清理脚本:

#!/bin/bash
echo "=== Step 1: 写回脏页 ==="
sync

echo "=== Step 2: 清理Page Cache ==="
echo 1 > /proc/sys/vm/drop_caches
sleep 2

echo "=== Step 3: 清理Dentry和Inodes ==="
echo 2 > /proc/sys/vm/drop_caches
sleep 2

echo "=== Step 4: 清理全部缓存 ==="
echo 3 > /proc/sys/vm/drop_caches

echo "=== 完成: $(date) ==="
free -h

关键点

  • 我分 3 步清理,而不是直接 echo 3,因为在生产环境中分阶段释放内存可避免突然的 I/O 峰值;
  • 每步后 sleep 是为了给内核调度时间,避免对正在访问磁盘的业务造成瞬时冲击。

2. 安全清理 Swap

Swap 的清理不能一刀切,否则可能瞬间占满内存导致系统假死。我的做法是分区滚动清理:

先确认 Swap 情况:

swapon --show
free -h

对每个 Swap 设备单独执行:

for swap_dev in $(awk '/^\/dev/ {print $1}' /proc/swaps); do
    echo "清理Swap设备: $swap_dev"
    swapoff $swap_dev
    sleep 2
    swapon $swap_dev
done

难点与坑点

如果服务器内存不足,swapoff 会失败或导致 OOM;

因此,我先用 vmstat 1 实时观察 si/so(swap in/out)情况,确保在清理过程中 I/O 不爆炸。

四、提升长期内存健康度

清理缓存和 Swap 只是临时缓解措施,要想长期稳定,我在实战中做了如下优化:

调整 swappiness 降低 Swap 依赖

sysctl vm.swappiness=10
echo "vm.swappiness = 10" >> /etc/sysctl.conf

调整缓存回收策略

sysctl vm.vfs_cache_pressure=200
echo "vm.vfs_cache_pressure = 200" >> /etc/sysctl.conf

vfs_cache_pressure 值越大,内核越积极回收 dentry/inode 缓存,避免缓存无限膨胀。

监控与自动化

  • 使用 cron 每天低峰期执行一次安全清理脚本;
  • 结合 Prometheus + Node Exporter 监控内存、缓存和 Swap 使用趋势。

五、我的经验总结

不要迷信一次性清理,分阶段、分 Swap 设备的渐进式清理才是生产环境可行方案;

清理 Swap 的风险远大于清理缓存,一定要先确保内存足够;

调优比清理更重要:通过调整 swappiness、vfs_cache_pressure 可以显著减少频繁手动干预的需求;

监控与预案缺一不可,我曾在一次没有预案的清理中触发 OOM,差点让线上服务中断。

通过这套方法,我成功在不重启服务器的情况下,将一台内存使用率 95%+、Swap 60% 的数据库服务器恢复到健康状态,业务延迟明显下降。

六、附录:生产环境下的智能内存清理与监控方案

在多次实战后,我意识到手动清理虽然安全,但不适合 7×24 小时的生产环境,因为我们无法一直盯着内存曲线。于是我设计了一套自动化、带安全阈值的内存清理机制。

1. 智能内存清理脚本

这个脚本的核心目标是:

  • 监控内存和 Swap 使用率
  • 在达到阈值时自动清理缓存和 Swap
  • 记录日志和报警
#!/bin/bash
# smart_mem_cleaner.sh
# 智能内存清理脚本
# 建议放在 /usr/local/sbin 并由 cron 定期执行

LOG_FILE="/var/log/mem_cleaner.log"
MEM_THRESHOLD=80    # 内存占用超过80%开始清理缓存
SWAP_THRESHOLD=40   # Swap 占用超过40%开始清理Swap
DATE=$(date "+%Y-%m-%d %H:%M:%S")

function log() {
    echo "[$DATE] $1" >> $LOG_FILE
}

# 获取内存和Swap使用情况
MEM_USED=$(free | awk '/Mem/ {printf("%.0f", $3/$2 * 100)}')
SWAP_USED=$(free | awk '/Swap/ {if ($2>0) printf("%.0f", $3/$2 * 100); else print 0}')

log "当前内存使用率: ${MEM_USED}%, Swap使用率: ${SWAP_USED}%"

# Step 1: 内存缓存清理
if [ "$MEM_USED" -ge "$MEM_THRESHOLD" ]; then
    log "内存使用率高于${MEM_THRESHOLD}%,开始清理缓存..."
    sync
    echo 1 > /proc/sys/vm/drop_caches
    sleep 2
    echo 2 > /proc/sys/vm/drop_caches
    sleep 2
    echo 3 > /proc/sys/vm/drop_caches
    log "缓存清理完成,当前内存使用率: $(free | awk '/Mem/ {printf("%.0f", $3/$2 * 100)}')%"
fi

# Step 2: Swap 清理
if [ "$SWAP_USED" -ge "$SWAP_THRESHOLD" ]; then
    log "Swap使用率高于${SWAP_THRESHOLD}%,开始分区滚动清理..."
    while read swap_dev _; do
        log "清理Swap设备: $swap_dev"
        swapoff $swap_dev && sleep 2 && swapon $swap_dev
    done < <(awk '/^\/dev/ {print $1}' /proc/swaps)
    log "Swap清理完成,当前Swap使用率: $(free | awk '/Swap/ {if ($2>0) printf("%.0f", $3/$2 * 100); else print 0}')%"
fi

log "=== 清理完成 ==="

部署细节

设置执行权限:

chmod +x /usr/local/sbin/smart_mem_cleaner.sh

添加到 crontab,每 30 分钟执行一次:

*/30 * * * * /usr/local/sbin/smart_mem_cleaner.sh

日志保存在 /var/log/mem_cleaner.log,可配合 logrotate 做轮转。

2. 结合监控和报警

为了让这套方案足够“智能”,我在服务器上结合了 Prometheus + Node Exporter 和报警策略:

采集指标:

node_memory_MemAvailable_bytes
node_memory_SwapUsed_bytes

设置报警规则(Prometheus Alertmanager 示例):

groups:
- name: linux-memory-rules
  rules:
  - alert: HighMemoryUsage
    expr: (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) < 0.2
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "内存使用率高"
      description: "可用内存低于20%,可能需要清理缓存或扩容。"

  - alert: HighSwapUsage
    expr: (node_memory_SwapUsed_bytes / node_memory_SwapTotal_bytes) > 0.5
    for: 10m
    labels:
      severity: critical
    annotations:
      summary: "Swap使用率过高"
      description: "Swap使用率超过50%,可能导致I/O抖动。"

3. 我的生产经验总结

  • 自动化脚本必须保守:只在达到阈值后才触发,并且分阶段清理;
  • Swap 清理要谨慎:先判断内存充足,否则可能触发 OOM;
  • 报警优先于清理:先通知再执行,可避免误操作;
  • 长期方案是优化内存管理策略,如调整 vm.swappiness、vfs_cache_pressure、升级内存或优化应用。

经过这套方案部署后,我的数据库服务器可以长期稳定运行半年以上无需重启,并且内存和 Swap 始终保持在健康区间。

目录结构
全文