Діагностика тупикових ситуацій у SQL Server 2005


82

У базі даних 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;
    }
}

Чи рекомендуєте ми створювати новий контекст для кожного контролера, або для кожної сторінки, або ... частіше?


2
Який <a href=" en.wikipedia.org/wiki/… режим</a> ви використовуєте, "песимістичний" (на основі блокування) або "оптимістичний" (<a href = " en.wikipedia.org/wiki/ … )?
Джон Сіракуза,

Я погоджуюсь з відповіддю Гая вище - замість того, щоб намагатися обійти симптом, чому б не усунути основні причини? Додавши загальну кількість AnswerCount до таблиці Posts, ви створили потенційний ресурс блокування. Чи хотів би Джефф опублікувати свою ERD для StackOverflow, щоб люди могли критикувати?
andyp

2
Ого - чули про це у своєму подкасті зі Скоттом. Не можу повірити, що він також не поставляється з коробки з кращою конфігурацією. Я покажу це нашим адміністраторам баз даних (оскільки вони теж широко використовують "nolock")
Ден Еспарза,

3
Дивіться: samsaffron.com/archive/2008/08/27/Deadlocked+ з тієї причини, що це зайшло у глухий кут. Увімкнення ізоляції знімків є хорошим рішенням цієї проблеми.
Sam Saffron

Відповіді:


44

За даними MSDN:

http://msdn.microsoft.com/en-us/library/ms191242.aspx

Коли параметри бази даних READ COMMITTED SNAPSHOT або ALLOW SNAPSHOT ISOLATION увімкнені, логічні копії (версії) зберігаються для всіх модифікацій даних, виконаних у базі даних. Кожного разу, коли рядок модифікується певною транзакцією, екземпляр Database Engine зберігає версію раніше зафіксованого образу рядка в tempdb. Кожна версія позначена порядковим номером транзакції транзакції, яка внесла зміни. Версії модифікованих рядків пов'язані ланцюжком за допомогою списку посилань. Найновіше значення рядка завжди зберігається в поточній базі даних і прив'язується до рядків версій, що зберігаються в tempdb.

Для короткочасних транзакцій версія модифікованого рядка може бути кешована в пулі буферів без запису в дискові файли бази даних tempdb. Якщо потреба у рядку версій короткочасна, вона просто випаде з буферного пулу і не обов'язково спричинить накладні витрати на введення-виведення.

Здається, що за додаткові накладні витрати передбачено невеликий штраф, але він може бути незначним. Ми повинні перевірити, щоб переконатися.

Спробуйте встановити цю опцію та ВИДАЛІТЬ усі NOLOCK з запитів коду, якщо це дійсно не потрібно. NOLOCK або використання глобальних методів в обробнику контексту бази даних для боротьби з рівнями ізоляції транзакцій баз даних є смугами для вирішення проблеми. NOLOCKS замаскує основні проблеми з нашим рівнем даних і, можливо, призведе до вибору ненадійних даних, де рішенням буде автоматичний вибір / оновлення версій рядків.

ALTER Database [StackOverflow.Beta] SET READ_COMMITTED_SNAPSHOT ON

3
"NOLOCKS замаскує основні проблеми з нашим рівнем даних" ... Який тип проблем маскує NOLOCK? Якщо я вважаю, що мені потрібен NOLOCK, на які проблеми слід звертати увагу?
Метт Гамільтон,

3
Що з цією відповіддю робить її "відповіддю"? Я досі не розумію, чому прочитане в мілісекундах негайно заблокує запис? Я гадаю, відповідь "user13484" дуже погана, якщо на це так, посилання на неї немає.
RichardTheKiwi

37

NOLOCK і READ UNMMITTED - це слизький схил. Ви ніколи не повинні їх використовувати, якщо не розумієте, чому тупик відбувається першим. Мене хвилювало б, що ви скажете: "Ми додали (nolock) до всіх запитів SQL". Потрібність додавати WITH NOLOCK скрізь є вірною ознакою того, що у вас є проблеми на рівні даних.

Заява про оновлення виглядає дещо проблематично. Ви визначаєте підрахунок раніше в транзакції, або просто витягуєте його з об'єкта? AnswerCount = AnswerCount+1коли додається запитання - це, мабуть, кращий спосіб вирішити це. Тоді вам не потрібна транзакція, щоб отримати правильний підрахунок, і вам не доведеться турбуватися про проблему паралельності, якій ви потенційно піддаєтесь.

