Go 怎么做性能优化芝麻开门篇

一、性能优化的流程

我们在对某个功能(或单个接口)做性能优化的时候。一般是该功能(或接口)性能无法满足我们的业务要求,所以被迫优化。在开始优化之前,我们需要明白一些理论知识。

1、常见的性能优化指标
1.1 响应时间
  • 定义:接口从接收到请求到返回响应所需的时间。
  • 常见优化方法:
    • 缓存:减少对数据库或后端系统的频繁访问。
    • 异步处理:将耗时操作放到后台,避免阻塞主线程。
    • 数据压缩:减小返回的数据量,减少传输时间。
    • 优化算法:使用高效的算法和数据结构
1.2 并发数
  • 定义:接口能够同时处理的并发请求数量。
  • 常见优化方法:
    • 增加并发连接池:避免线程或数据库连接的瓶颈。
    • 限流(Rate Limiting):通过限制并发请求数,保证系统稳定性。
    • 适当配置线程池、数据库连接池等资源。
1.3 吞吐量
  • 定义:单位时间内,接口可以处理的请求数量。通常以每秒请求数(RPS)来表示。
  • 常见优化方法:
    • 使用负载均衡:通过多个服务器分担请求压力。
    • 数据库优化:通过分库分表、索引等方法提高数据库查询性能。
    • 使用高效的缓存策略,减少对后端的访问。
    • 异步化操作,提升并发处理能力。
1.4 资源消耗
  • 定义:接口处理请求时消耗的系统资源(如 CPU、内存、I/O)。
  • 常见优化方法:
    • 优化数据处理流程,减少不必要的计算。
    • 使用合适的缓存机制,避免重复计算。
    • 监控和优化内存使用,防止内存泄漏。
    • 使用合适的压缩算法,减少 I/O 操作。

这里有人可能会对RPS有点不大熟悉,那么它和QPS的区别是什么呢?

2.QPS和RPS的区别
2.1 QPS(Queries Per Second)
  • 定义:QPS 表示系统每秒钟处理的查询次数,通常用于描述 数据库查询 或 搜索引擎 的查询频率。

  • 使用场景:QPS 多用于衡量查询系统(如数据库、缓存系统、搜索引擎等)的处理能力,反映的是系统能够处理的查询请求的数量。

    例如:对于一个数据库,QPS 可能表示每秒从数据库中查询的记录数。如果某个搜索引擎的 QPS 为 1000,意味着每秒钟有 1000 个查询请求提交到搜索引擎,它需要处理并返回相应的搜索结果。

2.2 RPS(Requests Per Second)
  • 定义:RPS 表示系统每秒钟能够处理的请求次数。RPS 一般用于描述 Web 服务器 或 API 接口 的吞吐量,反映的是系统接受并处理请求的能力。
  • 使用场景:RPS 多用于 Web 应用、API 服务、负载均衡器等,描述这些系统在处理 HTTP 请求时的能力。
    例如:对于一个 Web 服务器,RPS 可能表示每秒处理的 HTTP 请求数量。如果某个 Web 服务器的 RPS 为 5000,意味着每秒钟该服务器可以处理 5000 个 HTTP 请求。
指标 区别 常用场景
QPS 每秒查询次数,常用于数据库或查询引擎 数据库、缓存、搜索引擎
RPS 每秒请求次数,常用于衡量 Web 服务器或 API 的吞吐量 Web 服务器、API 接口

实际上,QPS 是针对查询类操作的吞吐量度量,RPS 是针对网络请求的吞吐量度量。

3、性能优化怎么做
3.1 确定性能优化目标

在开始做性能优化之前,首先你要明确你的优化目标是什么,不同的优化目标对应着不同的优化策略,确定好性能优化目标,一方面可以避免过度优化 ,另一方面,是避免优化不足导致我们的系统无法继续支持后续发展。比如说,接口的QPS要达成多少,接口延迟需要达到多少,通过这些目标,才能明确我们的优化是否达标。

怎么算过度优化呢,比如说,一个内部系统页面查询太慢了,现在需要2s,然后你大费周章把它优化到了10ms , 花费了大量的时间和人力调整服务架构,搞分布式,提高了系统的复杂性,增大了系统维护成本,就没太大必要,因为它只是一个内部系统,我们在做任何决策的时候,都需要权衡我们的付出和收获,即ROI思想,把核心的资源用在核心的部分上

我们可以根据实际业务情况,来设定目标,例如搞活动的时候,我们要大概预估会有多少的活动用户,来确定某个查询接口的QPS,在评估上要有一定的系统冗余,即在保守估计上✖️个1.2~1.5倍,这样即使估摸不准确,或者突然有更多的参与用户,我们的系统也可以保证尽量不出问题。

