Диагностика взаимоблокировок в SQL Server 2005 - PullRequest
82 голосов
/ 21 августа 2008

Мы видим некоторые пагубные, но редкие тупиковые условия в базе данных 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;
    }
}

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

Ответы [ 22 ]

0 голосов
/ 21 августа 2008

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

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

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

0 голосов
/ 21 августа 2008

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

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

Begin Tran
Insert into Table (Columns) Values (Values)
Select Max(ID) From Table
Commit Tran
...