Один із простих способів обійти цей тип тупикової ситуації без великої роботи та без увімкнення брудних зчитувань - це використання "Snapshot Isolation Mode"(нове у SQL 2005), яке завжди дасть вам чисте прочитання останніх немодифікованих даних. Ви також можете досить легко ловити та повторювати операції, що зайшли в глухий кут, якщо ви хочете витончено обробити їх.


4
Я з JEzell - перше, на що я поставив нуль, це 'SET AnswerCount = <фіксоване значення>'. Звідки це значення? Це змушує мене задуматися, чи не було ви де-небудь в транзакції витягнуто її таким чином, щоб захопити купу замків. Я б почав із цього. І так, глобальний NOLOCK - це пластир.
Кован

25

Питання ОП полягало у питанні, чому ця проблема виникла. Цей пост сподівається відповісти, що, залишаючи можливі рішення для вироблення іншими.

Можливо, це проблема, пов’язана з індексом. Наприклад, скажімо, таблиця Posts має некластеризований індекс X, який містить ParentID та одне (або більше) полів, що оновлюються (AnswerCount, LastActivityDate, LastActivityUserId).

Виникне тупикова ситуація, якщо cmd SELECT робить блокування спільного читання в індексі X для пошуку за ParentId, а потім потрібно зробити блокування спільного читання в кластерному індексі, щоб отримати решту стовпців, тоді як cmd UPDATE робить ексклюзив для запису блокувати кластерний індекс і потрібно отримати ексклюзивну блокування для індексу X, щоб оновити його.

Тепер у вас ситуація, коли A заблокував X і намагається отримати Y, тоді як B заблокував Y і намагається отримати X.

Звичайно, нам знадобиться ОП, щоб оновити його публікацію додатковою інформацією про те, які індекси знаходяться в грі, щоб підтвердити, чи це насправді причина.


Я погоджуюсь з цим аналізом - SELECT та UPDATE обробляють рядки в іншому порядку, тому кожен намагається отримати блокування рядка, яке має інший.
Mike Dimmick

Це найкраща відповідь на весь цей потік і дає єдине пояснення, чому насправді відбувається тупиковий стан. На жаль, це не відповідь №1, оскільки вона найкраща тут.
Джонатан Кехаяяс

У деяких відповідях не вистачає суті про те, що замок знаходиться між двома простими атомними твердженнями. Це єдиний пост, який намагається це пояснити. Незважаючи на те, що твердження просте, оновлення таблиці може включати кілька оновлень CIX та NCIX, що розширюється на багато операцій. Те саме для READ, що включає обхід NCIX, пошук закладок CIX. Це не має нічого спільного із об’єднанням таблиць у тому самому порядку тощо (чи читають люди запитання ??)
RichardTheKiwi

18

Мені досить незручно з приводу цього питання та відповідей супровідника. Існує багато "спробуйте цей чарівний пил! Ні, той чарівний пил!"

Я ніде не бачу, що ви аналізували замки, які вибираєте, і визначали, який саме тип замків зайшов у глухий кут.

Все, що ви вказали, - це те, що трапляються деякі замки, а не те, що забиває шлях.

У SQL 2005 ви можете отримати більше інформації про те, які замки виймаються, використовуючи:

DBCC TRACEON (1222, -1)

так що, коли настане глухий кут, ви будете мати кращу діагностику.


13
Тупик негайно обробляється монітором блокування в SQL Server. DMV марні для усунення тупикової ситуації, оскільки жертва буде обрана та вбита до того, як ви зможете помітити її появу.
Джонатан Кехаяяс

14

Ви створюєте екземпляр нового LINQ до об'єкта SQL DataContext для кожної операції або, можливо, ви використовуєте однаковий статичний контекст для всіх своїх дзвінків? Спочатку я спробував останній підхід, і, наскільки пам’ятаю, це спричинило небажане блокування в БД. Тепер я створюю новий контекст для кожної атомної операції.


10

Перш ніж спалити будинок, щоб упіймати муху за допомогою NOLOCK, ви можете захотіти поглянути на той графік глухого кута, який ви мали зафіксувати за допомогою Profiler.

Пам'ятайте, що для тупикової ситуації потрібно (принаймні) 2 замки. З’єднання 1 має Блокування А, хоче Блокування В - і навпаки для З’єднання 2. Це нерозв’язна ситуація, і хтось повинен дати.

