这里遇到了一些困难:我们的系统偶尔遇到僵局。而且我根本没有强大的数据库并发背景。
System.Data.SqlClient.SqlException: Transaction (Process ID 69) was deadlocked on
lock resources with another process and has been chosen as the deadlock victim.
Rerun the transaction.
有几个访问数据库的应用程序:使用Entity Framework访问数据库的主要MVC应用程序和几个简单的控制台应用程序,每个应用程序使用ADO.NET和原始SQL查询数据库,并通过BinaryTap ActiveRecord插入数据。
不幸的是,我是客户组织的FNG,所以我无法部署和测试新想法。此外,我们正在使用SSMS Express,因此我无法访问SQL事件探查器。但是我立即解决问题并不重要,更重要的是我记录了我对问题的分析。
当它说我应该重新运行该事务时,错误消息是否有任何道理?这是我们的DaoBase - 我们每个HttpContext使用一个ObjectContext(通过Db属性)。我们总是将我们的Dao更新(但不是查询)放在SafeAction中,以便它们包含在事务中。我是否试图正确地重新运行交易?
public abstract class DaoBase
{
protected static CaseMateEntities Db
{
get
{
return ContextHelper<CaseMateEntities>.GetCurrentContext();
}
}
protected static void SafeAction(Action<ObjectContext> action)
{
Exception exception = null;
try {
using (var scope = new TransactionScope()) {
try {
if (Db.Connection.State != ConnectionState.Open)
Db.Connection.Open();
if (action != null)
action(Db);
Db.SaveChanges(SaveOptions.DetectChangesBeforeSave);
scope.Complete();
} catch (Exception ex) {
exception = ex;
if (exception is UpdateException)
// TODO: Is this a proper way to rerun a transaction?
scope.Complete();
}
}
if (exception == null) {
Db.AcceptAllChanges();
} else {
throw exception;
}
} finally {
Db.Connection.Close();
}
}
}
其他应用程序通过ADO.NET/Raw SQL查询数据库。他们各自的SELECT语句没有WITH (NOLOCK)
指定 - 也许他们应该?您是否有任何需要纯查询锁定的情况?查询会创建什么类型的锁:行和页锁?如果我告诉EF不要锁定查询,那么实体框架生成的查询呢?
感谢所有读过这篇文章的人。我知道这是一个复杂的问题,我有很多阅读要做..
答案 0 :(得分:13)
死锁分析需要访问SQL事件探查器才能在死锁时查看数据库服务器上的情况。特别是如果您不是在DB上执行的SQL查询的所有者,这是必要的。使用EF时,您不是所有者 - EF会生成查询。必须在数据库查询和使用事务执行的数据库操作的顺序上解决死锁=您必须知道数据库中发生了什么。
使用隔离级别需要非常了解应用程序和数据库上运行的任何其他应用程序。如果将隔离级别设置为read uncommitted,则会破坏事务的核心规则之一 - 隔离。以读取未提交模式运行的事务可以读取其他事务未提交的数据(脏数据) - 如果该事务回滚您的代码可以使用无效数据并将数据库移动到不一致状态(或者对某些数据库约束失败)。 SQL查询中的NOLOCK
提示与全局使用read uncommitted相同,但提示仅针对单个查询中的单个表。
使用NOLOCK
或读取未提交是不是很糟糕?不,但您必须绝对确定何时这样做=您必须了解您的应用程序(以及使用该数据库的其他应用程序),并确保用于获取未提交数据的这些查询不会用于任何其他数据修改或任何风险决策。< / p>
TransactionScope
的默认隔离级别是可序列化的,这是事务的最严格级别(=更频繁地导致死锁)。您应该首先使用Read committed isolation level(但是您必须确保在事务期间不会从数据库中多次读取相同的数据)以减少数据库锁定,但它很可能无法解决问题(它可以降低频率)。 More关于隔离级别。