当前位置: 首页 > 科技观察

如何缩短处理数十亿数据的“计划任务”的执行时间?

时间:2023-03-14 18:33:59 科技观察

继续回答水友们的问题。问题抽象:用户会员系统;用户会有积分记录,每月统计一次积分,对不同积分等级的会员进行不同的业务处理;数据假设:假设用户为100w级别;即流量数据日增量在100万级别,流量数据月增量在3kW级别,三个月流量数据量在亿级别;常见的解决方案:使用计划任务在每个月的第一天计算一次。//(1)查询所有用户uids[]=selectuidfromt_user;//(2)遍历每个用户foreach$uidinuids[]{//(3)查询用户3个月内的分数scores[]=selectuidfromt_flowwhereuid=$uidandtime=[3个月内];//(4)遍历得分流foreach$scoreinscores[]{//(5)计算总得分sum+=$score;}//(6)根据得分switch(sum)做业务处理)升级降级,送优惠券,送奖励;}一个月执行一次定时任务会有什么问题?计算量巨大,处理的数据量大,耗时长。据水友说,需要1-2天。画外音:外层100W级用户;内层9kW级流量;业务处理需要10次以上的数据库交互。可以多线程并行处理吗?是的,每个用户的流水线处理是不耦合的。改成多线程并行处理,比如按照用户拆分,会有什么问题?每个线程都要访问数据库进行业务处理,数据库不一定能处理。这类问题的优化方向是:相同的数据,减少重复计算的次数;共享CPU计算时间,尽量分散处理而不是集中处理;减少单次计算数据量;如何减少相同数据重复计算的次数?如上图所示,假设每格为1个月的分流量数据(约3kW)。3月末计算时,需要查询计算1月、2月、3月的9kW数据;4月底计算时,需要查询计算2月、3月、4月的9kW数据;…会发现2月和3月的数据(粉色部分)被反复查询和计算了很多次。画外音:对于这个业务,每个月的数据会计算3次。新增月度积分流量汇总表,每次只计算当月增量:flow_month_sum(month,uid,flow_sum)月末只计算当月积分,数据量减少为1/3、耗时也减少到1/3;同时,把前2个月的营业额加起来,就可以得到最近3个月的总分(这个动作几乎不需要时间);画外音:这个表的量级和user表的数据量是一样的,100w级别。这样每笔积分交易只会计算一次。如何分配CPU计算时间,减少单次计算的数据量?业务需求是每个月重新计算一次分数,但是一个月集中计算量太大,耗时太长,可以分配到每天计算。如上图所示,月点流量汇总表升级为日点流量汇总表。如果将每月一次集中计算分成30次分布式计算,每次计算的数据量减少到1/30,处理时间仅需几十分钟。即使每小时计算一次,每次计算的数据量也可以减少到原来的1/24,而且每次处理只需要几分钟。虽然时间缩短了,但毕竟是定时任务。分数流能实时计算吗?每天只增加100,000分,实时计算“每日积分流量汇总”。使用DTS(或运河)添加一个分数流量计监视器。当用户的分数发生变化时,每天的分数流量会实时累积,每小时计算一次的定时任务会平均分配到“每小时”,每天都会增加新的。100w流水,数据库写入压力每秒10次以上,完全可以承受。画外音:如果不能使用DTS/canal,可以使用MQ。总结一下,对于这种一次性集中处理大量数据的定时任务,优化思路是:相同的数据,减少重复计算的次数;共享CPU计算时间,尽量分散处理(即使是实时处理),而不是集中处理;减少单次计算的数据量;希望大家有所启发,想法比结论更重要。欢迎大家继续提问,有问题必答。【本文为专栏作者《58神剑》原创稿件,转载请联系原作者】点此阅读更多该作者好文