Те, що ви показали на сьогоднішній день, вирішується простим блокуванням, яким Sql Server із задоволенням займається протягом усього дня.

Я підозрюю, що ви (або LINQ) починаєте транзакцію з цим оператором UPDATE і вибираєте якусь іншу інформацію перед рукою. Але вам дійсно потрібно повернутися до графіка тупикових ситуацій, щоб знайти замки, утримувані кожним потоком, а потім повернутися через Profiler, щоб знайти оператори, які спричинили надання цих блокувань.

Я сподіваюся, що для заповнення цієї головоломки існує принаймні 4 твердження (або твердження, яке займає кілька блокувань - можливо, у таблиці повідомлень є тригер?).


7

Вам буде все одно, чи ваш профіль користувача застарів на кілька секунд?

Ні - це цілком прийнятно. Встановлення базового рівня ізоляції транзакцій - це, мабуть, найкращий / найчистіший шлях.


5

Типовий глухий кут читання / запису походить від доступу до порядку індексу. Read (T1) знаходить рядок за індексом A, а потім шукає прогнозований стовпець за індексом B (зазвичай кластеризованим). Запис (T2) змінює індекс B (кластер), а потім повинен оновити індекс A. T1 має S-Lck на A, хоче S-Lck на B, T2 має X-Lck на B, хоче U-Lck на A. Deadlock , затяжка. Т1 вбито. Це поширене в середовищах з великим OLTP-трафіком і лише трохи забагато індексів :). Рішення полягає в тому, щоб або зчитування не повинно переходити з A на B (тобто включений стовпець в A, або видаляти стовпець із прогнозованого списку), або T2 не переходити з B на A (не оновлювати індексований стовпець). На жаль, Linq тут вам не друг ...


BTW A і B є індексами однієї таблиці
Ремус Русану,

3

@Jeff - Я, безумовно, не фахівець у цьому, але мав хороші результати, створюючи новий контекст майже на кожному дзвінку. Я думаю, це схоже на створення нового об’єкта Connection під час кожного дзвінка за допомогою ADO. Накладні витрати не такі погані, як ви могли б подумати, оскільки пул з'єднань все одно все одно буде використовуватися.

Я просто використовую глобальний статичний помічник, такий:

public static class AppData
{
    /// <summary>
    /// Gets a new database context
    /// </summary>
    public static CoreDataContext DB
    {
        get
        {
            var dataContext = new CoreDataContext
            {
                DeferredLoadingEnabled = true
            };
            return dataContext;
        }
    }
}

а потім я роблю щось подібне:

var db = AppData.DB;

var results = from p in db.Posts where p.ID = id select p;

І я б зробив те саме для оновлень. У будь-якому випадку, у мене немає майже стільки трафіку, як у вас, але я точно отримував певний замок, коли рано використовував спільний DataContext лише з декількома користувачами. Ніяких гарантій, але, можливо, варто спробувати.

Оновлення : Знову ж таки, дивлячись на свій код, ви ділитесь лише контекстом даних протягом усього життя цього конкретного екземпляра контролера, що, в основному, здається чудовим, якщо він якимось чином не використовується одночасно за допомогою безлічі викликів у контролері. У темі на тему, Скотгу сказав:

Контролери живуть лише для одного запиту - тому в кінці обробки запиту вони збирають сміття (що означає, що DataContext збирається) ...

Так чи інакше, це може бути не це, але знову ж таки, мабуть, варто спробувати, можливо, у поєднанні з деяким тестуванням навантаження.


3

З. Чому ви спочатку зберігаєте AnswerCountв Postsтаблиці?

Альтернативним підходом є усунення "зворотного перепису" до Postsтаблиці, не зберігаючи AnswerCountв таблиці, а динамічно обчислюючи кількість відповідей на допис, як потрібно.

Так, це означатиме, що ви запускаєте додатковий запит:

SELECT COUNT(*) FROM Answers WHERE post_id = @id

або більш типово (якщо ви відображаєте це для домашньої сторінки):

SELECT p.post_id, 
     p.<additional post fields>,
     a.AnswerCount
FROM Posts p
    INNER JOIN AnswersCount_view a
    ON <join criteria>
WHERE <home page criteria>

але це, як правило, призводить до INDEX SCANі може бути ефективнішим у використанні ресурсів, ніж використання READ ISOLATION.

