转载

11年前
先分析一下,如果像楼主那样处理,会发生什么样的冲突:
1、对于update来说,不同线程间的update不会有影响,因为数据库会处理这个冲突。
2、对于select来说,如果在delete前又有线程update,就会出现重复的数据,这样造成重复的处理;
3、对于insert来说,如果在delete前又有线程insert,就会造成重复的数据插入t_tableB表。
4、对于delete来说,如果一个线程刚update完,还没来得及select和insert,另一个线程就delete了,那么数据就会丢失。
……这里的冲突很多,就不一一列举了。

首先提个建议,楼主之前分别用PushStatus=-1表示未处理,PushStatus=0表示处理中/完毕。
可以再引入一个状态来区分处理中和处理完毕。如PushStatus=0表示处理中,PushStatus=2表示处理完毕。
------------------------------------------

宗旨:减少冲突,减少阻塞,提高并发,尽量批量操作。

方案1-模仿CAS的无阻塞算法: 【缩小更新的粒度】
1、先找出符合要求的100条;
SELECT ID,Name,Content FROM t_tableA WHERE PushStatus=-1 limit 100
2、处理每条数据,处理前先更新,更新成功后再处理。(这样能避免数据的重复处理,把同步的任务交给了数据库)
UPDATE t_tableA SET PushStatus=0 WHERE PushStatus=-1 AND ID=:ID
3、对该条数据进行insert和delete,此处的修改都是按照ID的,所以不会造成冲突。
点评:没有使用锁,线程间不用阻塞等待。但是每条数据都要分开更新,如果每条数据处理时间很短的话,那么效率就会大打折扣。

方案2-读取锁
1、更新-读取-更新:先更新是为了批量获取,获取后再更新是为了批量占用已选数据。
把这个过程锁起来,保证每次读的时候不会冲突,这样就不会产生重复处理的数据了。
UPDATE t_tableA SET PushStatus=0 WHERE PushStatus=-1 limit 100;
SELECT ID,Name,Content FROM t_tableA WHERE PushStatus = 0;
UPDATE t_tableA SET PushStatus=2 WHERE PushStatus=0;
点评:在读取的时候加锁,读取和占用更新都是批量的,既解决了数据冲突的问题,也大大减少数据库的操作次数。
但是,由于加了读取锁,所以要尽量增大每次读取的数量,减少读取的次数,否则锁竞争会影响效率。


方案3-分角色线程
这个场景中,有四个操作:读取数据、处理数据、转移数据、清理数据。
所以我们可以分成三种不同的线程来处理:
1、读取线程Read:负责读取数据,update和select操作,先update成处理中状态PushStatus=0,select获取数据,然后分配给数据处理线程Deal来做。
2、数据处理线程Deal:负责处理数据,处理完毕后update成完成状态PushStatus=2,因为数据都是读取线程Read传过来的,由读取线程统一管理,所以数据处理线程只管处理,不用考虑数据的冲突,真正实现并发操作。
3、清理线程Clear:负责把已完成PushStatus=2的数据转移到B表,然后清理A表。【清理的时候也涉及到数据冲突的问题,类似上面的情况,就不再讨论了】
点评:这三种线程角色相互独立,可以并行;而同一种线程角色中又可以并发,实现横向和纵向的并发。
但是结构相对比较复杂,适合规模大的负责的业务场景。
理想状态:
读取线程Read获取数据后调用数据处理线程Deal,在Deal处理过程中,Read继续获取新的数据,等Deal处理完毕了,Read刚好获取完新的数据,然后又传到Deal中来处理。【两者线程数量的比例根据各自处理时间来定】
另外,如果B表的实时性不高的话,清理线程Clear一个就够了,让他每隔一段时间扫一扫就可以了。