3

这里有点棘手:我们的系统偶尔会遇到死锁。而且我根本没有强大的数据库并发背景。

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.

有几个应用程序访问数据库:使用实体框架访问数据库的主 MVC 应用程序和几个简单的控制台应用程序,每个应用程序都使用 ADO.NET 和原始 SQL 查询数据库,并通过 BinaryTap ActiveRecord 插入数据。

不幸的是,我是客户组织的 FNG,所以我无法部署和测试新想法。此外,我们使用的是 SSMS Express,所以我无法访问 SQL Profiler。但我立即解决问题并不重要,更重要的是我记录我对问题的分析。

当错误消息说我应该重新运行事务时,它是否有任何真实性?这是我们的 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 不要锁定查询吗?

感谢所有阅读本文的人。我知道这是一个复杂的问题,我有很多阅读要做..

4

1 回答 1

13

死锁分析需要访问 SQL 探查器以查看数据库服务器在解除死锁时的情况。特别是如果您不是在 DB 上执行的 SQL 查询的所有者,这是必要的。使用 EF 时,您不是所有者 - EF 生成查询。必须解决数据库查询和使用事务执行的数据库操作的顺序的死锁=您必须知道数据库中发生了什么。

使用隔离级别需要非常了解您的应用程序和数据库上运行的任何其他应用程序。如果您将隔离级别设置为未提交的读取,则您正在破坏事务的核心规则之一 - 隔离。以读取未提交模式运行的事务可以读取其他事务未提交的数据(脏数据) - 如果该事务回滚,您的代码可以处理无效数据并将数据库移动到不一致的状态(或在某些数据库约束上失败)。NOLOCKSQL 查询中的提示与全局使用未提交读取相同,但提示仅针对单个查询中的单个表。

Is it bad to use NOLOCK or read uncommitted? No but you must be absolutely sure when to do that = you must understand your application (and other applications using the database) and make sure that these queries used to get uncommitted data are not used for any other data modifications or any risk decisions.

Default isolation level for TransactionScope is serializable which is the most restrictive level for transaction (= causes deadlock more often). You should start by using Read committed isolation level (but you must ensure that same data are not read multiple times from the database during a transaction) to reduce database locking but it will most probably doesn't solve the problem (it can reduce the frequency). More about isolation levels.

于 2011-12-12T23:35:03.590 回答