Існує не один спосіб зняти шкіру з кота. Передчасна денормалізація схеми бази даних може спричинити проблеми масштабованості.


3

Ви точно хочете, щоб для параметра READ_COMMITTED_SNAPSHOT було ввімкнено, що не є типовим. Це дає вам семантику MVCC. Це те саме, що Oracle використовує за замовчуванням. Наявність бази даних MVCC настільки неймовірно корисна, НЕ використовувати її - божевільно. Це дозволяє запустити всередині транзакції:

Оновити USERS Set FirstName = 'foobar'; // вирішити спати рік.

тим часом, не здійснюючи вищезазначене, кожен може продовжувати обирати з цієї таблиці просто чудово. Якщо ви не знайомі з MVCC, ви будете вражені тим, що змогли прожити без нього. Серйозно.


3

Налаштування за замовчуванням нечитаного читання - це не гарна ідея. Ви, безсумнівно, внесете невідповідності і в кінцевому підсумку отримаєте проблему, яка є гіршою, ніж у вас зараз. Ізоляція знімків може добре спрацювати, але це кардинальна зміна способу роботи Sql Server і ставить величезні навантаження на tempdb.

Ось що вам слід зробити: використовуйте функцію try-catch (у T-SQL), щоб виявити умову блокування. Коли це станеться, просто повторно запустіть запит. Це стандартна практика програмування баз даних.

Є хороші приклади цієї техніки в Біблії Sql Server 2005 Пола Нілсона .

Ось короткий шаблон, який я використовую:

-- Deadlock retry template

declare @lastError int;
declare @numErrors int;

set @numErrors = 0;

LockTimeoutRetry:

begin try;

-- The query goes here

return; -- this is the normal end of the procedure

end try begin catch
    set @lastError=@@error
    if @lastError = 1222 or @lastError = 1205 -- Lock timeout or deadlock
    begin;
        if @numErrors >= 3 -- We hit the retry limit
        begin;
            raiserror('Could not get a lock after 3 attempts', 16, 1);
            return -100;
        end;

        -- Wait and then try the transaction again
        waitfor delay '00:00:00.25';
        set @numErrors = @numErrors + 1;
        goto LockTimeoutRetry;

    end;

    -- Some other error occurred
    declare @errorMessage nvarchar(4000), @errorSeverity int
    select    @errorMessage = error_message(),
            @errorSeverity = error_severity()

    raiserror(@errorMessage, @errorSeverity, 1)

    return -100
end catch;    

2
чому це рішення змушує мене журитися? !! я б розглядав ЧОМУ тупик .. не пластир для бідних людей над цією проблемою.
Pure.Krome

2

Одне, що працювало у мене в минулому, - це переконатися, що всі мої запити та оновлення отримують доступ до ресурсів (таблиць) в однаковому порядку.

Тобто, якщо один запит оновлюється в порядку Таблиця1, Таблиця2, а інший запит оновлює його в порядку Таблиці2, Таблиця1, тоді Ви можете побачити тупикові ситуації.

Не впевнений, чи можливо вам змінити порядок оновлень, оскільки ви використовуєте LINQ. Але на це є щось подивитися.


1

Вам буде все одно, чи ваш профіль користувача застарів на кілька секунд?

Кілька секунд, безумовно, будуть прийнятними. Здається, у будь-якому випадку це було б не так довго, якщо тільки величезна кількість людей не подає відповіді одночасно.


1

Я згоден з Джеремі в цьому. Ви запитуєте, чи слід створювати новий контекст даних для кожного контролера або для кожної сторінки - я схильний створювати новий для кожного незалежного запиту.

В даний час я будую рішення, яке раніше реалізовувало статичний контекст, як ви, і коли я кидав тонни запитів на звіра сервера (мільйон +) під час стрес-тестів, я також отримував блокування читання / запису випадковим чином.

Як тільки я змінив свою стратегію використання іншого контексту даних на рівні LINQ для кожного запиту, і довірився, що сервер SQL може працювати за допомогою магії об'єднання з'єднань, блокування, здавалося, зникли.

Звичайно, я зазнав певного тиску, тому намагався здійснити кілька речей одночасно, тож я не можу бути на 100% впевненим, що саме це виправило, але я маю високий рівень впевненості - скажімо так .


1

Ви повинні реалізувати брудні читання.

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

Якщо вам абсолютно не потрібна ідеальна цілісність транзакцій з вашими запитами, вам слід використовувати брудні зчитування при доступі до таблиць з високою одночасністю. Я припускаю, що ваша таблиця повідомлень буде однією з таких.

