2009-09-09 3 views
4

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

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

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

CREATE PROCEDURE [dbo].[ClearLog] 
(
    @Age int = 30 
) 
AS 
BEGIN 
-- SET NOCOUNT ON added to prevent extra result sets from 
-- interfering with SELECT statements. 
SET NOCOUNT ON; 

    -- DELETE ERRORLOG 
    WHILE EXISTS (SELECT [LogId] FROM [dbo].[Error_Log] WHERE DATEDIFF(dd, [TimeStamp], GETDATE()) > @Age) 
    BEGIN 
    SET ROWCOUNT 10000 
    DELETE [dbo].[Error_Log] WHERE DATEDIFF(dd, [TimeStamp], GETDATE()) > @Age 

    WAITFOR DELAY '00:00:01' 
    SET ROWCOUNT 0 
    END 
END 
+0

«Я изучил другие варианты создания таблиц и вставки записей, чтобы сохранить, а затем обрезать источник, этот метод не будет работать для меня». Как так? Ответ на это влияет на возможные решения. –

+0

Будет ли какой-то тайм-аут, если я урезаю исходную таблицу? Разве это не вызовет проблему в приложении, пытающемся записать в таблицу журналов? Это была моя забота. – Picflight

+0

Каков режим восстановления базы данных? (Полный, Bulk_Logged, Simple) –

ответ

4

Вот как я бы это сделать:

CREATE PROCEDURE [dbo].[ClearLog] ( 
@Age int = 30) 
AS 
BEGIN 
    SET NOCOUNT ON; 
    DECLARE @d DATETIME 
     , @batch INT; 
    SET @batch = 10000; 
    SET @d = DATEADD(dd, [email protected], GETDATE()) 
    WHILE (1=1) 
    BEGIN 
     DELETE TOP (@batch) [dbo].[Error_Log] 
     WHERE [Timestamp] < @d; 
     IF (0 = @@ROWCOUNT) 
      BREAK 
    END 
END 
  • Сделать Tiemstamp сравнения SARGable
  • Отделить GetDate() в начале партии, чтобы произвести последовательную прогон (в противном случае он может блокировать в бесконечном цикле как новый возраст записей, поскольку старые удаляются).
  • использование TOP вместо SET ROWCOUNT (deprecated: Using SET ROWCOUNT will not affect DELETE, INSERT, and UPDATE statements in the next release of SQL Server.)
  • чек @@ ROWCOUNT разорвать петлю вместо избыточных ВЫБРАТЬ
+0

Я только что проверил это на 9 миллионов строк, удалив 1000 за раз. Все прошло отлично. –

+0

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

1

Если у вас есть возможность восстановления таблицы журнала ошибок на схеме секционирования одним из вариантов было бы разделение таблицы на дату и свопинг разделов. Сделайте google-поиск для «alter table switch partition», чтобы копать немного дальше.

+0

Это будет трюк, если он впишется в вашу архитектуру. –

1

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

CREATE PROCEDURE [dbo].[ClearLog] 
(
    @Age int = 30 
) 
AS 
BEGIN 
    SET NOCOUNT ON; 
    SET ROWCOUNT 10000 --I assume you are on an old version of SQL Server and can't use TOP 
    DELETE dbo.Error_Log Where Timestamp>GETDATE()[email protected] 
    WAITFOR DELAY '00:00:01' --why??? 
    SET ROWCOUNT 0 
END 

так, как он обрабатывает дату не будет укоротить время, и вы будете удалять только 30 минут стоят данных каждый раз.

+0

Еще один хороший трюк - можете ли вы выделить место, когда удаляются, и таким образом минимизировать их эффект? –

+0

@Philip Kelley, это идея с «каждые 30 минут», но все же ограничение до 10000, это будет распространять нагрузку с другими «реальными» транзакциями пользователя –

+0

ах. Это все время будет проходить процедуру «не так ли?». Я бы рекомендовал переместить тайм-аут на задание агента SQL, которое каждые 30 минут вызывает процедуру для удаления строк. –

0

Раствор я использовал в прошлом было временно установить модель восстановления с «Bulk Записан», а затем обратно в «Full» в конце хранимой процедуры:

DECLARE @dbName NVARCHAR(128); 
SELECT @dbName = DB_NAME(); 

EXEC('ALTER DATABASE ' + @dbName + ' SET RECOVERY BULK_LOGGED') 

WHILE EXISTS (...) 
BEGIN 
    -- Delete a batch of rows, then WAITFOR here 
END 

EXEC('ALTER DATABASE ' + @dbName + ' SET RECOVERY FULL') 

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

1

Если ваша база данных находится в режиме полного восстановления, единственный способ свести к минимуму влияние ваших заявлений о удалении - это «просто удалить их» - удалять столько всего за «интервал транзакции». Например, если вы делаете резервные копии t-log каждый час, удаляйте, скажем, 20 000 строк в час. Это может не потерять все, что вам нужно все сразу, но все ли это произойдет через 24 часа или через неделю?

Если ваша база данных находится в режиме ПРОСТОЙ или БОЛЬШОЙ_LOGGED, это должно привести к удалению ее в куски. Но, поскольку вы уже это делаете, мне придется угадать, что ваша база данных находится в режиме FULL восстановления. (Это или соединение, вызывающее эту процедуру, может быть частью транзакции.)

Смежные вопросы