精华内容
下载资源
问答
  • $lock_status = $redis->get('lock_state');if ($lock_status == 0 || empty($lock_status)) {$redis->set('lock_state', 3600, 1); #操作上锁#操作代码$redis->set('lock_state', 3600, 0);...

    $lock_status = $redis->get('lock_state');

    if ($lock_status == 0 || empty($lock_status)) {

    $redis->set('lock_state', 3600, 1); #操作上锁

    #操作代码

    $redis->set('lock_state', 3600, 0); #操作解锁

    } else {

    #上锁后的操作

    }

    http://cache.baiducontent.com/c?m=9f65cb4a8c8507ed4fece763105d8a214307dd22668c9b4268d4e419cf735b360631b6e4707e56508082222116af3e0db7af2b312a0022b499ca8a48dfb999292d8c21367308c31c528516ed910432c151872ae9b86ae7ad803784ddd2c4de2444ba53120d87e7fa501763ba78f1642692d58e3c634860cdfa4613&p=9061d616d9c15dff57e89139474c&newp=8b2a9702c08311a05de8de241b4392695c02dc3051ddd001298ffe0cc4241a1a1a3aecbb24241502d9c478610abb0f31aba7747d605f76f08d88&user=baidu&fm=sc&query=php+%B2%A2%B7%A2%B2%E5%C8%EB%CA%FD%BE%DD&qid=fcd1032700017706&p1=23

    展开全文
  • 这事第一个版本的代码逻辑,通过数据库中是否有纪录来防止重复创建 @Transactional public Boolean createGroupTicket(String orderId){ //......code //判断逻辑 if (!org.apache.commons.lang3.StringUtils....

    这事第一个版本的代码逻辑,通过数据库中是否有纪录来防止重复创建

    @Transactional
    public Boolean createGroupTicket(String orderId){
        //......code
        //判断逻辑
        if (!org.apache.commons.lang3.StringUtils.isEmpty(orderDO.getGroupTicketNo())) {
            //已创建过组合工单
            log.info("订单{}已创建过组合工单,工单号为{}", orderDO.getOrderId(), orderDO.getGroupTicketNo());
            throw new RuntimeException("创建组合工单失败,已创建过组合工单");
        }
        //.......code
        String ticketNo = compositeTicketService.createInstallTransferTicket(installmentTransferCreateDTO);
            log.info("订单{}创建组合工单成功,组合工单工单号为:{}", orderDO.getOrderId(), ticketNo);
            //将组合工单号落地
            OrderDO orderParam = new OrderDO();
            orderParam.setOrderId(orderDO.getOrderId());
            orderParam.setGroupTicketNo(ticketNo);
            orderOperateDAO.updateOrderByOnDemand(orderParam);
    }

    但是测试MM发现创建了2条纪录,查询日志发现:第一次请求在写入数据库之前,第二次请求已经执行过了判断逻辑,所以并没有查询到写入纪录,从而导致重复创建。

    解决方案,加入分布式锁,第二个版本代码如下

    @Transactional
    public Boolean createGroupTicket(String orderId){
        //防止出现并发情况重复创建
        String lockKey = "GroupTicketCreate_" + orderId;
        boolean locked = distributedLockService.lock(lockKey, 20);
        if(!locked){
            log.info("创建组合工单,加锁失败,orderId:{}",orderCustomerDO.getOrderId());
            throw new RuntimeException("加锁失败");
        }
        log.info("加锁成功,lockKey:{}",lockKey);
        //......code
        //判断逻辑
        //.....将组合工单号落地
        distributedLockService.releaseLock(lockKey);
        log.info("释放锁,lockKey:{}",lockKey);
    }

    经过此次修改,以为解决了问题,而且正常运行了2天,没有报出重复创建的bug。然而在一次下班之前,又出现了这种情况。

    image

    查询日志发现,在第一个请求落地数据库,释放锁后;第二个请求获取锁进来之后,居然通过了判断逻辑。简单的说,就是第一次请求执行完代码之后,并没有写入数据库....WTF。

    查询sql日志如下:

    20180201:18:19:26.373 [DubboServerHandler-172.17.40.222:20881-thread-132] [org.apache.ibatis.logging.jdbc.BaseJdbcLogger:139] DEBUG  ==>  Preparing: update cl_order set `order_id` = ?, `date_update` = ?, `group_ticket_no` = ? where order_id = ? 
    20180201:18:19:26.374 [DubboServerHandler-172.17.40.222:20881-thread-132] [org.apache.ibatis.logging.jdbc.BaseJdbcLogger:139] DEBUG  ==> Parameters: 20279(Long), 2018-02-01 18:19:26.373(Timestamp), ZH2018020103201(String), 20279(Long)
    20180201:18:19:26.390 [DubboServerHandler-172.17.40.222:20881-thread-132] [org.apache.ibatis.logging.jdbc.BaseJdbcLogger:139] DEBUG  <==    Updates: 1
    20180201:18:19:26.429 [DubboServerHandler-172.17.40.222:20881-thread-135] [org.apache.ibatis.logging.jdbc.BaseJdbcLogger:139] DEBUG  ==>  Preparing: select a.group_ticket_no as group_ticket_no, a.status_code as statusCode ... from cl_order where order_id = ? 

    通过sql日志看出,线程132确实更新了数据库,紧接着线程135查询数据库并没有查出数据。结论是:线程132释放锁之后,数据库事务并没有立即提交成功;而线程135获得锁之后的查询逻辑执行在线程132的事务提交之前,打了个时间差,导致创建了2条数据。

    终极解决方案,加锁的逻辑加在事务方法的外层。代码如下:

    //防止出现并发情况重复创建
    String lockKey = "GroupTicketCreate_" + orderId;
    boolean locked = distributedLockService.lock(lockKey, 20);
    if(!locked){
        log.info("创建组合工单,加锁失败,orderId:{}",orderCustomerDO.getOrderId());
        throw new RuntimeException("加锁失败");
    }
    log.info("加锁成功,lockKey:{}",lockKey);
    //事务方法
    bool flag = orderService.createGroupTicket(orderId);
    //.....code
    distributedLockService.releaseLock(lockKey);
    log.info("释放锁,lockKey:{}",lockKey);

    总结:确保事务执行完之后释放锁!

    转载于:https://www.cnblogs.com/xiaojinhua/p/8405989.html

    展开全文
  • 我核实了数据之后,分析认为是并发请求下导致数据不一致性问题,并做了重现。其实这并不是一个需要频繁调用的功能,但是客户连续点击了两次,导致出现了并发问题。除了前端优化,这里重点探讨后台方面代码层面的...

    前段时间工作中,有客户反应了系统中某类待办重复出现两次的情况。我核实了数据之后,分析认为是并发请求下导致的数据不一致性问题,并做了重现。其实这并不是一个需要频繁调用的功能,但是客户连续点击了两次,导致出现了并发问题。除了前端优化,这里重点探讨后台方面代码层面的处理,最终解决问题。

    一、情景分析

    Asp.net程序部署Web服务,是多主线程并发执行的,当多个用户请求进入同一个后台函数时,后进入的请求有可能会获取到非最新状态的数据。

    结合我遇到的实际情况举个例子,假设后台函数Func1,先读取表TableA,TableB的数据,进行处理后,存入TableB中,而数据库事务执行会在函数结束前才提交。请求Req1执行Func1提交事务之前,Req2又进入Func1并读取了TableA,TableB的数据,这时Req1执行完成,这就相当于Req2拿到的已经是旧的数据,在旧的数据的基础上再做数据处理操作,结果自然不正确了。

    说到这里,你可能还不能想象具体会出现什么问题,而确实这种并发情况在非幂等功能下才会导致数据错误,下面就举实例说明。

    二、实例重现

    现在有数据表Info,Info2,Info2的数据就是基于Info表数据产生的,两个表都有字段-证件号码IdentNo。

    函数SyncWork()的功能为:

    1,读取Info表和Info2表中共同的IdentNo行数据,将Info表中的其它字段同步到Info2表;

    2,读取Info表中有,而Info2表中没有的IdentNo行数据,将这些数据插入Info2表。

    表实体代码实现如下:

     1     /// <summary>
     2     /// 信息表
     3     /// </summary>
     4     public class Info
     5     {
     6         public int Id { get; set; }
     7         /// <summary>
     8         /// 证件号码
     9         /// </summary>
    10         public string IdentNo { get; set; }
    11         /// <summary>
    12         /// 姓名
    13         /// </summary>
    14         public string Name { get; set; }
    15         /// <summary>
    16         /// 爱好
    17         /// </summary>
    18         public string Hobby { get; set; }
    19         /// <summary>
    20         /// 备注信息
    21         /// </summary>
    22         public string Bz { get; set; }
    23     }
    24 
    25     /// <summary>
    26     /// 信息表2
    27     /// </summary>
    28     public class Info2
    29     {
    30         public int Id { get; set; }
    31         /// <summary>
    32         /// 证件号码
    33         /// </summary>
    34         public string IdentNo { get; set; }
    35         /// <summary>
    36         /// 姓名
    37         /// </summary>
    38         public string Name { get; set; }
    39         /// <summary>
    40         /// 爱好
    41         /// </summary>
    42         public string Hobby { get; set; }
    43         /// <summary>
    44         /// 创建时间
    45         /// </summary>
    46         public DateTime CreateTime { get; set; }
    47         /// <summary>
    48         /// 最后修改时间
    49         /// </summary>
    50         public DateTime? UpdateTime { get; set; }
    51         /// <summary>
    52         /// 评分
    53         /// </summary>
    54         public int? Score { get; set; }
    55     }
    View Code

     SyncWork()代码实现如下,代码中加入了辅助的输出信息:

     1         public static string SyncWork()
     2         {
     3             StringBuilder sb = new StringBuilder();
     4             //
     5             int threadId = Thread.CurrentThread.ManagedThreadId;
     6             sb.AppendLine($"{DateTime.Now.ToString("HH:mm:ss:fff")}.线程Id:{threadId}");
     7             //
     8             MyDbContext db = new MyDbContext();
     9             sb.AppendLine($"{DateTime.Now.ToString("HH:mm:ss:fff")}.db初始化");
    10             //新增数据
    11             var dataToAdd = db.Info.Where(x => !db.Info2.Select(y => y.IdentNo).Contains(x.IdentNo))
    12                 .ToList();
    13             sb.AppendLine($"{DateTime.Now.ToString("HH:mm:ss:fff")}.获取待新增数据{dataToAdd.Count}条");
    14             dataToAdd.ForEach(x =>
    15             {
    16                 var info2 = new Info2
    17                 {
    18                     IdentNo = x.IdentNo,
    19                     Name = x.Name,
    20                     Hobby = x.Hobby,
    21                     CreateTime = DateTime.Now
    22                 };
    23                 db.Info2.Add(info2);
    24             });
    25             //更新原有数据
    26             var dataToEdit = db.Info.AsQueryable().Join(db.Info2.AsQueryable(), m => m.IdentNo, n => n.IdentNo, 
    27                 (m, n) =>  new
    28                 {
    29                     info = m,
    30                     info2 = n
    31                 })
    32                 .ToList();
    33             sb.AppendLine($"{DateTime.Now.ToString("HH:mm:ss:fff")}获取待更新数据{dataToEdit.Count}条");
    34             dataToEdit.ForEach(x =>
    35             {
    36                 x.info2.Name = x.info.Name;
    37                 x.info2.Hobby = x.info.Hobby;
    38                 x.info2.UpdateTime = DateTime.Now;
    39             });
    40             sb.AppendLine($"{DateTime.Now.ToString("HH:mm:ss:fff")}开始休眠5s");
    41             Thread.Sleep(5000);
    42             sb.AppendLine($"{DateTime.Now.ToString("HH:mm:ss:fff")}dbSaveBegin");
    43             db.SaveChanges();
    44             sb.AppendLine($"{DateTime.Now.ToString("HH:mm:ss:fff")}dbSaveEnd");
    45             return sb.ToString();
    46         }
    View Code

     里边的这几行代码就是问题重现的重点了:

    1             sb.AppendLine($"{DateTime.Now.ToString("HH:mm:ss:fff")}开始休眠5s");
    2             Thread.Sleep(5000);
    3             sb.AppendLine($"{DateTime.Now.ToString("HH:mm:ss:fff")}dbSaveBegin");
    4             db.SaveChanges();
    5             sb.AppendLine($"{DateTime.Now.ToString("HH:mm:ss:fff")}dbSaveEnd");

    在db提交之前,我们让此线程休眠了5s,以模仿现实中一些耗时的操作。这5s期间可能有后续的N个新请求线程进入此函数,那么这些线程获取的数据都会是同样的旧数据了。第一个执行完函数的线程,提交了更改,而后续这些线程再提交的更改,便是基于旧的数据做的更改了。

    下面开始重新问题,我们执行一次请求,两表数据情况分别如下:

     然后,Info表中新增了一条数据:

     这时,我们再执行请求,预期结果应该将行003老虎的数据添加到Info2中,但是我们现在模拟并发,连续调用2次请求看看结果:

     

    可以看到,居然将003老虎的数据插入了两次。这就是并发带来的副作用了。

    附上两次请求的辅助输出信息:

     1 //Request1
     2 22:50:05:953.线程Id:44
     3 22:50:05:953.db初始化
     4 22:50:05:982.获取待新增数据1条
     5 22:50:06:000.获取待更新数据2条
     6 22:50:06:001.开始休眠5s
     7 22:50:11:001.dbSaveBegin
     8 22:50:11:084.dbSaveEnd
     9 //Request2
    10 22:50:07:240.线程Id:48
    11 22:50:07:240.db初始化
    12 22:50:07:270.获取待新增数据1条
    13 22:50:07:287.获取待更新数据2条
    14 22:50:07:287.开始休眠5s
    15 22:50:12:287.dbSaveBegin
    16 22:50:12:339.dbSaveEnd

    三、问题解决

    既然问题是并发请求导致的,而这个功能不是需要频繁调用的功能,最简便的解决方法就是,我们可以设置此功能同一时间只能由一个线程来访问,即通过lock()的方式。

    最终实现代码如下:

     1     public class InfoSync
     2     {
     3         private static object syncObject = new object();
     4         public static string Sync()
     5         {
     6             lock (syncObject)
     7             {
     8                 return SyncWork();
     9             }
    10         }
    11         private static string SyncWork()
    12         {
    13             //...  
    14         }
    15     }
    View Code

    同时贴出示例控制器的简单实现:

     1     public class DataController : Controller
     2     {
     3         // GET: Data
     4         public ActionResult Index()
     5         {
     6             try
     7             {
     8                 var str = InfoSync.Sync();
     9                 return Content(str);
    10             }
    11             catch (Exception ex)
    12             {
    13                 return Content($"程序发生错误:{ex.Message}\n内部错误:{ex.InnerException.Message}");
    14             }
    15         }
    16     }
    View Code

    四、总结

    类似文中数据同步并发情况的实际应用情况还有很多,比如系统有时会需要产生编号,我们会访问数据库中这类编号的最新值,然后计算出下一个编号值,如果不处理并发情况,业务量大时可能就会出现重复编号了。

    本文中,针对这类请求并发问题,通过代码锁的方式,将特定功能的并发请求执行转化为队列请求执行,从而避免了问题的发生。

    当然,处理并发还有其它途径,如通过数据库锁的方式,再如分布式部署情况下,我们用代码锁的方式也会失效了,实际工作中还需要根据具体情况采用最小代价成本的处理方式。

    --End

     

    转载于:https://www.cnblogs.com/jiujiduilie/p/10328232.html

    展开全文
  • 背景:近日发现后台服务器有个问题,用户存在重复注册的问题,由于使用第三方账户的授权和登录,有的用户会在网络不好的情况下,多次点击登录授权,因此会造成多次访问提交...目的:1、清除数据库中重复数据;2、...

    背景:近日发现后台服务器有个问题,用户存在重复注册的问题,由于使用第三方账户的授权和登录,有的用户会在网络不好的情况下,多次点击登录授权,因此会造成多次访问提交。采用先向服务器发送ajax网络请求,检查是否有重复注册账户,通过返回的值判定,是登录还是注册;由于手机会因为网络或者内存的问题,导致页面卡顿、缓慢,用户多次点击发送请求,因此会发生多次创建账户的问题。

    目的:1、清除数据库中重复的数据;2、采用标识符防止多次访问

    补充,对于同一个账户情况下,收藏者多次收藏同一个数据问题,需要使用多个条件的分组条件

    select * from `r_tab_memberfavorite` group by openid,content_id having count(*)>1;

    此时,显示出来的默认是id编号较小的哪一个数据,删除的时候可以直接使用。

    解决方法:

    1、首先要查询出那些用户重复注册

    mysql的语法两种查询方式

    [1]、利用表名称先查询全部的表结果作为一个别名,然后利用原表中查询的唯一建和别名的唯一建进行匹配,返回结果,排序显示(也可以不排序),该方法相对比较慢

    select * from `r_tab_member` a where ((select count(*) where openid=a.openid)>1) order by desc;

    [2]、使用group by 语句+having +聚合函数对结果进行过滤得到重复的关键字的数据分组,然后查询出所有的结果

    select * from `r_tab_member` where openid in (select openid from `r_tab_member` group by openid having count(openid)>1;

    [3]、删除重复的数据,只保留一条

    delete from `r_tab_member` where openid in (select openid from `r_tab_member` group by openid having count(openid>1)) and id not in (select min(id) from `r_tab_member` group by openid having count (openid)>1);

    2、可以在客户端进行约束,也可以在服务端进行约束

    客户端相对容易,只需要在用户点击授权后对该方法进行标识位的锁定,等到服务器返回结果,方法执行完毕,解锁,方能够进行二次操作。

    服务端没有测试,思路:将检查重复的方法和创建用户的方法合二为一,再进行相应的数据库操作线程锁定,保证只有一个线程能成功的创建数据,其他的只能失败。

    展开全文
  • 系统高并发出现 重复数据 怎么解决? 系统随着业务的增长,接口请求量也增加。最常见的问题数据重复,涉及到资金的影响就很大。还有问题就是查询少量的数据,要很长时间。其他还有其他一些问题。这次先介绍处理数据...
  • 场景:用户注册的时候,代码明明做了去重还有加上了事务(估计是事务没有成功加上),数据库还是有时候能看到重复数据同一出现,就是同一时间插入了重复数据,后面用webbench测试工具做压力测试,果不其然,真是...
  • 这样的话就会有个问题,多个请求同时到达时,都会判断redis key中是否有值,没值的时候再调用大数据服务接口,然后遍历依次存储,这样就会导致redis中存储的数据重复。 想达到这么个效果:假设同时进来了100个请求...
  • $lock_status = $redis->get('lock_state'); if ($lock_status == 0 || empty($lock_status)) { $redis->set('lock_state', 3600, 1); #操作上锁 #操作代码 ...set('lock_state', 3600...
  • 问题描述:在给移动端做一个收藏功能的时候,正常情况下,登录人点击收藏按钮...但是,当移动端猛戳收藏按钮的时候,就出现数据重复的情况。 解决办法:使用Hibernate 注释 唯一键约束 uniqueConstraints
  • 并发重复插入数据的场景之一

    千次阅读 2017-08-11 10:31:32
    会再次推送该条状态B的工单,B进来首先查找系统中是否有该条工单,而这时状态A的工单还没有完成插入的逻辑,状态B会认为之前这笔工单没有推送过,会直接执行插入的逻辑,这就导致插入了重复数据。 这里最初的实现...
  • 为了防止异常导致的事务没有执行成功而下一次不能执行事务的可能。需要针对事务块代码进行捕获异常进行处理,总之,finally 需要清除掉当前正在执行的标记。 2.可以通过Mysql建立数据字典表,设置好主键,通过本地....
  • 同一秒可能有5次的相同请求,这样导致创建两条相同的记录 用memcache的原子性,根据请求的参数去生成key,再来就判断有没有,有就返回处理中 效果如下: 可以看到第一次请求,还没返回,第二次请求就过来了,第二次...
  • TP5高并发不存现重复数据

    千次阅读 2018-12-24 09:57:25
    当我们想要进行一个 查询当前数据表里某个值最大值,然后+1存入表里,如果有高并发,就会出现两个操作,同时进行,出现两次查询都查出...tp5的lock,要搭配事务一起使用,就可以实现,高并发,不会出现重复数据 ...
  • 前端请求php写入数据的接口太频繁,导致mysql中出现大量重复数据,如何处理回复内容:前端请求php写入数据的接口太频繁,导致mysql中出现大量重复数据,如何处理这个问题从三个方面来回答题主:前端前端的话要对请求...
  • 1.背景描述 应用框架:Spring + SpringMVC + Hibernate  数据库:Oracle11g 一家文学网站向我系统推多线程低并发推送数据,我...由于网络原因,客户可能连续推送了两条重复数据,两条数据时间间隔非常小,因此导致
  • 最近工作遇到一个问题,数据重复插入到DB,前端和后端都做了表单防止重复提交功能,依然还是在并发场景或者网络延迟的场景下,数据还是会重复插入。 项目中在新增数据前做了一次查询,因为数据库内容比较多,导致...
  • 本文前篇是对场景的分析,后篇会有解决方案,读完本篇你将可以仅仅使用两个注解即可解决并发重复提交问题。可以直接看方案四,直接读推荐解决方案。场景分析重复提交问题是一个老生常谈的问题,项目中经常会遇到这种...
  • 并发防止重复提交

    2016-12-09 16:33:47
    项目中遇到了服务器接口重复被调用的情况,导致数据多次提交的问题。对于请求的重复提交,前端有一些治标的办法,后台也有一些常规的token验证。但是在并发的情况下,一些传统的办法失去了作用。 1、比如说前台js...
  • 在做ERP订单接入仓库库存的时候出现了一个问题。下单会读取该商品是否有库存。... 那么当u1在读取出数据的时候发现库存有一个 但是还没有写入库存的操作 u2也下单成功并提交 那么就会导致数据错乱 ...
  • 公司的app新版本上线,其中有一个摇一摇的功能,由于安卓端没有做好处理,导致在同一时间数据post提交了2次。在后台判断发现并没有插入的数据,然后啪啪啪一下子插入了2条。也就是在插入前没有判断是否插过了。当时...
  • 问题描述前段时间遇到个问题,自己内部系统调用出现重复请求导致数据混乱。发生条件:接受到一个请求,该请求没有执行完成又接受到相同请求,导致数据错误(如果是前一个请求执行完成,马上又接受相同请求不会有问题)...
  • 目前实现的思路: <p>1.... 每次返回的数据大概是1500-2000条左右 ...但是现在接口请求会有大量的重复数据导致数据库每天数据量很多,如果设置唯一约束,批量入库的时候数据库会发生死锁,有没有好的解决思路。</p>
  • 在整理应用中旧系统数据移植的问题,发现表中出现了几条异常记录,记录除主键不同外,记录的时间和其它内容完全一致。 业务本身的逻辑是,如果碰到数据库中有相关的记录需要把这些记录置为无效,然后插入一条记录。...
  • 之所以出现更新丢失,脏读,不可重复读,幻读,是因为当两个事务同时进行数据库操作的时候,两者之间互相不知道对方的存在,对自身所处的环境过分乐观,从而没有对操作的数据做一定的保护处理,最终导致一些问题的...
  • 在整个供应链系统中,会有很多种单据(采购单、入库单、到货单、运单等等),在涉及写单据数据的接口时(增删改操作),即使前端做了相关限制,还是有可能因为网络或异常操作产生并发重复调用的情况,导致对相同单据...
  • 事物的四个特性是: 1 ,atmotic(原子性) 2,consistent(一致性) 3,isolated(隔离性) 4,durable(持久性) ...假设表t中有1,5,9,数据 ...2,重复读 当事物A第一次查询数据之后,事物B修改了数...
  • 就算在读时用for update,但在返回结果到Java时,在做更新数据之前,总会有那么一点空隙,可能会导致高并下操作数据库时访问到相同的数据。 而通过SQL中的with语法,首先更新数据,再将更新后受影响数据行放到...
  • 数据并发的问题

    2017-09-16 21:31:46
    数据库中的相同数据可能同时被多个事务访问,如果没有采取必要的隔离措施,就会导致各种并发问题,破坏数据的完整性。这些问题可以归结为5类,包括3类数据读问题(脏读、不可重复读和幻象读)以及2类数据更新问题...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 871
精华内容 348
关键字:

并发导致重复数据