Це може дати вам так зване "фантомне читання", коли ваш запит діє на дані транзакції, яка не була здійснена.

Ми не працюємо тут з банківським сайтом, нам не потрібна ідеальна точність кожного разу

Використовуйте брудні зчитування. Ви маєте рацію в тому, що вони не дадуть вам ідеальної точності, але вони повинні прояснити ваші проблеми з блокуванням.

Без цього нам довелося б обернути кожен зроблений нами виклик LINQ (ну, прості для читання, а це переважна більшість) у 3-4-рядковий блок коду транзакції, що негарно

Якщо ви реалізуєте брудні читання в "контексті базової бази даних", ви завжди можете обернути свої окремі виклики, використовуючи більш високий рівень ізоляції, якщо вам потрібна цілісність транзакції.


1

То в чому проблема впровадження механізму повторної спроби? Завжди існує можливість виникнення тупикової ситуації, то чому б не мати певної логіки, щоб її визначити і просто спробувати ще раз?

Чи не принаймні деякі інші варіанти запровадять покарання за продуктивність, які застосовуються постійно, коли система повторної спроби вводиться рідко?

Крім того, не забувайте про якісь журнали, коли трапляється повторна спроба, щоб ви не потрапляли в ситуацію рідкісних випадків.


1

Тепер, коли я бачу відповідь Джеремі, мені здається, я пам’ятаю, що почув, що найкращою практикою є використання нового DataContext для кожної операції з даними. Роб Конері написав декілька постів про DataContext, і він завжди повідомляє їх, а не використовує синглтон.

Ось шаблон, який ми використовували для Video.Show ( посилання на перегляд джерела в CodePlex ):

using System.Configuration;
namespace VideoShow.Data
{
  public class DataContextFactory
  {
    public static VideoShowDataContext DataContext()
    {
        return new VideoShowDataContext(ConfigurationManager.ConnectionStrings["VideoShowConnectionString"].ConnectionString);
    }
    public static VideoShowDataContext DataContext(string connectionString)
    {
        return new VideoShowDataContext(connectionString);
    }
  }
}

Тоді на рівні сервісу (або навіть більш детальний, для оновлень):

private VideoShowDataContext dataContext = DataContextFactory.DataContext();

public VideoSearchResult GetVideos(int pageSize, int pageNumber, string sortType)
{
  var videos =
  from video in DataContext.Videos
  where video.StatusId == (int)VideoServices.VideoStatus.Complete
  orderby video.DatePublished descending
  select video;
  return GetSearchResult(videos, pageSize, pageNumber);
}

0

Я повинен був би погодитися з Грегом до тих пір, поки встановлення рівня ізоляції для читання незв’язаних не має жодних негативних наслідків для інших запитів.

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

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

0

Зі мною добре, якщо мій профіль навіть на кілька хвилин застарів.

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

Якщо реалізація "READ UNMOMMITTED" не вирішить вашу проблему, тоді важко допомогти, не знаючи набагато більше про обробку. Може існувати якийсь інший варіант налаштування, який би допоміг цій поведінці. Якщо якийсь гуру MSSQL не прийде на допомогу, я рекомендую надіслати проблему постачальнику.


0

Я продовжував би все налаштовувати; як працює дискова підсистема? Яка середня довжина черги диска? Якщо резервні копії вводу-виводу виконуються, справжньою проблемою можуть бути не два ці запити, які зайшли у глухий кут, можливо, це ще один запит, який створює вузькі місця для системи; Ви згадали про запит, який займає 20 секунд і який уже налаштований, чи є інші?

Зосередьтеся на скороченні довготривалих запитів, я впевнений, що проблеми з тупиком зникнуть.


0

Мав ту саму проблему, і не може використовувати "IsolationLevel = IsolationLevel.ReadUncommitted" у TransactionScope, оскільки на сервері не ввімкнено DTS (!).

Ось що я зробив із методом розширення:

public static void SetNoLock(this MyDataContext myDS)
{
    myDS.ExecuteCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED");
}

Отже, для тих, хто використовує критичні таблиці паралельності, ми вмикаємо "nolock" таким чином:

using (MyDataContext myDS = new MyDataContext())
{
   myDS.SetNoLock();

   //  var query = from ...my dirty querys here...
}

Пропозиції вітаються!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.