Добавлено 7/11 . Проблема заключается в возникновении взаимоблокировок из-за сканирования индекса во время MERGE JOIN. В этом случае транзакция пытается получить S-блокировку для всего индекса в родительской таблице FK, но ранее другая транзакция устанавливает X-блокировку для значения ключа индекса.
Позвольте мне начать с небольшого примера (используется база данных TSQL2012 из 70-461):
CREATE TABLE [Sales].[Orders](
[orderid] [int] IDENTITY(1,1) NOT NULL,
[custid] [int] NULL,
[empid] [int] NOT NULL,
[shipperid] [int] NOT NULL,
... )
Столбцы [custid], [empid], [shipperid]
являются соответствующими параметрами [Sales].[Customers], [HR].[Employees], [Sales].[Shippers]
. В каждом случае у нас есть кластеризованный индекс по указанному столбцу в таблице parrent.
ALTER TABLE [Sales].[Orders] WITH CHECK ADD CONSTRAINT [FK_Orders_Customers] FOREIGN KEY([custid]) REFERENCES [Sales].[Customers] ([custid])
ALTER TABLE [Sales].[Orders] WITH CHECK ADD CONSTRAINT [FK_Orders_Employees] FOREIGN KEY([empid]) REFERENCES [HR].[Employees] ([empid])
ALTER TABLE [Sales].[Orders] WITH CHECK ADD CONSTRAINT [FK_Orders_Shippers] FOREIGN KEY([shipperid])REFERENCES [Sales].[Shippers] ([shipperid])
Я пытаюсь использовать INSERT [Sales].[Orders] SELECT ... FROM
другую таблицу, [Sales].[OrdersCache]
которая имеет ту же структуру, что и [Sales].[Orders]
внешние ключи. Еще одна вещь, которая может быть важна, чтобы упомянуть таблицу, [Sales].[OrdersCache]
является кластеризованным индексом.
CREATE CLUSTERED INDEX idx_c_OrdersCache ON Sales.OrdersCache ( custid, empid )
Как и ожидалось, когда я пытаюсь вставить небольшие объемы данных, LOOP JOIN работает нормально, делая поиск индекса по внешним ключам.
При больших объемах данных оптимизатор запросов использует MERGE JOIN как наиболее эффективный способ поддержки ключа foregn в запросе.
И это не имеет никакого отношения, кроме использования OPTION (LOOP JOIN) в нашем случае с внешними ключами или INNER LOOP JOIN в явном случае JOIN.
Ниже приведен запрос, который я пытаюсь выполнить в своей среде:
INSERT Sales.Orders (
custid, empid, shipperid, ... )
SELECT custid, empid, 2, ...
FROM Sales.OrdersCache
Глядя на план, мы видим, что все 3 внешних ключа проверены с помощью MERGE JOIN. Для меня это неподходящий способ, так как он использует INDEX SCAN с полной блокировкой индекса.
Использование OPTION (LOOP JOIN) не подходит, поскольку оно стоит почти на 15% дороже, чем MERGE JOIN (я думаю, регрессия будет больше при увеличении объемов данных).
В операторе SELECT вы можете увидеть одно значение shipperid
атрибута для всего вставленного набора. По моему мнению, должен быть способ сделать фазу проверки для вставленного набора быстрее, по крайней мере, для неизменяемого атрибута. Что-то вроде:
- сделать LOOP JOIN, MERGE JOIN, HASH JOIN, если у нас есть неопределенное подмножество для проверки JOIN
- если есть только одно явное значение проверенного столбца, мы делаем проверку только один раз (INDEX SEEK).
Существует ли какой-либо общий шаблон для преодоления описанной выше ситуации с использованием структур кода, дополнительных объектов DDL и т. Д.?
Добавлено 20/07. Решение. Оптимизатор запросов уже выполняет оптимизацию проверки «один ключ - внешний ключ» с помощью MERGE JOIN. И делает только для таблицы Sales.Shippers, оставляя LOOP JOIN для других объединений в запросе одновременно. Поскольку у меня есть несколько строк в родительской таблице, Query Optimizer использует алгоритм объединения Sort-merge и сравнивает каждую строку во внутренней таблице с родительской таблицей только один раз. Так что это ответ на мой вопрос, есть ли какой-то конкретный механизм для эффективной обработки отдельных значений в наборе во время проверки одного ключа. Это не очень идеальное решение, но именно так SQL Server оптимизирует ситуацию.
Исследование влияния на производительность показало, что в моем случае оператор вставки MERGE JOIN и LOOP JOIN стал примерно равным с 750 одновременно вставленными строками со следующим преимуществом MERGE JOIN (в ресурсе времени ЦП). Поэтому использование OPTION (LOOP JOIN) является подходящим решением для моего бизнес-процесса.