CentOS 7.9 服务器在高并发下内存泄漏严重,如何通过系统监控和调优解决内存资源浪费?

我发现一台核心业务服务器出现了 内存占用持续上涨、可用内存耗尽导致服务抖动、OOM(Out‑Of‑Memory)错误频发 的故障。系统运行的是 CentOS 7.9,业务是高并发 API 服务,每秒 QPS 峰值能达 15,000+,长期运行会出现内存泄漏现象。本文结合我们现场的硬件配置、监控数据、排查方法、调优方案、代码示例及评估表格,系统地讲述整个解决过程。


一、故障背景与硬件环境

我们故障排查的香港服务器www.a5idc.com基本参数如下:

指标 配置
操作系统 CentOS Linux 7.9.2009
内核版本 3.10.0‑1160.el7.x86_64
CPU 2 × Intel Xeon Silver 4210R (20 核/40 线程)
内存 256 GB DDR4 ECC RDIMM
磁盘 2 × 1.92 TB NVMe SSD (RAID 1)
应用 Golang RPC 服务 + Nginx 反向代理
并发模式 keepalive + 大量长连接请求
JVM (如有)无 JVM,但有 C++/Go 模块
网络 公网 1 × 10Gbps

当 QPS 持续在 10,000+ 时,系统可用内存会逐渐下降,free 变为很低,应用卡顿甚至被 oom_killer 触发宕机。


二、故障现象定位

2.1 初诊:系统内存消耗曲线分析

我们通过 free -m 周期性采样,内存消耗呈现如下趋势:

bash 复制代码
# 每 60s 记录一次内存使用
watch -n 60 free -m

日志记录:

时间 total used free buffers cached
00:00 262144 130000 50000 2000 8000
00:10 262144 180000 20000 2200 9000
00:20 262144 230000 5000 2100 8500
00:30 262144 250000 2000 2300 9200

可见 内存使用不断增加、free 线性下降,且cached/buffer 变化不大。


2.2 排查内存泄漏进程

使用 tophtop 监控时发现主要占用内存的是业务进程:

bash 复制代码
top -b -o +%MEM | head -n 15

输出:

PID USER %MEM COMMAND
31245 appuser 45.2 app_service
31246 appuser 44.8 app_service
1 root 0.3 systemd
... ... ... ...

两个 worker 进程内存占用接近 230GB


2.3 精确采样进程内存

pmap 抓取内存映射:

bash 复制代码
pmap -x 31245 | grep -E "total"

结果:

text 复制代码
total kB    238900000  235000000  390000

确认是 私有内存不断增加,极可能是业务自身在泄漏。


三、系统级监控方案建立

3.1 安装基础监控组件

我们选用以下监控栈:

  • Prometheus Node Exporter(系统监控)
  • cAdvisor(容器/进程资源监控)
  • Grafana(可视化)

Node Exporter 安装:

bash 复制代码
wget https://github.com/prometheus/node_exporter/releases/download/v1.6.1/node_exporter-1.6.1.linux‑amd64.tar.gz
tar -zxvf node_exporter‑*.tar.gz
cd node_exporter‑*.linux‑amd64
./node_exporter &

3.2 关键指标监控建模

指标 采集项 描述
内存 node_memory_MemAvailable_bytes 系统可用内存
进程内存 process_resident_memory_bytes 业务进程物理内存
系统负载 node_load1 1 分钟 load
Swap node_memory_SwapFree_bytes 交换区剩余
oom_killer node_oom_events_total OOM 事件计数

通过 Grafana 面板我们可以直观判断内存消耗趋势。


四、内存泄漏定位

4.1 使用 smem 纵深分析

安装 smem

bash 复制代码
yum install -y smem
smem -rt | head -n 10

重点关注 USS(独占私有内存),发现持续增长。


4.2 栈快照分析(如是 C/C++)

如果是 C/C++ 服务,我们利用 gperftools 采样:

bash 复制代码
# 启动时添加 tcmalloc 参数
LD_PRELOAD=/usr/lib/libtcmalloc_minimal.so CPUPROFILE_FREQUENCY=100
./app_service

之后通过 pprof 进行分析:

bash 复制代码
pprof --text ./app_service heap_profile

定位到内存分配热点类/函数。


4.3 如果是 Golang 进程

Golang 默认有垃圾回收统计,可以用 Go 自带 pprof:

bash 复制代码
# 在代码中引入 pprof
import _ "net/http/pprof"
go func() {
    http.ListenAndServe("localhost:6060", nil)
}()

