接口优化解决方案

目录

1.数据量比较大,批量操作数据入库

2.耗时操作考虑异步处理

3.合理恰当并行调用

4.合理拆分接口

5.合理使用缓存

6.优化程序逻辑、代码

7.SQL优化(比如:添加索引)

8.压缩传输内容

9.考虑使用文件/MQ等其他方式暂存,异步再落地DB

10.跟产品讨论需求最恰当,最合理的实现方式


在工作过程中,程序员经常会遇到接口响应过慢,这时候,就需要对程序接口进行优化。

1.数据量比较大,批量操作数据入库

 
优化前:
//for循环单笔入库
for(orderDetail detail:list){
    insert(detail);
}
优化后:
// 批量入库,mybatis demo实现
<insert id="insertBatch" parameterType="java.util.List">
        insert into trans_detail( id,orderNo,amount,payer,payee) values
    <foreach collection="list" item="item" index="index" separator=",”>
        ( #{item.id},#{item.orderNo}, #{item.amount},#{item.payer},#{item.payee})
    </foreach>
</insert>

性能对比:
单位(ms)
for循环单笔入库
批量入库
500条
1427
1143
1000条
1866
1411
解析
批量插入性能更好,更加省时间,为什么呢?
打个比方:假如你有一个电梯,需要让100人上楼顶,,电梯一次最多搭乘10个人,你可以选择一次搭乘1人,也可以一次搭乘10人你觉得哪种方式更方便,时间消耗更少?
无疑一次搭乘10人效率会更高。

2.耗时操作考虑异步处理

耗时操作,考虑用异步处理,这样可以降低接口耗时。有时候,接口性能优化,需要重新梳理一下业务逻辑,看看是否有设计上不太合理的地方。

比如有个用户请求接口中,需要做业务操作,给运营发送邮件,和记录操作日志。为了实现起来比较方便,通常我们会将这些逻辑放在接口中同步执行,势必会对接口性能造成一定的影响。

接口内部流程图如下:

给运营发送邮件和添加用户操作日志功能,被提交到了两个单独的线程池中。

这样接口中重点关注的是业务操作,把其他的逻辑交给线程异步执行,这样改造之后,让接口性能瞬间提升了。

但使用线程池有个小问题就是:如果服务器重启了,或者是需要被执行的功能出现异常了,无法重试,会丢数据。

那么这个问题该怎么办呢?

使用 mq 改造之后,接口逻辑如下:

对于发送邮件和用户操作日志功能,在接口中并没真正实现,它只发送了 mq 消息到 mq 服务器。然后由 mq 消费者消费消息时,才真正的执行这两个功能。

这样改造之后,接口性能同样提升了,因为发送 mq 消息速度是很快的,我们只需关注业务操作的代码即可。

3.合理恰当并行调用

在开发接口时,我们常常需要在某个接口中,调用其他服务的接口。
比如有这样的业务场景:
在用户信息查询接口中需要返回多个信息:用户名称、地址、性别、等级、积分等信息。

而用户名称、性别、等级、头像在用户服务中,用户名等在个人信息基础服务中,等级在等级服务中,积分在积分值服务中。为了汇总这些数据统一返回,需要另外提供一个对外接口服务。

于是,用户信息查询接口需要调用用户查询接口、等级查询接口和积分成长值查询接口,然后汇总数据统一返回。

调用过程如下图所示:

调用远程接口总耗时 530ms = (用户)200ms + (等级)150ms + (成长值)180ms

显然这种串行调用远程接口性能是非常不好的,调用远程接口总的耗时为所有的远程接口耗时之和。这样无疑会影响到接口相应速度。

我们可以进行如下操作

        // 查询客户基础信息
        CompletableFuture<ClientBase> f1 = CompletableFuture
                .supplyAsync(() -> clientBaseMapper.queryByClientId(clientId),CommonThreadPool.getExecutor());

        // 查询客户等级信息
        CompletableFuture<ClientLevel> f2 = CompletableFuture
                .supplyAsync(() -> clientLevelMapper.queryByClientId(clientId), CommonThreadPool.getExecutor());

        // 查询客户成长值信息
        CompletableFuture<ClientGrowthLevel> f3 = CompletableFuture
                .supplyAsync(() -> clientGrowthLevel.queryByClientId(clientId), CommonThreadPool.getExecutor());
                
        CompletableFuture<Void> total = CompletableFuture
                .allOf(f1, f2, f3);
        total.get();

此时我们把串行查询改成了并行查询,无疑会提高接口的相应速度。

4.合理拆分接口

在开发接口时,我们常常需要在某个接口中,调用其他服务的接口。
比如有这样的业务场景:
在用户信息查询接口中需要返回多个信息:用户基础信息,用户头像等。

有些开发者会把这作为一个接口进行开发:用户综合接口,但把该接口拆分成用户基础信息接口,和用户头像接口无疑会提高用户综合接口的相应速度。

5.合理使用缓存

在部分业务场景,恰当地使用缓存,是可以大大提高接口性能的。这里的缓存包括:Redis,JVM本地缓存,memcached,或者Map等。

在写代码中,我们通常会遇到这样一种情况,我们的枚举值配置在数据库中,而查询客户基本信息列表无疑会查询返回证件类型等字段。

如果我们查询中获取到证件类型的枚举,不要在for循环中去逐条调用查询枚举配置,而是需要在外层查询一次证件类型枚举,转换成map,然后从map中取值填充。

我们可以进行如下操作

Category parent = categoryMapper.getCategoryByCode("CertificateType");
Map map = parent.stream().collect(Collectors.toMap(Category::getCode, Category::getDesc);

for(Client client :List){
    client.setCertificateTypeName(map.get(client.getCertificateType()))
}

6.优化程序逻辑、代码

避免出现死循环
有些时候,写代码一不留神,循环语句就出现死循环了。

出现这种情况往往就是 condition 条件没处理好,导致没有退出循环,从而导致死循环。

出现死循环,大概率是代码的 bug 导致的,不过这种情况很容易被测出来。

但是,可能还有一种比较隐秘的死循环代码,当用正常数据时,测不出问题,一旦出现有异常数据,才会复现死循环的问题。

避免无限递归
无限递归的场景会严重影响接口性能,最终会发生堆栈溢出。总之,在写递归代码时,建议设置递归深度(假设限定为 5),然后在递归方法中做一定判断,如果深度大于 5 时,则自动返回,这样就可以避免无限递归了。

7.SQL优化(比如:添加索引)

我们可以通过这些方式优化我们的SQL:

加索引
避免返回不必要的数据
优化sql结构
分库分表
读写分离

8.压缩传输内容

压缩传输内容,文件变得更小,因此传输会更快啦。10M带宽,传输10k的报文,一般比传输1M的会快呀;打个比喻,一匹千里马,它驮着一百斤的货跑得快,还是驮着10斤的货物跑得快呢?

解析:如果你的接口性能不好,且传输报文比较大的话,这时候是可以考虑压缩文件内容传输的,可能会有意想不到的效果。

9.考虑使用文件/MQ等其他方式暂存,异步再落地DB

如果数据太大,落地数据库实在是慢的话,可以考虑先用文件的方式保存,或者考虑MQ,先落地,再异步保存到数据库。

然后再从mq中消费数据,或者读取文件内容,在进行业务处理。

如果耗时瓶颈就在数据库插入操作这里了,那就考虑文件保存或者MQ或者其他方式暂存吧,文件保存数据,对比一下耗时,有时候会有意想不到的效果哦。

10.跟产品讨论需求最恰当,最合理的实现方式

比如有个页面需要展示客户好友列表的需求,产品说要展示所有的客户好友列表,如果一个用户的客户列表信息好大,你拉取所有客户数据回来,接口性能就降下来了。如果产品打桩分析,会发现,一般用户看客户列表,也就看前几页,或者可以做个好友列表按钮,这样就不会影响用户也买你的展示了。那个超大分页加载问题也是类似的。即limit +一个超大的数,一般会很慢的。

更多消息资讯,请访问昂焱数据

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值