• EF Core高并发场景下不引入锁的问题
  • 发布于 2个月前
  • 202 热度
    7 评论

问题:EF Core 不引入锁,高并发场景 ExecuteSqlRawAsync("UPDATE Users SET Balance = Balance + {0} WHERE UserId = {1}");后如何获取 Updated 后的值?


背景:

用于余额变动记录。再查一遍肯定不行,极端情况下一个用户会同时发 10000 个下单请求(客户端随硬件交付,没有升级功能,无法更新),这样不加锁余额变动记录就不准了。加锁的话性能太差了。

用户评论
  • pckillers
  • SELECT Balance, version FROM Users WHERE UserId = {userId};
    UPDATE Users SET Balance = Balance + {amount}, version=version+1 WHERE UserId = {userId} AND version={version};
    成功 return Balance + amount;

  • 2024/5/16 12:04:00 [ 0 ] [ 0 ] 回复
  • Jeff
  • 1. 存储过程
    update 和返回最新值
    ---
    2. 引入 version ,乐观锁自旋
    2.1 select version;
    2.2 update version=version+1 where version = {old_version}

    如果 update 成功,说明 select -》 update 之间没有修改,update 成功,新旧值
    如果 失败,重复 2.1-2.2 并引入随机等待
    ---
    3. select * for update 提前加锁
    然后 UPDATE Users SET Balance = Balance + {0} WHERE UserId = {1}
    再次 select 得到最新值
    在同一个事务
  • 2024/5/16 11:51:00 [ 0 ] [ 0 ] 回复
  • Fayer
  • EF 不是有实体追踪嘛,实体设置需要更新的字段,直接保存就好了。至于并发的问题,你需要在表中设计一个字段,数据存时间戳。查询出来数据,给实体某一些字段赋值,然后进行更新,如果当前数据的时间戳和数据库中的数据时间戳不一致,表示这条数据被操作过了,会触发一个异常的,直接抛出来就好了。
  • 2024/5/16 11:43:00 [ 0 ] [ 0 ] 回复
  • 夜灵霜影
  • 这个取决于你用什么数据库吧? 跟 EF 好像是没什么关系. 你如果觉得不靠谱你就在外面加个 tranaction. 然后设置数据库隔离等级 RC. 这样你只需要在后面再接一个 SELECT 就 OK 了. 而且你也完全可以先查出 Balance, 然后更新. 更新成功后然后用程序计算出新的 Balance 直接返回.
    SELECT Balance FROM Users WHERE UserId = {userId};
    拿到之后先不管.
    UPDATE Users SET Balance = Balance + {amount} WHERE UserId = {userId};
    然后 return balance + amount;
  • 2024/5/16 11:34:00 [ 0 ] [ 0 ] 回复