如何在Linux服务器中清除RAM缓存、缓冲区和Swap而无需重启?
一、实战背景:为什么我要清理内存而不重启?
几个月前,我在维护一台线上 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 始终保持在健康区间。
