接口性能优化方法总结

接口性能优化是后端开发人员经常碰到的一道面试题,因为它是一个跟开发语言无关的公共问题。

这个问题既可以很简单,也可以相当复杂。

导致接口性能问题的原因多种多样,不同项目的不同接口,其原因可能各不相同。

下面列举几种常见的性能优化方案:

一、索引优化

不管是查询、新增、修改,一个接口必然会去后端请求数据库,所以直接从sql层面进行优化是在设计时必须考虑的一方面。如果查询的时候经常使用的某些字段没有添加索引,就可以根据条件给某些字段添加索引,然后在看sql的具体耗时。

如果添加上了索引,要看一下这个索引有没有生效要怎么看呢?

答案是:可以使用 EXPLAIN 命令,查看 MySQL 的执行计划,它会显示索引的使用情况。

这个命令将显示查询的执行计划,包括使用了哪些索引。

如果索引生效,你会在输出结果中看到相关的信息。

通过这几列可以判断索引使用情况,执行计划包含列的含义如下图所示:

SQL语句没有使用索引,除去没有建索引的情况外,最大的可能性是索引失效了。

以下是索引失效的常见原因:

二、SQL优化

如果优化了索引之后效果不明显,接下来可以尝试优化一下SQL语句,因为相对于修改Java代码来说,改造SQL语句的成本要小得多。

以下是SQL优化的15个小技巧:

三、远程调用

有时候,我们需要在一个接口中调用其他多个服务的接口。

例如,有这样的业务场景:

在用户信息查询接口中需要返回以下信息:用户名称、性别、等级、头像、积分和成长值。

其中,用户名称、性别、等级和头像存储在用户服务 中,积分存储在积分服务 中,成长值存储在成长值服务中。为了将这些数据统一返回,我们需要提供一个额外的对外接口服务。

因此,用户信息查询接口需要调用用户查询接口、积分查询接口和成长值查询接口,然后将数据汇总并统一返回。

调用过程如下图所示:

调用远程接口总耗时 530ms = 200ms + 150ms + 180ms

显然这种串行调用远程接口性能是非常不好的,调用远程接口总的耗时为所有的远程接口耗时之和。

(1)串行改并行

调用远程接口的总耗时为200ms,这等于耗时最长的那次远程接口调用时间。

在Java 8之前,可以通过实现Callable接口来获取线程的返回结果。

在Java 8之后,可以通过CompletableFuture类来实现这一功能。

以下是一个使用CompletableFuture的示例:

(2)数据异构

为了提升接口性能,尤其在高并发场景下,可以考虑数据冗余,将用户信息、积分和成长值的数据统一存储在一个地方,比如Redis。

这样,通过用户ID可以直接从Redis中查询所需的数据,从而避免远程接口调用。

但需要注意的是,如果使用了数据异构方案,就可能会出现数据一致性问题。

用户信息、积分和成长值有更新的话,大部分情况下,会先更新到数据库,然后同步到redis。

但这种跨库的操作,可能会导致两边数据不一致的情况产生。

redis和数据库如何保证一致性呢?

为了保证Redis和数据库之间的数据一致性,可以采用以下策略:

1)基于事务的一致性: 使用数据库的事务来确保数据库操作的一致性。在操作Redis和数据库时,要么两者都成功,要么都失败,使用数据库的事务可以保证这一点。

2)使用锁: 在并发情况下,可以使用锁来确保数据的一致性。当操作Redis和数据库时,首先获取锁,然后执行操作,最后释放锁。

3)读写直写策略: 在更新数据库后,同时更新Redis。这样,当读取数据时,可以直接从Redis获取,避免了数据库的IO开销。

4)异步串行化: 当更新数据库时,通过消息队列异步通知Redis更新,保证Redis的数据最终是一致的,但不保证实时一致性。

5)失败重试和回滚: 如果Redis或数据库的更新失败,应该有重试机制,并在必要时实现回滚操作。

四、重复调用

在我们的日常工作代码中,重复调用非常常见,但如果没有控制好,会严重影响接口的性能。

 解决方案:可以通过批量查询来优化性能,减少数据库的查询次数。

五、异步处理

核心逻辑可以同步执行,同步写库。非核心逻辑,可以异步执行,异步写库。

异步处理方案

异步处理通常有两种主要方式:多线程和消息队列(MQ)

六、避免大事务

大事务,引发性能的问题

那么我们该如何优化大事务呢?

为了避免大事务引发的问题,可以考虑以下优化建议:

少用@Transactional注解

将查询(select)方法放到事务外

事务中避免远程调用

事务中避免一次性处理太多数据

有些功能可以非事务执行

有些功能可以异步处理

七、锁粒度

在一些业务场景中,为了避免多个线程并发修改同一共享数据而引发数据异常,通常我们会使用加锁的方式来解决这个问题。

