Наилучшая практика для хранимой процедуры архивирования SQL

StackOverflow https://stackoverflow.com/questions/1876472

Вопрос

У меня очень большая база данных (~ 100 ГБ), состоящая в основном из двух таблиц, размер которых я хочу уменьшить (обе имеют ок.50 миллионов записей).У меня есть архивная база данных, настроенная на том же сервере с этими двумя таблицами, использующими ту же схему.Я пытаюсь определить наилучший концептуальный способ удаления строк из текущей базы данных и вставки их в архивную базу данных.В псевдокоде это то, что я сейчас делаю:

Declare @NextIDs Table(UniqueID)
Declare @twoYearsAgo = two years from today's date

Insert into @NextIDs 
     SELECT top 100 from myLargeTable Where myLargeTable.actionDate < twoYearsAgo

Insert into myArchiveTable
<fields>
SELECT <fields> 
FROM myLargeTable INNER JOIN @NextIDs on myLargeTable.UniqueID = @NextIDs.UniqueID

DELETE MyLargeTable
FROM MyLargeTable INNER JOIN @NextIDs on myLargeTable.UniqueID = @NextIDs.UniqueID

Прямо сейчас для завершения 1000 записей требуется ужасно медленные 7 минут.Я протестировал Удаление и Вставку, оба заняли ок.3,5 минуты на завершение, так что не обязательно, что одно из них значительно более неэффективно, чем другое.Кто-нибудь может указать на некоторые идеи оптимизации в этом?

Спасибо!

Это SQL Server 2000.

Редактировать:В большой таблице в поле ActionDate есть кластеризованный индекс.Есть два других индекса, но ни на один из них не ссылаются ни в одном из запросов.Архивная таблица не имеет индексов.На моем тестовом сервере это единственный запрос, поступающий на SQL Server, поэтому он должен обладать достаточной вычислительной мощностью.

Код (это выполняет цикл партиями по 1000 записей одновременно):

 DECLARE @NextIDs TABLE(UniqueID int primary key)
DECLARE @TwoYearsAgo datetime
SELECT @TwoYearsAgo = DATEADD(d, (-2 * 365), GetDate())

WHILE EXISTS(SELECT TOP 1 UserName FROM [ISAdminDB].[dbo].[UserUnitAudit] WHERE [ActionDateTime] < @TwoYearsAgo)
BEGIN

BEGIN TRAN

--get all records to be archived
INSERT INTO @NextIDs(UniqueID)
        SELECT TOP 1000 UniqueID FROM [ISAdminDB].[dbo].[UserUnitAudit] WHERE [UserUnitAudit].[ActionDateTime] < @TwoYearsAgo

--insert into archive table
INSERT INTO [ISArchive].[dbo].[userunitaudit] 
(<Fields>)
SELECT  <Fields>
FROM  [ISAdminDB].[dbo].[UserUnitAudit] AS a
        INNER JOIN @NextIDs AS b ON a.UniqueID = b.UniqueID

--remove from Admin DB
DELETE [ISAdminDB].[dbo].[UserUnitAudit] 
FROM  [ISAdminDB].[dbo].[UserUnitAudit] AS a
INNER JOIN @NextIDs AS b ON a.UniqueID = b.UniqueID 

DELETE FROM @NextIDs

COMMIT

END
Это было полезно?

Решение

Инструкции INSERT и DELETE объединяются на

[ISAdminDB].[dbo].[UserUnitAudit].UniqueID

Если для этого нет индекса, и вы указываете, что его нет, вы выполняете два сканирования таблицы.Вероятно, это источник медлительности, b / c сканирование таблицы SQL Server считывает всю таблицу в таблицу scratch, выполняет поиск в таблице scratch совпадающих строк, затем удаляет таблицу scratch.

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

Другие советы

Фактически у вас есть три варианта выбора, которые необходимо запустить перед выполнением ваших команд вставки / удаления:

для 1 - й вставки:

SELECT top 100 from myLargeTable Where myLargeTable.actionDate < twoYearsAgo

для 2- й вставки:

SELECT <fields> FROM myLargeTable INNER JOIN NextIDs 
on myLargeTable.UniqueID = NextIDs.UniqueID

для удаления:

(select *)
FROM MyLargeTable INNER JOIN NextIDs on myLargeTable.UniqueID = NextIDs.UniqueID

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

  1. запустите profiler и посмотрите, что происходит с чтением / записью и т.д.

  2. проверьте использование индекса для всех трех операторов.

  3. попробуйте запустить SELECTs возвращает только PK, чтобы увидеть, вызвана ли задержка выполнением запроса или извлечением данных (есть, напримерлюбые полнотекстовые индексированные поля, TEXT поля и т.д.)

Есть ли у вас индекс в исходной таблице для столбца, который вы используете для фильтрации результатов?В данном случае это будет ActionDate .

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

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

Есть ли какие-либо другие действия на сервере в течение этого времени?Происходит ли какая-либо блокировка?

Надеюсь, это даст вам отправную точку.

Если вы можете предоставить точный код, который вы используете (возможно, без имен столбцов, если есть проблемы с конфиденциальностью), то, возможно, кто-то сможет найти другие способы оптимизации.

Редактировать:Вы проверили план запроса для вашего блока кода?Я сталкивался с проблемами с табличными переменными, подобными этой, когда оптимизатор запросов не мог определить, что табличная переменная будет небольшого размера, поэтому он всегда пытался выполнить полное сканирование таблицы в базовой таблице.

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

Другим вариантом было бы использовать обычную таблицу для хранения идентификаторов.

Существуют ли какие-либо индексы в myLargeTable.ActionDate и .UniqueID?

Пробовали ли вы партии большего размера, чем 100?

Что занимает больше всего времени?ВСТАВИТЬ или УДАЛИТЬ?

Вы могли бы попробовать сделать это, используя предложение output:

declare @items table (
  <field list just like source table> )

delete top 100 source_table
  output deleted.first_field, deleted.second_field, etc
  into @items
  where <conditions>

insert archive_table (<fields>)
  select (<fields>) from @items

Вы также могли бы сделать это в одном запросе, выполнив 'output into' непосредственно в архивную таблицу (устраняя необходимость в table var).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top