Диагностика взаимоблокировок в SQL Server 2005

82

В базе данных Stack Overflow SQL Server 2005 мы наблюдаем некоторые пагубные, но редкие условия взаимоблокировки.

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

UPDATE [dbo].[Posts]
SET [AnswerCount] = @p1, [LastActivityDate] = @p2, [LastActivityUserId] = @p3
WHERE [Id] = @p0

Другой оператор взаимоблокировки может быть разным, но обычно это какое-то тривиальное простое чтение таблицы сообщений. Этого всегда убивают в тупике. Вот пример

SELECT
[t0].[Id], [t0].[PostTypeId], [t0].[Score], [t0].[Views], [t0].[AnswerCount], 
[t0].[AcceptedAnswerId], [t0].[IsLocked], [t0].[IsLockedEdit], [t0].[ParentId], 
[t0].[CurrentRevisionId], [t0].[FirstRevisionId], [t0].[LockedReason],
[t0].[LastActivityDate], [t0].[LastActivityUserId]
FROM [dbo].[Posts] AS [t0]
WHERE [t0].[ParentId] = @p0

Чтобы быть предельно ясным, мы наблюдаем не тупиковые ситуации записи / записи, а только чтение / запись.

На данный момент у нас есть смесь LINQ и параметризованных SQL-запросов. Мы добавили with (nolock)ко всем SQL-запросам. Возможно, это помогло некоторым. У нас также был один (очень) плохо написанный запрос значка, который я исправил вчера, который выполнялся каждый раз более 20 секунд и выполнялся каждую минуту сверх этого. Я надеялся, что это было источником некоторых проблем с блокировкой!

К сожалению, около 2 часов назад у меня возникла еще одна ошибка тупика. Точно такие же симптомы, пишут один и тот же виновник.

Поистине странно то, что SQL-оператор блокировки записи, который вы видите выше, является частью очень определенного пути кода. Он выполняется только тогда, когда к вопросу добавляется новый ответ - он обновляет родительский вопрос с новым количеством ответов и последней датой / пользователем. Это, очевидно, не так уж и часто по сравнению с огромным количеством операций чтения, которые мы выполняем! Насколько я могу судить, мы не выполняем огромное количество операций записи в приложении.

Я понимаю, что NOLOCK - это своего рода гигантский молот, но большинство запросов, которые мы здесь выполняем, не обязательно должны быть такими точными. Будете ли вы беспокоиться, если ваш профиль пользователя устарел на несколько секунд?

Как говорит здесь Скотт Хансельман, использование NOLOCK с Linq немного сложнее .

Мы заигрываем с идеей использования

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

в контексте базовой базы данных, чтобы все наши запросы LINQ имели этот набор. Без этого нам пришлось бы заключать каждый вызов LINQ, который мы делаем (ну, простые считывающие, а это подавляющее большинство из них), в блок кода транзакции из 3-4 строк, что некрасиво.

Думаю, я немного разочарован тем, что тривиальное чтение в SQL 2005 может привести к тупиковой ситуации при записи. Я мог видеть, что тупики записи / записи являются огромной проблемой, но читает? У нас нет банковского сайта, нам не нужна постоянная точность.

Идеи? Мысли?


Вы создаете экземпляр нового объекта LINQ to SQL DataContext для каждой операции или, возможно, используете один и тот же статический контекст для всех ваших вызовов?

Джереми, мы по большей части разделяем один статический текст данных в базовом контроллере:

private DBContext _db;
/// <summary>
/// Gets the DataContext to be used by a Request's controllers.
/// </summary>
public DBContext DB
{
    get
    {
        if (_db == null)
        {
            _db = new DBContext() { SessionName = GetType().Name };
            //_db.ExecuteCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED");
        }
        return _db;
    }
}

Вы рекомендуете создавать новый контекст для каждого контроллера, или для каждой страницы, или… чаще?

