每日一Go-27、Go语言进阶-性能剖析与优化

0 阅读3分钟

当你的Go服务上线后,你肯会遇到这些问题:

  • CPU超过90%甚至100%

  • 内存突然飙升

  • 某个接口偶尔变慢

  • 某段代码特别耗时

pprof就是Go官方为你准备的扫描仪,用来帮你看清程序内部发生了什么。

1、pprof是什么

pprof是Go内置的性能分析工具,支持以下分析:

类型用途
CPU Profile查看CPU热点,找出最耗时的函数
Heap Profile找出内存占用、内存泄漏来源
Goroutine查看当前goroutine调度、阻塞情况
Mutex分析锁竞争
Block识别由于通道、锁等被阻塞的点

2、如何使用pprof(Web方式)

最常用:用于Web服务(Gin/标准http)

package main

import (
"log"
"net/http""net/http/pprof"
)

func main() {
//开启pprof的方式
        go func() {
		log.Println(http.ListenAndServe(":6060", nil))
	}()

	http.ListenAndServe(":8080", nil)
}

启动服务后,访问以下地址:

指标地址
CPUhttp://localhost:6060/debug/pprof/profile?seconds=30
内存http://localhost:6060/debug/pprof/heap
goroutinehttp://localhost:6060/debug/pprof/goroutine
web汇总http://localhost:6060/debug/pprof/

web汇总如图

图片

3、单独在代码中采集 CPU Profile和内存

3.1 主要用于命令行工具

f, _ := os.Create("cpu.prof")
pprof.StartCPUProfile(f)
time.Sleep(30 * time.Second)
pprof.StopCPUProfile()

3.2 内存

f, _ := os.Create("mem.prof")
pprof.WriteHeapProfile(f)

4、如何查看pprof文件

4.1 命令行查看

go tool pprof cpu.prof

4.2 打开web页面查看

go
8081

会自动弹出web界面

图片

图片

5、查看火焰图

火焰图的核心规则是:越宽的块=越多的CPU时间/内存占用;越高的块=调用链更深

6、举个实例

package main

import (
"log"
"math"
"net/http""net/http/pprof"
)

// 这个函数消耗大量的CPU计算
func slow() {
for i := 0; i < 1e7; i++ {
		math.Sqrt(float64(i))
	}
}
func main() {
//开启pprof的方式
go func() {
		log.Println(http.ListenAndServe(":6060", nil))
	}()

	http.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
		slow()
		w.Write([]byte("done"))
	})
	http.ListenAndServe(":8080", nil)
}

启动服务,然后不断地访问测试地址,同时收集cpu的pprof信息

//收集cpu信息
curl http://localhost:6060/debug/pprof/profile?seconds=20 -o cpu.prof
//查看火焰图
go tool pprof -http=:8081 cpu.prof

启动火焰图后,我们看见确实math.Sqrt占用了大量的CPU资源。这就是你要优化的地方。

图片

7、内存泄漏定位示例

var data [][]byte

func leak() {
	b := make([]byte, 10<<20) // 10MB
	data = append(data, b)    // 不断增长
}

采集内存

curl
6060
20

打开火焰图

go
8082

在图上我们看见main.leak占用内存最多。

图片

8、Go性能优化的基本思路

CPU优化方向

  • 减少不必要的计算

  • 热点函数重写

  • 使用更高效的数据结构

  • 较少锁竞争

  • 避免gotoutine大量创建销毁

内存优化方向

  • 减少临时对象分配,使用对象复用池 sync.Pool

  • 减少逃逸到堆,减少返回指针、减少使用局部变量

  • 检查是否存在内存泄漏

  • 缓存热点数据

并发优化方向

  • 尽量减少共享内存

  • 任务拆分避免过度

  • 避免无约束的goroutine,用worker pool

9、安全

生产环境不要暴露pprof,可用内网防火墙或者白名单可以访问,其他的均禁止。

10、源码地址

pan.baidu.com/s/1B6pgLWfS…

性能优化和人生一样,不是拼命就能更快,而是找到真正拖慢你的瓶颈,持续迭代,人生和程序都会跑的更快更稳。


如果您喜欢这篇文章,请点赞、推荐+分享给更多朋友,万分感谢!