Мы видим некоторые пагубные, но редкие тупиковые условия в базе данных 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;
}
}
Рекомендуете ли мы создавать новый контекст для каждого Контроллера, или для Страницы, или ... чаще?