在我的数据库中处理并发:
客户端a更新一行
客户端b尝试更新同一行
客户端b需要等待客户端a提交其更新
两个客户端a和b示例都是模拟的,并使用以下代码:
using (myEntities db = new myEntities ())
{
db.Database.Connection.Open();
try
{
using (var scope = db .Database.BeginTransaction(System.Data.IsolationLevel.Serializable))
{
{
var test = db.customer_table.Where(x => x.id == 38).FirstOrDefault();
test.bank_holder_name = "CLIENT NAME XXXX";
db.SaveChanges(); <=== CLIENT B stop here while client A still in progress. After CLIENT A finish commit, here will throw *Deadlock found error*"
scope.Commit();
}
}
}
catch (Exception ex)
{
throw;
}
}
这不是我所期望的,客户机b应该等待并且不允许查询任何关于row id=38的数据,但是它可以继续,直到 SaveChanges
最后抛出一个错误。
因此,我怀疑这可能是由linq(不正确的行/表锁)引起的
我的代码编辑如下:
using (myEntities db = new myEntities ())
{
db.Database.Connection.Open();
try
{
using (var scope = db .Database.BeginTransaction(System.Data.IsolationLevel.Serializable))
{
{
var test = db.Database.ExecuteSqlCommand("Update customer_table set bank_holder_name = 'CLIENT XXXXX' where pu_id = 38"); <===== Client B is stop here and proceed after Client A is completed
db.SaveChanges();
scope.Commit();
}
}
}
catch (Exception ex)
{
throw;
}
}
最后,事务处理的是上面的代码(不是linq函数)。这太令人困惑了,linq在使事务工作不一致的行为背后做了什么?
2条答案
按热度按时间vlju58qv1#
这是由于ef代码生成了两个sql语句:
SELECT
对于线路:…以及随后的
UPDATE
对于SaveChanges()
打电话。对于可序列化的隔离级别,当
SELECT
语句已运行。当他们中的一个或另一个第一次尝试执行UPDATE
它们无法获得所需的独占锁,因为另一个客户端上有一个共享锁。然后,另一个客户机本身尝试获取一个独占锁,这样就出现了死锁情况。这个
ExecuteSqlCommand
只需要一个update语句,因此不会发生死锁。可序列化的隔离级别可以大大降低并发性,这个示例正好说明了原因。您会发现,较不严格的隔离级别将允许ef代码工作,但存在虚记录、不可重复读取等风险。然而,这些风险很可能是您为了提高并发性而愿意承担和/或减轻的风险。
iyfjxgzm2#
不要先获取实体。而是创建一个“存根实体”并更新它,例如
也就是说