记录一次性能优化的过程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/my_momo_csdn/article/details/85307907

性能优化:

一、背景

查询接口,一个复杂条件查询,符合条件的记录有17W,查询首页的100条,postman测试接口耗时在1.6-2秒不等。
(重复查询同一个条件,mongodb会将符合条件的记录加载到内存,因此后面查询会快一些,然后趋于一个比较稳定
的值1.6s 附近)

1.查看查询过程,因为是分页,需要知道总记录的条数(countByCondition,这一步耗时0.7-0.9秒,是性能瓶颈)同时
还需要查询100条记录(queryByCondition,这里迭代的时间在400-500ms不等),再加上处理时间,网络传输,总共在
1.6左右

二、优化

2.1 优化第一步:异步处理countByCondition;

每一次查询都统计一次,开销很大,串行处理由其明显;将countByCondition的动作通过另一个线程执行。最后的
效果是时间降低到0.9秒左右,因为是并行执行查询和计数,所以时间基本上接近耗时较久的操作。

2.2 优化第二步:缓存countByCondition结果;

countByCondition的结果只是统计的值,这个值用于计算总页数,而数据部分queryByCondition是从数据库取得,
不存在缓存一致性问题. countByCondition的值在一定范围内偏差一点点并没有影响,比如上面的17W数据,算出
来的页面总共1700多页,一点误差几乎无影响,所以这里可以缓存countByCondition的结果,在redis中有的话,
就从redis取,没有则按照步骤一去数据库查询。这样的结果是除了第一次没有缓存的时候需要0.9秒左右,后面都只
需要0.40-0.5s左右,测试从redis取count只需要3-6ms,忽略不计。

2.3 优化第三步骤:去除特征值减少网络传输;

大部分查询的时候,特征值并不需要参与业务中进行处理,之前没有遇到性能问题的时候,都是怎么方便怎么来,
没有在意细节,java程序从mongodb加载数据的时候需要走网络传输,实测含特征值的数据大小在35KB左右,去
除特征值在2kb左右,几乎95%的大小在特征值这个绝大部分查询场景没有用的字段上,因此增加字段过滤,从数据
库加载数据的时候,就去除对特征值字段的加载,到这一步之后,效果是加载耗时在300-400ms左右,大约减少了
100ms左右。到这一步用postman测试接口,首次在0.9s左右,后续的次数都在0.35秒左右

2.4 细节补充

1.count在redis的缓存失效时间;因为count只会影响计算得到页数,不会影响数据,所以这里一致性不算高,这里
我设置的是60秒
2.后续对于查询的数据也可以缓存,但是那样一旦有数据新增进库,就需要对缓存做更新,这里实现其实并不是很
简单,暂时没有做,后续可以做一个优化点,如果实现的话,热点数据查询到200ms以内应该可以,不过要保证强一
致性需要考虑的很多。
3.上述还未基于大数据量做验证,后面还需在真实环境中验证。
4.另外在做的过程中为了方便记录方法的耗时和记录日志,写了两个AOP的工具注解,可以参阅,

2.5 代码


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EnableCountTimeIntellif {

}

@Component
@Aspect
public class EnableCountTimeAspect {

    private static final LoggerUtilI LOG = LoggerUtilI.getLogger(EnableCountTimeAspect.class);

    @Around("@annotation(ect)")
    public Object doAround(ProceedingJoinPoint pjp, EnableCountTimeIntellif ect) throws Throwable {
        long begin = System.currentTimeMillis();
        Object obj = pjp.proceed();
        String methodName = pjp.getSignature().getName();
        String className = pjp.getSignature().getDeclaringTypeName();
        LOG.info(className + "." + methodName + " method elapsed time is: " + (System.currentTimeMillis() - begin) + " ms");
        return obj;
    }
}

三、待续

优化过程中使用的工具

1.异步处理过程并获得结果,Callable+FutureTask;
2.线程池执行任务,避免重复创建线程 。
3.自定义注解增强方法,并将附加功能与方法主流程解耦。
4.

猜你喜欢

转载自blog.csdn.net/my_momo_csdn/article/details/85307907