Go trace 和 pprof 应该先看哪一个
Go trace 和 pprof 没有固定的先后顺序:CPU 持续偏高、分配量明显增加,通常先看对应的 pprof profile;请求很慢但 CPU 不高、怀疑调度等待或并行任务没有充分运行,优先采集覆盖异常窗口的 execution trace。若已经明确怀疑锁或 channel 阻塞,block、mutex profile 也可以直接给出聚合线索,不必先绕一遍 CPU profile。
官方地址:https://go.dev/doc/diagnostics
pprof 更适合回答“成本累计在哪些调用栈”,trace 更适合回答“运行与等待如何发生”。CPU profile 不是完整请求耗时表,execution trace 也不是跨服务调用链追踪。
工具能力的差别,决定了第一份材料
这里讨论的是两种诊断能力,不是宣布某个版本新发布了功能。pprof 把采样成本归到调用栈,便于从 top 和调用关系中缩小热点;runtime/trace 记录运行时事件,适合观察 goroutine 的运行、阻塞、调度以及 GC 等现象。它们是互补材料,不是同一份数据的两种皮肤。
| 已观察到的症状 | 先取得的材料 | 要回答的问题 |
|---|---|---|
| CPU 持续偏高 | CPU profile | 哪些调用栈消耗 CPU |
| 分配频繁 | alloc_space / alloc_objects | 哪些位置产生分配压力 |
| 存活堆持续增长 | inuse_space / inuse_objects | 存活对象的分配位置 |
| 低 CPU、高延迟或并行度异常 | 短窗口 execution trace | 运行与等待之间的关系 |
| 明确怀疑同步竞争 | block 或 mutex profile | 阻塞或竞争成本归属 |

