Длинный запрос выполняется с помощью оптимизированных для памяти таблиц с ежедневным архивированием данных в реальном времени - PullRequest
0 голосов
/ 02 декабря 2018

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

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

Сценарий, который изначально выполнял этот процесс, начал давать сбой.Он менял Serializable изоляцию из Snapshot в режиме ReadCommitted, но делал полное сканирование таблицы, поэтому блокировал все операции вставки на 1-2 часа, что было неприемлемо.

Вопрос (ы)
Болевая точка для архивирования заключается в том, что нам нужно обернуть вставку и удалить вместе в одной транзакции.Есть ли лучший способ сделать ниже?Лучше не иметь внешних ключей и использовать триггер или ограничение?Приведенные ниже сценарии либо блокируют таблицу слишком долго, либо выполняются слишком долго (1-4 часа).

Сценарий

Четыре таблицы SQL

  1. Order (таблица, оптимизированная для памяти)
  2. OrderDetail (таблица, оптимизированная для памяти)
  3. OrderHistory (файл / обычная таблица - индекс ColumnStore)
  4. OrderDetailHistory (файл / обычная таблица - индекс ColumnStore)

Таблицы Order и OrderDetail получают в среднем массовую вставку между 50-700 вставками в секунду.

Обновления выполняются только во время операции пакетной вставки.

В течение дня таблица заказов может содержать от 3 до 6 миллионов записей.Таблица OrderDetail может быть в 2-3 раза больше, чем таблица Order.

OrderHistory и OrderDetailHistory могут иметь данные за 1-7 дней, поэтому может варьироваться от 10 до 50 миллионов записей в любой точке.

OrderDetail имеет ссылку FK на Order в столбце Id.

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

Попытка 1

Сценарий, вызывающий проблему, делает следующее:

BEGIN TRAN
INSERT INTO OrderHistory
SELECT * FROM Order o 
WHERE o.CreatedAt <= 1 day

INSERT INTO OrderDetailHistory
SELECT * FROM OrderDetail od
WHERE od.CreatedAt <= 1 day

DELETE FROM Order
WHERE CreatedAt <= 1 day

DELETE FROM OrderDetail
WHERE CreatedAt <= 1 day
COMMIT

База данных запущена с изоляцией моментальных снимковуровень с Read Committed.

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

Попытка 2

Итак, я изменил скрипт, создав дваОптимизированные памятью определяемые пользователем таблицы для выбора данных в первую очередь за пределами транзакции в эти таблицы udf.Затем в отдельной транзакции вставьте в нашу таблицу истории, а затем, наконец, удалите в отдельной транзакции.Идея состоит в том, что, если вставки будут успешными, но удаление не удастся, то при следующем запуске он не будет пытаться повторно вставить данные дважды.Недостатком этого является то, что в данных истории будет дублирование до тех пор, пока он не будет запущен снова, и он работал за 2 часа до истечения времени ожидания нашего инструмента планирования, поэтому это не кажется идеальным.

INSERT INTO @OrderLoadingUdfTable
SELECT * FROM Order o 
WHERE o.CreatedAt <= 1 day

INSERT INTO @OrderDetailLoadingUdfTable
SELECT * FROM OrderDetail od
WHERE od.CreatedAt <= 1 day

BEGIN TRAN
INSERT INTO OrderHistory
SELECT * FROM @OrderLoadingUdfTable  
WHERE CreatedAt <= 1 day
AND Id NOT IN (SELECT Id FROM OrderHistory)

INSERT INTO OrderDetailHistory
SELECT * FROM @OrderDetailLoadingUdfTable 
WHERE CreatedAt <= 1 day
AND Id NOT IN (SELECT Id FROM OrderDetailHistory)
COMMIT

BEGIN TRAN
DELETE FROM Order
WHERE CreatedAt <= 1 day

DELETE FROM OrderDetail
WHERE CreatedAt <= 1 day
COMMIT
...