Джефф Этвуд
источник
2
Какой <a href=" en.wikipedia.org/wiki/… режим</a> вы используете: «пессимистический» (на основе блокировки) или «оптимистичный» (<a href = " en.wikipedia.org/wiki/ … )?
Джон Сиракуза,
Я согласен с ответом Гая выше - вместо того, чтобы пытаться обойти симптом, почему бы не устранить основные причины? Добавив промежуточный итог AnswerCount в таблицу сообщений, вы создали потенциальный блокирующий ресурс. Хотел бы Джефф опубликовать свой ERD для StackOverflow, чтобы люди могли критиковать?
Andyp 03
2
Ух ты, слышал об этом в твоем подкасте со Скоттом. Не могу поверить, что он не поставляется из коробки с лучшей конфигурацией. Я покажу это нашим администраторам баз данных (потому что они тоже широко используют nolock)
Дэн Эспарза,
3
См .: samsaffron.com/archive/2008/08/27/Deadlocked+, потому что это было взаимоблокировкой. Включение изоляции моментальных снимков - хорошее решение этой проблемы.
Sam Saffron,

Ответы:

44

Согласно MSDN:

http://msdn.microsoft.com/en-us/library/ms191242.aspx

Когда параметры базы данных READ COMMITTED SNAPSHOT или ALLOW SNAPSHOT ISOLATION включены, логические копии (версии) сохраняются для всех изменений данных, выполняемых в базе данных. Каждый раз, когда строка изменяется определенной транзакцией, экземпляр компонента Database Engine сохраняет версию ранее зафиксированного образа строки в базе данных tempdb. Каждая версия помечена порядковым номером транзакции, которая внесла изменение. Версии измененных строк связываются с помощью списка ссылок. Самое новое значение строки всегда сохраняется в текущей базе данных и связано с версионными строками, хранящимися в tempdb.

Для краткосрочных транзакций версия измененной строки может кэшироваться в пуле буферов без записи в дисковые файлы базы данных tempdb. Если потребность в версированной строке недолговечна, она просто будет удалена из пула буферов и не обязательно приведет к накладным расходам ввода-вывода.

Похоже, что из-за дополнительных накладных расходов производительность немного снижается, но может быть незначительной. Мы должны проверить, чтобы убедиться.

Попробуйте установить эту опцию и УДАЛИТЬ все NOLOCK из запросов кода, если это действительно не необходимо. NOLOCK или использование глобальных методов в обработчике контекста базы данных для борьбы с уровнями изоляции транзакций базы данных - это повязки на проблему. NOLOCKS замаскирует фундаментальные проблемы с нашим уровнем данных и, возможно, приведет к выбору ненадежных данных, где автоматическое управление версиями строк выбора / обновления кажется решением.

ALTER Database [StackOverflow.Beta] SET READ_COMMITTED_SNAPSHOT ON
Джефф Далгас
источник
3
«NOLOCKS маскирует фундаментальные проблемы с нашим уровнем данных» ... Какие проблемы маскирует NOLOCK? Если я думаю, что мне нужен NOLOCK, какие проблемы мне следует искать?
Мэтт Гамильтон,
3
Что в этом ответе делает его «ответом»? Я до сих пор не понимаю, почему чтение за миллисекунды привело бы к тупиковой ситуации, немедленно убив записи? Я слишком плохо догадываюсь, что ответ "user13484", на него нет ссылки, если это так.
RichardTheKiwi
37

NOLOCK и READ UNCOMMITTED - скользкая дорожка . Вы никогда не должны использовать их, если не понимаете, почему сначала возникает тупик. Меня беспокоит, что вы скажете: «Мы добавили с (nolock) ко всем запросам SQL». Необходимость везде добавлять WITH NOLOCK - верный признак того, что у вас есть проблемы на уровне данных.

Сама инструкция обновления выглядит немного проблематичной. Вы определяете счетчик раньше в транзакции или просто берете его из объекта? AnswerCount = AnswerCount+1когда вопрос добавлен, вероятно, лучший способ справиться с этим. Тогда вам не нужна транзакция, чтобы получить правильный счет, и вам не нужно беспокоиться о проблеме параллелизма, которой вы потенциально можете подвергнуть себя.

Один из простых способов обойти проблему взаимоблокировки этого типа без большой работы и без включения грязного чтения - это использовать "Snapshot Isolation Mode"(новый в SQL 2005), который всегда дает вам чистое чтение последних немодифицированных данных. Вы также можете довольно легко перехватить и повторить тупиковую инструкцию, если хотите изящно с ними обращаться.

