Вставьте, если не существует в RCSI - PullRequest
0 голосов
/ 13 марта 2020

У меня есть база данных с READ_COMMITTED_SNAPSHOT_ISOLATION, установленной в ON (не могу изменить это).

Я вставляю новые строки в таблицу на многих параллельных сеансах, но только если их там еще нет (classi c) проверка левого соединения).

Код вставки выглядит следующим образом:

INSERT INTO dbo.Destination(OrderID)
SELECT DISTINCT s.OrderID
FROM dbo.Source s
LEFT JOIN dbo.Destination d ON d.OrderID = s.OrderID
WHERE d.OrderID IS NULL;

Если я запускаю это во многих параллельных сеансах, я получаю много повторяющихся ошибок ключа, так как разные сеансы пытаются вставить одни и те же идентификаторы OrderID снова и снова.

Это ожидается из-за отсутствия блокировок SHARED в RCSI.

Здесь (согласно моим исследованиям) рекомендуется использовать подсказку READCOMMITTEDLOCK. как это:

LEFT JOIN dbo.Destination d WITH (READCOMMITTEDLOCK) ON d.OrderID = s.OrderID

Это несколько работает, так как значительно уменьшает ошибки дубликата ключа, но (к моему удивлению) не устраняет их полностью.

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

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

Я пробовал разные подсказки (SERIALIZABLE), но это сделало его хуже и скопилось меня с тупиками.

Как я могу заставить эту вставку работать под RCSI?

1 Ответ

3 голосов
/ 13 марта 2020

Правильная подсказка блокировки для чтения таблицы, в которую вы собираетесь вставить, - это (UPDLOCK, HOLDLOCK), которая будет устанавливать блокировки U в строках по мере их чтения, а также устанавливать блокировки диапазона в стиле SERIALIZABLE, если строка не ' t существует.

Проблема вашего подхода заключается в том, что каждый клиент пытается вставить пакет строк, и каждый пакет должен либо полностью завершиться успешно, либо завершиться неудачно. Если вы используете блокировку на уровне строк, у вас всегда будет сценарий ios, в котором сеанс успешно вставляет одну строку, но затем блокируется в ожидании чтения или вставки следующей строки. Это неизбежно приводит к сбоям или тупикам PK в зависимости от типа используемой блокировки строк.

Решение состоит в том, чтобы:

1) вставлять строки по одной, а не удерживайте блокировки одной строки во время проверки и вставьте следующую строку.

2) Просто перейдите к tablockx или Applciation Lock , чтобы заставить ваши одновременные сеансы сериализоваться через этот бит код.

Таким образом, вы можете иметь высококонкурентные или пакетные загрузки, но вы не можете иметь и то, и другое. Ну в основном.

3) Вы можете включить IGNORE_DUP_KEY в индексе, который вместо ошибки просто пропустит любой дубликат при вставке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...