ВИБІР / ВСТАВЛЕННЯ Тупик


10

Цей примір розміщує бази даних SharePoint 2007 (SP). Ми зазнали численних тупиків SELECT / INSERT проти однієї широко використовуваної таблиці в базі даних вмісту SP. Я звузив залучені ресурси, обидва процеси вимагають блокування некластеризованого індексу.
INSERT потребує блокування IX на ресурсі SELECT, а SELECT - замок S на ресурсі INSERT. Графік глухого кута зображує і три ресурси, 1.) два з SELECT (паралельні потоки виробник / споживач), і 2.) ВСТАВКА.
Я додав для Вашого огляду графік тупикової ситуації. Оскільки це структура коду та таблиці Microsoft, ми не можемо вносити жодних змін.
Однак я прочитав на сайті MSFT SP, що вони рекомендують встановити параметр конфігурації рівня MAXDOP для інстанцій на 1. Оскільки цей екземпляр поділяється серед багатьох інших баз даних / програм, цей параметр неможливо відключити.


Тому я вирішив спробувати не допустити паралельних цих операцій SELECT. Я знаю, що це не рішення, а більше тимчасова модифікація для вирішення проблем. Таким чином, після цього я збільшив "поріг витрат для паралелізму" з нашого стандарту 25 до 40, навіть якщо навантаження не змінилося (SELECT / INSERT часто трапляються), тупики зникли. Моє питання чому?

SPID 356 INSERT має IX блокування на сторінці, що належить некластеризованому індексу
SPID 690 SELECT Ідентифікатор виконання 0 має S блокування на сторінці, що належить тому ж некластеризованому індексу

Тепер

SPID 356 хоче заблокувати IX на ресурсі SPID 690, але не може його досягти, оскільки SPID 356 блокується SPID 690 Ідентифікатор виконання 0 Блокування
SPID 690 Ідентифікатор виконання 1 вимагає блокування S на ресурсі SPID 356, але не може отримати його, оскільки SPID 690 Ідентифікатор виконання 1 блокується SPID 356, і тепер у нас є глухий кут.

План виконання можна знайти на моєму SkyDrive

Повні відомості про тупик можна знайти тут

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

Таблиця приймачів подій
Ідентифікатор UniqueIdentifier № 16
Назви NVARCHAR немає 512
SiteId UniqueIdentifier немає 16
WEBID UniqueIdentifier немає 16
HOSTID UniqueIdentifier немає 16
HostType Int № 4
ItemId Int № 4
DIRNAME NVARCHAR немає 512
LeafName NVARCHAR № 256
Типу INT № 4
SequenceNumber INT № 4
Складання NVARCHAR немає 512
Класу NVARCHAR № 512
Дані nvarchar № 512
Фільтр nvarchar № 512
SourceId tContentTypeId no 512
SourceType int no 4
Credential int no 4
ContextType varbinary no 16
ContextEventType varbinary no 16
ContextId varbinary no 16
ContextObjectId varbinary no 16
ContextCollectionId varbinary no 16

index_name index_description index_keys
EventReceivers_ByContextCollectionId некластерізованний розташований на PRIMARY SiteId, ContextCollectionId
EventReceivers_ByContextObjectId NONCLUSTERED розташований на PRIMARY SiteId, ContextObjectId
EventReceivers_ById NONCLUSTERED, унікальний , розташований на PRIMARY SiteId, Id
EventReceivers_ByTarget кластерний, унікальний , розташований на PRIMARY SiteId, WEBID, НомерУзла, HostType, тип, ContextCollectionId, ContextObjectId, ContextId, ContextType, ContextEventType, SequenceNumber, Assembly, Class
EventReceivers_IdUnique некластеризований, унікальний, унікальний ключ, розміщений на PRIMARY Id


2
Що робити proc_InsertEventReceiverта proc_InsertContextEventReceiverробити, що ми не можемо побачити у XDL? Крім того, щоб зменшити паралелізм, чому б не просто вплинути на цей вислів безпосередньо (використовуючи MAXDOP 1), а не ф'юшинг із налаштуваннями для всього сервера?
Аарон Бертран

