asp.net并发请求导致的数据重复插入问题

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

一、情景分析

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     }

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         }

--End

原文地址:https://www.cnblogs.com/jiujiduilie/p/10328232.html

时间: 2024-10-05 04:09:22

asp.net并发请求导致的数据重复插入问题的相关文章

[ECSHOP二次开发]解决分类商品Ajax连续请求导致的数据重复

0x00: 首先声明一个全局变量. var control = true; 然后,在滑动处罚ajax请求的代码处,做一个判断. if (control) { $('.get_more').click(); }; 这个地方是获取数据的函数以及ajax请求的函数 get_data: function() { var ile; control=false;    //首先进来这个函数之后吧全局变量设置成false以防重复请求 lock = true; target.children(".more_lo

ASP模拟POST请求异步提交数据的方法

这篇文章主要介绍了ASP模拟POST请求异步提交数据的方法,本文使用MSXML2.SERVERXMLHTTP.3.0实现POST请求,需要的朋友可以参考下 有时需要获取远程网站的某些信息,而服务器又限制了GET方式,只能通过POST数据提交,这个时候我们可以通过asp来实现模拟提交post数据,网上有挺多这样的例子的.下面的是我自己写的比较简洁易懂的函数. 首先,需要一个编码设置的函数,因为asp一般为gbk的,而标准的网站现在大都使用utf-8的.所以需要转换. 代码如下: function

并发请求导致数据表死锁问题

同事有一次开发时遇到的, 情况大概是这样的. 多线程异步请求mysql数据库,发起更新操作, 因为请求量大,不同请求时隔毫秒级, 造成数据表索引资源抢占问题. 最终导致索引资源无法释放,数据表无法访问. 有个不知名的猿友也遇到了该问题,解决方案也已给出,这里就不多描述,请点击下方链接查看详情. https://blog.csdn.net/guanfengliang1988/article/details/80356648 原文地址:https://www.cnblogs.com/winniejo

order by 导致分页数据重复问题

此情况出现的原因是因为我们所order by的时间相同导致的  ,  当 order by的时间相同的时候 系统对数据的排序可能变得随机化,即一会儿这条数据在前面,一会儿这条数据在后面了 ,所以 当翻页的时候我们很容易便看到了重复的数据,所以在分页的时候使用oerder by的时候最后在目标排序字段的基础上加上一个 有办法唯一的字段对 数据进行排序

关于.Net Core 前后端分离跨域请求时 ajax并发请求导致部分无法通过验证解决办法。

项目中有这样一个页面.页面加载的时候会同时并发6个ajax请求去后端请求下拉框. 这样会导致每次都有1~2个"浏览器预请求"不通过. 浏览器为什么会自动发送"预请求"?请看以面连接 https://blog.csdn.net/charleslei/article/details/51906635 那么解决办法无非就是尽量避免发送"预请求". 后来经过反复测试发现"预请求"可以通过设置Access-Control-Max-Age

InnerJoin分页导致的数据重复问题排查

2016年8月9号美好的七夕的早上,我精神抖擞地来到公司.一会之后,客服宅宅MM微信我,说一个VIP大店铺订单导出报表中一个订单有重复行.于是,我赶紧开始查探问题所在.经过一天的反复仔细追查(当然还包括各种事项的打断),终于发现这个问题的原因所在... 有个订单主表 order ,以及一个订单商品表 order_item ; order 与 order_item 是一对多的关系:其中一个订单 order_no 会对应多个商品 item_id,而一个商品 item_id 仅对应一个订单 order

讨论一个并发线程导致的数据保存失败的问题

环境: 前端采用异步提交的方式,将选择的多个附件分批发送到服务端 后端采用标准的SpringMVC架构来处理请求,采用声明式事务,控制在service层 现象: 后台保存附件信息到数据库的时候,总是报主键唯一性约束错误 分析: 前端采用的是异步提交,第一个附件正在保存的时候,第二个附件也进入了保存方法,而保存方法的逻辑是先生成主键,然后调用save方法保存数据,那么问题就来了,第一个附件进入保存方法,生成主键,在还没有执行save方法的时候,第二个附件进入了保存方法,也生成了主键,此时生成的主键

Nginx反向代理因proxy_next_upstream导致数据重复插入问题

环境:前面采用Nginx做负载均衡,后接2台Web服务器缘由:系统经常出现给用户多次结算收益原因:因结算系统高峰时存在处理缓慢,在结算的时候处理时间长,导致超过了proxy_connect_timeout 30;proxy_send_timeout 30;proxy_read_timeout 60;三个时间,同时还设置了proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_404;保证服务

并发下,使用redis防止数据重复插入(数据库未对表字段设置唯一情况下)

@Controller @RequestMapping("/myTest") public class TestController {     @Autowired(required = false)     private TestService testService;          @Autowired     private RedisUtil redisUtil;     @RequestMapping("add")     public void