一次性集中处理大量数据的定时任务,如何缩短执行时间?

简介: 处理亿级数据的“定时任务”,如何缩短执行时间?

作者:58沈剑

问题抽象:
(1)用户会员系统;
(2)用户会有分数流水,每个月要做一次分数统计,对不同分数等级的会员做不同业务处理;
数据假设:

image

(1)假设用户在100w级别;
(2)假设用户日均1条流水,也就是说日增流水数据量在100W级别,月新增流水在3kW级别,3个月流水数据量在亿级别;
常见解决方案:
用一个定时任务,每个月的第一天计算一次。

//(1)查询出所有用户
uids[] = select uid from t_user;
//(2)遍历每个用户
foreach $uid in uids[]{
         //(3)查询用户3个月内分数流水
        scores[]= select score from t_flow
                  where uid=$uid and time=[3个月内];
         //(4)遍历分数流水
        foreach $score in scores[]{
                   //(5)计算总分数
                  sum+= $score;
        }
         //(6)根据分数做业务处理
        switch(sum)
        升级降级,发优惠券,发奖励;
}

一个月执行一次的定时任务,会存在什么问题?
计算量很大,处理的数据量很大,耗时很久,按照水友的说法,需要1-2天。
画外音:外层循环100W级别用户;内层循环9kW级别流水;业务处理需要10几次数据库交互。
可不可以多线程并行处理?
可以,每个用户的流水处理不耦合。
改为多线程并行处理,例如按照用户拆分,会存在什么问题?
每个线程都要访问数据库做业务处理,数据库有可能扛不住。
这类问题的优化方向是:
(1)同一份数据,减少重复计算次数;
(2)分摊CPU计算时间,尽量分散处理,而不是集中处理;
(3)减少单次计算数据量;
如何减少同一份数据,重复计算次数?
image

如上图,假设每一个方格是1个月的分数流水数据(约3kW)。

3月底计算时,要查询并计算1月,2月,3月三个月的9kW数据;
4月底计算时,要查询并计算2月,3月,4月三个月的9kW数据;

会发现,2月和3月的数据(粉色部分),被重复查询和计算了多次。
画外音:该业务,每个月的数据会被计算3次。
新增月积分流水汇总表,每次只计算当月增量:
flow_month_sum(month, uid, flow_sum)
(1)每到月底,只计算当月分数,数据量减少到1/3,耗时也减少到1/3;
(2)同时,把前2个月流水加和,就能得到最近3个月总分数(这个动作几乎不花时间);
画外音:该表的数量级和用户表数据量一致,100w级别。
这样一来,每条分数流水只会被计算一次。
如何分摊CPU计算时间,减少单次计算数据量呢?
业务需求是一个月重新计算一次分数,但一个月集中计算,数据量太大,耗时太久,可以将计算分摊到每天。
image

如上图,月积分流水汇总表,升级为,日积分流水汇总表。
把每月1次集中计算,分摊为30次分散计算,每次计算数据量减少到1/30,就只需要花几十分钟处理了。
甚至,每一个小时计算一次,每次计算数据量又能减少到1/24,每次就只需要花几分钟处理了。

虽然时间缩短了,但毕竟是定时任务,能不能实时计算分数流水呢?
每天只新增100w分数流水,完全可以实时累加计算“日积分流水汇总”。
image

使用DTS(或者canal)增加一个分数流水表的监听,当用户的分数变化时,实时进行日分数流水累加,将1小时一次的定时任务计算,均匀分摊到“每时每刻”,每天新增100w流水,数据库写压力每秒钟10多次,完全扛得住。
画外音:如果不能使用DTS/canal,可以使用MQ。

总结,对于这类一次性集中处理大量数据的定时任务,优化思路是:
(1)同一份数据,减少重复计算次数;
(2)分摊CPU计算时间,尽量分散处理(甚至可以实时),而不是集中处理;
(3)减少单次计算数据量;
希望大家有所启示,思路比结论重要。

最后
欢迎大家一起交流,喜欢文章记得点个赞哟,感谢支持!

相关文章
|
21天前
|
SQL 前端开发 JavaScript
数据库查询 定时
数据库查询 定时
16 1
|
3月前
|
程序员 开发工具 git
批处理--节约你的开发时间
批处理--节约你的开发时间
|
3月前
Cron表达式每隔两小时执行一次
Cron表达式每隔两小时执行一次
108 1
|
8月前
|
存储 NoSQL Java
使用Redisson RLock锁防止定时任务短周期重复执行
在开发定时任务时,如果任务执行周期较短,可能会导致任务在前一次执行尚未完成时就再次触发,从而产生重复执行的问题。为了解决这个问题,我们可以借助Redisson的RLock锁机制,确保任务只有在前一次执行完成后才能再次执行。本文将介绍如何使用Redisson RLock锁来避免定时任务的重复执行。
256 0
使用Redisson RLock锁防止定时任务短周期重复执行
|
9月前
|
关系型数据库 MySQL 调度
定时任务优化
简单描述一下定时任务的优化
96 0
|
11月前
|
Java 调度
Quartz-Cron表达式统计最近几次的执行时间
Quartz-Cron表达式统计最近几次的执行时间
200 0
|
11月前
AS执行时间计算
long start=System.currentTimeMillis();
34 0
|
负载均衡 算法 Java
记一次线上频繁FGC的事件和解决方式
1.大量的请求,调用的地方要注意是否会导致内存的大量消耗,尽可能使用池化技术,单例等,减少创建,销毁的系统开销;2.CMS 的几个缺点,可以参考《深入java虚拟机》,对CPU占用会比较高,无法处理浮动垃圾,还有就是CMS使用的是标记-清除算法,会导致大量的空间碎片,碎片过多的话,导致分配大对象很困难,所以不得不进行FGC,也可能是这个原因导致了本文说的一直FGC的问题。
311 0
记一次线上频繁FGC的事件和解决方式
|
SQL 关系型数据库 MySQL
mysql查询优化实战:查询用时一分半降到三毫秒
项目中的课程预约记录查询功能,线下门店反馈说进入到页面需要等2分钟
mysql查询优化实战:查询用时一分半降到三毫秒
|
JavaScript 算法 前端开发
不到一秒才叫优化
之前做完的一个项目,业务逻辑写完之后,首屏渲染能到3~4秒,这对于用户体验是不能接受的,所以忙里偷闲把项目优化完之后http发送到响应时间:705ms,DOM构建完毕:452ms,页面加载完毕:678ms,清爽的感觉很上头~看来优化还是很有必要的!所以本篇记录一下优化过程。
206 0