多线程并发处理

tilts1234 2013-01-11 06:20:31
数据库里有两个表A,表B,结构一样。
需要做的是把表A的数据拿出来,处理后放到表B。
现在我是用线程取,每10秒调用一次存储过程。
每次取100条数据。
存储过程如下:

UPDATE t_tableA SET PushStatus =0 WHERE PushStatus=-1 limit 100
SELECT ID,Name,Content FROM t_tableA WHERE PushStatus = 0;
INSERT INTO t_tableB SELECT * FROM t_tableA WHERE PushStatus = 0;
DELETE FROM t_tableA WHERE PushStatus=0;

然后线程如下

package cn;

public class Thread implements Runnable{

private String sendUrl ;
private int milliSecond ;

public SmsSendThread(String sendUrl, String milliSecond) {
this.sendUrl=sendUrl;
this.milliSecond=Integer.parseInt(milliSecond);
}

public void run() {
while(true){
try {
Thread.sleep(milliSecond);
Service.getInstance().send(sendUrl);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

在Service类的send方法里调用了存储过程取数据,处理数据,并返回值后按照ID进行处理成功的标识更改。
我首先起了一个线程调用三次 如下

Thread sst = new Thread(sendUrl,milliSecond);
new Thread(sst).start();
new Thread(sst).start();
new Thread(sst).start();


速度增加了,但是肯定会有重复的数据。

怎么才能让在没有重复数据的情况下增加处理的速度呢?

如没明白可提问我再补充
...全文
5962 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
dingchunyang 2014-06-19
  • 打赏
  • 举报
回复
sust2012 2013-01-12
  • 打赏
  • 举报
回复
你可以先把记录分页,然后一个线程去处理一页的数据。
dracularking 2013-01-12
  • 打赏
  • 举报
回复
这种多线程没意义吧?在数据库方,从A表取数据,为了防止重复总是要同步的,你觉得呢? 觉得倒是可以多线程处理数据和插入数据
悲催的程序猿 2013-01-12
  • 打赏
  • 举报
回复
学习了!
Jacky-止涯 2013-01-12
  • 打赏
  • 举报
回复
引用 楼主 tilts1234 的回复:

UPDATE t_tableA SET PushStatus =0 WHERE PushStatus=-1 limit 100
SELECT ID,Name,Content FROM t_tableA WHERE PushStatus = 0;
INSERT INTO t_tableB SELECT * FROM t_tableA WHERE PushStatus = 0;
DELETE FROM t_tableA WHERE PushStatus=0;
先分析一下,如果像楼主那样处理,会发生什么样的冲突: 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一个就够了,让他每隔一段时间扫一扫就可以了。 楼主也可以结合不同方案,组合成一个新的构想!
Jacky-止涯 2013-01-12
  • 打赏
  • 举报
回复
引用 1 楼 stalendp 的回复:
我觉得你应该把对数据库读写的那一块同步起来。
引用 4 楼 wymbest 的回复:
楼主在不,我觉得 这个功能最耗时间的应该是 在jAVA段与数据库通讯的过程, 而当线程真正进入到 存储过程中后 会很很快执行完那几行SQL语句。 所以可以让线程在存储过程中 排队依次执行那几行SQL,就是当一个线程执行这几行SQL时将其锁住,其他线程等到当前线程执行完SQL后(COMMIT;),在执行这几行SQL。这样大家只是在耗时很短的SQL执行时同步,其他过程都是……
楼主的操作中,数据库的读写贯穿了整个流程,对数据的处理也被数据库的读写包围了,所以如果要同步这一块的话,那么多线程就会阻塞成单线程,同步就没有意义了。
引用 3 楼 wymbest 的回复:
当某个线程进入到存储过程中,并开始使用A表时,就将A表锁住如何? 用for update; 不一定对,需要看看锁住那一块,才不会出现重复插入。
锁表是一件很危险的事情: 1、锁表后,不只是这个操作,连其他程序想操作这个表,也会被阻塞,大大降低了效率; 2、在解锁的时候,如果出现了异常没有解锁成功,那么整个表就会一直锁着,这个你懂的……
wymbest 2013-01-11
  • 打赏
  • 举报
回复
楼主在不,我觉得 这个功能最耗时间的应该是 在jAVA段与数据库通讯的过程, 而当线程真正进入到 存储过程中后 会很很快执行完那几行SQL语句。 所以可以让线程在存储过程中 排队依次执行那几行SQL,就是当一个线程执行这几行SQL时将其锁住,其他线程等到当前线程执行完SQL后(COMMIT;),在执行这几行SQL。这样大家只是在耗时很短的SQL执行时同步,其他过程都是异步的。就该就达到 目的了吧,呵呵 我凭空想的,你看咋样?
wymbest 2013-01-11
  • 打赏
  • 举报
回复
当某个线程进入到存储过程中,并开始使用A表时,就将A表锁住如何? 用for update; 不一定对,需要看看锁住那一块,才不会出现重复插入。
stalendp 2013-01-11
  • 打赏
  • 举报
回复
我觉得你应该把对数据库读写的那一块同步起来。

50,530

社区成员

发帖
与我相关
我的任务
社区描述
Java相关技术讨论
javaspring bootspring cloud 技术论坛(原bbs)
社区管理员
  • Java相关社区
  • 小虚竹
  • 谙忆
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