使用多个实例时的 EF 核心并发
Posted
技术标签:
【中文标题】使用多个实例时的 EF 核心并发【英文标题】:EF core concurrency when using multiple instances 【发布时间】:2022-01-02 07:16:57 【问题描述】:我有类似这样的代码:
var records = db.Records.Where(r => r.IsProcessing == false).Take(100).ToList();
records.IsProcessing = true;
await db.SaveChangesAsync()
...further work with recods
如果这段代码在一个微服务的多个实例上运行,会发生并发问题吗? (即两个服务将获得相同的记录集)。如果是,我该如何预防?
如果我的服务同时调用此方法,我想阻止我的服务从数据库中获取相同的记录。
【问题讨论】:
您无法阻止同时获取记录。有concurrency tokens 用于处理并发冲突。在您的情况下,您可能希望实施“先赢”策略。 为每个处理器分配一个唯一的 ID。首先将 NULL 的行更新为您的 id,首先使用原始 sql / 过程。然后选择它们。 【参考方案1】:您可以使用在可序列化事务中运行的存储过程来检索行。您可能需要表中的其他属性,例如 AssignedTo 记录分配给该行的服务,以及指示处理已完成的另一个属性。否则,如果服务检索了一些行但在完成所有处理之前失败,则这些行将保持未处理状态。当服务检索行时,它可以使用 AssignedTo = self 或 AssignedTo Is Null 之类的条件。大概还有时间戳或其他方式来确定所选行的优先级。
【讨论】:
【参考方案2】:是的,它会的,而且预防起来可能并不容易。
想象一下,这些服务的任务是更新帐户余额。 他们读取余额,并在其上添加存款金额:
service 1: on a busy machine (slow)
service 2: on a not so busy machine (fast)
"john" has £100 on his account
service 1: read
service 2: read
service 1: balance = balance + 100;
service 2: balance = balance + 100;
service 2: set processing = true
service 2: set processing = true
service 2: update record
service 1: update record.
john ends up with £200 instead of £300.
你需要引入一个事务范围,也许用 redis 来管理它, 这样每个实例都有一个事实点来检查是否可以进入和退出更新范围。如果愿意,可以通过网络进行“IsProcessing?或 SetIsProcessing”。
【讨论】:
以上是关于使用多个实例时的 EF 核心并发的主要内容,如果未能解决你的问题,请参考以下文章
在同一域中具有多个 Laravel 实例时的会话/cookie 错误