我做的系统管理后台有一个页面,可以查询或导出数据。查询条件位于页面最顶部,下面有个「查询」按钮,点击之后根据查询条件向后端发起请求,后端返回一个数据列表,页面将其分页展示。
这个功能再简单不过,但是「查询」旁边的「导出」却没按常理出牌,导出超时。。。
页面查询没毛病,导出却不行,第一反应是不是导出的数据太多,导致查询超时了。看了一下导出 URL(GET 请求),pageSize 是 260,按理说区区 260 条数据,不可能查询超时吧,我把 pageSize 改成与页面查询一样大小,又请求了一遍,发现还是不行,这个就有些诡异了。
登录预发机器,看日志,发现 Feign RPC 调用超时。
找到 remote api 的代码,梳理代码逻辑,在可能耗时的语句前后都加上一行日志,预发发布,重新请求。
真凶找到了,说到底还是代码逻辑问题,首先到某张表中查询出 250 条数据,然后用这 250 条数据的 id list 作为条件去查询另一张表,取到 3000 多条数据,再用一个 for 循环去宽表拿这 3000 多条数据的某个相关信息,查询宽表用的还是 rpc,不超时才怪。
在找到真正原因之前,我还纠结了好久是不是 mybatis 的 PagerHelper 没起作用:为什么我要查询 250 条数据,却读出了 3000 多条。后来找之前维护这块代码的同事了解了下,原来「查询」按钮和「导出」按钮所处理的数据不一样,我看代码时先入为主了,误认为「导出」只是把「查询」到的数据用 Excel 保存下来而已。
好吧,这个也反应出产品设计上的一个问题,既然「查询」和「导出」的处理对象不一样,就应该明确的写明「导出xxx」,而不是只用「导出」二字,很容易让人误以为其处理的数据与「查询」一样。
那么如何解决这个查询宽表的耗时问题呢?
这就要回归的业务问题,首先要搞清楚用户要拿这个数据干什么,迅速电话了一下需求方,拉上产品组了个小群,讨论了一番后发现,其中某个字段根本不重要,导出结果没必要带上它。这样就好搞了,把 rpc 查询宽表的语句删掉,直接在内存中组装数据就 OK 了。
问题虽然解决了,但是还要接续思考:如果业务需求必须要带上那个不必要的字段怎么办呢?
这样问题就变的复杂了,即使 rpc 查询速度很快,随着查询数据量变大,for 循环次数一多,必定会导致超时,因此最根本的方式还是数据聚合,把几张关联关系表的数据组合成一张宽表,直接一个 select 语句搞定。
问题虽然简单,暴露出来的问题不少:
- 产品设计不要反直觉
- 写代码一定要考虑到功能是否能支撑大数据量