У меня есть таблица (скажем, tableB
) с примерно 40 миллионами строк (всегда увеличивающихся). В настоящее время архивирование осуществляется DELETE .. OUTPUT .. INTO .. FROM ..
способом. Для архивирования 1000 строк изначально требовалось 3~5 секунд. Но чем больше строк удаляется, тем больше времени требуется. Например, после удаления 10 миллионов строк теперь требуется 35–40 секунд, чтобы удалить 1000 строк.
Чем это вызвано? Как я могу улучшить ситуацию (мне нужно заархивировать не менее 30 миллионов строк)? Если разделение — единственный способ, как я могу сделать это с минимальным временем простоя?
Другая информация:
tableB
имеет 2 столбца внешнего ключа (tableAId
,tableCId
)- Архивирование выполняется на основе поля даты и времени в
tableA
(в оператореDELETE
используетсяinner-join
) - План запроса показывает 81 % затрат на внутреннее соединение.
- Если я использую подсказку запроса
with (index=ix_time)
дляtableA
, план показывает 100% затрат на поиск по индексу для индекса в столбце внешнего ключаtableB
- И
tableA
, иtableB
имеют автоинкрементbigint
в качестве первичного ключа. tableB
имеет 4 индексаtableA
имеет 5 индексовtableA
содержит более 30 миллионов строк.- Индексы/статистика перестраиваются/реорганизуются/обновляются каждый день
- SQL Server 2008R2
- Windows Server 2008R2, 16 ядер, 32 ГБ ОЗУ.
- Я случайный банкомат
Выдержка из плана запроса:
Сценарий:
DECLARE @older_than datetime2(0) = '2015-10-01';
DECLARE @i int = 1;
DECLARE @j int = 0;
DECLARE @imax int = 1000;
DECLARE @jmax int = 50;
DECLARE @total int = 0;
DECLARE @t1 DATETIME2(3);
DECLARE @t2 DATETIME2(3);
DECLARE @timetook int;
WHILE @i > 0 AND @j < @jmax
BEGIN
SET @t1 = GETDATE();
DELETE TOP (@imax) ss
OUTPUT deleted.[Id]
,deleted.[columnA]
,deleted.[columnB]
INTO [MyArchive_Data].dbo.tableB([Id]
,columnA
,columnB)
FROM [MyLive_Data].dbo.tableB ss
INNER JOIN [MyLive_Data].dbo.tableA s ON s.Id = ss.tableAID
WHERE s.Time < @older_than;
SET @i = @@rowcount;
SET @j = @j + 1;
SET @total = @total + @i;
SET @t2 = GETDATE();
SET @timetook = datediff(second,@t1,@t2);
RAISERROR('LOOP %d COMPLETE [%d rows][%d sec]',10,1,@j, @total, @timetook) with nowait;
WAITFOR DELAY '00:00:03';
END
Обновить
Кажется, что если я исключаю конструкцию цикла (WHILE @i > 0 AND @j < @jmax
) и запускаю только оператор DELETE отдельно, это занимает 10-12 секунд. Я наблюдал планы запросов. Это были другие. С включенной конструкцией цикла она использовала index-seek
, но без нее использовалась index-scan
. Почему?
DELETE
заявление? - person Felix Pamittan   schedule 01.12.2015TOP
безORDER BY
. Это предназначено? - person Felix Pamittan   schedule 01.12.2015WHILE @i > 0 AND @j < @jmax
, оно сокращается до 10~12 секунд (вместо 40). Я наблюдал за планом запроса. К моему удивлению, планы запросов отличаются. При включенной конструкции цикла используется поиск по индексу. Но без него используется индексное сканирование (быстрее). Вопрос в том, ПОЧЕМУ? - person mshsayem   schedule 01.12.2015