# Go 服务突然变慢，pprof 官方怎么接到性能剖析这一层？

**URL:** <https://www.shiqidu.com/t/topic/2073>\
**Category:** 开发调优\
**Tags:** Go, 编程, 服务器, 问与答, 开源软件\
**Created:** [2026 年11 月 14 日 04:25 UTC](https://www.shiqidu.com/t/topic/2073 "2026-11-14T04:25:05Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

作者： ![shiqidu\_editor](https://www.shiqidu.com/user_avatar/www.shiqidu.com/shiqidu_editor/32/528_2.png) [@shiqidu\_editor](https://www.shiqidu.com/u/shiqidu_editor)\
发布日期： [2026 年11 月 14 日 04:25 UTC](https://www.shiqidu.com/t/topic/2073/1 "2026-11-14T04:25:05Z")

</div>

延迟上去了，结构化日志里的字段却都对得上。时间花在哪一段代码上，日志本身答不了。字段怎么打进结构化日志，可以看站内这篇 [Go 标准库 slog](https://www.shiqidu.com/t/topic/2059)。这里要接的是性能剖析，看 CPU、内存这些开销落在哪，两件事不要混成一篇。

[net/http/pprof 包文档](https://pkg.go.dev/net/http/pprof) 写明：这个包通过 HTTP 服务提供运行时剖析数据，格式就是 pprof 可视化工具所期望的那种。常见用法是为了副作用而导入，让它把处理函数注册上：

```go
import _ "net/http/pprof"

```

路径都以 `/debug/pprof/` 开头。文档还写了：从 Go 1.22 起，这些路径都必须用 GET 访问。如果程序本身还没有在跑 HTTP 服务，需要自己起一个。文档给的例子绑在 `localhost:6060`，写法是在 `main` 里加上：

```go
go func() {
    log.Println(http.ListenAndServe("localhost:6060", nil))
}()

```

默认会用 `DefaultServeMux`。如果没用默认 mux，就要把处理函数注册到自己正在用的那个 mux 上。[Diagnostics 文档的 Profiling 一节](https://go.dev/doc/diagnostics#profiling) 也写了，这些处理函数可以挂到别的路径和端口。该页举过把 `pprof.Profile` 注册到 `:7777` 上某个自定义路径的例子。剖析数据可以在测试里用 `go test` 采集，也可以通过 `net/http/pprof` 提供的端点采集，再用 pprof 工具过滤和可视化。

官方预先提供的 profile 类型，Diagnostics 页从这些讲起：`cpu` 看程序在主动消耗 CPU 周期时把时间花在哪（不是在睡眠或等 I/O）。`heap` 报告内存分配样本，用来观察当前和历史上的内存占用，并检查有没有泄漏。另外还有 `threadcreate`、`goroutine`，以及默认不启用的 `block`、`mutex`。后两者分别要先调用 `runtime.SetBlockProfileRate` 和 `runtime.SetMutexProfileFraction`。包文档的用法示例也是从 heap 和一段 CPU 剖析开始的：

```auto
go tool pprof http://localhost:6060/debug/pprof/heap
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30

```

第二条会采集 30 秒 CPU 剖析。`Profile` 这个处理函数对应的就是 CPU profile，文档写明持续时间由 `seconds` 这个 GET 参数决定，不传则默认 30 秒。同一参数用在 CPU profile 和 trace 上是采集时长。用在 allocs、block、goroutine、heap、mutex、threadcreate 这些 profile 上，文档写的是返回一份增量 profile。查询参数还可以用来改响应格式：`debug=0` 是二进制（默认），大于 0 则是纯文本。heap 上的 `gc=N` 在大于 0 时会先跑一轮垃圾回收再做剖析。想先看有哪些 profile 可用，文档让你打开 `http://localhost:6060/debug/pprof/`。默认情况下，`runtime/pprof.Profile` 里列出的那些 profile 都可以通过 Handler 拿到，另外还有这个包自己定义的 Cmdline、Profile、Symbol 和 Trace。执行跟踪数据是给 `go tool trace` 用的：文档的例子是先用 `curl` 把 `/debug/pprof/trace?seconds=5` 存成文件，再交给 `go tool trace` 打开。Trace 的持续时间同样由 `seconds` 决定，不传则默认 1 秒。

[Go 博客介绍 pprof 的文章](https://go.dev/blog/pprof) 后半也写了同一套 HTTP 接口：把 `net/http/pprof` 导入后，`go tool pprof` 只要给服务的剖析 URL，就会下载并查看一份实时 profile。博客前半演示的是独立程序里用 `runtime/pprof` 把 CPU 或内存 profile 写到文件，再交给 `go tool pprof` 看。对已经在跑的服务，走 HTTP 这一层更直接。Diagnostics 把可视化交给 `go tool pprof`，可以看文本列表、调用关系图，也可以用 weblist 在 HTML 里一行行对照源码耗时。上游 pprof 还支持火焰图，用来沿着某一条祖先路径放大或缩小。生产环境做剖析是可以的，但启用某些 profile（例如 CPU profile）会带来开销，建议先估开销再打开。多项剖析可能互相干扰，更稳妥的是一次只采一种。若服务有很多副本，该页的建议是周期性、随机挑一个进程来采集，把结果留下来再分析。它解决的是把运行时开销定位到代码路径上，并不是搭一整套监控或追踪平台。

你们现在是想先看 CPU 把时间花在哪，还是内存一直在涨、准备先拉一份 heap？
