性能调优实战 | 青训营笔记

276 阅读6分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第4篇笔记

02性能调优

2.1性能调优简介

  • 原则:数据而不是猜测,关注最大瓶颈而不是细节,不要过早也不要过度优化

2.2性能分析工具pprof实战

  • 可视化和分析性能分析数据的工具
  • 用途:
    • CPU Profiling:CPU 分析,按照一定的频率采集所监听的应用程序 CPU(含寄存器)的使用情况,可确定应用程序在主动消耗 CPU 周期时花费时间的位置
    • Memory Profiling:内存分析,在应用程序进行堆分配时记录堆栈跟踪,用于监视当前和历史内存使用情况,以及检查内存泄漏
    • Block Profiling:阻塞分析,记录 goroutine 阻塞等待同步(包括定时器通道)的位置
    • Mutex Profiling:互斥锁分析,报告互斥锁的竞争情况

2.2.1功能简介

image.png

2.2.2排查实战

浏览器查看指标描述备注
allocs内存分配情况可以用浏览器打开,但可读性不高
block阻塞操作情况可以用浏览器打开,但可读性不高
cmdline程序启动命令及参数可以用浏览器打开,显示 ./go-pprof-practice
goroutine当前所有goroutine的堆栈信息可以用浏览器打开,但可读性不高
heap堆上内存使用情况(同alloc)可以用浏览器打开,但可读性不高
mutex锁竞争操作情况可以用浏览器打开,但可读性不高
profileCPU占用情况浏览器打开会下载文件
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
  • list:根据正则表达式查找代码行
  • web:调用关系可视化 查看PProf的自带的可视化界面go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/heap"
  • heap-堆内存问题分析
    • 排查内存占用过高
      • heap,使用top-list-web查看占用最多内存空间的代码
    • 排查频繁内存回收
      • 频繁GC会影响程序性能,查找不停申请内存的地方(allocs),需要运行一段时间
        • 在 golang 里,对象是使用堆内存还是栈内存,由编译器进行逃逸分析并决定,如果对象不会逃逸,便可在使用栈内存,但总有意外,就是对象的尺寸过大时,便不得不使用堆内存。

使用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 程序的垃圾回收》