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 限制避免一处失控造成整机失效。
相关推荐
cuijiecheng20182 分钟前
Rocky Linux 安装 libltc、ltc-tools 和 ltcdump
linux
BlueAsia_Lab2 分钟前
BQB 认证执行标准是什么?核心条款通俗解读
linux·运维·网络
小小、码农6 分钟前
【网络】套接字(Socket)编程——TCP版
linux·服务器·网络·c++·网络协议·tcp/ip
younuo365510 分钟前
广州网站搭建费用明细:域名、服务器与开发成本全解析
服务器·前端·github
邪修king10 分钟前
Re:Linux 系统篇(十六):进程篇(五):O (1) 调度算法深度解析 —— 优先级数组、位图优化与活跃 / 过期双队列
linux·运维·算法
苏三福11 分钟前
ubuntu 在文件管理系统中用sftp打开远程文件空白的问题解决
linux·运维·ubuntu
云飞云共享云桌面14 分钟前
10 人三维研发团队,如何利用单台图形服务器承载 SolidWorks 建模与仿真?
运维·服务器·网络·自动化·制造
在线考试系统推荐16 分钟前
拆成多道小题,优考试“理解题“怎么用?
服务器·人工智能·学习
邪修king17 分钟前
Re:Linux 系统篇(十四):进程篇(三):进程退出与特殊进程 —— 僵尸 Z 状态、孤儿进程深度解析
linux·运维·服务器
lingran__17 分钟前
Git 完全指南(一):本地仓库基础操作
linux·git·开发工具·devops·版本控制