前言
接口性能问题,对于从事后端开发的同学来说,是一个绕不开的话题。想要优化一个接口的性能,需要从多个方面着手。
本文将会接着接口性能优化这个话题,从实战的角度出发,聊聊我是如何优化一个慢查询接口的。
上周我优化了一下线上的批量评分查询接口,将接口性能从最初的20s
,优化到目前的500ms
以内。
1. 案发现场
我们每天早上上班前,都会收到一封线上慢查询接口汇总邮件,邮件中会展示接口地址
、调用次数
、最大耗时
、平均耗时
和traceId
等信息。
我看到其中有一个批量评分查询接口,最大耗时达到了20s
,平均耗时也有2s
。
用skywalking
查看该接口的调用信息,发现绝大数情况下,该接口响应还是比较快的,大部分情况都是500ms左右就能返回,但也有少部分超过了20s的请求。
这个现象就非常奇怪了。
莫非跟数据有关?
比如:要查某一个组织的数据,是非常快的。但如果要查平台,即组织的根节点,这种情况下,需要查询的数据量非常大,接口响应就可能会非常慢。
但事实证明不是这个原因。
很快有个同事给出了答案。
他们在结算单列表页面中,批量请求了这个接口,但他传参的数据量非常大。
怎么回事呢?
当初说的需求是这个接口给分页的列表页面调用,每页大小有:10、20、30、50、100,用户可以选择。
换句话说,调用批量评价查询接口,一次性最多可以查询100条记录。
但实际情况是:结算单列表页面还包含了很多订单。基本上每一个结算单,都有多个订单。调用批量评价查询接口时,需要把结算单和订单的数据合并到一起。
这样导致的结果是:调用批量评价查询接口时,一次性传入的参数非常多,入参list中包含几百、甚至几千条数据都有可能。
2. 现状
如果一次性传入几百或者几千个id,批量查询数据还好,可以走主键索引,查询效率也不至于太差。
但那个批量评分查询接口,逻辑不简单。
伪代码如下:
public List<ScoreEntity> query(List<SearchEntity> list) {
//结果
List<ScoreEntity> result = Lists.newArrayList();
//获取组织id
List<Long> orgIds = list.stream().map(SearchEntity::getOrgId).collect(Collectors.toList());
//通过regin调用远程接口获取组织信息
List<OrgEntity> orgList = feginClient.getOrgByIds(orgIds);
for(SearchEntity entity : list) {
//通过组织id找组织code
String orgCode = findOrgCode(orgList, entity.getOrgId());