ASP.NET 6.0+EF+MySQL并发场景下如何避免重复记录生成?
解决ASP.NET 6.0并发请求下MySQL重复群组记录问题
你的问题是典型的竞态条件:并发请求同时执行「查询是否存在→插入新记录」的逻辑,导致多个请求都判定记录不存在,最终重复插入。以下是可靠的解决方案:
1. 数据库层面添加唯一约束(最根本)
仅给name字段加普通索引不够,必须添加唯一约束,让数据库直接拦截重复插入操作,这是最底层的保障。
执行MySQL语句添加唯一约束:
ALTER TABLE groups ADD CONSTRAINT uc_group_name UNIQUE (name);
应用层处理逻辑:捕获MySQL的唯一键冲突异常(错误码1062),此时查询并返回已存在的群组即可。示例代码:
try { var newGroup = new Group { Name = groupName }; _dbContext.Groups.Add(newGroup); await _dbContext.SaveChangesAsync(); return newGroup; } catch (DbUpdateException ex) when (ex.InnerException is MySqlException mysqlEx && mysqlEx.Number == 1062) { // 唯一键冲突,查询已存在的群组 return await _dbContext.Groups.FirstOrDefaultAsync(g => g.Name == groupName); }
2. 事务加行级锁(避免竞态查询)
在查询是否存在时,使用行级锁锁定查询范围,阻止其他并发事务插入相同名称的群组。适合需要严格控制流程的场景。
EF Core示例代码:
using var transaction = await _dbContext.Database.BeginTransactionAsync(System.Data.IsolationLevel.RepeatableRead); try { // 使用FOR UPDATE锁定查询结果(若不存在则锁定间隙,防止插入相同值) var existingGroup = await _dbContext.Groups .FromSqlRaw("SELECT * FROM groups WHERE name = {0} FOR UPDATE", groupName) .FirstOrDefaultAsync(); if (existingGroup != null) { await transaction.CommitAsync(); return existingGroup; } var newGroup = new Group { Name = groupName }; _dbContext.Groups.Add(newGroup); await _dbContext.SaveChangesAsync(); await transaction.CommitAsync(); return newGroup; } catch { await transaction.RollbackAsync(); throw; }
注意:需确保MySQL表使用InnoDB引擎(默认支持事务和行级锁)。
3. 缓存配合数据库约束(优化性能)
之前的缓存方案失效,是因为没有和数据库操作强一致。正确的缓存逻辑应该是:
- 查询时先读缓存,若存在直接返回
- 缓存不存在则走「数据库加锁查询/插入」流程
- 插入成功后再将群组信息写入缓存
- 若捕获到唯一键冲突,查询到已存在的群组后再写入缓存
这样既利用缓存提升性能,又通过数据库约束和锁保证数据一致性。
内容的提问来源于stack exchange,提问作者Rameez Raja
相关产品推荐
相关产品推荐

