公司首页有个展示列表的接口,我也曾参与过其中部分方法的编写,反应时间很慢大约有3-4s,因为无从下手优化,请教D哥,D哥将代码重写将代码的运行时间减少到了0.6s左右,但是D哥也说不清原来代码为什么这么慢,于是我就对原来的代码和改进的代码进行了粗略分析。
在分析代码运行时间的工具上,看了网上各种各样的性能分析的工具,也试用了JProfiler,但是感觉过于底层分析,不是我想要的,选择了Spring提供的StopWatch类来进行断点记录对应时间,简单粗暴,符合我的需求。
原来的接口,平均耗时3.7s,大致步骤抽象如下。
1. 新建集合,查询出A
2. 新建集合,查询出B
3. 新建集合,查询出C
3. 新建集合,进行一个封装的多查询(D)
4. 排序
5. 新建集合,过滤筛选(E)
6. 手动分页得到F
使用Stopwatch后分析报告如下
ms % taskName
00005 000% 开始执行方法
00259 007% 查询A
00072 002% 查询B
00001 000% 查询C
02755 073% 封装多查询 D
00000 000% 排序
00705 019% 过滤 E (for循环调用数据库并做筛选)
00000 000% 手动分页得到F
这里我们看到D步骤和E步骤占用了92%的时间,问题就出现在这两个步骤上,而D很明显是大头.
于是对D内部再进行分析
ms % taskName
00118 005% D1
02427 093% D2 (for循环里调数据库查询486次 486*0.005 =2.43s)
00059 002% D3
00000 000% D4
所以主要问题就出现在D2和E上面
下面我们再对新接口做分析,大致步骤如下 (与上面的ABCDEF并不完全一样)
1. 新建集合 A
2. 筛选 B in (A)
2. 筛选 C in (B)
3. 集合 D in (B,C)
4. 分页,由D得到E,进行封装多查询,得到F
综上
原始接口进行各种筛选和处理后,最后分页.
新接口先获得筛选后的集合,然后再分页查询(10条),再处理,少了几百次调数据库。.
优化分析
1. 原始的逻辑总体上属于按顺序进行,a-b-c,每次数据会筛选减少,所以没法分页.
而改进的则是倒过来先获得条件b,c 然后a in (b,c),这样可以就可以进行分页,减少数据处理量.
2. 原始接口的大约80%时间用于for循环调用数据库进行判断.
而在改进中, 对于20%(E步骤)的优化,是一次查询先取出所有符合条件的对象集合b,作为后面的判断条件,使得a in(b)
对于60%(D2步骤)的优化,则是先进行了分页,使得数据量从486条变为10条,再进行for循环调用数据库就不会占用太多时间了。
其他可能的优化点
1.流的使用