JEzell
источник
4
Я с JEzell - первое, на чем я остановился, это «SET AnswerCount = <фиксированное значение>». Откуда эта ценность? Это заставляет меня задаться вопросом, извлекали ли вы ее из другого места транзакции способом, который захватывает кучу блокировок. Я бы начал с этого. И да, глобальный NOLOCK - это пластырь.
Коуэн,
25

Вопрос OP заключался в том, чтобы спросить, почему возникла эта проблема. Этот пост надеется ответить на этот вопрос, оставляя возможные решения на усмотрение других.

Вероятно, это проблема, связанная с индексом. Например, допустим, что таблица Posts имеет некластеризованный индекс X, который содержит ParentID и одно (или несколько) обновляемых полей (AnswerCount, LastActivityDate, LastActivityUserId).

Взаимоблокировка может произойти, если команда SELECT выполняет блокировку совместного чтения по индексу X для поиска по ParentId, а затем ему необходимо выполнить блокировку общего чтения в кластеризованном индексе, чтобы получить оставшиеся столбцы, в то время как команда UPDATE выполняет исключительную запись заблокировать кластеризованный индекс и получить монопольную блокировку записи для индекса X, чтобы обновить его.

Теперь у вас есть ситуация, когда A заблокировал X и пытается получить Y, тогда как B заблокировал Y и пытается получить X.

Конечно, нам понадобится OP, чтобы обновить свою публикацию дополнительной информацией о том, какие индексы используются, чтобы подтвердить, действительно ли это является причиной.

MrB
источник
Я согласен с этим анализом - SELECT и UPDATE обрабатывают строки в разном порядке, поэтому каждый пытается получить блокировку строки, которая есть у другого.
Майк Диммик,
Это лучший ответ на весь поток и единственное объяснение того, почему на самом деле возникает тупик. К сожалению, это не ответ №1, потому что он лучший здесь.
Джонатан Кехайас,
В некоторых ответах отсутствует момент о том, что блокировка находится между двумя атомарными простыми операторами. Это единственный пост, который пытается это объяснить. Хотя инструкция проста, обновление таблицы может включать в себя несколько обновлений CIX и NCIX, которые расширяются во многие операции. То же самое для READ с обходом NCIX, поиском по закладкам CIX. Это не имеет ничего общего с объединением столов в одном порядке и т. Д. (Люди читают вопросы ??)
RichardTheKiwi
18

Мне довольно не нравится этот вопрос и сопутствующие ответы. Есть много фраз: «Попробуйте эту волшебную пыль! Нет, эту волшебную пыль!»

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

Все, что вы указали, - это то, что происходят некоторые блокировки, а не тупиковые.

В SQL 2005 вы можете получить больше информации о том, какие блокировки снимаются, используя:

DBCC TRACEON (1222, -1)

так что когда возникает тупик, у вас будет лучшая диагностика.

Леон Бамбрик
источник
13
Тупиковая ситуация немедленно обрабатывается монитором взаимоблокировки в SQL Server. DMV бесполезны для устранения тупиковой ситуации, потому что жертва будет выбрана и убита прежде, чем вы сможете ее обнаружить.
Джонатан Кехайас,
14

Вы создаете экземпляр нового объекта LINQ to SQL DataContext для каждой операции или, возможно, используете один и тот же статический контекст для всех ваших вызовов? Первоначально я пробовал второй подход, и, насколько я помню, он вызвал нежелательную блокировку БД. Теперь я создаю новый контекст для каждой атомарной операции.

Jeremcc
источник
10

Прежде чем сжечь дом, чтобы поймать муху с помощью NOLOCK повсюду, вы можете взглянуть на график тупиков, который вы должны были захватить с помощью Profiler.

Помните, что для тупиковой ситуации требуется (как минимум) 2 блокировки. Соединение 1 имеет блокировку A, хочет блокировку B - и наоборот для соединения 2. Это неразрешимая ситуация, и кто-то должен уступить.

То, что вы показали до сих пор, решается простой блокировкой, которую Sql Server с радостью делает в течение всего дня.