为什么只看 CPU 火焰图容易选错方向?
一个接口慢,可能在等数据库、网络、锁、限流槽位,也可能只是排队。CPU profile 中某个函数占比较高,不等于它占用了相同比例的请求墙钟时间;进程大部分时间不在使用 CPU 时,少量真正运行的代码仍然会在 CPU 样本中成为“第一名”。
反过来,trace 可以展示等待和调度现象,但不自动解释远端数据库执行计划,也不能把每个运行时事件都映射为某个业务请求。需要按目标任务补充日志、指标或 trace 的 task / region 标记;跨进程分析还需要分布式追踪。不要因为两者都叫 trace,就期待 go tool trace 自动给出完整 RPC 调用链。
对内存也要先选对口径:alloc_space 是累计分配,不是现在仍占着的堆;inuse_space 是存活分配的统计,不是进程 RSS。看到分配热点后,还需判断对象是否被长期保留,不能只凭一个高排名就宣布发现泄漏。
先用一个固定编码任务熟悉材料
下面是本次原创的最小基准,只编码固定的目录条目,不包含网络、锁竞争或故意阻塞,因此用于熟悉 CPU 与分配归因,而非演示真实线上高延迟。将它保存为全新目录中的 encode_test.go;文章没有执行代码,也没有生成任何实测 profile 或耗时排名。
package probe
import (
"encoding/json"
"io"
"testing"
)
type CatalogRow struct {
Code int `json:"code"`
Label string `json:"label"`
}
func BenchmarkEncodeCatalog(b *testing.B) {
// 固定输入,准备数据不计入基准计时。
rows := make([]CatalogRow, 2048)
for i := range rows {
rows[i] = CatalogRow{Code: i, Label: "sample-item"}
}
encoder := json.NewEncoder(io.Discard)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i
新目录没有 go.mod 时先初始化模块;已有模块不要重复初始化。采用同一个测试二进制,分开运行采集,避免把开启多种诊断时的结果当作无干扰基线。
# 仅在新建的示例目录中初始化,然后构建固定测试二进制 go mod init example.com/probe go test -c -o encode.test # 单独采集 CPU;跳过普通测试,只运行指定基准 ./encode.test -test.run='^$' -test.bench='^BenchmarkEncodeCatalog$' -test.benchtime=2s -test.cpuprofile=cpu.pprof # 单独采集内存 profile;保留默认采样,不把它当成逐次分配日志 ./encode.test -test.run='^$' -test.bench='^BenchmarkEncodeCatalog$' -test.benchtime=2s -test.memprofile=mem.pprof # 单独采集执行追踪;短窗口示例不等于线上通用采集时长 ./encode.test -test.run='^$' -test.bench='^BenchmarkEncodeCatalog$' -test.benchtime=2s -test.trace=trace.out
命令预期分别写出 CPU、内存和执行追踪文件,但具体占比、采样数和耗时取决于环境及工具链,不在这里预填。基准框架会做校准,因此 benchtime 不是整个进程或完整 trace 的严格总时长;分析时仍要关注真正执行目标负载的部分。
# 在同一目录查看累计 CPU 与分配成本的调用栈排名 go tool pprof -top ./encode.test cpu.pprof go tool pprof -top -alloc_space ./encode.test mem.pprof # 由读者在本机查看执行追踪;不要把查看入口公开到外网 go tool trace trace.out

从聚合热点转向等待分析时保留哪些条件?
假设真实问题是批量导出时延迟突然升高,先用监控确认 CPU、分配、请求量和异常窗口。如果 CPU 热点能够解释资源饱和,就围绕热点做单变量修改;如果 CPU 并不忙且已有线索指向等待,采集该窗口的短 trace,比盯着 CPU top 第一行反复优化更有针对性。这是排查建议,不是本例基准的实验结论。
切换工具不应顺便换了输入规模、并发数和运行阶段。至少记录 Go 工具链、二进制、GOMAXPROCS、负载条件、采集窗口,以及是否开启其他诊断。修改后应回到原来的业务指标比较;profile 里某个函数占比降低,可能只是别的成本上升,并不自动表示延迟改善。
block 与 mutex profile 还需要配置相应采集率,默认不能假设已经收集到足够样本。block 主要把同步阻塞归到等待位置,mutex 则有不同的竞争归因语义;两张排名不能当成同一种时间直接相加。采集开销也没有对所有程序通用的固定比例,应先在受控环境评估。
怎样避免拿到文件却下错结论?
不要用很短且没有目标请求的采集窗口否定一个偶发问题,也不要把不同版本二进制的符号解释混在一起。trace 文件可能很大,业务标记和诊断材料还可能携带内部名称,应按受控数据管理。本文示例只离线生成文件,不要求公开 pprof HTTP 端口。
pprof 是不是只能看 CPU?
不是。内存、goroutine、block 和 mutex 等材料分别回答不同问题。选择 pprof 后还必须选对 profile 类型与样本口径。
trace 看到 goroutine 在等,能直接改锁吗?
不能。等待可能是正常背压,也可能与网络、调度或工作分配有关。先把等待位置对应到业务负载,再决定是否需要 block、mutex profile 或外部依赖指标补充。
要不要一开始把两种工具全开?
通常先按问题取得最小必要材料,再补另一种视角。若必须同时采集,应把额外干扰计入解释,不能直接拿它与未开诊断的结果比较后归因。
Go slog JSON 日志为什么混入不可序列化字段
- 上一篇
- Go slog JSON 日志为什么混入不可序列化字段
- 下一篇
- Go runtime.ReadMemStats 为什么不能直接代表 RSS
-
- Golang · Go问答 | 58分钟前 | 垃圾回收 · Go问答 · Go 延迟 GC GOGC GOMEMLIMIT
- Go GC 控制参数改了为什么延迟反而升高
- 333浏览 收藏
-
- Golang · Go问答 | 1小时前 | go ·
- Go runtime.ReadMemStats 为什么不能直接代表 RSS
- 423浏览 收藏
-
- Golang · Go问答 | 1小时前 |
- Go slog JSON 日志为什么混入不可序列化字段
- 470浏览 收藏
-
- Golang · Go问答 | 1小时前 |
- Go time.Timer.Stop 返回 false 后怎样避免重复处理到期事件
- 246浏览 收藏
-
- Golang · Go问答 | 1小时前 |
- Go 服务器优雅退出为什么仍有请求被截断
- 451浏览 收藏
-
- Golang · Go问答 | 1小时前 |
- Go http.Request FormValue 为什么会吞掉解析错误
- 129浏览 收藏
-
- Golang · Go问答 | 1小时前 | 性能排查 · Go问答 · Go io.Writer 短写 WriteTo net.Buffers
- Go net.Buffers WriteTo 为什么可能只写出部分数据
- 196浏览 收藏
-
- Golang · Go问答 | 1小时前 |
- Go WaitGroup Add 放错位置为什么会 panic
- 325浏览 收藏
-
- Golang · Go问答 | 1小时前 |
- Go 反射设置字段失败通常缺哪一步
- 264浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 600次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 641次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 673次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 639次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 458次使用
-
- 用Nginx反向代理部署go写的网站。
- 2023-01-17 502浏览
-
- GoLand调式动态执行代码
- 2023-01-13 502浏览
-
- Go JSONJSON null 解码到指针后状态不符合预期怎么办
- 2026-10-18 501浏览
-
- Go crypto/rand.Text 的长度为什么不是固定字符数
- 2026-10-04 501浏览
-
- Go strings.ToValidUTF8 清洗日志内容的边界
- 2026-10-03 501浏览

