这是我参与「第三届青训营 -后端场」笔记创作活动的的第4篇笔记
02性能调优
2.1性能调优简介
- 原则:数据而不是猜测,关注最大瓶颈而不是细节,不要过早也不要过度优化
2.2性能分析工具pprof实战
- 可视化和分析性能分析数据的工具
- 用途:
- CPU Profiling:CPU 分析,按照一定的频率采集所监听的应用程序 CPU(含寄存器)的使用情况,可确定应用程序在主动消耗 CPU 周期时花费时间的位置
- Memory Profiling:内存分析,在应用程序进行堆分配时记录堆栈跟踪,用于监视当前和历史内存使用情况,以及检查内存泄漏
- Block Profiling:阻塞分析,记录 goroutine 阻塞等待同步(包括定时器通道)的位置
- Mutex Profiling:互斥锁分析,报告互斥锁的竞争情况
2.2.1功能简介
2.2.2排查实战
| 浏览器查看指标 | 描述 | 备注 |
|---|---|---|
| allocs | 内存分配情况 | 可以用浏览器打开,但可读性不高 |
| block | 阻塞操作情况 | 可以用浏览器打开,但可读性不高 |
| cmdline | 程序启动命令及参数 | 可以用浏览器打开,显示 ./go-pprof-practice |
| goroutine | 当前所有goroutine的堆栈信息 | 可以用浏览器打开,但可读性不高 |
| heap | 堆上内存使用情况(同alloc) | 可以用浏览器打开,但可读性不高 |
| mutex | 锁竞争操作情况 | 可以用浏览器打开,但可读性不高 |
| profile | CPU占用情况 | 浏览器打开会下载文件 |
| threadcreate | 当前所有创建的系统线程的堆栈信息 | 可以用浏览器打开,但可读性不高 |
| trace | 程序运行跟踪信息 | 浏览器打开会下载文件 |
因为 cmdline 没有什么实验价值,trace 与本文主题关系不大,threadcreate 涉及的情况偏复杂,所以这三个类型的采样信息这里暂且不提。
在程序启动之后在命令行里使用:go tool pprof http://localhost:6060/debug/pprof/profile?seconds=10进入命令交互式终端,top命令查看占用较高的调用,list + func查看问题具体在代码的哪一个位置,web命令图形化显示调用栈的信息。
- top
- 输入top之后会出现五列
- flat:给定函数上运行耗时
- flat%:同上的 CPU 运行耗时总比例
- sum%:给定函数累积使用 CPU 总比例
- cum:当前函数加上它之上的调用运行总耗时
- cum%:同上的 CPU 运行耗时总比例
- -inuse_space:分析应用程序的常驻内存占用情况
- -alloc_objects:分析应用程序的内存临时分配情况
- 什么情况下flat=cum?什么情况下flat=0
- cum-flat得到的是函数中调用其他函数所消耗的资源
- 所以在函数中没有对其他函数进行调用时,cum - flat = 0
- 函数中只有调用其他函数的逻辑时,flat = 0
- 输入top之后会出现五列
- list:根据正则表达式查找代码行
- web:调用关系可视化
查看PProf的自带的可视化界面
go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/heap" - heap-堆内存问题分析
- 排查内存占用过高
- heap,使用top-list-web查看占用最多内存空间的代码
- 排查频繁内存回收
- 频繁GC会影响程序性能,查找不停申请内存的地方(allocs),需要运行一段时间
- 在 golang 里,对象是使用堆内存还是栈内存,由编译器进行逃逸分析并决定,如果对象不会逃逸,便可在使用栈内存,但总有意外,就是对象的尺寸过大时,便不得不使用堆内存。
- 频繁GC会影响程序性能,查找不停申请内存的地方(allocs),需要运行一段时间
- 排查内存占用过高
使用web页面查看goroutine的调用情况go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/goroutine",正常查看数据调用图并不清晰,可以查看View菜单中的Flame Graph视图,通过火焰图进行性能分析
- goroutine 泄露也会导致内存泄露
- mutex 锁的竞争会导致阻塞
- block channel也会导致阻塞
- 两个block只显示一个,因为另一个总用时太少被过滤掉了
总结
以上介绍了五种使用pprof采集的常用性能指标: CPU、堆内存、Goroutine、 锁竞争和阻塞;两种展示方式:交互式终端和网页;四种视图Top、Graph、 源码和火焰图。
pprof除了http的获取方式之外,也可以直接在运行时调用runtime/pprof包将指标数据输出到本地文件。视图中还有一个更底层的「反汇编」视图。
2.2.3采样过程和原理
- CPU采样会记录所有的调用栈和它们的占用时间
- 堆内存采样,内存采样在实现上依赖了内存分配器的记录,所以只记录在对上的分配,且会参与GC的内存,一些其他的内存分配。内存的采样是一个持续的过程。
- alloc 的两项指标是 从程序运行开始的累计指标
- inuse 的两项指标通过累计分配减去累计释放得到的程序当前持有的指标。
- goroutine和系统线程采样,记录所有用户发起且在运行中的goroutine/创建的所有系统线程的信息
- block和mutex,记录阻塞耗时发生的调用栈、次数和耗时。block记录的是一个阈值,锁记录的是一个比例。
2.3性能调优案例
2.3.1业务服务优化
2.3.1.1基本概念
- 服务:能单独部署,承载一定功能的程序
- 依赖:Service A 的功能实现依赖 Service B 的响应结果,称为 Service A 依赖 Service B
- 调用链路:能支持一个接口请求的相关服务集合及其相互之间的依赖关系
- 基础库:公共的工具包、中间件
2.3.1.2业务优化
-
流程
- 建立服务性能评估手段
- 分析性能数据,定位性能瓶颈
- 重点优化项改造
- 优化效果验证
-
建立压测评估链路
- 服务性能评估
- 构造请求流量
- 压测范围
- 性能数据采集
-
分析性能火焰图,定位性能瓶颈
- pprof 火焰图
-
重点优化项分析
- 规范组件库使用
- 高并发场景优化
- 增加代码检查规则避免增量劣化出现
- 优化正确性验证
-
上线验证评估
- 逐步放量,避免出现问题
-
进一步优化,服务整体链路分析
- 规范上游服务调用接口,明确场景需求
- 分析业务流程,通过业务流程优化提升服务性能
2.3.2基础库优化
-
适应范围更广,覆盖更多服务
-
AB 实验 SDK 的优化
- 分析基础库核心逻辑和性能瓶颈
- 完善改造方案,按需获取,序列化协议优化
- 内部压测验证
- 推广业务服务落地验证
2.3.3Go 语言优化
-
适应范围最广,Go 服务都有收益
-
优化方式
- 优化内存分配策略
- 优化代码编译流程,生成更高效的程序
- 内部压测验证
- 推广业务服务落地验证
2.4总结
- 对于pprof工具,熟悉相关功能,理解基本原理,能够使用改工具进行性能排查
- 性能调优的重点:保证正确性,定位主要的瓶颈
参考
golang pprof 实战:blog.wolfogre.com/posts/go-pp…
《Golang 大杀器之性能剖析 PProf》
《如何监控 golang 程序的垃圾回收》