SELECT / INSERT Deadlock

10

Этот экземпляр размещает базы данных SharePoint 2007 (SP). Мы сталкивались с многочисленными взаимоблокировками SELECT / INSERT с одной интенсивно используемой таблицей в базе данных контента SP. Я сузил задействованные ресурсы, оба процесса требуют блокировки некластеризованного индекса.
Для INSERT требуется блокировка IX для ресурса SELECT, а для SELECT требуется блокировка S для ресурса INSERT. График взаимоблокировки показывает и три ресурса: 1.) два из SELECT (параллельные потоки производителя / потребителя) и 2.) INSERT.
Я приложил график взаимоблокировки для вашего обзора. Поскольку это код Microsoft и структура таблиц, мы не можем вносить никаких изменений.
Однако на сайте MSFT SP я прочитал, что они рекомендуют установить для параметра конфигурации уровня экземпляра MAXDOP значение 1. Поскольку этот экземпляр является общим для многих других баз данных / приложений, этот параметр нельзя отключить.


Поэтому я решил попытаться предотвратить параллельное выполнение этих операторов SELECT. Я знаю, что это не решение, а скорее временная модификация, чтобы помочь с устранением неполадок. Поэтому я увеличил «Пороговое значение стоимости для параллелизма» с наших стандартных 25 до 40 после этого, даже несмотря на то, что рабочая нагрузка не изменилась (SELECT / INSERT происходит часто), мертвые блокировки исчезли. У меня вопрос почему?

SPID 356 INSERT имеет блокировку IX на странице, принадлежащей некластеризованному индексу.
SPID 690 SELECT ID выполнения 0 имеет блокировку S на странице, принадлежащей тому же некластеризованному индексу.

Сейчас же

SPID 356 хочет получить блокировку IX для ресурса SPID 690, но не может ее получить, поскольку SPID 690 блокируется идентификатором выполнения SPID 690 0 S-блокировка
SPID 690 Идентификатор выполнения 1 требует блокировки S для ресурса SPID 356, но не может получить его, поскольку идентификатор выполнения SPID 690 1 блокируется SPID 356, и теперь у нас есть тупик.

План выполнения можно найти на моем SkyDrive

Полная информация о тупике может быть найдена здесь

Если кто-то может помочь мне понять, почему я был бы очень признателен.

Таблица EventReceivers.
Идентификатор 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 нет 512
SourceType int нет 4
Credential int нет 4
ContextType varbinary № 16
ContextEventType varbinary № 16
ContextId varbinary № 16
ContextObjectId varbinary № 16
ContextCollectionId varbinary № 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

SQLJarHead
источник
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
Здравствуйте, господа, я создал zip-пакет со всеми подробностями о SkyDrive. Я изменил тело исходного сообщения, чтобы включить URL для пакета. Пожалуйста, не говорите мне, в чем проблема, просто предоставьте руководство и заставьте меня работать над этим. ПРИМЕЧАНИЕ. Я не могу вносить какие-либо изменения кода или DDL-изменения в базовую схему.
SQLJarHead
1
Итак, вы не можете изменить код и не можете изменить схему. Какие еще решения вы ожидаете от нас? Если вы беспокоитесь о прекращении поддержки Microsoft, то это подразумевает, что у вас есть поддержка Microsoft, и в этом случае - учитывая все ограничения, которые вы сказали нам, что вы не можете сделать - рассматривали ли вы возможность открыть заявку в службу поддержки Microsoft?
Аарон Бертран

Ответы:

14

На первый взгляд, это выглядит как классический тупик поиска . Основными составляющими этого тупикового паттерна являются:

  • SELECTзапрос , который использует , не покрывающий некластерный индекс с ключом Lookup
  • INSERTзапрос , который модифицирует кластерный индекс , а затем некластеризованный индекс

SELECTОбращается к некластеризованный индекс, а затем кластерный индекс. INSERTДоступ кластерного индекса первый, то некластеризованный индекс. Доступ к одним и тем же ресурсам в другом порядке с получением несовместимых блокировок - это, конечно, отличный способ «зайти» в тупик.

В этом случае SELECTзапрос:

ВЫБРАТЬ запрос

... и INSERTзапрос:

Вставить запрос

Обратите внимание на поддержание некластеризованных индексов, выделенных зеленым цветом.

Нам нужно было бы увидеть серийную версию SELECTплана, если она сильно отличается от параллельной версии, но, как отмечает Джонатан Кехайяс в своем руководстве по работе с блокировками , этот конкретный шаблон блокировки очень чувствителен к деталям синхронизации и реализации внутренних запросов. Этот тип тупика часто приходит и уходит без очевидной внешней причины.

