文章目录
- 前言
- 一、悲观并发控制的核心思想
- 二、实现步骤
- 1)针对SQL Server数据库
- 2)针对MySQL数据库
- 三、不同数据库的锁语法
- 四、注意事项
- 五、适用场景
- 六、与乐观并发的对比
- 总结
前言
Entity Framework (EF) Core 默认主要支持 乐观并发控制(通过并发令牌或 RowVersion),而 悲观并发控制 需要开发者手动实现(通常借助数据库事务和锁机制)
一、悲观并发控制的核心思想
在操作数据前 显式锁定资源(如行锁、表锁),阻止其他事务修改,直到当前事务完成。通常通过数据库的锁机制实现。
二、实现步骤
- 开启事务
使用 BeginTransaction 或 BeginTransactionAsync 开启事务。 - 显式加锁查询
使用 FromSqlRaw 或 FromSqlInterpolated 编写原生 SQL 查询,指定锁机制(如 UPDLOCK)。 - 操作数据
在事务内修改数据,此时锁保持有效。 - 提交事务
提交后释放锁。(trans.Commit();)
1)针对SQL Server数据库
-
示例:
using (var transaction = context.Database.BeginTransaction()) {try{// 查询并锁定目标行(SQL Server 使用 UPDLOCK)long houseId = 1;var house = context.Houses.FromSqlInterpolated($"SELECT * FROM T_Houses WITH (UPDLOCK) WHERE Id = {houseId}").FirstOrDefault();if (string.IsNullOrEmpty(house.Owner)){// 修改数据house.Owner = paramName;context.SaveChanges();}// 提交事务,释放锁transaction.Commit();}catch (Exception){transaction.Rollback();throw;} }
2)针对MySQL数据库
-
示例:
Console.WriteLine("请输入名字"); string name=Console.ReadLine(); using (MyDBContext dbContext=new MyDBContext()) using (var trans=dbContext.Database.BeginTransaction()) {Console.WriteLine($"{DateTime.Now}-准备select for Update");var house=dbContext.Houses.FromSqlInterpolated($"select * from T_Houses where Id=1 for update").Single();Console.WriteLine($"{DateTime.Now}-完成select for Update");if (!string.IsNullOrEmpty(house.Owner)){if (house.Owner==name){Console.WriteLine("此房源已被您抢到");}else{Console.WriteLine($"此房源已被{house.Owner}抢占");}trans.Commit();Console.ReadLine(); return;}house.Owner=name;Thread.Sleep(5000);Console.WriteLine($"恭喜{name}抢到了房源{house.Name}");dbContext.SaveChanges();trans.Commit();Console.ReadLine(); }
三、不同数据库的锁语法
- SQL Server: WITH (UPDLOCK) 或 WITH (ROWLOCK)
- PostgreSQL: SELECT … FOR UPDATE
- MySQL: SELECT … FOR UPDATE
四、注意事项
- 事务范围
保持事务尽可能短,避免长时间锁定导致性能问题。 - 死锁风险
确保锁定顺序一致,减少死锁概率。 - 数据库兼容性
不同数据库的锁语法和行为可能不同,需参考具体数据库文档。
五、适用场景
- 高竞争环境(如库存扣减、抢购)
- 需要强制串行化操作的场景
六、与乐观并发的对比
悲观并发 | 乐观并发 | |
---|---|---|
实现方式 | 显式加锁(事务+锁机制) | 版本检查(并发令牌、RowVersion) |
性能 | 高竞争时可能更高效 | 低冲突时更高效 |
复杂度 | 需要手动管理锁和事务 | EF Core 内置支持 |
总结
EF Core 实现悲观并发需通过数据库锁和事务手动控制。虽然灵活,但需谨慎处理锁的范围和事务生命周期,避免性能问题。如果业务允许,优先考虑乐观并发(如 ConcurrencyCheck 特性或 RowVersion)。