Мы наблюдаем некоторые вредные, но редкие, тупиковые условия в базе данных Qaru 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 для каждой операции или, возможно, вы используете один и тот же статический контекст для всех ваших вызовов?
Джереми, мы разделяем один статический datacontext в базовом контроллере по большей части:
private DBContext _db;
/// <summary>
/// Gets the DataContext to be used by a Request 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;
}
}
Рекомендуете ли вы создавать новый контекст для каждого контроллера или на странице или чаще?