У базі даних 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 до об'єкта 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;
}
}
Чи рекомендуєте ми створювати новий контекст для кожного контролера, або для кожної сторінки, або ... частіше?