1
Мені цікаво, які ваші великі налаштування MAXDOP і скільки у вас процесорів (логічних). SharePoint дійсно працює краще і вважає за краще бути на сервері з MAXDOP-сервером у ширину 1 .. Мені це не подобається, але це те, як вони його розробили. Чи можете ви розмістити фактичні плани виконання? Все, що я бачу за цим посиланням, - це .xdl (графік тупикової ситуації)
Майк Уолш,

Здрастуйте, панове, я дуже вдячний, що ви витратили час, поза своїм напруженим графіком, щоб відповісти. Я опублікую як процедури, так і плани виконання Вашого огляду на сайті SkyDrive. Я думав над тим, щоб змінити код, щоб він включив параметр запиту MAXDOP (1), однак це робить недійсною нашу підтримку з Microsoft. Фізичний сервер ProLiant DL580 G4 MAXDOP налаштування 4, загалом 8 фізичних процесорів і H / T вимкнено.
SQLJarHead

Привіт, панове, я створив поштовий пакет із усіма подробицями на SkyDrive. Я змінив тіло оригінальної публікації, щоб включити URL-адресу пакету. Будь ласка, не кажіть мені, у чому проблема, просто дайте вказівки та змусьте мене працювати над цим. ПРИМІТКА. Я не в змозі вносити будь-які зміни коду чи зміни DDL в основні схеми.
SQLJarHead

1
Отже, ви не можете змінити код і не можете змінити схему, які ще рішення ви очікуєте від нас? Якщо ви турбуєтесь про те, щоб не втратити підтримку Microsoft, то це означає, що у вас є підтримка Microsoft, і в цьому випадку - враховуючи всі обмеження, які ви нам сказали, що не можете робити - чи розглядали ви відкриття квитка підтримки у Microsoft?
Аарон Бертран

Відповіді:


14

Начебто це виглядає як класичний тупик пошуку . Основними інгредієнтами цієї моделі тупикової ситуації є:

  • SELECTзапит , який використовує , не покриває некластерние індекс з ключем Lookup
  • INSERTзапит , який модифікує кластерний індекс , а потім некластерізованний індекс

Спочатку SELECTотримує доступ до некластеризованого індексу, потім кластерного індексу. INSERTДоступ кластерного індексу перший, то некластерізованний індекс. Доступ до тих самих ресурсів в іншому порядку, придбання несумісних блокувань - це чудовий спосіб «досягти» тупикової ситуації, звичайно.

У цьому випадку SELECTзапит:

ВИБРАТИ запит

... і INSERTзапит:

ВСТАВИТИ запит

Зауважте зелене виділення некластеризованих індексів.

Ми повинні були б побачити серійну версію цього SELECTплану в разі , якщо вона сильно відрізняється від паралельної версії, але , як Джонатан Kehayias відзначає в своєму керівництві до Handling Дозволи блокувань , даний шаблон тупикового дуже чутливий до часу і внутрішнім виконання запиту деталей реалізації. Цей тип глухого кута часто настає і проходить без очевидних зовнішніх причин.

З огляду на доступ до відповідної системи та відповідні дозволи, я впевнений, що врешті-решт ми зможемо розібратися, чому саме тупикова ситуація відбувається з паралельним планом, але не з послідовним (якщо мати однакову загальну форму). Потенційні рядки запиту включають перевірку на оптимізовані вкладені петлі та / або попереднє вилучення - обидва вони можуть внутрішньо підвищити рівень ізоляції до REPEATABLE READтривалості оператора. Можливо також, що якась особливість паралельного присвоєння діапазону пошуку індексу сприяє проблемі. Якщо серійний план стане доступним, я можу провести деякий час, вивчаючи деталі, оскільки це потенційно цікаво.