Я подозреваю, что вы (или LINQ) начинаете транзакцию с этим оператором UPDATE и заранее ВЫБИРАЕТЕ какую-то другую информацию. Но вам действительно нужно пройти через график тупиковых ситуаций, чтобы найти блокировки, удерживаемые каждым потоком, а затем вернуться через Profiler, чтобы найти операторы, которые вызвали эти блокировки.

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

Марк Брэкетт
источник
7

Будете ли вы беспокоиться, если ваш профиль пользователя устарел на несколько секунд?

Нет - это вполне приемлемо. Установка базового уровня изоляции транзакции, вероятно, является лучшим / самым чистым способом.

Грег Херлман
источник
5

Типичная взаимоблокировка чтения / записи возникает из-за доступа по порядку индекса. Чтение (T1) находит строку по индексу A, а затем ищет спроецированный столбец по индексу B (обычно кластеризованный). Запись (T2) изменяет индекс B (кластер), а затем обновляет индекс A. T1 имеет S-Lck на A, хочет S-Lck на B, T2 имеет X-Lck на B, хочет U-Lck на A. Тупик , затяжка. T1 убит. Это распространено в средах с интенсивным OLTP-трафиком и слишком большим количеством индексов :). Решение состоит в том, чтобы либо при чтении не переходить от A к B (т.е. включенный столбец в A, либо удалять столбец из спроецированного списка), либо T2 не должен переходить от B к A (не обновлять индексированный столбец). К сожалению, linq здесь вам не друг ...

Ремус Русану
источник
Кстати, A и B - это индексы одной и той же таблицы
Ремус Русану
3

@Jeff - Я определенно не эксперт в этом, но я добился хороших результатов с созданием нового контекста почти при каждом вызове. Я думаю, что это похоже на создание нового объекта Connection при каждом вызове ADO. Накладные расходы не так велики, как вы думаете, поскольку пул соединений все равно будет использоваться.

Я просто использую глобальный статический помощник вроде этого:

public static class AppData
{
    /// <summary>
    /// Gets a new database context
    /// </summary>
    public static CoreDataContext DB
    {
        get
        {
            var dataContext = new CoreDataContext
            {
                DeferredLoadingEnabled = true
            };
            return dataContext;
        }
    }
}

а потом делаю что-то вроде этого:

var db = AppData.DB;

var results = from p in db.Posts where p.ID = id select p;

И я бы сделал то же самое с обновлениями. В любом случае, у меня не так много трафика, как у вас, но я определенно получал некоторую блокировку, когда на раннем этапе использовал общий DataContext с небольшим количеством пользователей. Никаких гарантий, но, возможно, стоит попробовать.

Обновить : опять же, глядя на свой код, вы используете только контекст данных на время жизни этого конкретного экземпляра контроллера, что в основном кажется прекрасным, если только он каким-то образом не используется одновременно несколькими вызовами внутри контроллера. В ветке по этой теме ScottGu сказал:

Контроллеры работают только для одного запроса - поэтому в конце обработки запроса они собираются сборщиком мусора (что означает сбор DataContext) ...

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

Jeremcc
источник
3

Вопрос : Почему вы хранение AnswerCountвPosts таблице?

Альтернативный подход состоит в том, чтобы исключить «обратную запись» в Postsтаблицу, не сохраняя AnswerCountв таблице, а динамически вычисляя количество ответов на сообщение по мере необходимости.

Да, это будет означать, что вы выполняете дополнительный запрос:

SELECT COUNT(*) FROM Answers WHERE post_id = @id

или более типично (если вы показываете это для домашней страницы):

SELECT p.post_id, 
     p.<additional post fields>,
     a.AnswerCount
FROM Posts p
    INNER JOIN AnswersCount_view a
    ON <join criteria>
WHERE <home page criteria>

но это обычно приводит INDEX SCANк использованию ресурсов и может быть более эффективным, чем использование READ ISOLATION.

Есть несколько способов снять шкуру с кошки. Преждевременная денормализация схемы базы данных может вызвать проблемы с масштабируемостью.

Парень
источник
3

Вы определенно хотите, чтобы READ_COMMITTED_SNAPSHOT был включен, а по умолчанию это не так. Это дает вам семантику MVCC. Это то же самое, что Oracle использует по умолчанию. Наличие базы данных MVCC настолько невероятно полезно, что НЕ использовать ее - безумие. Это позволяет вам запускать внутри транзакции следующее:

