事前准备
明确约束
假设每条数据1kb,所有要插入的数据都储存到一个服务器上
- 尽可能快
- 尽可能不丢失
- 尽可能保证顺序
面临挑战
- 单数据库难以支撑如此高数据量
- 内存也无法同时支撑如此高数据量
- 可能在过程中出现插入错误,甚至宕机
- 如果是正在使用的数据库会对正常使用数据库、甚至网络带宽造成影响
- 采用多并发会产生诸多相关并发问题
方案尝试
设定将数据全部放置在服务器S,并通过多个工作节点进行数据插入
数据处理
考虑数据量,10亿条数据,每条1kb,那么大约是接近1tb。显然这么大的数据是不能一次加载到内存的,也不能直接储存到一个文件中,亦不能储存到同表中,因为一般而言单表最大储存2000w数据
单表不超过2000w行的原因在于按照最小行数据计算,2000w大约就是三层b+树,再大会增加磁盘IO的查询次数,并且会使得总储存量级到达百亿。再考虑到如buffer的优化手段被限制住,那么性能会进一步地下降
因此,可以将数据分为100份,那么每份就是大约10gb。数据库进行分库分表,每份数据对应一张表,每10份对应一库
基本流程
设置一个批次插入为一个任务,在此定每次插入1000条数据,那么每个文件总共就是1w个任务,这1w个任务便为一个任务组
服务器S储存所有文件,多个节点作为工作节点进行数据插入,
-
每个工作节点绑定多个任务组
-
工作节点根据当前负载情况和进度申请任务数。比如工作节点w1最初申请了100任务,那么服务器S预锁定这100任务,其他工作节点无法获取
-
服务器S根据预定任务数持续传输文件内容
-
工作节点收到数据后,连接数据库,根据负载情况开启多线程对所接收任务,以任务ID组合行数作为表id进行插入数据
若插入过程中出现错误,则重新尝试3次后。若仍不行,则log记录失败行数、错误信息等
-
若数据插入完成,通知服务器S,服务器S更新任务状态已完成
-
若任务超时仍未完成,则服务器S释放任务状态
-
工作节点完成本节点绑定的所有任务之后,便可以开始抢占其他节点任务,以未完成量最多的任务组为优先
抢占之后可能会出现任务恰好完成了,但由于id一致,因此能够保证幂等
-
进入抢占状态,服务器S在每次申请时,都进行加锁,获取完相应任务后解锁
关于服务器S的细节
- 服务器S可以缓存下一批数据到内存中,那么在获取任务之后,就能很快进行数据传输了
- 服务器S实时对每个任务组剩余任务数进行排序
- 抢占最开始以最大未完成为优先,之后则以上一次抢占到的任务组为优先
关于工作节点的细节
- 工作节点依据本地机器当前cpu、内存、网络负载以及处理增长速率来决定同时请求任务处理的最大数。比如cpu处于满载情况,那么就不应该申请任务。
- 工作节点每空余一小批次任务额度情况下向服务器S请求下一批次任务
- 若由于内存、网络等问题丢失任务数据,可向服务器S再次请求
更多问题
若所有工作节点同时对一任务组进行数据插入呢?
每一个任务组设置最大同时任务数。超过该任务数,进行抢占其他任务组或者等待
如果数据量太大不适合在单服务器储存怎么办?
可以分成多份到多服务器,每个工作节点根据绑定的任务组到对应的服务器获取任务进行数据插入。
并在多服务器中选定一个做为主服务器,储存各个服务器任务完成情况。那么在抢占时便可以依据此定位到相应服务器去抢占数据
如果服务器并不是已经存在的数据,而是持续不断传输的数据源该如何办呢?
根据数据id进行分组,每一个工作节点处理绑定组的数据。若一组任务堆积量太大,或者堆积时间太长,则让其他工作节点每处理完一批本组数据,便要处理该堆积数据,直到该堆积数据低于阈值量或者阈值时间