5 个接口性能提升的通用技巧
前言
作为后端开发人员,我们总是在编写各种 API,无论是为前端 web 提供数据支持的HTTP REST API
,还是提供内部使用的RPC API
。这些 API 在服务初期可能表现不错,但随着用户数量的增长,一开始响应很快的 API 越来越慢,直到用户抱怨:“你的系统太糟糕了。” 我只是浏览网页。为什么这么慢?”。这时候你就需要考虑如何优化你的 API 性能了。
要想提高你的 API 的性能,我们首先要知道哪些问题会导致接口响应慢。API 设计需要考虑很多方面。开发语言层面只占一小部分。哪个部分设计不好就会成为性能瓶颈。影响 API 性能的因素有很多,总结如下:
数据库慢查询
复杂的业务逻辑
糟糕的代码
资源不足
........
在这篇文章中,我总结了一些行之有效的 API 性能优化技巧,希望能给有需要的朋友一些帮助。
欢迎关注个人公众号『JAVA 旭阳』交流沟通
1. 并发调用
假设我们现在有一个电子商务系统需要提交订单。该功能需要调用库存系统进行库存查扣,还需要获取用户地址信息。最后调用风控系统判断本次交易无风险。这个接口的设计大部分可能会把接口设计成一个顺序执行的接口。毕竟我们需要获取到用户地址信息,完成库存扣减,才能进行下一步。伪代码如下:
如果我们仔细分析这个函数,就会发现几个方法调用之间并没有很强的依赖关系。而且这三个系统的调用都比较耗时。假设这些系统的调用耗时分布如下
stockService.check()
需要150
毫秒。addressService.getByUser()
需要200
毫秒。riskControlSerivce.check()
需要300
毫秒。
如果顺序调用此 API,则整个 API 的执行时间为650ms(150ms+200ms+300ms)
。如果能转化为并行调用,API 的执行时间为300ms
,性能直接提升50%
。使用并行调用,大致代码如下:
2. 避免大事务
所谓大事务,就是历经时间很长的事务。如果使用 Spring @Transaction
管理事务,需要注意是否不小心启动了大事务。因为 Spring 的事务管理原理是将多个事务合并到一个执行中,如果一个 API 里面有多个数据库读写,而且这个 API 的并发访问量比较高,很可能大事务会导致太大大量数据锁在数据库中,造成大量阻塞,数据库连接池连接耗尽。
相信在很多人写的业务中都出现过这种代码,远程调用操作,一个非 DB 操作,混合在持久层代码中,这种代码绝对是一个大事务。它不仅需要查询用户地址和扣除库存,还需要插入订单数据和订单明细。这一系列操作需要合并到同一个事务中。如果 RPC 响应慢,当前线程会一直占用数据库连接,导致并发场景下数据库连接耗尽。不仅如此,如果事务需要回滚,你的 API 响应也会因为回滚慢而变慢。
这个时候就需要考虑减小事务了,我们可以把非事务操作和事务操作分开,像这样:
或者,您可以使用 spring 的编程事务TransactionTemplate
。
3. 添加合适的索引
我们的服务在运行初期,系统需要存储的数据量很小,可能是数据库没有加索引来快速存储和访问数据。但是随着业务的增长,单表数据量不断增加,数据库的查询性能变差。这时候我们应该给你的数据库表添加适当的索引。可以通过命令查看表的索引(这里以 MySQL 为例)。
ALTER TABLE
通过命令添加索引。
有时候,即使加了一些索引,数据查询还是很慢。这时候你可以使用explain
命令查看执行计划来判断你的 SQL 语句是否命中了索引。例如:
你会得到一个分析结果:
一般来说,索引失效有几种情况:
不满足最左前缀原则。例如,您创建一个组合索引
idx(a,b,c)
。但是你的 SQL 语句是这样写的select * from tb1 where b='xxx' and c='xxxx';
。索引列使用算术运算。
select * from tb1 where a%10=0
;索引列使用函数。
select * from tb1 where date_format(a,'%m-%d-%Y')='2023-01-02';
like
使用关键字的模糊查询。select * from tb1 where a like '%aaa'
;使用
not in
或not exist
关键字。等等
4. 返回更少的数据
如果我们查询大量符合条件的数据,我们不需要返回所有数据。我们可以通过分页的方式增量提供数据。这样,我们需要通过网络传输的数据更少,编码和解码数据的时间更短,API 响应更快。
但是,传统的limit offset
方法用于 paging( select * from product limit 10000,20)
。当页面数量很大时,查询会越来越慢。这是因为使用的原理limit offset
是找出10000
条数据,然后丢弃前面的9980
条数据。我们可以使用延迟关联来优化此 SQL。
5. 使用缓存
缓存是一种以空间换时间的解决方案。一些用户经常访问的数据直接缓存在内存中。因为内存的读取速度远快于磁盘 IO,所以我们也可以通过适当的缓存来提高 API 的性能。简单的,我们可以使用 Java 的HashMap
、ConcurrentHashMap
,或者caffeine
等本地缓存,或者Memcached
、Redis
等分布式缓存中间件。
总结
我在这里列出了五个通用的 API 性能优化技巧,这些技巧只有在系统有一定的并发压力时才有效。如果本文对你有帮助的话,请留下一个赞吧。
欢迎关注个人公众号『JAVA 旭阳』交流沟通
版权声明: 本文为 InfoQ 作者【JAVA旭阳】的原创文章。
原文链接:【http://xie.infoq.cn/article/e74144d2bdcaf5bdd73e3ba65】。文章转载请联系作者。
评论