今天有一个线上系统,客户反映有一个查询异常缓慢,要几分钟才出得来,于是老大安排我来排查一下。
这个系统差不多有3万左右的用户,每个用户会实时上报一些数据,上报数据量比较大,就分了两个服务器。服务器A专门用来存储和处理上报的数据;服务器B为业务服务器,用户信息等基础数据在服务器B上。
这个查询的业务是要从服务器A获取每个用户上报的最新一条数据,再结合服务器B的用户信息数据,统计每个单位的最新上报情况。服务器A是通过restful接口提供服务的,以前的实现是通过接口从服务器A获取到最新上报数据realList,再查出用户信息数据userList,然后在内存里进行统计,基本实现就是两个for循环:
以上代码的问题在于实现倒是简单了,但是两个list的数据量都是3万左右,双重循环就要9亿次。这能不慢吗。
如果最新上报数据也在服务器B上,那就好办了,直接连表查询就可以,可是现在没有,所以最初想到的解决办法有两种:
(1)服务器B上建一张实时的最新数据表,然后再用一个定时任务把数据从A存到B上。这样查询统计时只需关联表查询就可以了。但是需要动到数据库,还要定时任务,增加了不稳定性。
(2)每次查询建临时表,再把数据存到临时表,然后再连表查询,这看似简单了一点,但是可能存在并发问题,而且3万条数据存储也需要时间,效率不一定能满足。
以上两种方案看似都可以解决问题,那还有没有更简单的解决方案呢?老大交代时说尽量用最简单的方法来解决,而且得知系统用户基本就稳定在3万左右。所以我考虑先从代码逻辑方面进行优化,能不动到数据库是最好的。以前用了双重循环,那么可不可以只用一层循环,只要降低了循环次数,效率就会有提升。 最终我的解决方案是这样的:
(1)首先循环realList,以userid为key放到一个Map里;
(2)再循环userList,然后在Map里查找userid对应的上报数据。
由于Map查找速度非常快,基本可以忽略。这样每次循环3万次,总共就只需要循环6万次。循环次数从9亿降到了6万,直接降低了近一万倍,整个查询在1秒左右就出来了。
代码实现:
此次优化并没有涉及很高技术,只是一个程序复杂度的优化。主要还是瞬间把循环次数降低了近10000倍,就感觉干了一件大事,发个文嘚瑟一下!