10.2 pprof工具详解
pprof基础概念
什么是pprof?
pprof是Go官方提供的性能分析工具,用于:
- CPU性能分析
- 内存分配分析
- Goroutine分析
- 阻塞分析
- 互斥锁分析
数据采集方式
go
// 方式1: 通过HTTP endpoint(推荐)
import _ "net/http/pprof"
func main() {
go func() {
http.ListenAndServe("localhost:6060", nil)
}()
// 应用代码...
}
// 访问方式
// http://localhost:6060/debug/pprof/
// 方式2: 手动采集
import "runtime/pprof"
func main() {
// CPU分析
f, _ := os.Create("cpu.prof")
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
// 应用代码...
// 内存分析
f2, _ := os.Create("mem.prof")
pprof.WriteHeapProfile(f2)
f2.Close()
}CPU性能分析实战
采集CPU数据
bash
# 方式1: 通过HTTP endpoint采集(30秒)
curl -o cpu.prof http://localhost:6060/debug/pprof/profile?seconds=30
# 方式2: 使用go tool pprof
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
# 方式3: 在benchmark中采集
go test -cpuprofile=cpu.prof -bench=.分析CPU数据
bash
# 交互式分析
go tool pprof cpu.prof
# 常用命令
(pprof) top10 # Top 10 CPU消耗函数
(pprof) top10 -cum # 按累计时间排序
(pprof) list main # 查看函数详情
(pprof) web # 生成可视化图(需要graphviz)
# 示例输出
flat flat% sum% cum cum%
250ms 25.00% 25.00% 250ms 25.00% runtime.memmove
200ms 20.00% 45.00% 450ms 45.00% main.processData
150ms 15.00% 60.00% 150ms 15.00% runtime.mallocgc火焰图分析
bash
# 生成火焰图(需要go 1.11+)
go tool pprof -http=:8080 cpu.prof
# 浏览器访问 http://localhost:8080
# 选择 VIEW -> Flame GraphCPU优化案例
问题定位:
bash
(pprof) top5
400ms 40.00% runtime.memmove
300ms 30.00% main.processData
200ms 20.00% runtime.mallocgc优化前代码:
go
// ❌ 频繁切片append导致内存分配和拷贝
func processData(data []byte) []byte {
var result []byte
for _, chunk := range splitData(data) {
result = append(result, chunk...) // 频繁扩容
}
return result
}优化后代码:
go
// ✅ 预分配容量
func processData(data []byte) []byte {
chunks := splitData(data)
totalSize := 0
for _, chunk := range chunks {
totalSize += len(chunk)
}
result := make([]byte, 0, totalSize) // 预分配
for _, chunk := range chunks {
result = append(result, chunk...)
}
return result
}
// 性能提升: 40% → 15% CPU消耗内存性能分析实战
采集内存数据
bash
# 方式1: HTTP endpoint
curl -o mem.prof http://localhost:6060/debug/pprof/heap
# 方式2: benchmark
go test -memprofile=mem.prof -bench=.
# 方式3: 程序退出时保存
func main() {
defer func() {
f, _ := os.Create("mem.prof")
pprof.WriteHeapProfile(f)
f.Close()
}()
// ...
}分析内存数据
bash
# 交互式分析
go tool pprof -sample_index=alloc_space mem.prof # 总分配
go tool pprof -sample_index=inuse_space mem.prof # 当前使用
# Top 10内存分配
(pprof) top10
100MB 30.00% 30.00% 100MB 30.00% main.buildRequest
80MB 24.00% 54.00% 80MB 24.00% main.processData
50MB 15.00% 69.00% 50MB 15.00% runtime.malg
# 查看函数详情
(pprof) list main.buildRequest内存泄露排查
步骤1: 建立基线
bash
# 记录初始内存使用
curl http://localhost:6060/debug/pprof/heap > base.prof步骤2: 压测后对比
bash
# 压测一段时间
ab -n 10000 http://localhost:8080/api
# 记录压测后内存
curl http://localhost:6060/debug/pprof/heap > after.prof
# 对比差异
go tool pprof -base base.prof after.prof步骤3: 定位泄露
bash
(pprof) top
50MB 100.00% main.GlobalCache.Set
30MB 60.00% main.NewRequest内存泄露案例:
go
// ❌ 全局map无限增长
var GlobalCache = make(map[string]*Data)
func Set(key string, data *Data) {
GlobalCache[key] = data // 永远不删除
}
// ✅ 使用LRU缓存或定期清理
type LRUCache struct {
mu sync.Mutex
data map[string]*Data
queue []string
max int
}
func (c *LRUCache) Set(key string, data *Data) {
c.mu.Lock()
defer c.mu.Unlock()
if len(c.data) >= c.max {
// 删除最老的数据
oldest := c.queue[0]
delete(c.data, oldest)
c.queue = c.queue[1:]
}
c.data[key] = data
c.queue = append(c.queue, key)
}Goroutine分析
采集Goroutine数据
bash
# 方式1: HTTP endpoint
curl -o goroutine.prof http://localhost:6060/debug/pprof/goroutine
# 方式2: runtime
pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
# 方式3: pprof工具
go tool pprof http://localhost:6060/debug/pprof/goroutine分析Goroutine泄露
bash
# 查看goroutine数量
(pprof) top
1000 50.00% main.worker
500 25.00% main.handler
# 查看堆栈
(pprof) traces
-----------+-------------------------------------------------------
1000 main.worker
runtime.mainGoroutine泄露案例:
go
// ❌ Goroutine泄露
func handler() {
for {
conn, err := listener.Accept()
if err != nil {
log.Println(err)
continue
}
go handleConn(conn) // 无限创建,永不退出
}
}
func handleConn(conn net.Conn) {
// 没有超时和退出机制
for {
data := make([]byte, 1024)
conn.Read(data) // 如果客户端不发送数据,goroutine永久阻塞
}
}
// ✅ 正确处理
func handleConn(conn net.Conn) {
defer conn.Close()
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
done := make(chan struct{})
go func() {
defer close(done)
// 处理连接...
}()
select {
case <-done:
return
case <-ctx.Done():
return // 超时退出
}
}Block和Mutex分析
Block分析(阻塞)
go
// 开启block profiling
runtime.SetBlockProfileRate(1) // 采样率:1=全部采样bash
# 采集数据
curl -o block.prof http://localhost:6060/debug/pprof/block
# 分析
go tool pprof block.prof
(pprof) top
500ms main.processData
200ms main.writeToDBMutex分析(互斥锁)
go
// 开启mutex profiling
runtime.SetMutexProfileFraction(1) // 采样率bash
# 采集数据
curl -o mutex.prof http://localhost:6060/debug/pprof/mutex
# 分析
go tool pprof mutex.prof
(pprof) top
300ms main.processRequest
150ms main.updateCache锁优化案例:
go
// ❌ 细粒度锁导致高竞争
var mu sync.Mutex
var cache = make(map[string]string)
func Get(key string) string {
mu.Lock()
defer mu.Unlock()
return cache[key]
}
// ✅ 使用读写锁
var mu sync.RWMutex
var cache = make(map[string]string)
func Get(key string) string {
mu.RLock() // 读锁,允许并发读
defer mu.RUnlock()
return cache[key]
}
// ✅ 使用sync.Map(高并发场景)
var cache sync.Map
func Get(key string) (string, bool) {
value, ok := cache.Load(key)
if !ok {
return "", false
}
return value.(string), true
}Trace工具
使用trace
bash
# 采集trace数据
curl -o trace.out http://localhost:6060/debug/pprof/trace?seconds=5
# 可视化分析
go tool trace trace.outtrace分析维度
# 浏览器打开后可以看到:
1. View trace: 时间线视图
- Goroutines: Goroutine调度情况
- Network: 网络阻塞
- Syscalls: 系统调用
- GC: 垃圾回收
2. Goroutine analysis: Goroutine分析
- 每个goroutine的运行时间
- 阻塞时间
- 同步等待时间
3. Network/Syscall/Heap profile: 各维度分析性能分析SOP
标准流程
1. 建立基线
- 采集初始性能数据
- 记录关键指标(QPS、延迟、CPU、内存)
2. 问题定位
- 使用pprof分析瓶颈
- 查看top N函数
- 生成火焰图
3. 优化实施
- 针对性优化代码
- 避免过早优化
- 一次只改一个点
4. 验证效果
- 重新采集数据
- 对比优化前后
- 确保性能提升
5. 监控告警
- 建立性能监控
- 设置告警阈值
- 持续跟踪常用命令速查
bash
# CPU分析
go tool pprof cpu.prof
(pprof) top10
(pprof) list functionName
(pprof) web
# 内存分析
go tool pprof -sample_index=inuse_space mem.prof
go tool pprof -sample_index=alloc_space mem.prof
(pprof) top10
# 对比分析
go tool pprof -base base.prof after.prof
# Goroutine分析
go tool pprof http://localhost:6060/debug/pprof/goroutine
# Trace分析
go tool trace trace.out
# Benchmark
go test -bench=. -benchmem -cpuprofile=cpu.prof最佳实践
[ ] 生产环境开启pprof endpoint
[ ] 定期采集性能数据
[ ] 建立性能基线
[ ] 使用火焰图快速定位
[ ] 一次只优化一个瓶颈
[ ] 数据驱动决策
[ ] Benchmark验证效果
[ ] 建立监控告警
[ ] 文档化优化过程
[ ] 团队分享经验学习检查点
完成本章节后,验证:
- [ ] 掌握pprof各种采集方式
- [ ] 能分析CPU和内存瓶颈
- [ ] 排查过内存泄露问题
- [ ] 检测并修复Goroutine泄露
- [ ] 使用trace分析并发问题
- [ ] 会生成和分析火焰图
- [ ] 建立完整的性能分析流程