确定性能目标,需要基于你手中拥有的资源和实际业务,做决策和权衡,比如说在保证接口时延不变的情况下,提高单机的QPS

说实话,决策和权衡能力很依赖一个人的经验,当然如果我们自己没有太多经验,那么我们可以去借鉴别人的经验。多实践多思考,必有收获。

3.2 找到瓶颈

为了更好的找到某个功能的瓶颈,我们可以对这个功能进行性能测试,单个请求测试和集群整体压测(全链路压测)。

单个请求测试的目的是确定我们自己的服务是否存在可优化点,比如接口延时是否符合预期,服务资源的使用是否合理,有没有异常报错

全链路压测的目的则是针对某个具体的功能进行上下游压测,方便我们找到整个链路中其他服务的卡点,来确定瓶颈是在本服务还是其他服务。

经常做活动服务的朋友应该对这方面比较有认知,活动对接口时延,QPS等要求都特别高,因此每次活动前都要模拟压测,确保活动当天不会出问题,一般都是进行全链路压测,通过链路追踪工具可以方便的看出具体是在哪个节点比较慢(本质上就是打日志分析),如果是本服务的瓶颈,我们可以针对性对具体的接口进行优化,如果是其他下游服务的瓶颈,则需要让其他团队帮忙优化。

当瓶颈点有多个时,我们可以找到最大的影响瓶颈,一步步做优化,为了控制好改造的成本,先解决一个相对好解决或者说是改造价值比较大的瓶颈,后续性能目标达不到再继续优化其他的点。

3.2 怎么对瓶颈进行优化

找到了瓶颈之后,我们就要分析瓶颈的原因是什么,不同的瓶颈分析的思路也是不同的。

如果是本服务的接口:在压测过程中,我们可以观察服务的资源消耗情况(内存、CPU、协程数),通过资源的使用情况来判断接口存在的问题。如果发现是内存的消耗过高导致单机吞吐量低,可以通过 pprof 抓取内存的火焰图,看具体是哪段代码在频繁的分配内存

而对于下游组件的瓶颈,例如说mysql。有一些常见的分析思路:

1、是否是服务调用 MySQL 的 IO 时间过长,这种情况可以看下是不是返回的数据量过大。

2、查看我们的 MySQL 请求是否存在慢查询,判断 SQL、索引使用合不合理。

3、如果排除了上述两个因素,需要分析下是否是因为我们的表太大、从库不够,还是说 MySQL 本身支持不了这么高的吞吐和这么严的延时要求

本质上观察几个点。

  • 服务和mysql的连接是否正常(连接池、网络等)
  • mysql 的响应是否正常(慢查询、查询量等)

针对Go来说。一个接口的瓶颈常见的问题有如下几点。

  • 网络吞吐量
  • Go服务资源限制
  • 代码逻辑
  • 下游组件的瓶颈

用人话说,首先可以先看看是不是代码问题,代码是不是有存在循环的数据库查询,可以改成批量查询来优化,同步操作能不能改成异步处理,内存使用是否合理,有没有内存泄漏,有没有协程泄漏,能不能加缓存层,网络调用是否有复用连接。

网络本身是否有问题,带宽,网络延迟。

服务的资源(内存、CPU、硬盘)够不够用

下游服务是不是高性能的组件。使用方式是否正确。

3.3 验证目标达成

为了验证性能目标是否达成,我们可以对调优过的应用进行性能测试,与优化前的各项指标进行对比,观测其是否符合预期。

如果瓶颈点没有消除或者性能指标不符合预期,则需要重复找瓶颈点、瓶颈分析、性能调优和验证目标是否达成的步骤,直到达成性能目标

二、性能优化的工具(篇幅太长没必要,第二篇再写)

pprof 工具
benchmark 功能
trace 工具
相关推荐
王中阳Go4 小时前
又遇百度,能否 hold 住?
后端·go
drebander5 小时前
Netty 性能优化与调试指南
网络·性能优化·netty
小石潭记丶1 天前
Windows如何安装go环境,离线安装beego
go·beego
developerFBI1 天前
计算机进制的介绍
go
Amd7941 天前
索引与性能优化
性能优化·数据库管理·sql优化·数据库索引·数据访问·索引类型·查询效率
夜半被帅醒1 天前
并发编程性能优化策略设计模式的实际应用JAVA
java·设计模式·性能优化
迈克柯里喵1 天前
CUDA算子手撕与面试指南
c++·算法·面试·职场和发展·性能优化·求职招聘·gpu算力
许留山1 天前
写一个方法,将2N个元素的数组随机两两组合
django·go
所幸你是例外2 天前
简单了解一下 Go 语言构建约束?
经验分享·笔记·后端·学习·golang·go·学习方法