Обновить ПОЛЬЗОВАТЕЛИ Установить FirstName = 'foobar'; // решаем поспать год.

тем временем, не выполняя вышеуказанного, каждый может продолжать выбирать из этой таблицы. Если вы не знакомы с MVCC, вы будете шокированы тем, что когда-либо могли жить без него. Шутки в сторону.

аквинский
источник
3

Установка по умолчанию на чтение незафиксированных - не лучшая идея. Вы, несомненно, внесете несоответствия и в конечном итоге столкнетесь с проблемой, которая хуже той, что у вас есть сейчас. Изоляция снимков может хорошо работать, но это резкое изменение пути Sql Server работает и ставит огромные нагрузку на базу данных tempdb.

Вот что вам следует сделать: используйте try-catch (в T-SQL) для обнаружения состояния взаимоблокировки. Когда это произойдет, просто запустите запрос повторно. Это стандартная практика программирования баз данных.

Хорошие примеры этой техники можно найти в Библии Пола Нильсона по Sql Server 2005 .

Вот быстрый шаблон, который я использую:

-- Deadlock retry template

declare @lastError int;
declare @numErrors int;

set @numErrors = 0;

LockTimeoutRetry:

begin try;

-- The query goes here

return; -- this is the normal end of the procedure

end try begin catch
    set @lastError=@@error
    if @lastError = 1222 or @lastError = 1205 -- Lock timeout or deadlock
    begin;
        if @numErrors >= 3 -- We hit the retry limit
        begin;
            raiserror('Could not get a lock after 3 attempts', 16, 1);
            return -100;
        end;

        -- Wait and then try the transaction again
        waitfor delay '00:00:00.25';
        set @numErrors = @numErrors + 1;
        goto LockTimeoutRetry;

    end;

    -- Some other error occurred
    declare @errorMessage nvarchar(4000), @errorSeverity int
    select    @errorMessage = error_message(),
            @errorSeverity = error_severity()

    raiserror(@errorMessage, @errorSeverity, 1)

    return -100
end catch;    
Эрик Зи Берд
источник
2
почему это решение заставляет меня съеживаться? !! Я бы посмотрел, ПОЧЕМУ есть тупик ... а не лейкопластырь для бедняков по этой проблеме.
Pure.Krome
2

Одна вещь, которая работала для меня в прошлом, - убедиться, что все мои запросы и обновления обращаются к ресурсам (таблицам) в одном и том же порядке.

То есть, если один запрос обновляется в порядке Table1, Table2, а другой запрос обновляет его в порядке Table2, Table1, тогда вы можете увидеть взаимоблокировки.

Не уверен, можно ли изменить порядок обновлений, так как вы используете LINQ. Но посмотреть есть на что.

Майкл Шарек
источник
1

Будете ли вы беспокоиться, если ваш профиль пользователя устарел на несколько секунд?

Несколько секунд определенно было бы приемлемо. В любом случае, не похоже, что это будет так долго, если только огромное количество людей не отправят ответы одновременно.

a_hardin
источник
1

Я согласен с Джереми в этом. Вы спрашиваете, следует ли вам создавать новый контекст данных для каждого контроллера или для каждой страницы - я обычно создаю новый для каждого независимого запроса.

В настоящее время я создаю решение, которое использовалось для реализации статического контекста, как и вы, и когда я бросал тонны запросов на чудовище сервера (более миллиона) во время стресс-тестов, я также случайным образом получал блокировки чтения / записи.

Как только я изменил свою стратегию на использование другого контекста данных на уровне LINQ для каждого запроса и поверил, что SQL-сервер может использовать свою магию пула соединений, блокировки, казалось, исчезли.

Конечно, у меня был некоторый дефицит времени, поэтому я пробовал несколько вещей одновременно, поэтому я не могу быть на 100% уверен, что именно это исправило, но у меня высокий уровень уверенности - давайте скажем так .

РобертTheGrey
источник
1

Вы должны реализовать грязное чтение.

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

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

Это может дать вам так называемое «фантомное чтение», когда ваш запрос воздействует на данные из транзакции, которая не была зафиксирована.

