Тупики с Entity Framework - как повторно выполнить транзакцию?

Здесь возникла сложная ситуация: в нашей системе периодически случаются тупики. И у меня нет сильного опыта параллелизма в базе данных.

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, которое обращается к базе данных с помощью Entity Framework, и несколько простых консольных приложений, каждое из которых запрашивает базу данных с помощью ADO.NET и необработанного SQL и вставляет данные через BinaryTap ActiveRecord.

К сожалению, я работаю FNG в клиентской организации, поэтому не могу развернуть и протестировать новые идеи. Кроме того, мы используем SSMS Express, поэтому у меня нет доступа к SQL Profiler. Но менее важно, чтобы я немедленно решил проблему, и более важно, чтобы я задокументировал свой анализ проблемы.

Есть ли правда в сообщении об ошибке, когда в нем говорится, что я должен повторно выполнить транзакцию? Вот наш DaoBase - мы используем один ObjectContext на HttpContext (через свойство 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) обозначение - возможно, они должны? Есть ли обстоятельства, когда вы хотите заблокировать чистые запросы? И какие типы блокировок может создать запрос: блокировки строк и страниц? Как насчет запросов, сгенерированных Entity Framework, я должен сказать EF не блокировать запросы?

Спасибо всем, кто прочитал это далеко. Я знаю, что это сложная проблема, и у меня есть много чтения, чтобы сделать..

1 ответ

Решение

Анализ взаимоблокировки требует доступа к профилировщику SQL, чтобы увидеть ситуацию на сервере базы данных в нерабочее время. Особенно, если вы не являетесь владельцем SQL-запросов, выполняемых на БД, это необходимо. При использовании EF вы не являетесь владельцем - EF генерирует запросы. Необходимо разрешить взаимоблокировку запросов к базе данных и порядок операций с базой данных, выполняемых с транзакцией = вы должны знать, что произошло в базе данных.

Игра с уровнем изоляции требует очень хорошего знания как вашего приложения, так и любого другого приложения, работающего в базе данных. Если вы устанавливаете уровень изоляции для чтения незафиксированным, вы нарушаете одно из основных правил транзакций - изоляцию. Транзакции, выполняемые в режиме чтения без фиксации, могут считывать данные незафиксированными (грязные данные) другой транзакцией - если эта транзакция откатывает назад, ваш код может работать с недопустимыми данными и переводить базу данных в несогласованное состояние (или не работать из-за некоторых ограничений базы данных). NOLOCK Подсказка в запросе SQL аналогична глобальному использованию read uncommitted, но подсказка предназначена только для одной таблицы в одном запросе.

Это плохо использовать NOLOCK или читать без обязательств? Нет, но вы должны быть абсолютно уверены, когда это делать = вы должны понимать свое приложение (и другие приложения, использующие базу данных) и убедиться, что эти запросы, используемые для получения незафиксированных данных, не используются для каких-либо других изменений данных или любых решений о риске.

Уровень изоляции по умолчанию для TransactionScope является сериализуемым, что является наиболее ограничительным уровнем для транзакции (= чаще всего вызывает взаимоблокировку). Вам следует начать с использования уровня изоляции Read Committed (но вы должны убедиться, что одни и те же данные не читаются из базы данных несколько раз во время транзакции), чтобы уменьшить блокировку базы данных, но это, скорее всего, не решит проблему (это может снизить частоту).). Подробнее об уровнях изоляции.

Другие вопросы по тегам