问题描述:
之前有信创项目反馈了一个批量导入的性能问题,400条数据需要3分钟,5000条需要20分钟以上,系统初始需要导入的各类数据有上百万。
原因分析:
联系项目导入20~30条数据,并启用程序跟踪收集性能数据发现,有大量对字典表的SQL查询。
与功能开发同事沟通确认,该SQL是导入模板中有几个关联字段,用户录入的是编码或名称,后台需要根据编号或名称查询对应的Id值,目前是简单粗暴的循环处理。
原因倒是很清楚,如果只是为了减少对关联字段的查询次数,可以直接一次将关联数据全部加载做缓存处理,前提是关联数据的全量数据不能太大,否则容易导致OMM。
优化方案:
按列汇总所有数据并去重,建立当前关联列数据的Map<name, id>缓存,分批次去DB检索数据,并存入Map缓存:
1、取出一个批次的数据 names(n=names.size(), n<=500)
2、检查Map缓存中是否已存在
2.1、如果Map已缓存,就根据map直接标记检查结果,并从names移除该数据
2.2、如果n<300,则再次执行第1步骤,直至n=300或当前列所有数据均已被处理
3、select id, name from helpdata where name in (names);
4、将name->id作为键值对加入map
5、将names 与 nameList的差集,按 name –> null 加入map
6、重新执行第1步骤,直至当前列所有数据被处理完毕
此方案基于一个这样的事实:一次导入或处理的数据是相对有限的(一般而言,<=10w最多也不会超过50w,再多也是分成多个批次处理),而这一个批次中某列的关联数据有可能是百万乃至千万的量级。
如果按行循环处理,需要循环rowNum * columnNum次,以10w行、20列引用字段为例,需要执行200w次SQL查询请求;
如果一次将关联帮助数据全部加载至内存,则对于项目、往来等可能存在很大数据量的场景,会直接导致应用OOM;
按照本方案,理论上最坏的情况也仅执行4k次SQL查询请求(10w行、20列),而真实场景是一次处理的数据中关联列字段值重复概率非常高,实际执行的SQL请求和内存占用远低于理论的最大值。
效果验证:
产品功能按此方案优化,部署到项目后反馈良好:内存占用稳定,1k数据(2个关联列)处理响应时间从之前的3分钟提升至10秒左右。
标签:编码,缓存,批量,校验,关联,导入,names,SQL,数据 From: https://www.cnblogs.com/zhaoguan_wang/p/18398908