Учитывая доступ к соответствующей системе и соответствующие разрешения, я уверен, что в конечном итоге мы сможем точно выяснить, почему возникает тупик с параллельным планом, а не с последовательным (принимая ту же общую форму). Потенциальные направления исследований включают проверку для оптимизации вложенных циклов и / или предварительной выборки - оба из которых могут внутренне перерасти уровень изоляции в REPEATABLE READтечение срока действия заявления. Также возможно, что некоторая особенность параллельного назначения диапазона поиска индекса вносит свой вклад в проблему. Если серийный план станет доступным, я мог бы потратить некоторое время на изучение деталей, поскольку это потенциально интересно.

Обычным решением для этого типа взаимоблокировки является создание покрытия индекса, хотя количество столбцов в этом случае может сделать это нецелесообразным (и, как мне сказали, мы не должны связываться с такими вещами в SharePoint). В конечном счете, рекомендация в отношении планов только для серийных номеров при использовании SharePoint существует по какой-то причине (хотя и не обязательно является хорошей, когда дело доходит до нее). Если изменение порога стоимости для параллелизма решает проблему на данный момент, это хорошо. В более долгосрочной перспективе я бы, вероятно, хотел бы разделить рабочие нагрузки, возможно, с помощью регулятора ресурсов, чтобы внутренние запросы SharePoint получали желаемое MAXDOP 1поведение, а другое приложение могло использовать параллелизм.

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

Пол Уайт 9
источник
6

Если это был классический тупик поиска , список ресурсов будет включать как Кластерный индекс, так и Некластерный индекс. Как правило, SELECT удерживает блокировку SHARED на индексе NC и ожидает блокировку SHARED на CI, в то время как INSERT получает блокировку EXCLUSIVE на CI и ожидает блокировку EXCLUSIVE на NC. В этом случае список ресурсов в взаимоблокировке xml перечислит оба этих объекта.

Поскольку график взаимоблокировки включает только индекс NC, мы можем исключить эту опцию.

Кроме того, если это была мертвая блокировка из-за Nested Loop Join с UNORDERED PREFETCH , план выполнения сообщит нам, используется ли алгоритм UNORDERED PREFETCH, что опять-таки не так (см. Обновление ниже).

Это оставляет нас предполагать, что это тупик из-за параллельного плана.

График взаимоблокировки не отображается должным образом, но если вы посмотрите на XML взаимоблокировки, вы увидите, что два потока из инструкции SELECT (SPID 690) участвуют в взаимоблокировке. Поток потребителя удерживает блокировку SHARED на странице 1219645 и ожидает производителя на порту 801f8ed0 (e_waitPipeGetRow). Поток производителя ожидает общей блокировки на странице 1155940.

Оператор INSERT удерживает блокировку IX на странице 1155940 и ожидает блокировки IX на странице 1219645, что приводит к тупику.

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

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

Видимо, план использует алгоритм OPTIMIZED Nested Loop

Это объясняет, почему SHARED-блокировки удерживаются до конца оператора. REPEATABLE READ в сочетании с параллельным планом является более уязвимым для взаимоблокировки, чем последовательный план, потому что параллельный план может получать и сохранять блокировки от различных диапазонов индекса, тогда как последовательный план получает блокировки более последовательным способом.

Рожи П Томас
источник
Согласовано. Если бы этот тупик был связан с фактическим LOOKUP, ресурс ожидания для SELECT ссылался бы на кластерный индекс. Я смог исключить это, отобразив заголовок страницы для каждого ресурса ожидания (SPID 690 Wait Resource = PAGE: 1155940 | SPID 356 Wait Resource = PAGE 1219645) через DBCC PAGE, и оба были с индексом ID 5 (IndexID 5 = EventReceivers_ByContextObjectId) который указывает на индекс NC в указанной таблице (EventReceivers).
SQLJarHead
Господа, еще раз спасибо, что нашли время помочь проанализировать этот интересный вопрос. Пара вопросов: 1.) Рожи указывает, что параллельный SPID запрашивает более одной страницы. Я не вижу этого ни в одном из планов выполнения. Если посмотреть на число строк, для оператора INDEX SEEK только один поток из двух производителей обрабатывает любые строки. Как вы определили, что он запрашивает более одной страницы? (1/2)
SQLJarHead
2.) Будет ли алгоритм OPTIMIZED Nested Loop всегда устанавливать уровень изоляции REAPTABLE READ? Я проверил выходные данные XML планов выполнения и вижу только чтение, зафиксированное для соединения SPID. Я предполагаю, что это вызывается только на уровне оператора плана. (2/2)
SQLJarHead
Поведение блокировки OPTIMIZED Nested Loops сравнимо с REPEATABLE READ (удерживает блокировки до конца оператора ), но не дает явного задания уровня изоляции транзакции REPEATABLE READ. Я думаю, что это тоже отвечает на ваш вопрос. Дело не в том, что параллельные потоки запрашивают более одной страницы за раз, но один параллельный поток удерживает блокировку на одной странице, а другой поток ожидает блокировку на другой странице
Roji P Thomas