然而,如果锁的设计不当,导致锁的粒度过粗,也会对接口性能产生显著的负面影响。

这里说一下MySQL数据库中的三种锁

表锁:

优点:加锁快,不会出现死锁。

缺点:锁定粒度大,锁冲突的概率高,并发度最低。

行锁:

优点:锁定粒度最小,锁冲突的概率低,并发度最高。

缺点:加锁慢,会出现死锁。

间隙锁:

优点:锁定粒度介于表锁和行锁之间。

缺点:开销和加锁时间介于表锁和行锁之间,并发度一般,也会出现死锁。

锁与并发度

并发度越高,接口性能越好。因此,数据库锁的优化方向是:

优先使用行锁

其次使用间隙锁

最后使用表锁

八、分页处理

调用接口从数据库获取数据需要经过网络传输。如果数据量过大,无论是数据获取速度还是网络传输速度都会受到带宽限制,从而导致耗时较长。

那么,这种情况下该如何优化呢?

答案是:分页处理。

将一次性获取所有数据的请求,改为分多次获取,每次只获取一部分用户的数据,最后进行合并和汇总。

九、加缓存

通常情况下,我们最常用的缓存是:Redis和Memcached。将一些经常查询需要的固定不变的数据加入到缓存中,请求的时候优先从缓存里面去取数据,这样会大大提高接口的查询性能,因为缓存是存在内存中的,避免了大量的数据库查询。

十、分库分表

有时候,接口性能受限的并不是其他方面,而是数据库。

当系统发展到一定阶段,用户并发量增加,会有大量的数据库请求,这不仅需要占用大量的数据库连接,还会带来磁盘IO的性能瓶颈问题。

此外,随着用户数量的不断增加,产生的数据量也越来越大,一张表可能无法存储所有数据。由于数据量太大,即使SQL语句使用了索引,查询数据时也会非常耗时。

那么,这种情况下该怎么办呢?

答案是:需要进行分库分表。

十一、监控功能

优化接口性能问题,除了上面提到的这些常用方法之外,还需要配合使用一些辅助功能,因为它们真的可以帮我们提升定位问题的效率。

开启慢查询日志

通常情况下,为了定位SQL的性能瓶颈,我们需要开启MySQL的慢查询日志。把超过指定时间的SQL语句单独记录下来,方便以后分析和定位问题。

开启慢查询日志需要重点关注三个参数:

slow_query_log:慢查询开关

slow_query_log_file:慢查询日志存放的路径

long_query_time:超过多少秒才会记录日志

通过MySQL的SET命令可以设置:

加监控

为了在出现SQL问题时能够及时发现,我们需要对系统做监控。

目前业界使用比较多的开源监控系统是:Prometheus。

它提供了监控和预警的功能。

监控如下信息:

接口响应时间

调用第三方服务耗时

慢查询sql耗时

cpu使用情况

内存使用情况

磁盘使用情况

数据库使用情况

链路跟踪

有时候,一个接口涉及的逻辑非常复杂,例如查询数据库、查询Redis、远程调用接口、发送MQ消息以及执行业务代码等等。

这种情况下,接口的一次请求会涉及到非常长的调用链路。如果逐一排查这些问题,会耗费大量时间,此时我们已经无法用传统的方法来定位问题。

有没有办法解决这个问题呢?

答案是使用分布式链路跟踪系统:SkyWalking。

在SkyWalking中,可以通过traceId(全局唯一的ID)来串联一个接口请求的完整链路。你可以看到整个接口的耗时、调用的远程服务的耗时、访问数据库或者Redis的耗时等,功能非常强大。

相关推荐
黑狼传说21 小时前
前端项目优化:极致最优 vs 相对最优 —— 深入探索与实践
前端·性能优化
Lill_bin1 天前
Lua编程语言简介与应用
开发语言·数据库·缓存·设计模式·性能优化·lua
人工智能培训咨询叶梓2 天前
MobiLlama,面向资源受限设备的轻量级全透明GPT模型
人工智能·gpt·语言模型·自然语言处理·性能优化·多模态·轻量级
Flying_Fish_roe3 天前
JVM 性能优化与调优-ZGC(Z Garbage Collector)
jvm·性能优化
Flying_Fish_roe3 天前
JVM 性能优化与调优-GraalVM
jvm·性能优化
四代水门3 天前
游戏性能优化
游戏·性能优化
安卓美女3 天前
Android自定义View性能优化
android·性能优化
Flying_Fish_roe3 天前
JVM 性能优化与调优-Shenandoah GC
jvm·性能优化
旺小仔.3 天前
【数据结构篇】~排序(1)之插入排序
c语言·数据结构·算法·链表·性能优化·排序算法
洁洁!3 天前
深入分析计算机网络性能指标
网络·计算机网络·性能优化