У нас нет банковского сайта, нам не нужна постоянная точность

Используйте грязное чтение. Вы правы в том, что они не дадут вам идеальной точности, но они должны решить ваши проблемы с мертвой блокировкой.

Без этого нам пришлось бы заключать каждый вызов LINQ, который мы делаем (ну, простые считывающие, а это подавляющее большинство из них), в блок кода транзакции из 3-4 строк, что некрасиво

Если вы реализуете грязное чтение в «контексте базовой базы данных», вы всегда можете обернуть свои индивидуальные вызовы, используя более высокий уровень изоляции, если вам нужна транзакционная целостность.

Сейбар
источник
1

Так в чем же проблема с реализацией механизма повтора? Всегда будет возможность возникновения тупика, так почему бы не иметь логики, чтобы определить его и просто попробовать еще раз?

Разве по крайней мере некоторые из других вариантов не будут вводить штрафы за производительность, которые применяются постоянно, когда система повторных попыток срабатывает редко?

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

Джон Дайер
источник
1

Теперь, когда я вижу ответ Джереми, мне кажется, я помню, что слышал, что лучше всего использовать новый DataContext для каждой операции с данными. Роб Конери написал несколько сообщений о DataContext, и он всегда сообщает о них, а не использует синглтон.

Вот шаблон, который мы использовали для Video.Show ( ссылка на исходный код в CodePlex ):

using System.Configuration;
namespace VideoShow.Data
{
  public class DataContextFactory
  {
    public static VideoShowDataContext DataContext()
    {
        return new VideoShowDataContext(ConfigurationManager.ConnectionStrings["VideoShowConnectionString"].ConnectionString);
    }
    public static VideoShowDataContext DataContext(string connectionString)
    {
        return new VideoShowDataContext(connectionString);
    }
  }
}

Затем на уровне обслуживания (или даже более детально, для обновлений):

private VideoShowDataContext dataContext = DataContextFactory.DataContext();

public VideoSearchResult GetVideos(int pageSize, int pageNumber, string sortType)
{
  var videos =
  from video in DataContext.Videos
  where video.StatusId == (int)VideoServices.VideoStatus.Complete
  orderby video.DatePublished descending
  select video;
  return GetSearchResult(videos, pageSize, pageNumber);
}
Джон Гэллоуэй
источник
0

Я должен согласиться с Грегом, если установка уровня изоляции на чтение незафиксированных не оказывает вредного воздействия на другие запросы.

Мне было бы интересно узнать, Джефф, как его установка на уровне базы данных повлияет на такой запрос, как следующий:

Begin Tran
Insert into Table (Columns) Values (Values)
Select Max(ID) From Table
Commit Tran
GateKiller
источник
0

Меня устраивает, если мой профиль даже на несколько минут устарел.

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

Если реализация «READ UNCOMMITTED» не решает вашу проблему, тогда трудно помочь, не зная больше об обработке. Может быть другая настройка, которая поможет в таком поведении. Если на помощь не придет какой-нибудь гуру MSSQL, я рекомендую передать проблему поставщику.

Брюсэтк
источник
0

Я бы и дальше все настраивал; как работает дисковая подсистема? Какова средняя длина дисковой очереди? Если выполняется резервное копирование операций ввода-вывода, реальной проблемой могут быть не эти два запроса, которые находятся в тупике, это может быть другой запрос, который является узким местом системы; вы упомянули настроенный запрос, занимающий 20 секунд, есть ли другие?

Сосредоточьтесь на сокращении длительных запросов, я уверен, что проблемы с тупиками исчезнут.

SqlACID
источник
0

Была та же проблема, и я не мог использовать «IsolationLevel = IsolationLevel.ReadUncommitted» в TransactionScope, потому что на сервере не включен DTS (!).

Вот что я сделал с методом расширения:

public static void SetNoLock(this MyDataContext myDS)
{
    myDS.ExecuteCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED");
}

Итак, для выбранных, использующих критические таблицы параллелизма, мы включаем блокировку следующим образом:

using (MyDataContext myDS = new MyDataContext())
{
   myDS.SetNoLock();

   //  var query = from ...my dirty querys here...
}

Предложения приветствуются!


источник