这是我参与「第五届青训营 」伴学笔记创作活动的第 10 天
此次课程主要介绍了基于pprof工具的项目性能分析。
pprof的采样过程和原理
CPU采样
CPU采样对象是函数调用和它们占用的时间,采样率为固定值100次/s,采样时间为从手动启动到手动结束。
其采样主要基于系统的定时信号机制。Go进程启动的时候,向操作系统注册定时器,定时器触发就采样CPU数据。操作系统会每10ms向进程发送一次SIGPROF信号,而进程每次接收到信号就会记录调用堆栈,每100ms读取已经记录的调用栈记录到写缓冲,写缓冲写到输出流保存到文件,结束时结束定时器。
heap采样
采样程序通过内存分配器在堆上分配和释放内存,记录分配或释放的大小和数量,而采样率是每分配512KB就记录一次,可以在运行开头修改,采样时间上是从程序运行开始到采样时,支持四种采样指标alloc_space, alloc_objects,inuse_space,inuse_object。计算使用分配内存的算法是用已分配内存减去空闲内存。
协程与线程创建采样
Gouroutine主要记录所有用户发起且在运行中的goroutine以及runtime.main的调用栈信息,而ThreadCreate记录程序创建的所有系统线程信息,其采样流程都从Stop The World开始,遍历协程链表或切片,输出创建堆栈,再Start the World 通过比较两个时间点的差值来判断时间段指标。
阻塞和锁采样
阻塞操作主要采样阻塞操作的次数和耗时,在发生阻塞操作后,向文件报告调用栈和消耗时间,当阻塞耗时超过阈值时会被记录。mutex主要采样争抢锁的次数和耗时,在发生锁竞争操作后,记录固定比例的锁操作。
性能调优案例
按照场景可以分为业务服务优化,基础库优化(如监控数据库,日志数据库等)和Go语言优化。
业务服务优化
部分基本概念解说如下:
- 服务:是能单独部署,承载一定功能的程序。
- 依赖:比如service A的功能实现依赖ServiceB的相应结果,也即为ServiceA依赖ServiceB。
- 调用链路:能支持一个接口请求的相关服务集合以及其相互之间的依赖关系。
- 基础库:公共的工具包,中间件。
流程:
- 建立服务性能评估手段:需要多维度和高层次,不同负载情况的性能表现差异,而请求流量构造,要注意不同请求参数覆盖逻辑不同。需要注意压测范围是基于单机器压测还是集群压测,以及性能数据采集是基于单机或集群。单机性能是非线性的,即使提升CPU使用率,成功的请求数量也到达瓶颈。压测平台关注数据包括接口耗时,平均QPS,最大QPS,请求数等。
- 分析性能数据,定位性能瓶颈:基于pprof的火焰图可以观察代码什么位置出现性能瓶颈问题。分析性能数据,利用率在80%和利用率在40%的不同场景下可能存在性能问题等。
- 重点优化项改造:改造基础是正确性,其验证方法是,相同服务,相同请求再请求一次,新修改返回值和之前数据录制的回放是否一致。
- 优化效果验证:通过重复压测验证,或者基于上线评估优化效果,关注服务监控, 逐步放量,然后收集性能数据。
- 进一步优化:在实际场景中,可能存在服务调用接口多次重复使用问题,可以规范接口明确场景需求,此外,分析业务流程,通过业务流程优化来提升服务性能。
基础库优化
优化流程为,首先分析基础库核心逻辑和性能瓶颈,包括设计完善改造方案,数据按需获取,数据序列化协议优化等,其次是进行内部压测验证,以及推广业务服务落地验证。
Go 语言优化
编译器运行时优化,是适用范围最广的优化,可以实现优化内存分配策略,优化代码编译流程,生成更高效程序,进行内部压测验证,以及推广业务服务落地验证,优点是接入简单,只需调整编译配置,而且通用性强。