登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go问答

Go 服务内存一直涨怎么排查:从告警到 pprof 的线上处理手册

来源:17golang原创

时间:2026-07-07 16:41:10 467浏览 收藏

Go 服务内存一直涨,先别急着下定论说是内存泄漏。线上处理优先按稳妥的顺序走:先看RSS是不是已经逼近容器的资源限制,再交叉对比Go堆占用、goroutine数量、连接数和缓存命中率这些核心指标;如果内存增长已经开始影响服务稳定性,先做限流或者回滚操作止住故障影响,再抓取 pprof 留存现场信息。最终的根因不一定是内存泄漏,有可能是长切片长期持有大对象、缓存没设置上限、goroutine无意义堆积、HTTP响应体没正常关闭,也可能单纯是流量上涨后原来的内存预算配小了。

要点速览

  • 先把RSS和Go heap两个指标区分开:RSS接近容器限制的时候会先触发稳定性风险,Go heap的数值才更贴近Go侧业务对象的实际占用情况。
  • 内存告警触发的第一时间,优先留存 /debug/pprof/heap、goroutine数和最近几次发布的版本信息。
  • 如果内存上涨同时伴随错误率上升,先做限流、摘除异常流量或者回滚版本,别因为慢慢排查把小问题拖成大故障。
  • 故障处理完复盘阶段,要补全内存预算规则、缓存上限配置、pprof访问权限控制和发布后固定观察窗口。

触发信号:先确认是不是稳定性风险

内存曲线往上走不代表马上就会出故障,但如果RSS已经接近容器资源限制,或者进程已经开始被系统频繁重启,就要按线上故障的标准流程处理。这个阶段别只盯着单张监控图看,要把几个关联的指标放在一起交叉验证:RSS、Go heap、GC执行频率、goroutine总数、请求量、错误率和最近一次上线的时间点。

Go 服务内存上涨时 RSS 阈值告警和限流动作资源预算图

信号 优先判断 常见动作
RSS 接近限制 容器或者操作系统会优先触发OOM杀进程 限流、扩容、摘除异常实例流量
heap 持续上涨 Go侧业务对象可能被长期持有无法释放 抓取heap profile快照
goroutine 数同步上涨 大概率出现了逻辑阻塞或者goroutine泄漏 抓取goroutine profile快照
发布后内存才开始上涨 新版本引入问题的概率更高 准备回滚或者缩小新版本灰度比例

快速判断:RSS、heap 和 goroutine 分开看

很多人排查卡壳,都是因为把“进程整体占用内存”和“Go运行时管理的堆对象”混为一谈。RSS是操作系统侧统计到的进程常驻内存,Go heap是Go运行时自己管理的堆对象大小,两个指标有关联但并不完全对等。比如heap没明显上涨但RSS涨了,大概率和mmap调用、cgo逻辑、内存碎片、第三方依赖库或者文件缓冲有关;如果heap持续一路上涨,才更符合业务对象没被正常释放的特征。

如果服务已经正常暴露pprof接口,可以先间隔着取几份快照:

curl -o heap-1.pb.gz http://127.0.0.1:6060/debug/pprof/heap
sleep 60
curl -o heap-2.pb.gz http://127.0.0.1:6060/debug/pprof/heap
curl -o goroutine.txt http://127.0.0.1:6060/debug/pprof/goroutine?debug=2

随后用 go tool pprof 分析内存里的对象分布:

go tool pprof -top heap-2.pb.gz
go tool pprof -http=:8081 heap-2.pb.gz

如果只单独抓取一份heap快照,很容易把正常业务高峰的内存占用误判成泄漏。更稳妥的做法是间隔1到5分钟连续抓2到3份快照,对比哪些对象的数量在持续上涨。

处理步骤:先止血,再保留证据

内存占用已经接近上限的时候,所有排查动作都要给服务稳定性让路。能先降低风险就先操作:给大流量入口做限流,把异常实例从负载均衡后端摘除,或者直接把新版本的灰度比例降回0。处理过程中至少要留存一份完整的heap和goroutine快照,不然等直接回滚之后现场全没了,后续复盘只能靠猜。

Go 服务通过 pprof heap 快照定位内存风险并回滚恢复

一套经过验证的稳妥处理顺序可以参考下面流程:

确认告警 -> 抓 pprof -> 限流或摘流量 -> 判断是否回滚 -> 观察 RSS 和错误率

如果是单个特殊接口引发的内存上涨,比如数据导出、批量查询、大图片处理这类逻辑,可以优先临时关掉这个接口的入口或者调低它的允许并发数。如果是全量发布之后整体内存都在涨,直接回滚版本大多比在线上临时改参数调试要稳妥得多。

回滚路径:不要等到 OOM 才动手

内存故障最忌讳抱着“还能再观察一会”的心态拖着不处理。RSS已经紧贴容器限制的时候,一次随机的流量尖峰就可能直接把进程推到OOM触发点。只要确认新版本上线时间和内存开始上涨的时间有明确的对应关系,就要提前把回滚的准备工作做足:要用到的镜像版本、相关配置变更、数据库开关、灰度流量比例都要确保可以在短时间内恢复到旧版本状态。

回滚之后要持续观察三个核心指标:RSS是不是停止继续上涨,错误率是不是逐步回落,进程异常重启次数是不是降到零。如果回滚之后RSS还在持续上涨,那就说明根因大概率不在这次发布里,或者还有其他没切回旧版本的异常实例。

复盘项:把下一次排查的时间变短

这类问题处理完之后,最值得补充的不是大段的说明文档,而是几个能直接缩短后续排查耗时的配置项:pprof访问权限的保护规则、内存预算监控面板、全局缓存的大小硬上限、核心接口的并发上限,还有发布后固定30分钟的内存指标观察规则。

代码层面也要补上对应的检查点。常见的内存问题坑点包括:把超大切片放进全局缓存、全局map只做写入从来不清理、HTTP响应体调用完没正常关闭、后台异步goroutine没设置退出条件、channel阻塞导致异步任务无限堆积。每一类问题单独看都不复杂,但放到大流量的线上环境里影响会被快速放大。

相关问题

Go 服务 heap 没涨但 RSS 涨,算内存泄漏吗?

不能直接下这个结论。heap没涨说明Go侧堆对象大概率不是主要原因,还要逐一排查cgo逻辑、mmap调用、内存碎片、第三方依赖库和系统文件缓冲。先把不同维度的指标拆开分析,再确定后续的排查方向。

pprof 需要一直对外开放吗?

不建议直接在公网暴露。生产环境可以只在内网、独立调试端口或者带身份校验的入口开放,需要排查问题的时候临时开启就行。pprof返回的数据里可能包含函数名、代码存储路径和很多运行时细节,不能随意对外公开。

内存上涨时应该先扩容还是先回滚?

如果新版本上线时间和内存上涨的时间高度吻合,优先准备回滚操作;如果确实是正常流量上涨、业务必须承接这部分请求,可以先扩容把服务稳住,再慢慢分析内存对象的来源。两个动作不是互斥的,核心原则是别让服务先被OOM直接打挂。

抓 heap 会不会影响线上服务?

采集快照会产生一定的CPU和内存开销,但这个代价远小于完全没留证据的情况。业务高峰期可以只抓一次短快照,避免频繁重复采集;如果实例已经处于不稳定状态,先把流量摘除再抓快照会更稳妥。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>