Fork me on GitHub

【数据扫描】并发获取数据的思路讲解,例如短信发送内容获取、数据库中的批量数据处理

一、业务介绍

  我要实现的一个功能就是要把数据库中订单明细表中的数据逐一扫描出来进行发送的工作。就像是下面的这张订单明细表一样,我需要把表里的数据逐一扫描出来,然后分别把对应的消息内容发送给对应的手机号码。听起来似乎是一个很简单的业务逻辑,似乎一个线程直接扫描表数据获取其中的一条或者数条数据,然后进行发送,最后根据结果修改一下对应的状态就可以了。是的,单线程的话,足够的安全,但是扫描的性能始终是有限的。只有在数据量小且不必保证可用性的时候才可能使用这种方式进行实现。要想性能高,还得需要是多线程处理。

 

二、实现思路

  多线程并发扫描:开启N个线程,每个线程扫描一定区间范围内的数据,比如每次10条。

    线程1:扫描id=1到10的数据
    线程2:扫描id=21到30的数据
    线程3:扫描id=11到20的数据
    线程4:扫描id=31到40的数据
    线程5:扫描id=41到50的数据
    线程6:扫描id=51到60的数据

    。。。

 

  上面的设计描述的不是很好,我的意思其实是,每个线程可以扫描一定区间的数据,但是也需要支持一些特殊情况。例如:数据“断层”,比如数据量不是10的倍数,可能总数是21条。这样的场景,我们的线程处理也得做一个兼容功能。

    线程1:扫描id=1到10的数据
    线程2:扫描id=11到20的数据
    线程3:扫描id=21到21的数据

  虽然只有一条数据,但是也不能懈怠。于是这里就涉及了一个多线程扫描控制的功能,也就是线程在扫描的时候需要分配好自己需要处理的区间数据。

  这个也不难处理,我认为可以加一个线程扫描任务信息表,记录每个线程负责的用户数据。

 

 

 

 

 

   同时,通过该表信息,也可以实现扫描任务的补偿工作。也就是当正在工作的线程如果突然宕掉(意外、人为导致了处理终止),其他空闲的线程可以接替其工作完成剩余的扫描任务。

   此外,多线程还需要有一个协调工作,就是要如何保证多个线程有序的获取各自需要扫描的ID区间这个问题。如果并发不是很大的话,可以通过分布式锁来解决这个问题。

   大概的总流程是这样的(需要提前配置好扫描区间范围的值,比如10,每次一个线程最多扫描10行记录):

   1)每个线程启动的时候,加分布式锁(比如key为[LOCK:ORDERDETAIL:202208])。

 

   2)加锁失败则进行重试。

   3)加锁成功,则获取当前已扫描的ID最大值(这个最大值,可以存放在缓存中,我目前采用的方案是存放在Redis当中,比如key为[SCAN:POSITION:ORDERDETAIL:202208])

   4)获取数据库的订单明细表当中最大的ID值,也就是获取到数据库目前的ID情况,如果为null,当作0处理。

   5)如果获取到的ID最大值为空,也正常,说明是第一个抢到锁的线程,直接当作0处理。

   6)接着就是比对已扫描的ID和数据库最大ID的数据大小了。如果已扫描的ID=数据库最大ID,说明目前已经扫描完成,则跳过处理。

   7)如果已扫描的ID<数据库最大ID,说明还有扫描的任务需要进行。此时,需要按照区间范围更新当前已扫描的ID最大值(我的是更新redis上key为[SCAN:POSITION:ORDERDETAIL:202208]的值)。

   8)更新当前已扫描的ID最大值完成之后,需要记录数据到扫描任务信息表。

   9)数据插入成功,就可以释放分布式锁。

   10)执行自己区间内的ID数据查询(此处带上状态条件查询,避免后续补偿线程的重复扫描),由于是通过ID获取数据,所以相对的查询性能还是可靠的。查询到数据之后,可以执行相应的业务逻辑,根据业务逻辑执行情况,更新订单明细表的状态和任务进度信息表(更新当前ID位置和最新操作时间)【此处最好加上事务控制,如果出现异常情况,可以针对事务进行订单明细表数据的状态调整】。

   11)接着,该线程正常执行完成,就可以删除任务进度信息的数据,去执行下一轮的分布式锁争抢。

   12)补偿线程的话,就是扫描任务进度信息,判断最新操作时间是否小于设定的时间间隔(比如5分钟,如果最新操作时间5分钟没变化,说明该线程已经5分钟没有更新进度了,可能有些不正常),然后获取记录的当前ID位置和结束ID位置以及待发送的状态条件进行数据扫描,执行逻辑也是从第1个步骤开始,到第11个步骤结束后,继续扫描任务进度信息实现补偿工作。

 

  

三、总结 

   我思考的主要实现逻辑如上所示。如果可以的话, 其实可以加个任务调度中心进来,不必依赖锁来进行。不过做的事情也是一样的,就是各线程的ID区间分配。不过,我司业务的话,分布式锁已经足够支撑使用。该功能主要的问题点还是要注意业务幂等性的控制。

  今天的分享就到这里,谢谢大家!

 

posted @ 2022-07-31 22:29  罗西施  阅读(286)  评论(0编辑  收藏  举报