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