携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第14天,点击查看活动详情
前言
在日常工作的开发中,我们通常开发完接口,使接口业务能正常执行的情况下,我们还是要关注接口的性能,主要体现在接口的执行时间,如果遇到执行比较慢的接口,我们就需要对它进行优化了,从而来提升对整个服务的性能,作为一个工作几年的后端开发,接口的性能优化是我们必备的技能。
如何看接口的执行时间
通常我们需要去优化接口,首先要去定位到我们的慢接口是哪个,那么定位到慢接口有哪些方式呢?
1.链路追踪 Tracing Analysis 通过阿里的链路追踪插件,可以查看接口调用的性能指标,类似的插件平台还有很多种,这里重要列举一种。
以及调用链每个环节涉及到的接口请求相应时间,都可以通过链路追踪获取到。
2.AOP切面 使用AOP自定义注解,使用切面的方式对接口实行前置、后置、环绕的一个处理,记录接口调用的情况,并使用log表把数据记录在表中,通过数据库的字段来看接口的执行效率。
慢接口如何进行优化
1.字段加索引
索引优化是首先想到的,我们可以对访问DB的表查询频率比较高的一些字段加索引。 索引的类型有:普通索引、主键索引、唯一索引、复合索引、全文索引、前缀索引;
开发过程中比较常见的就是加复合索引,也就是通过where后面的条件进行加索引,通过最左匹配的原则来进行检索。
当然我们还要考虑哪些字段适合加索引,一般来说我们需要在主键字段、非空字段、字段长度较短、更新不频繁的字段、分组字段或排序字段都应该加索引的。

CREATE INDEX idx_name ON table (a,b,c);
2.sql优化
Sql优化我就不举例子,转载了一篇别人总结不错的文章供大家参考 Sql优化总结!详细!(2021最新面试必问)_布诺i的博客-CSDN博客_sql优化 转自-csdn,作者: 布诺i
3.数据分页
对于请求大批次数据,我们不应该对数据进行全部的批量处理,可以进行数据分页,分批次对数据进行批量处理,最后进行整合。 可以通过limit来限制我们一次处理的条数。
4.批量请求接口
这样可以一次性通过调用接口来拿到所需的数据,减少了对接口的访问,避免了创建连接产生的开销。
实际代码我们可以这样写:
final List<String> applyCds = resultModelList.stream().map(TReconsiderVO::getApplyCd).collect(Collectors.toList());
final List<BriefOrderModel> orderList = orderCommonService.getOrderList(applyCds);
循环里面去操作数据库,也可以利用Mybatis的批量请求数据库。
5.并行请求接口
在某些特定的业务场景下,我们的接口方法中去调用其他服务的方法,比如服务A、B、C,我们的业务逻辑先调A服务,我们不用等待接口的返回在去进行业务逻辑的判断的情况下,这样串行请求会使我们的接口响应累加变长。
所以要考虑进行并行请求接口,Java8引入了 CompletableFuture 异步线程请求接口。 并行请求接口就可以在200ms的时间去连续请求三个接口
代码示例:
public void getAsync() throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(5);
CompletableFuture aFuture = CompletableFuture.supplyAsync(() -> {
return "执行A服务返回结果";
}, executor);
CompletableFuture bFuture = CompletableFuture.supplyAsync(() -> {
return "执行B服务返回结果";
}, executor);
CompletableFuture cFuture = CompletableFuture.supplyAsync(() -> {
return "执行C服务返回结果";
}, executor);
CompletableFuture.allOf(aFuture, bFuture, cFuture).join();
aFuture.get();
bFuture.get();
cFuture.get();
}
6.mq异步请求
例如像这种生成订单、发短信业务,接口的请求量就比较大,可以采用mq消息中间件,可以在请求量大的情况下进行削峰,把所有请求都发送到mq服务上面进行接收,因为发送mq是相当快的,这样接口可以专注于业务,剩下的交给mq来解决,当然维护消息中间件也是需要花功夫的。
7.加入缓存
缓存现在是每个分布式系统必不可少的中间件,在高并发的场景下,通过访问我们的业务接口,如果是频繁请求数据量比较大的接口是比较耗时间的,合理的利用缓存,可以让我们避免一些重复获取数据的操作,从而大大的减少我们接口的请求时间。
同时使用redis缓存也要注意我们缓存和数据库一致性如何保持,还有就是要考虑缓存穿透、缓存击穿、缓存雪崩的情况,包括对key的设计也要合理,避免Big key 影响性能。
二级缓存
也可以通过定义二级缓存,Mybatis、Guava、Spring cache,这些都是本地缓存,在接口访问我们本地数据库拿数据的时候,可以开启二级缓存。 当然也是要注意对于频繁更新的数据不要放入二级缓存,很容易出现缓存数据库不一致的情况。
8.分布式环境下锁的颗粒度
加锁是为了避免并发的情况下来保证数据的一致性,但是也是需要控制锁的颗粒度,我们需要合理的去选择加锁的对象,尽量去减小锁的颗粒度,如果锁的粒度比较大对于类、方法、以及调用方法的方法进行加锁,都会去影响接口的吞吐量。 尽量去选择全局唯一业务code、订单号这种加锁,也要设置获取锁的时间和过多长时间锁会自动释放,业务处理完记得一定要去释放锁,避免死锁情况。
final String applycdKey = String.format(Constants.REDIS_LOCK_KEY_APPLYCD, applyCd);
final RLock applycdLock = RedissonClientUtil.getFairLock(applycdKey);
try {
if (applycdLock.tryLock(10, 10, TimeUnit.SECONDS)) {
//业务逻辑
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}finally {
//释放applycd锁
applycdLock.unlock();
}
总结
总而言之,我们作为一个合格的程序员,肯定要对自己写的接口进行优化,从而保证整个系统的运行效率。当然我们对接口速度优化的同时,也会带来其他问题,也不能一味的的追求性能,也要做到性能和稳定性兼顾,这才是作为一个出色的程序员应该做的。