EF Core悲观并发控制

文章目录

  • 前言
  • 一、悲观并发控制的核心思想
  • 二、实现步骤
    • 1)针对SQL Server数据库
    • 2)针对MySQL数据库
  • 三、不同数据库的锁语法
  • 四、注意事项
  • 五、适用场景
  • 六、与乐观并发的对比
  • 总结


前言

Entity Framework (EF) Core 默认主要支持 乐观并发控制(通过并发令牌或 RowVersion),而 悲观并发控制 需要开发者手动实现(通常借助数据库事务和锁机制

一、悲观并发控制的核心思想

操作数据前 显式锁定资源(如行锁表锁),阻止其他事务修改,直到当前事务完成。通常通过数据库的锁机制实现。

二、实现步骤

  1. 开启事务
    使用 BeginTransactionBeginTransactionAsync 开启事务。
  2. 显式加锁查询
    使用 FromSqlRawFromSqlInterpolated 编写原生 SQL 查询,指定锁机制(如 UPDLOCK)。
  3. 操作数据
    在事务内修改数据,此时锁保持有效。
  4. 提交事务
    提交后释放锁。(trans.Commit();)

1)针对SQL Server数据库

  1. 示例

    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数据库

  1. 示例:

    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();
    }
    

三、不同数据库的锁语法

  1. SQL Server: WITH (UPDLOCK) 或 WITH (ROWLOCK)
  2. PostgreSQL: SELECT … FOR UPDATE
  3. MySQL: SELECT … FOR UPDATE

四、注意事项

  1. 事务范围
    保持事务尽可能短,避免长时间锁定导致性能问题。
  2. 死锁风险
    确保锁定顺序一致,减少死锁概率。
  3. 数据库兼容性
    不同数据库的锁语法和行为可能不同,需参考具体数据库文档。

五、适用场景

  1. 高竞争环境(如库存扣减、抢购)
  2. 需要强制串行化操作的场景

六、与乐观并发的对比

悲观并发 乐观并发
实现方式 显式加锁(事务+锁机制) 版本检查(并发令牌、RowVersion)
性能 高竞争时可能更高效 低冲突时更高效
复杂度 需要手动管理锁和事务 EF Core 内置支持

总结

EF Core 实现悲观并发需通过数据库锁和事务手动控制。虽然灵活,但需谨慎处理锁的范围和事务生命周期,避免性能问题。如果业务允许,优先考虑乐观并发(如 ConcurrencyCheck 特性或 RowVersion)。

你可能感兴趣的:(.netcore,sql)