业务背景
跑批通常指代的是我们应用程序针对某一批数据进行特定的处理
在金融业务中一般跑批的场景有分户日结、账务计提、欠款批扣、不良资产处理等等
具体举一个例子 🌰
客户在我司进行借款,并约定每月 10 号码还款,在客户自主授权银行卡签约后
在每月 10 号(通常是凌晨)我们会在客户签约的银行卡上进行扣款
然后可能会有一个客户、两个客户、三个客户、四个客户、好多个客户都需要进行扣款,所以这一“批” 所有数据,我们都要统一地进行扣款处理,即为我们“跑批”的意思
跑批任务是要通过定时地去处理这些数据,不能因为其中一条数据出现异常从而导致整批数据无法继续进行操作,所以它必须是健壮的;并且针对于异常数据我们后续可以进行补偿处理,所以它必须是可靠的;并且通常跑批任务要处理的数据量较大,我们不能让它处理的时间过于久,所以我们必须考虑其性能处理;总结一下,我们跑批处理的应用程序需要做到的要求如下
-
健壮性
-
针对于异常数据,不可能导致程序崩溃
-
可靠性
-
针对于异常数据,我们后续可以跟踪
-
大数据量
-
针对于大数据量,可在规定的时间内进行处理完毕
-
性能方面
-
必须在规定的时间内处理完从而避免干扰任何其他应用程序的正常运行
跑批风险
一些未接触过跑批业务的同学,可能会犯一些错误·
-
「查询跑批数据,未进行分片处理」
-
这种情况具体有两种情况
-
一种是同学无意识进行分片处理,直接根据查询条件将全量数据查出;
-
第二种情况呢,不单是在跑批的时候可能出现的情况,在平时的业务开发过程中也可能发现,针对于查询条件未进行判空处理。比如 select id from t_user_account weher account_id = "12"; 然而在业务处理过程中,account_id 为空,却直接进行查询,数据量一旦上来,就容易导致 OOM 悲剧
-
「未对数据进行批量处理」
-
这种情况也是同学们容易犯的一个错误,通常我们跑批可能会涉及到数据准备的过程,那么有的同学就会直接梭哈,边循环跑批数据边去查找所需的数据,一方面 for 嵌套的循环处理,时间复杂度通常是随着你的 for 个数上升的,在项目中一个同学在保费代扣的跑批任务中,进行了五次 for 循环,这个时间复杂度就是 O(n ^ 5)了,并且如果你的方法未进行事务管理的话,数据库的连接释放也是一个非常消耗资源的事情
-
上一个伪代码可能会比较好理解
// 调用数据库查询需跑批数据List<BizApplyDo> bizApplyDoList = this.listGetBizApply(businessDate);// for 循环处理数据for(BizApplyDo ba : bizApplyDoList) {// 业务处理逻辑.. 省略// 查询账户数据List<BizAccountDo> bizAccountDoList = this.listGetBizAccount(ba