精华内容
下载资源
问答
  • 文章目录基础概念原因分析解决方案多线程插入解决:多线程更新解决 基础概念 幂等性 : 在编程中.一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。 简单来说:幂等就是一个操作,不论...

    基础概念

    幂等性 : 在编程中.一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。
    简单来说:幂等就是一个操作,不论执行多少次,产生的效果和返回的结果都是一样的。
    幂等性操作:
    1、查询操作:查询一次和查询多次,在数据不变的情况下,查询结果是一样的。select是天然的幂等操作;

    2、删除操作:删除操作也是幂等的,删除一次和多次删除都是把数据删除。(注意可能返回结果不一样,删除的数据不存在,返回0,删除的数据多条,返回结果多个) ;
    3.插入操作:插入情况下默认主键唯一,所以多次插入同一个数据不是幂等的
    4.更新操作: 这里分两种情况:

    1、update t set money=100  where id=1
    2、update t set money=money+100  where id=1
    

    第一种则是幂等的,第二种则是不幂等的

    总结:
    幂等与你是不是分布式高并发还有JavaEE都没有关系。关键是你的操作是不是幂等的。
    要做到幂等性,从接口设计上来说不设计任何非幂等的操作即可。譬如说需求是:当用户点击赞同时,将答案的赞同数量+1。改为:当用户点击赞同时,确保答案赞同表中存在一条记录,用户、答案。赞同数量由答案赞同表统计出来。在设计系统时,是首要考虑的问题,尤其是在像支付宝,银行,互联网金融公司等涉及的都是钱的系统,既要高效,数据也要准确,所以不能出现多扣款,多打款等问题,这样会很难处理,用户体验也不好。

    这里可参考我的另一篇文章:
    幂等问题 8种方案解决重复提交

    原因分析

    如下这种操作很常见:

    if(用户不存在)
    {
        xxxxx
        存储用户到数据库
    }
    else
    {
        重复推送,不采取任何措施
    }
    

    这个操作还没有执行完毕,第二条拥有相同数据的线程已经进入并通过了if的检验,导致数据库存储了两条相同的数据。这就是是多线程的并发导致了程序的判断逻辑失效。

    解决方案

    单机模式下,简单采用sync即可,这里讨论分布式情景下避免重复插入问题
    这里主要说下数据库解决多线程,其余方案可参考上文中提到的:

    幂等问题 8种方案解决重复提交

    多线程插入解决:

    1、 insert时带有where条件的写法(类似于索引)
    1.1 、插入单条记录

    普通的 INSERT INTO 插入:
    INSERT INTO card(cardno, cardnum) VALUES('1111', '100');

    对于普通的 INSERT 插入,如果想要保证不插入重复记录,我们只有对某个字段创建唯一约束实现(比如:cardno卡号不能重复);
    如果要保证多个字段不会重复,可以考虑联合唯一索引!
    创建索引后程序处理:

    if (该cardno在数据库表中存在) {  
        update();  
    } else {   
        try {  
             insert();  
             //违反唯一性约束会报异常:InvocationTargetException 
             } catch (InvocationTargetException e) {  
             //如果重复插入已经有数据,则进行更新
             update();  
         }   
    }  
    

    这里还有个问题,就是如果表中记录逻辑删除的时候采用唯一索引会出现BUG

    重点在这里
    那有没有不创建唯一约束,仅通过 INSERT INTO 一条语句实现的方案呢?

    答案:有的, INSERT INTO IF EXISTS 具体语法如下:
    INSERT INTO table(field1, field2, fieldn) SELECT 'field1', 'field2', 'fieldn' FROM DUAL WHERE NOT EXISTS(SELECT field FROM table WHERE field = ?)
    其中的 DUAL 是一个临时表,不需要物理创建,这么用即可。

    针对上面的card示例的改造如下:
    INSERT INTO card(cardno, cardnum) SELECT '111', '100' FROM DUAL WHERE NOT EXISTS(SELECT cardno FROM card WHERE cardno = '111')
    1.2、插入多条记录

    INSERT INTO user  (id, no,add_time,remark)
    select * from (
    SELECT 1 id, 1 no, NOW() add_time,'1,2,3,1,2' remark FROM DUAL
    UNION ALL
    SELECT 1 no, 2 no, NOW() add_time,'1,2,3,1,2' remark FROM DUAL
    UNION ALL
    SELECT 1 no, 3 no, NOW() add_time,'1,2,3,1,2' remark FROM DUAL
    ) a where not exists (select no from user b where a.no = b.no)
    

    上述是实现user表的no字段不重复,插入三条记录。
    另外,附上mybatis批量写入no字段不重复的实现语句。

    INSERT INTO user (id, no,add_time,result)
    select * from (
    <foreach collection="list" item="obj" separator=" UNION ALL " >
    SELECT #{obj.id} id, #{obj.no} no, #{obj.addTime} add_time,#{obj.result} result FROM DUAL
    </foreach>
    ) a where not exists (select no from user b where a.no = b.no)
    

    多线程更新解决

    1、update使用 乐观锁 version版本法
    情景:
     比如两个用户同时购买一件商品,库存只有1件了!在数据库层面实际操作应该是库存进行减2操作,但是由于高并发的情况,第一个用户购买完成进行数据读取当前库存并进行减1操作,由于这个操作没有完全执行完成,这样就会出现商品超卖!

    select goods_num,version from goods where goods_name = "小本子";
    update goods set goods_num = goods_num -1,version =查询的version值自增 where goods_name ="小本子" and version=查询出来的version;
    

    为什么加个version字段就满足了呢,因为数据库本身特性的帮忙,update语句执行的时候,如果更新的时候update语句不走索引就会将表锁住保证了一个时刻只有一个线程能进入更新,等这次更新释放锁以后才会执行下一次的update操作,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据,这样就能保证了程序的安全性。
    这种在大数据量和高并发下效率依赖数据库硬件能力,可针对非核心业务
    2.使用select … for update 悲观锁
    这种和 synchronized 锁住先查再insert or update一样,但要避免死锁,效率也较差,针对单体 请求并发不大 可以推荐使用

    获取数据的时候加锁获取:select * from table_xxx where id='xxx' for update;
    注意:id字段一定是主键或者唯一索引,不然是锁表,会死人的;悲观锁使用时一般伴随事务一起使用,数据锁定时间可能会很长,根据实际情况选用;
    这种在大数据量和高并发下效率依赖数据库硬件能力,可针对非核心业务

    参考文章:
    https://www.cnblogs.com/ganhaiqiang-20130831/articles/4478472.html
    https://www.cnblogs.com/lihuanliu/p/6764048.html

    幂等性解决:
    https://www.cnblogs.com/baizhanshi/p/10449306.html
    https://www.cnblogs.com/aspirant/p/11628654.html

    展开全文
  • 所有的方法都先不考虑性能问题,也不考虑业务,只看逻辑上能否达到去重复插入,因为第一,如果唯一性是必须要保证的,那么只能在先实现了,再去考虑优化;第二,如果能通过调整业务来规避,那我后面说的都是废话了...

    今天稍微研究了下这个问题从网上整理资料大致得到了如下方案,先粗略记录下,下班回去再更详细记录下。

    所有的方法都先不考虑性能问题,也不考虑业务,只看逻辑上能否达到去重,因为第一,如果唯一性是必须要保证的,那么只能先实现了唯一,再去考虑优化;第二,如果能通过调整业务来规避,那我后面说的都是废话了还有什么看头。

    1.添加唯一索引。

      但这种方法在表中有逻辑删除时会有问题。   

      后记:逻辑删除如果用ID来做删除标识,应该也行。设删除标识DEL_FLG,未删除的用0做标记,删除的用ID做标记。设需要去重的字段为FIELD_1,那么用FIELD_1和DEL_FLG组成联合索引。逻辑删除时将DEL_FLG设为与ID相同,未删除的DEL_FLG为0,

     这样联合索引不会影响已删除的,只会防止未删除数据的重复。

    2.加锁。但是据查oracle中行锁只能是写时阻塞,且必须要该行数据存在,所以要确保你用来加锁的字段在某个表已经存在(不存在的话也锁不住),比如:如果你要在user_permission表中存储user和permission的关系且user-permission的组合只能存在一条记录。那么此时如果两个A,B进程同时都插入(user1,permission1)就会在表中产生重复数据。因为可以确定user1肯定已经在user表中存在,permission1肯定已经在permission表中存在,所以你可以考虑用这两个表中的一个来加锁,具体插入步骤如下:

    a、select * from user u where u.user = user1 for update

    b、select count(*) from user_permission up where up.user = user1 and up.permission = permission1 然后判断结果是否大于0

    c、如果步骤b中结果等于0 则insert into from user_permission

    按照以上的步骤,如果A进程先执行a步骤,则B进程会在a步骤阻塞,等A进程执行完后面两步以后,B进程才会继续执行b步骤,但此时A进程已经插入数据了,B进程查询count等于1就不再插入。

    但这种方法也有问题:假如此时正巧有人需要在user表中修改user1的基础数据,就会被阻塞,等到别人执行完了才能执行;或者此时有个进程C想插入(user1,permisson2),虽然这条数据并没有与A,B冲突,但是因为进程C在a步骤也发现user1加锁了,所以也会阻塞,直到其他进程释放。

    3.多个连接需要插入的数据全插入后台的buff里,然后在后台用一个线程专门处理和数据库的交互,从buff逐条读取,再往数据库插。因为最终插入数据库只有一个线程,所以只要在插入前检测一下表中是否存在就可以了。

    但是这种在分布式系统中这种方法肯定就跪了。

    4.并发的数据都插入数据库的中间表,然后做个后台存储过程将中间表的数据往业务表插,这个过程中就可以保证相同数据只插入一条。

    5.先插入重复数据,再用后台存储过程删除多余的。

    3,5 思想其实一样,不过一个是交给后台处理,一个是交给数据库处理。

     

    其实还有一种方法,重复插入就重复插入呗。改,查的时候按最早插入时间来取第一条,删除的时候全删,统计的时候distinct ,当然在后台代码中也要做是否有已存在记录的检验,虽然不加锁不能完全避免重复,但也要尽量减少不是。(此条权当玩笑吧 ->_->)。

     

    大家都懂的,菜鸟写博客多多少少都会有问题,希望大家指正。只知道冷嘲热讽的就不用来了,当然,讽完还能提建设性意见的我也可以接受 ->_->

    展开全文
  • 解决高并发环境下Java多线程数据插入重复问题,有一个解决博客网站多线程博客网站数据重复的案例可供参考: 1.背景描述 应用框架:Spring + SpringMVC + Hibernate 数据库:Oracle11g 一家博客网站向我系统推多线程低...

    解决高并发环境下Java多线程数据插入重复问题,有一个解决博客网站多线程博客网站数据重复的案例可供参考:
    1.背景描述
    应用框架:Spring + SpringMVC + Hibernate 
    数据库:Oracle11g
    一家博客网站向我系统推多线程低并发推送数据,我这边观察日志和数据库,发现有一个作者被存储了2次到数据库中。按照程序的编写逻辑,重复的数据应当是会被判断出来不被存储的。
    2.原因分析
    可能是由于网络原因,客户可能连续推送了两条重复的数据,两条数据时间间隔非常小,因此导致了我们的

    if(用户不存在)
    {
        xxxxx
        存储用户到数据库
    }
    else
    {
        重复推送,不采取任何措施
    }


    这个操作还没有执行完毕,第二条拥有相同数据的线程已经进入并通过了if的检验,导致数据库存储了两条相同的数据。后来我自己写了个100并发的多线程测试程序,发现100条相同数据中有40条被插入到了数据库里!因此确定了是多线程的并发导致了程序的判断逻辑失效。
    3.解决思路:
    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条返回该作者已登记。
    下一步:
    针对所有可能出现高并发问的接口进行调整。
    4.提示
    这种加同步锁的方法在负载均衡下的多台应用服务器会失效!因为就算Spring保证了对象是单例的,但是多台服务器肯定是多个对象!因此synchronized将无效。解决方法是在数据库层对该对接公司的唯一记录加select锁,这样就能保证数据的不重复性,但是会降低该公司推送数据的效率(相当于逐条推送),但是公司与公司之间还是并行推送的。还有一个方法就是将业务逻辑写入存储过程,然后对存储过程加锁,这种方法太麻烦了,需求有变动就必须去修改存储过程,但是效率要比前者高得多。

    转载于:https://my.oschina.net/u/3628059/blog/1538378

    展开全文
  • 多线程并发取值重复问题

    千次阅读 2016-09-11 21:08:42
    取用时分秒+毫秒级别未能避免多线程并发取数时报错public synchronized static String getBatchDate(){ String serialNo = DateUtil.getDateFormat(new Date(), "yyyyMMddHHmmss"); return serialNo; }...

    取用时分秒+毫秒级别未能避免多线程并发取数时报错
    public synchronized static String getBatchDate(){
      String serialNo = DateUtil.getDateFormat(new Date(), "yyyyMMddHHmmss");
      return serialNo;
     }

     

    通过时间+数据库SEQ的方式,解决并发获取相同时间戳的问题;

    展开全文
  • 说明:如何生成线程式客户端 客户端库总是线程安全的。...或许你可能希望用自己的告警中断对服务器的长时间读取,以此来...为了避免连接中断时放弃程序,MySQL将在首次调用mysql_server_init()、mysql_init()或mysql_c
  • Java多线程:解决高并发环境下数据插入重复问题 [3]. 在多线程处理表数据的时候 ,怎么防止数据被重复处理? [4]. 解决多线程并发问题 [5]. Java中的多线程你只要看这一篇就够了 [6]. java中Executor、...
  • 通过前三篇博文的学习,已经编码实现多线程下载功能的核心代码,通过多个线程之间的管理和调度来处理下载任务,...多线程下载添加数据库支持 greenDao开源库自动生成数据库相关代码 完善网络请求接口中的进度更新功能
  • 多线程 在单独线程中运行任务的简单过程: 1. 定义一个实现了Runnable接口的类,任务代码写到run方法中 2. 创建一个该类的实例 3. 将这个实例传递给Thread类的构造体,实例化一个Thread对象 4. 通过Thread...
  • 数据库插入失败引出的多线程问题

    千次阅读 2016-06-29 15:38:35
    昨天遇到一个奇葩问题, 一组不重复的数据在插入数据库的时候 数据 a 出现unique failed , 但是插入成功 数据 b 没有报错, 但是插入失败 并且发现for循环内都遍历不到数据b , 但是for循环外是可以打印到它...
  • 我说我懂多线程,面试官立马给我发了offer

    万次阅读 多人点赞 2020-04-07 09:24:52
    这周来写写Java另一个重要的知识点:「多线程多线程大家在初学的时候,对这个知识点应该有不少的疑惑的。我认为主要原因有两个: 多线程在初学的时候不太好学,并且一般写项目的时候也很少用得上(至少在初学阶段...
  • 多线程锁定同一资源会造成死锁 线程池中的任务使用当前线程池也可能出现死锁 参考连接: https://blog.csdn.net/qq_35064774/article/details/51793656 情况一: 死锁是两个或多个线程互相等待对方所有用的资源...
  • 数据库的脏读、不可重复读、幻读都和事务的隔离性有关。所以先了解一下事务的4大特性。 事务的4大特性(ACID):原子性(Atomicity):事务是数据库的逻辑工作单位,它对数据库的修改要么全部执行,要么全部不执行,...
  • Java多线程超详解

    万次阅读 多人点赞 2019-06-11 01:00:30
    随着计算机的配置越来越高,我们需要将进程进一步优化,细分为线程,充分提高图形化界面的多线程的开发。这就要求对线程的掌握很彻底。 那么话不多说,今天本帅将记录自己线程的学习。 线程的相关API //获取当前...
  • Java多线程:解决高并发环境下数据插入重复问题

    万次阅读 热门讨论 2016-11-23 17:56:53
    一家文学网站向我系统推多线程低并发推送数据,我这边观察日志和数据库,发现有一个作者被存储了2次到数据库中。按照程序的编写逻辑,重复的数据是会被判断出来不被存储的。 2.原因分析 由于网络原因,客户可能...
  • 一家文学网站向我系统推多线程低并发推送数据,我这边观察日志和数据库,发现有一个作者被存储了2次到数据库中。按照程序的编写逻辑,重复的数据是会被判断出来不被存储的。2.原因分析 由于网络原因,客户可能连续推...
  • 多线程 取数据必须不重复的方案

    千次阅读 2018-10-16 15:06:15
    最近一直在处理数据上传和采集的问题, 因为写在asp.net 里面的web服务默认就是多线程的, 一个请求就是一个线程… 所以多线程之间为了不读取重复的数据, 就成了问题. 数据必须严格不重复, 同样的数据绝对不能处理2次...
  • Java多线程编程-(1)-线程安全和锁Synchronized概念 Java多线程编程-(2)-可重入锁以及Synchronized的其他基本特性 Java多线程编程-(3)-线程本地ThreadLocal的介绍与使用 Java多线程编程-(4)-线程间通信...
  • 10线程同时操作,频繁出现插入同样数据的问题。虽然在插入数据的时候使用了:insert inti tablename(fields....) select @t1,@t2,@t3 from tablename where not exists (select id from tablename where t1=@t1,t2=@...
  • t 甚或几 p 的数据的数据库系统,到手机上的一个有良好用户响应能力的 app,为了充分利用每个 CPU 内核,都会想到是否可以使用多线程技术。这里所说的“充分利用”包含了两个层面的意思,一
  • 比如说我在程序A中进行修改数据库。 namespace A { class program { static void Main(string[] args) { DbConnection conn = new DbConnetion(...); conn.Open(); ...
  • 一个进程可以有线程,但是只有一个主线程;线程切换分为两种:一种是I/O切换,一种是时间切换(I/O切换:一旦运行I/O任务时便进行线程切换,CPU开始执行其他线程;时间切换:一旦到了一定时间,线程也进行切换,CPU...
  • QT多线程编程详解

    万次阅读 多人点赞 2019-04-24 22:08:20
    一、线程基础 1、GUI线程与工作线程 每个程序启动后拥有的第一个线程称为主线程,即GUI线程。QT中所有的组件类和几个相关的类只能工作在GUI线程,不能工作在次...二、QT多线程简介 QT通过三种形式提供了对线程...
  • 同步多线程

    千次阅读 2013-11-02 08:50:57
    同步多线程(SMT)是一种在一个CPU 的时钟周期内能够执行来自多个线程的指令的硬件多线程技术。本质上,同步多线程是一种将线程级并行处理(多CPU)转化为指令级并行处理(同一CPU)的方法。 同步多线程是单个物理...
  • PHP 多服务器多线程同步推送思想

    千次阅读 2018-05-11 14:33:25
    为什么要使用多线程,效率翻倍提升,节省时间。...第三步:为了提高效率我们可能会用redis之类的缓存数据库而提高读取数据的速度,为了避免重复推送,推送的用户最好使用队列存储。实现:我们可以吧推送脚本分...
  • java 多线程注意事项

    千次阅读 2017-02-21 12:42:42
    少了浪费了系统资源,了造成系统拥挤效率不高。用线程池控制线程数量,其他线程排队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务...
  • Java多线程详解

    千次阅读 2016-04-12 20:55:01
    Java多线程详解         多线程简介 概述 多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体...
  • 这篇文章就是要让你入门java多线程多线程入门】

    千次阅读 多人点赞 2019-11-20 10:50:00
    就在前几天,有位朋友私信宜春,说期待出一篇多线程的文章,我当时内心是小鹿乱撞啊....于是这几天茶不思饭不想,好几天深夜皆是辗转反侧,两目深凝,以至于这几天走起路来格外飘飘然~我承认太夸张了,感觉又要被打~...
  • 《C#多线程编程实战》读书笔记

    千次阅读 多人点赞 2018-01-13 00:26:49
    本文是一篇读书笔记,由《C#多线程编程实战》一书中的内容整理而来,主要梳理了.NET中多线程编程相关的知识脉络,从Thread、ThreadPool、Task、async/await、并发集合、Parallel、PLINQ到Rx及异步I/O等内容,均有所...
  • JAVA多线程并发

    千次阅读 多人点赞 2019-09-18 12:14:29
    JAVA多线程并发1 JAVA并发知识库2 JAVA 线程实现/创建方式2.1 继承 Thread 类2.2 实现 Runnable 接口2.3 Callable 、Future 、ExecutorService 有返回值线程2.4 基于线程池的方式2.4.1 4种线程池2.4.1.1 ...
  • C#多线程

    千次阅读 2007-08-19 11:50:00
    它有一个命名空间:System.Threading,提供了多线程的支持。要开启一个新线程,需要以下的初始化:ThreadStart startDownload = new ThreadStart( DownLoad ); //线程起始设置:即每个线程都执行DownLoad(),注意:...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 111,763
精华内容 44,705
关键字:

多线程数据库避免重复