Java多线程:解决高并发环境下数据插入重复问题

Posted JAVA葵花宝典

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java多线程:解决高并发环境下数据插入重复问题相关的知识,希望对你有一定的参考价值。


应用框架:Spring + SpringMVC + Hibernate 

数据库:Oracle11g
一家文学网站向我系统推多线程低并发推送数据,我这边观察日志和数据库,发现有一个作者被存储了2次到数据库中。按照程序的编写逻辑,重复的数据是会被判断出来不被存储的。

由于网络原因,客户可能连续推送了两条重复的数据,两条数据时间间隔非常小,因此导致了我们的
if(用户不存在){
    xxxxx
   存储用户到数据库 
}else{
    重复推送,不采取任何措施 
}

这个操作还没有执行完毕,第二条拥有相同数据的线程已经进入并通过了if的检验,导致数据库存储了两条相同的数据。后来我自己写了个100并发的多线程测试程序,发现100条相同数据中有40条被插入到了数据库里!天啦噜!!!因此确定了是多线程的并发导致了程序的判断逻辑失效。  

  

1) 在Author主表中对 身份证号 添加了唯一索引,现在Author主表不会出现重复数据了,如果连续推送客户会收到一条推送失败的提示信息。
   
2) 但是AuthorOrg表(Author与AuthorOrg是一对多关系)依然会出现重复数据,想过添加siteId + userUniqueId的 联合唯一索引来解决,但是想到work表也会出现同样的问题,添加过多索引会导致DB占用空间无限增大,因此不采用。
   
3) 考虑使用synchronized对方法添加同步锁,但是这样会导致其他正常数据的推送线程也被阻塞,影响效率。因此不采用。
  
4) 使用对数据库添加行锁,实验发现还是会出现2条重复数据
       分析:
           理论上的结果应该是1条成功,149条失败。
           对数据库的select语句添加行锁必须作用于某条记录,但是第一次报送时,数据库中并没有这条数据,因此行锁根本没有加上,导致第二条数据成功异步使用select语句。
           第一次报送成功以后,数据库中有了这条数据,select语句成功的对这条记录添加了行锁,所以后边不会出现重复数据。因此此法不可用。
   
5) 即想提高效率不对方法添加synchronized,又想保证数据准确性,最后使用synchronized(siteId + uid) 在Controller层加锁(保证了只有重复数据被加锁,在Controller使用的原因是因为事务会在Service调用完毕才被提交,我实验过在Service同步,150并发会出现2条重复数据,因为事务还没来得及提交)
   
测试结果:测试了3次150并发  不到一秒的时间全部返回,结果1条登记成功,149条返回该作者已登记。 
 下一步:

   针对所有可能出现高并发问的接口进行调整。    这种加同步锁的方法在负载均衡下的多台应用服务器会失效!因为就算Spring保证了对象是单例的,但是多台服务器肯定是多个对象!因此synchronized将无效。解决方法是在数据库层对该对接公司的唯一记录加select锁,这样就能保证数据的不重复性,但是会降低该公司推送数据的效率(相当于逐条推送),但是公司与公司之间还是并行推送的。还有一个方法就是将业务逻辑写入存储过程,然后对存储过程加锁,这种方法太麻烦了,需求有变动就必须去修改存储过程,但是效率要比前者高得多。


原文地址:http://blog.csdn.net/nikobelic8/article/details/53308543

       
         
         
       
           
             
             
           
推荐阅读





JAVA葵花宝典


同学,看你骨骼惊奇,差一部宝典估计就能上天了,扫码关注修炼



以上是关于Java多线程:解决高并发环境下数据插入重复问题的主要内容,如果未能解决你的问题,请参考以下文章

怎么实现springMVC 多线程并发

Java并发编程:synchronized

Java并发编程:synchronized

Java并发编程:synchronized

多线程之synchronized

Java并发编程:synchronized