只有一个资源和隔离级别可序列化的死锁.
本文关键字:序列化 死锁 隔离级 有一个 资源 | 更新日期: 2023-09-27 18:22:37
我使用实体框架来处理长时间运行的任务(平均10-30秒)。我有很多worker实例,每个worker从数据库表中获取下一个任务id,然后获取该id的工作描述。
当然,对任务表的访问必须序列化,这样来自工作者的每个请求都会得到一个新的id
static int? GetNextDetailId()
{
int? id = null;
using ( var ctx = Context.GetContext() )
using ( var tsx = ctx.Database.BeginTransaction( System.Data.IsolationLevel.Serializable ))
{
var obj = ctx.DbsInstrumentDetailRaw.Where( x => x.ProcessState == ProcessState.ToBeProcessed ).FirstOrDefault();
if ( obj != null )
{
id = obj.Id;
obj.ProcessState = ProcessState.InProcessing;
ctx.SaveChanges();
}
tsx.Commit();
}
return id;
} // GetNextDetailId
不幸的是,当我和10名员工一起运行它时,我几乎立即得到
事务(进程ID 65)在锁定资源上与另一个进程发生死锁,已被选为死锁牺牲品。重新运行事务。
我对这种行为没有任何解释。我知道死锁的情况:我们有两个或更多的资源和两个或多个进程,它们试图以不同的顺序获取资源。但这里我们只有一种资源!我所希望的只是进程能够按顺序访问此资源。因此,如果A打开了一个事务,B应该简单地等待,直到A提交/回滚。这在这里似乎没有发生。
有人能请吗
揭示这里发生了什么,来教育我。
给这个问题一个("THE?")解决方案。我认为这个问题在编程中应该很常见。
谢谢Martin
您可以使用SQL事件探查器来探查SQL服务器上正在执行的SQL语句来验证这一点,但问题可能是,即使您在隔离级别设置为可序列化的事务中,也不会发出独占锁,因此会发生两个线程同时访问同一行的情况,两者都在尝试更新。
我看到的最好的建议是,如果您需要在这个级别控制锁定,请执行存储过程或SQL,而不是尝试使用LINQ。
在实体框架中使用select锁定表
揭示这里发生了什么,来教育我。
- CCD_ 1获取该表上的共享锁。对于可序列化隔离级别,此锁将一直保持到事务提交或回滚为止
ctx.SaveChanges()
需要一个独占锁来更新该行
在步骤1中,两个或多个事务可以同时获得共享锁,但在步骤2中,它们都不能获得独占锁。僵局
给这个问题一个("THE?")解决方案。
我能想出两种方法来解决这个问题。
- 更改操作顺序:更新一行,然后返回。您必须使用存储过程在EF中执行此操作
- 使用较低的隔离级别(例如,可重复读取)和乐观并发。不会出现死锁(共享锁将在
select
之后立即释放)。当两个工作程序尝试更新同一行时,其中一个将获得并发异常
好的,我现在做这个:
使用(tablockx,holdlock)从InstrumentDetailRaw中选择1,其中0=1"
在我的交易开始时,根据这篇帖子:
使用实体框架中的选择锁定表
真的很管用。10名工人连续工作数小时,没有出现死锁。