背景:在开发公司一个内部服务上的查询用户反馈数据需求时,发现页面响应过慢,每次查询用户反馈数据时耗时10s左右。虽然没有用户来反馈这个问题,但是明显不正常,所以准备解决。
原因:在执行代码中分段加入时间戳,获取每段代码的执行时间和总时间,以此来判断耗时较多的代码。在代码中,有两次rpc调用一个query服务,通过这个服务来查询数据库,第一个是查询反馈数据,第二个是通过userId查询用户姓名。发现第二个服务平均耗时8600ms,占用90%以上的时间。确定问题点后,开始优化。
波折:通过检查查询sql,发现sql中条件是userId >= minUserId and userId <= maxUserId,minUserId和maxUserId是通过遍历反馈数据查找到的最大、最小userId,而userId是表的主键,即查询已经使用了主键索引,似乎没有优化的空间。
新线索:后来深入研究发现一般的默认查询,结果集都有100多万条数据,初步怀疑是数据量过大导致查询结果返回给query服务时耗时过多。于是从这个角度出发,尝试减少数据量。
第二次波折:查询结果含有分页的参数,初步设想,既然每次只显示一页的数据,那么第二次rpc调用时只查询这一页数据的userId对应的姓名即可,这样就能减少数据量。仔细查看代码却发现目前查询大致流程如下
- 第一次rpc调用查询全量的反馈数据
- 第二次rpc调用查询反馈数据中userId对应的姓名,在反馈数据中设置对应userId的姓名
- 过滤一些不符合条件的反馈数据
- 对过滤结果进行分页后返回
即分页必须在过滤以后,否则无法保证数据能占满一页,所以不能通过分页参数来减少数据量。
新的希望:虽然分页不能用于第二次rpc调用查询,但是过滤分页时并不需要姓名信息,这意味着第二次rpc调用可以发生在过滤分页后,即只查询某一页反馈数据中userId的姓名,目前每页尺寸是15条数据,这样查询结果集最大是15条数据。
结果:通过调整查询中的一些步骤顺序,避免了绝大多数多余数据的查询,减小了查询结果数据集的大小。最终,第二次rpc调用平均耗时10ms,提高了800多倍速率,极大地缩短了响应时间。
反思:数据查询前尽可能根据逻辑条件缩小查询范围,减少查询结果集的大小,缩短查询时间,提高查询效率。