eIT.com.cn 2023/3/18 6:31:41 阅读 3 次
1、高并发的情况,时常会发生数据不稳定的情况 在看本节内容之前,请先看上一章SqlServer 高并发的情况下,如何利用锁保证数据的稳定性 本节内容,也是具体讨论如何在EF中实现这些操作 2、场景模拟,同上一章,抢券 EF 不考虑高并发的情况下,抢券代码为: string _currOwner = Console.ReadLine();//当前用户 using var ctx = new MyDBContext(); var cop = ctx.Coupons.Single(x => x.Id == 2); if (!string.IsNullOrEmpty(cop.Owner)) { Console.WriteLine($"券被抢了"); } else { cop.Owner = _currOwner; Thread.Sleep(5000); ctx.SaveChanges(); Console.WriteLine($"恭喜{_currOwner}抢到券{cop.Id}了"); } Console.ReadLine(); 打开两个进程,让tom和jerry同时先后进行抢券,模拟出一个券同时被两个用户抢到的情况
上图可用直观看出,都提示抢券成功,但是owner是晚一点点执行update的jerry,在实际生产中,无法给tom一个交代 3、解决并发问题 3.1 通过updlock,悲观并发控制 string _currOwner = Console.ReadLine();//当前用户 using var ctx = new MyDBContext(); using var tx = ctx.Database.BeginTransaction(); FormattableString sql = $@"select * from Coupons with(updlock) where id=2"; var cop = ctx.Coupons.FromSqlInterpolated(sql).Single(); if (!string.IsNullOrEmpty(cop.Owner)) { Console.WriteLine($"券被抢了"); } else { cop.Owner = _currOwner; Thread.Sleep(5000); ctx.SaveChanges(); Console.WriteLine($"恭喜{_currOwner}抢到券{cop.Id}了"); } tx.Commit(); Console.ReadLine(); 解决:但这个是排他锁,有可能造成线程卡顿问题
3.2 通过定义鉴权字段,乐观并发控制 CouponConfig添加配置 builder.Property(x => x.Owner).IsConcurrencyToken(); 抢券代码: string _currOwner = Console.ReadLine();//当前用户 using var ctx = new MyDBContext(); var cop = ctx.Coupons.Single(x => x.Id == 2); if (!string.IsNullOrEmpty(cop.Owner)) { Console.WriteLine($"券被抢了"); } else { Thread.Sleep(5000); try { cop.Owner = _currOwner; await ctx.SaveChangesAsync(); Console.WriteLine($"恭喜{_currOwner}抢到券{cop.Id}了"); } catch (DbUpdateConcurrencyException ex) { var entry = ex.Entries.First(); var dbValues = entry.GetDatabaseValues(); var newOwner = dbValues.GetValue<string>(nameof(Coupon.Owner)); Console.WriteLine($"并发冲突,{newOwner}已经抢到该券了"); } } 结果:
根据update语句,可用看出where加了owner=旧值,来判断是否发生过更改 3.3 添加数据版本标识 如果无法定义一个明确的鉴权字段,那么可用通过新增一个字段,来标识数据来进行鉴权 public class Coupon { public int Id { get; set; } public string Name { get; set; } public string? Description { get; set; } public string? Owner { get; set; } public byte[] RowVersion { get; set; } #迁移到数据库,类型为rowversion,当数据更新时,版本会自动递增 } 迁移后数据库表代码 CREATE TABLE [dbo].[Coupons] ( [Id] INT IDENTITY (1, 1) NOT NULL, [Name] NVARCHAR (MAX) NOT NULL, [Description] NVARCHAR (MAX) NULL, [Owner] NVARCHAR (MAX) NULL, [RowVersion] ROWVERSION NOT NULL, CONSTRAINT [PK_Coupons] PRIMARY KEY CLUSTERED ([Id] ASC) ); CouponConfig添加配置 builder.Property(x => x.RowVersion).IsRowVersion(); 抢券代码同3.2 结果:
理论和3.2相同,where会做一个rowversion的旧值判断
总结:这三种方法由浅入深,各有利弊,在并发量不大的情况下使用3.1,并发量较大的情况下使用3.2&3.3
感谢关注!!
|
• C#-垃圾回收机制(GC) (2023/3/30 16:42:14)
• SignalR, No Connection with that ID,IIS (2023/3/30 14:44:24)
• 记一次 .NET 某传感器采集系统 线程爆高分析 (2023/3/30 12:13:48)
• BitMap对象当前正在其他地方使用?加锁也没用? (2023/3/30 12:13:48)
• ASP.NET Core - 选项系统之源码介绍 (2023/3/30 9:42:47)
• unity 射线特性测试 (2023/3/29 21:59:48)
• c# 对序列化类XMLSerializer 二次封装泛型化方便了一些使用的步骤 (2023/3/29 20:50:13)
• ASP.NET Core - 选项系统之选项验证 (2023/3/29 18:24:40)
• C#面向对象核心-其它 (2023/3/28 22:21:13)
• C#面向对象核心-多态 (2023/3/28 21:13:46)
Java 安装配置 | Java泛型 | 路由协议 | Cocos2d-x | 软件应用问答 | 数据库文摘 | 数据仓库 | 无线网络解决方案 | MCSE | MSN | Biopython | 网吧媒体应用 | 应用服务器 | Spring | 使用Python教程进行密码学 | iOS(iPhone,iPad) | XQuery | Silverlight | QQ综合 | 互联网安全
合作媒体与友情链接 |
生活常识小贴士 | 软件开发教程 | 智慧城市生活网 | 息县通生活服务[移动版] | 息县商圈[移动版] | 美食菜谱 |
健康养生 | 法律知识 | 科技频道 | 电影影讯 | 留学考研学习 | 星座生肖|解梦说梦 |
关于我们 | 联系我们 | 合作媒体 | 使用条款 | 隐私权声明 | 版权声明 |
Copyright © 2023 eIT.com.cn. All Rights Reserved. | 豫ICP备2022012332号 |