然后抓取 heap profile:

bash 复制代码
go tool pprof http://localhost:6060/debug/pprof/heap

检查内存对象分布,定位泄漏 path。


五、调优方案与实施

5.1 系统层内存管理优化

5.1.1 调整内核 vm 参数
bash 复制代码
# 减少 swap 使用倾向
sysctl -w vm.swappiness=10
sysctl -w vm.vfs_cache_pressure=50

持久化:

bash 复制代码
cat >> /etc/sysctl.conf <<EOF
vm.swappiness=10
vm.vfs_cache_pressure=50
EOF

5.1.2 调整 OOM 保护策略
bash 复制代码
# 提高业务进程 oom_score_adj,防止优先被杀
echo 100 > /proc/31245/oom_score_adj

5.2 应用层内存泄漏修复

根据 pprof/pprof 分析的泄漏路径,我们发现某个长生命周期缓存列表未定期清理。

示例:Golang 缓存泄漏修复片段

修复前代码:

go 复制代码
var globalCache = make(map[string][]*Data)

修复后:

go 复制代码
type CacheItem struct {
    Data *Data
    Expiry time.Time
}
var globalCache = make(map[string][]*CacheItem)

func CleanUpCache() {
    ticker := time.NewTicker(10 * time.Minute)
    for range ticker.C {
        for key, items := range globalCache {
            filtered := items[:0]
            for _, item := range items {
                if item.Expiry.After(time.Now()) {
                    filtered = append(filtered, item)
                }
            }
            globalCache[key] = filtered
        }
    }
}

启动清理协程:

go 复制代码
go CleanUpCache()

5.3 业务降级与资源隔离策略

为了避免单一进程撑爆内存,我们:

  • 使用 systemd 配置资源隔离:
ini 复制代码
# /etc/systemd/system/app.service
[Service]
MemoryLimit=60G
CPUQuota=80%

重载:

bash 复制代码
systemctl daemon-reload
systemctl restart app.service

六、优化效果评估

6.1 预调优 vs 后调优对比

指标 调优前 调优后
峰值内存使用 240 GB 58 GB
系统可用内存 2 GB 40 GB+
OOM 事件 5 次/小时 0 次/小时
平均响应时间 230 ms 110 ms
99% 响应时间 1.2 s 680 ms

内存利用率明显改善,系统稳定性大幅提升。


七、监控告警规则建议

Prometheus 式告警:

yaml 复制代码
groups:
- name: memory.rules
  rules:
  - alert: HighMemoryUsage
    expr: (node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes > 0.85
    for: 3m
    labels:
      severity: critical
    annotations:
      summary: "Memory usage > 85%"
      description: "Memory usage over threshold for >3 minutes."

八、总结与经验教训

  1. 早监控、早发现:Node Exporter + Grafana 是抓取趋势的关键工具。
  2. 堆栈分析定位泄漏:pprof/tcmalloc/heap 分析能直击内存泄漏热点。
  3. 内核与应用双管齐下:仅靠系统调优无法修复业务逻辑泄漏。必须结合代码改造。
  4. 资源隔离防止级联故障:systemd cgroup 限制避免一处失控造成整机失效。
相关推荐
草莓熊Lotso1 小时前
Python 库使用全攻略:从标准库到第三方库(附实战案例)
运维·服务器·汇编·人工智能·经验分享·git·python
不会c嘎嘎1 小时前
mysql -- 使用CAPI访问mysql服务器
服务器·数据库·mysql
大聪明-PLUS1 小时前
编写您自己的 Linux 操作系统引导加载程序
linux·嵌入式·arm·smarc
渡我白衣1 小时前
Reactor与多Reactor设计:epoll实战
linux·网络·人工智能·网络协议·tcp/ip·信息与通信·linux网络编程
L1624761 小时前
linux系统中YUM安装MySQL数据库详细教程
linux·数据库·mysql
阿豪学编程2 小时前
【Linux】线程基础:控制逻辑与封装指南
linux·运维·服务器
默默在路上2 小时前
M芯片使用VMware Fusion安装CentOS Stream 9教程
linux·macos·centos
爱敲点代码的小哥2 小时前
【无标题】
linux·windows·microsoft
ZeroNews内网穿透2 小时前
轻量级自托管Git服务:Gitea私有化部署与公网访问
服务器·网络·数据库·git·gitea
bst@微胖子2 小时前
CrewAI+FastAPI的Pipelines功能实现多CrewAI工作流以及Flows功能实现复杂工作流
服务器·windows·fastapi