Azure应用并发插入重复数据:为何校验逻辑仍无效?
问题根源与解决方案
你的代码出现重复记录的核心原因是并发竞争条件:当多个客户端请求同时到达时,它们的「查询是否存在」操作会在对方的「插入记录」操作完成前执行,导致两个请求都判定记录不存在,最终都执行插入,产生重复条目。
以下是具体的解决方法:
1. 数据库层面添加唯一约束(最可靠的底层保障)
直接在Order表上给CustomerId和ExternalId的组合添加唯一约束,这是阻止重复的终极防线——即使应用层校验失效,数据库会直接拒绝重复插入。
以SQL Server为例,执行以下SQL语句:
ALTER TABLE Orders ADD CONSTRAINT UQ_Orders_CustomerId_ExternalId UNIQUE (CustomerId, ExternalId);
2. 改用数据库原子操作替代应用层分步校验
将「查询+插入」合并为数据库的原子操作,利用数据库的事务特性避免并发竞争。可以通过EF Core执行原生SQL实现:
var affectedRows = await _dataContext.Database.ExecuteSqlRawAsync( "INSERT INTO Orders (ExternalId, CustomerId) " + "SELECT @externalId, @customerId " + "WHERE NOT EXISTS (SELECT 1 FROM Orders WHERE CustomerId = @customerId AND ExternalId = @externalId)", new SqlParameter("@externalId", message.externalId), new SqlParameter("@customerId", message.customerId)); if (affectedRows == 0) { // 记录已存在,无需执行后续操作 }
这种方式让数据库在同一操作内完成检查和插入,彻底避免并发竞争。
3. 检查DbContext的生命周期配置
如果你的DbContext是单例或者生命周期过长,会导致上下文缓存旧数据,查询时可能读取的是缓存而非最新的数据库状态。确保在ASP.NET Core等框架中,DbContext使用Scoped生命周期(默认配置),每个请求创建独立的上下文实例,保证查询总是访问最新数据。
4. 捕获并处理唯一约束冲突异常
添加唯一约束后,并发插入时会抛出DbUpdateException,你需要在代码中捕获该异常并处理:
try { await _dataContext.SaveChangesAsync(); } catch (DbUpdateException ex) { if (ex.InnerException is SqlException sqlEx && sqlEx.Number == 2601) { // 处理重复记录场景,比如返回客户端「记录已存在」的提示 } else { throw; // 其他异常正常抛出 } }
内容的提问来源于stack exchange,提问作者Fernando
相关产品推荐
相关产品推荐

