接口性能调优方案分享:从2s到200ms的调优历程

平常在开发过程中,时常会遇到一些复杂查询或复杂业务,导致接口出现性能问题,基本上每一个程序猿在工作中都离不开对接口性能的调优。

今天就在这里记录一下最近在实际开发中对于一个业务和查询都复杂,且数据量较大的查询性能优化的心路历程。

接口优化前:

5W数据的业务处理,ApiPost请求耗时约2s,前端页面请求直接不用说了,加上界面渲染,直接干到3s多,而且这个接口还是查询比较频繁的接口。显然这样子的接口性能肯定是不行的。

接口优化后:

5W相同数据,优化后前端请求耗时平均约0.2s左右,接口查询刷新基本不用等待,达成要求!

下面我就对本次接口优化的过程做一个总结。

一、避免重复的查询sql调用

一般优化性能的第一步就是看你的代码是否存在明显的逻辑错误或重复调用,一般来说,性能耗时主要就是体现在两个地方:

  1. 业务复杂导致的业务处理耗时
  2. 数据库查询耗时

想要知道整个代码块中具体耗时体现在哪些地方就需要对功能模块分别加日志输出执行时间。

在对代码结构进行盘查的过程中,发现存在一些基本相同的sql被重复调用了,这就导致了接口在执行的过程中会不断的访问数据库,每一次访问数据库都是一个IO操作,重复这样的操作势必会造成耗时增加。sqk的重复调用主要体现在两点上:

(1)方法调用内外同样的表查询被执行两遍

也就是说,虽然可能方法内外都需要表中的某条数据,但是一般我们只需要在方法调用前执行一次该查询就可以了,之后将返回值作为参数的形式传入需要的方法即可,这样就可以完全避免sql在方法内外重复执行,从而增加查询消耗。

(2)for循环执行sql查询

这个算是比较基本的问题了,通过for循环遍历某些条件,再通过这些条件去分别请求数据库操作,一定会增加数据库的交互,从而增加耗时,所以在代码中无论业务大小,一定要避免for循环内部重复执行库操作。

可行的操作是:先通过stream流等操作,拿到所有的查询条件,然后对数据库执行批量操作,这样只需要一次sql交互,就可以将后面想要的数据都给查出来,如果需要获取,后面再通过map封装一下就可以了。

通过把上面的问题逐一优化之后,再去测试代码性能有了明显的提升,耗时降到了1.5s左右,这也可以算是通过优化业务代码来优化接口性能了。但是这个时间在操作比较频繁的接口来讲用户体验还不算好,因此还不能完全满足,那就继续对耗时代码进行优化。

二、不要用联表查询

继续对代码执行过程中输出日志,看具体的耗时代码,这一次发现耗时的代码主要体现在一些联表查询上,对于比较复杂的查询,在做联表查询时如果连接的表比较多,联表使用不当,势必也会造成性能问题。

因此我将联表查询拆分成单表查询加数据封装的形式,经过性能对比发现,采用单表的方式比联表查询的性能要优得多,通过看了其他的联调查询的耗时,也发现都是一样的。

因此建议大家在写联表查询的时候,如果时间允许,可以对比下单表和联表的情况下性能是否有明显差别,如果有当然要选择更优的一方。

所以,第二步我将耗时的联表查询改成了性能更优的单表批处理方式。

三、常用字段添加索引处理

除了将耗时的联表查询改为性能更优的单表查询外,还有一个数据查询比较关键的优化方案就是给常用字段增加索引,这个应该也是最快优化查询性能的方法。

比如我们可能在做批量查询时,经常会对某一字段执行in条件查询,那么就可以给这些常用字段增加上索引,来提高查询性能,

但是一般来说,一张表中的索引个数不建议超过三个,而且在使用索引的过程中,要注意索引是否真正使用,索引是否失效等问题,如果可以有效的使用索引,那数据库查询的性能也将会有明显的提升。

经过上述优化之后,原本2s耗时的接口,现在前端请求只需要大约0.6-0.8s左右,优化了一倍左右,其实效果还是蛮不错的,

但是对于对性能要求严格的程序猿们来讲,哪怕是0.1s的优化,也是对极致性能的追求!

因此在做完上面三步优化后,我继续对代码输出日志进行分析,发现整个执行流程中,业务封装的耗时仅占总耗时的百分之三十不到,大部分耗时还是体现在了数据查询上,这里说明一点,我的持久层框架使用的是mybatisPlus,因此在数据查询到模型映射的过程中也会存在一定的耗时问题,但是这些耗时是你只要使用数据库查询,基本上都是不可避免的。

四、增加缓存,Redis or 本地

对于数据库查询耗时的问题,我又想到能否通过增加缓存,来避免直接操作数据库,而是通过缓存来获取数据,这样不就可以避免数据库查询耗时了嘛。

我采用的方式是,针对整个接口中复杂且耗时的库查询,采用缓存的形式来存储数据,仅在缓存未命中的情况下执行库查询,对于通过缓存可以拿到的数据,直接从缓存中获取。从缓存中获取数据的时间几乎是毫秒级可以忽略。

因此,通过增加缓存,就在每次查询时完美的避开了耗时的库查询操作,将缓存完整的加上去之后,再去执行前端请求,5W数据的情况下接口耗时从原来的2s多稳定到了0.2s上下,性能优化了进10倍,至此,整个接口也算是优化到了比较满意的地步。

五、接口尽量避免返回大而全的VO&接口功能拆分

随着数据量的日渐增加,自然而然查询库数据的性能就会下降,而且对于业务复杂的接口来讲,往往需要在界面显示的数据并不是一张数据表就可以查询出来的,而是需要很多张数据表关联查询,甚至需要加上多层的业务封装都是非常常见的,这个时候如果我们直接把要展示的所有数据全部都通过一次请求查询出来的话,性能上必然耗时非常严重。

举个例子来讲:假如我要查询一个班级中的学生列表,每一个学生有多门选课,每一门课程有多次考试成绩,这些数据以树结构的形式展示在界面的一个列表中,那么如果我们直接在查询班级学生的接口中将学生选课,课程成绩等信息全部查询出来,首先在处理上就是耗时且复杂的业务。而且如果返回的数据条数过大,也会影响前端渲染和传输的带宽。

这个时候我们就可以让一次接口查询拆分为多次查询,如第一个我只返回每一个班级下的学生信息,当用户点击学生之后,我再对这个学生的选课信息进行查询,再次点击课程的时候再单独去查询这个学生这门课的成绩信息,这样拆分的方法会将原本的一个接口拆分为三个独立的接口,虽然看起来增加了工作量,但是实际上三个接口的业务变得独立,耦合性更低。而且分散到单个接口下不会有数据查询的性能问题。

所以这也是在进行性能优化时需要考虑的一个方法。

  • 28
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

꯭ 瞎꯭扯꯭蛋꯭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值