Транзакции становятся медленнее в SQL Server

Мы используем модель полного восстановления в SQL Server. У нас есть задание, которое объединяет промежуточную таблицу с финальной. Промежуточная таблица содержит миллионы строк. Финальная таблица также огромна с миллионами строк. Мы объединяем партии по 10 000 строк.

Ниже приведен псевдокод для одной партии:

BEGIN TRANSACTION

DELETE TOP 10000 * 
FROM <Staging Table> 
OUTPUT deleted.* INTO @TableVariable

MERGE INTO <Final Table> 
USING @TableVariable

COMMIT TRANSACTION

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

Есть ли способ улучшить производительность такого пакетного удаления и объединения? Вы рекомендуете использовать CHECKPOINT для принудительного использования модели полного восстановления?


person Venkataraman R    schedule 11.08.2017    source источник
comment
Просто мысль; присоединение огромной таблицы к табличной переменной может быть не лучшим путем, поскольку оно всегда будет обрабатывать табличную переменную так, как будто она содержит 1 строку.   -  person Xedni    schedule 11.08.2017
comment
Я понимаю. когда мы делаем пакеты, мы загружаем каждые 10 000 строк в табличную переменную и используем то же самое для слияния. В первый раз это быстро и становится все медленнее по мере продвижения каждой партии. Мой вопрос в том, как избежать замедления будущих партий.   -  person Venkataraman R    schedule 11.08.2017


Ответы (2)


Операции слияния часто можно улучшить, избегая лишних обновлений. Если обновлять нечего, поскольку целевая и исходная строки равны, не обновляйте строку. Это очень эффективно в случаях, когда большинство строк не изменились, потому что SQL Server записывает гораздо меньше информации в журнал транзакций.

Чтобы избежать лишних обновлений при операциях слияния, напишите оператор слияния следующим образом:

MERGE INTO target AS t
USING source AS s
ON t.id = s.id
WHEN MATCHED 
  AND ((t.col1 <> s.col1 
       OR t.col1 IS NULL AND s.col1 IS NOT NULL
       OR t.col1 IS NOT NULL AND s.col1 IS NULL)
  OR (t.col2 <> s.col2 
       OR t.col2 IS NULL AND s.col2 IS NOT NULL
       OR t.col2 IS NOT NULL AND s.col2 IS NULL)
  OR (t.col2 <> s.col3 
       OR t.col3 IS NULL AND s.col3 IS NOT NULL
       OR t.col3 IS NOT NULL AND s.col3 IS NULL))
THEN UPDATE SET
  col1 = s.col1, col2 = s.col2, col3 = s.col3
WHEN NOT MATCHED BY TARGET THEN 
    INSERT (id, col1, col2, col3)
    VALUES (s.id, s.col1, s.col2, s.col3);
person Jesús López    schedule 26.11.2019
comment
Я думаю, у вас должно быть условие ИЛИ здесь, так как один из столбцов мог измениться. В нашем случае у нас было около 100+ столбцов. Итак, мы выбрали простую операцию слияния WHEN MATCHED. Излишнего слияния можно избежать в случае небольшого набора столбцов. Но я думаю, что с большими столбцами длина транзакции может быть больше. - person Venkataraman R; 27.11.2019
comment
Ты прав. Перешел на ОР. Очевидно, что при большом количестве столбцов требуется больше процессора, но вы не узнаете, стоит ли оно того, пока не попробуете и не измерите. - person Jesús López; 27.11.2019
comment
согласованный. В этом проекте мы использовали подход с задержкой, и это решило нашу проблему. Как я уже говорил, это был давний проект, и вчера я понял, что на этот вопрос не хватает ответа. Итак, обновили подход, мы попробовали. Я проголосовал за ваш ответ, так как это хороший подход. к сожалению, мы об этом не подумали и не смогли попробовать. - person Venkataraman R; 27.11.2019

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

Нам удалось решить проблему нехватки памяти из-за регулирования транзакций в среде SQL Server. У нас были миллионы строк в промежуточной таблице. Эти 10 000 пакетов и задержка позволили убедиться, что мы не перегружаем сервер. Были люди, обращающиеся к серверу.


DECLARE @RowCount INT;

SET @RowCount = (SELECT COUNT(*) FROM StagingTable);

WHILE (@RowCount > 0)
BEGIN

    BEGIN TRANSACTION

    DELETE TOP 10000 * 
    FROM <Staging Table> 
    OUTPUT deleted.* INTO @TableVariable

    MERGE INTO <Final Table> 
    USING @TableVariable

    COMMIT TRANSACTION

    WAITFOR DELAY '00:00:10'; --artificially introduce 10 seconds delay

    SET @RowCount = (SELECT COUNT(*) FROM StagingTable);

END 

person Venkataraman R    schedule 26.11.2019
comment
это давний проект. В вопросе отсутствовал ответ. Я поставил ответ сейчас. У нас был сценарий, в котором нам нужно удалить данные из промежуточной таблицы и MERGE в финальную таблицу. Итак, нам пришлось использовать DELETE, а затем MERGE. - person Venkataraman R; 26.11.2019
comment
Да. Я имею в виду, что, избегая лишних обновлений, вы можете улучшить операцию слияния, поскольку в журнал транзакций записывается меньше информации. Используйте WHEN MATCHED AND source.col1 <> target.col2 AND source.col2 <> target.col2 ..... Помните о столбцах, допускающих значение NULL. Вы можете использовать (s.col1 <> t.col1 OR s.col1 IS NULL AND t.col1 IS NOT NULL OR s.col1 IS NOT NULL AND t.col1 IS NULL) - person Jesús López; 26.11.2019