Звичайним рішенням для цього типу тупикових ситуацій є створення індексу, хоча кількість стовпців у цьому випадку може зробити це непрактичним (і, крім того, ми не повинні возитися з такими речами на SharePoint, як мені кажуть). Зрештою, рекомендація щодо планів, що використовуються лише для серійних версій під час використання SharePoint, є з причини (хоча і не обов'язково, якщо це доходить до неї прямо). Якщо зміна порогу витрат на паралелізм вирішує питання на даний момент, це добре. Більш тривалий термін я, мабуть, хотів би розділити навантаження, можливо, використовуючи Resource Governor, щоб внутрішні запити SharePoint отримували потрібну MAXDOP 1поведінку, а інша програма могла використовувати паралелізм.

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


6

Якщо це був класичний тупик пошуку , список ресурсів включатиме як кластерний індекс, так і некластеризований індекс. Зазвичай SELECT утримуватиме БІЗНИЙ замок на індексі NC та чекатиме БІЛОГО блокування на CI, тим часом INSERT придбає ЕКСКЛЮЗИВНИЙ замок на CI та чекатиме ЕКСКЛЮЗИВНОГО блокування на NC. У списку ресурсів у тупику xml в цьому випадку будуть перераховані обидва ці об’єкти.

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

Крім того, якщо це було мертвим блокуванням через з'єднання Nested Loop з UNORDERED PREFETCH , план виконання покаже нам, чи використовується алгоритм UNORDERED PREFETCH, що тут знову не так (див. Оновлення нижче).

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

Графік тупикового коду не відображається належним чином, але якщо ви подивитеся на XML Deadlock, то видно, що два тупи з оператора SELECT (SPID 690) задіяні в тупиковому стані. Споживча нитка тримає БЕЗПЕЧНИЙ замок на СТОРІНКІ 1219645 і очікує на виробника на порту801f8ed0 (e_waitPipeGetRow). Нитка виробника чекає спільного блокування на СТОРІНКІ 1155940.

Оператор INSERT тримає блокування IX на СТОРІНКІ 1155940 і чекає блокування IX на СТОРІНКІ 1219645, що призводить до тупикової ситуації.

Я вірю, що під час використання послідовного плану для оператора SELECT послідовний план буде запобіжено, оскільки в жодному разі він не потребуватиме блокування SHARED на більш ніж одній сторінці. Я також думаю, що послідовний план буде майже таким же, як паралельний план (без оператора паралелізму).

[ОНОВЛЕНО на основі коментаря Павла]

Мабуть, план використовує алгоритм OPTIMIZED Nested Loop

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


Домовились. Якби цей тупик був пов'язаний з фактичним LOOKUP, ресурс очікування для SELECT посилався б на кластерний індекс. Я зміг виключити це, відображаючи заголовок сторінки для кожного ресурсу очікування (SPID 690 Wait Resource = PAGE: 1155940 | SPID 356 Resource Wait = PAGE 1219645) через DBCC PAGE, і обидва були на індексі ID 5 (IndexID 5 = EventReceivers_ByContextObjectId) який вказує на індекс NC у зазначеній таблиці (EventReceivers).
SQLJarHead

Панове, ще раз дякую, що знайшли час, щоб допомогти проаналізувати це цікаве питання. Кілька питань: 1.) Роджі зазначає, що паралельний SPID вимагає більше однієї сторінки. Я цього не бачу в жодному із планів виконання. Дивлячись на число рядків, для оператора INDEX SEEK лише одна нитка з двох виробників обробляє будь-які рядки. Як ви визначили, що він вимагає більше однієї сторінки? (1/2)
SQLJarHead

2.) Чи завжди алгоритм OPTIMIZED Nested Loop встановлюватиме рівень ізоляції на READTABLE READ? Я перевірив вихід XML планів виконання, і я бачу лише читання, здійснене для з'єднання SPID. Я роблю припущення, що це викликається лише на рівні оператора плану. (2/2)
SQLJarHead

Поведінка блокування OPTIMIZED Nested Loops порівнянна з REPEATABLE READ (зберігає блокування до кінця оператора ), але це не чіткість встановлює рівень ізоляції транзакції на REPEATABLE READ. Я думаю, що це теж відповідає на ваше запитання. Справа не в тому, що паралельні потоки запитують більше однієї сторінки одночасно, але одна паралельна нитка тримає замок на одній сторінці, а інша нитка чекає блокування на іншій сторінці
Roji P Thomas
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.