背景描述:
一批1亿左右的数据,需要计算所有数据属于哪些类型,然后归类发送到MQ中,这个是一个真实的业务场景,并且会有一定的并发读,可能同一时间10-20个类似的处理任务发生
分布式计算简单来说,是把一个大计算任务拆分成多个小计算任务分布到若干台机器上去计算,然后再进行结果汇总。 公司场景一批几亿数据需要进行计算匹配发送,单节点处理经过几轮优化可能需要5小时才能发送完,针对这样的场景开始了分布式计算的设计之路
不过数据一旦进行计算拆分,问题会变得非常复杂,像一致性、数据完整、通信、容灾、任务调度等问题也都来了。
适用场景:
- 同一时间存在一批大量数据需要协同计算处理
- 单个节点没有下一层分布式复杂事务的关联关系
- 单个分布式节点能够完成闭环计算(这点非常重要,如果分布式计算节点还依赖其他的分布式节点那么整个架构设计非常复杂并且容易出现异常无法处理)
分布式计算常用模式:
- 分片算法
- 主键取模分片(根据某个唯一键进行取模,不同节点处理不同的取模数据)
- 数据量分片(数据总量+分布式节点总数进行数据分配处理)
常用分布式定时任务框架来支持,例如XXL-JOB或者elastic-job,任务触发时同时通知所有计算节点进行协作,自带节点重试、存活结点计算,节点下标等功能,减少很多开发量
- 消息队列
- 将所有需要处理的数据发送到MQ队列中
- 消费者进行分布式消费+计算
- Hadoop(MapReduce)
- 平台进行分配调度计算
由于MapReduce计算输入和输出都是基于HDFS文件,所以大多数公司的做法是把mysql或sqlserver的数据导入到HDFS,计算完后再导出到常规的数据库中,这是MapReduce不够灵活的地方之一。 MapReduce优势在于提供了比较简单的分布式计算编程模型,使开发此类程序变得非常简单,像之前的MPI编程就相当复杂。
无论采用那种方式,本质是一样的
我们小组目前遇到的问题比较符合解决方案一,数据量没有到PB或者TB级别,用MapReduce不是那么合适,反而增加了很多复杂度,用MQ无法动态增加动态处理节点,消费者相关配置是初始化就决定的
在设计之初,我们需要确定利用分片做分布式计算的方式,常规的有以下几种方式:
- 利用分布式定时任务框架自带的参数进行分片计算(参数{index:1,date:20220501}...)分片1处理2022年0501的区域数据
- 数据总量自动分片处理(根据待处理的数据总量、分片总数)计算每个分片需要处理的待计算数据
我们这边综合考虑采用了第二种方式,因为数据不具有离散型,可能大量数据在一个分区,会导致分片计算出现倾斜,下面的脑图是设计初期做的一些发散性总结,其中黄色标示的链路是最终确定的设计方向
因为上面已经说过,通过数据量最分片计算方案的话,那么分片的节点总数就非常重要,而节点总数的前置确定、后置确定都会在异常处理时出现不同的逻辑,我们选择的是前置确定
我们最终设计的流程大致如下:
分布式计算的设计我们一定需要考虑异常场景的设计,异常场景分为:
- 数据初始化时的异常
- 分片计算过程中的数据库超时等异常
- 分片计算过程中节点重启
- 分片解散过程中节点离线
健壮的系统设计一般都会把所有非代码BUG的异常场景全部考虑进去,让代码能够自动修复从而完成数据处理的最终一致性,我们这边采用了正向、逆向任务方式去处理,正向捞取处理待计算的任务,逆向捞取各种异常失败的任务
这样处理可以让任务之间互相不会产生阻塞,因为大数据任务对线程的并发控制是很严格的,无论什么样的数据库都无法支持超大数据量的高并发查询,区别于我们认知的Msyql等OLTP类型的数据库
上面就是我们正特分布式计算任务的全景架构图,详细的任务串联流程:
需要注意的是分片节点确认需要计算的数据量时会出现非整数情况,推荐做法获取节点总数平均分摊后取余,将多出来的数据全部给第一个节点处理