web-dev-qa-db-ja.com

SSISDBをクリーンアップするにはどうすればよいですか?

これを設定したときに、保持期間を見落としていました。私のデータベースはかなり大きくなったので、サイズを小さくしたいと思います。保持期間(365でした)を単に変更すると、パッケージを実行するSSISで問題が発生します。少しずつ変更しましたが、削除ステートメントによってロックが作成され、新しいジョブの実行が妨げられました。

これを回避する方法はありますか?新しいSSISDBを作成することを考えました。

21
Paul Riker

Phil Brammerはこれに遭遇し、SSISカタログのケアとフィードに関連する他の多くの事柄に遭遇しました。彼は彼の投稿でそれをカバーしています カタログインデックスの推奨事項 .

根本的な問題

根本的な問題は、MSがRIを念頭に置いてSSISを設計しようとしたが、怠zyであり、明示的に処理するのではなくカスケード削除が発生することを許可したことです。

すぐに使用できる新しいSSIS 2012カタログデータベース(SSISDB)には、ほとんどのテーブル間でカスケード削除を行うための参照整合性が設定された、いくつかの基本的なインデックスが適用されます。

SQLエージェントジョブ「SSISサーバーメンテナンスジョブ」を入力します。このジョブはデフォルトで毎日深夜に実行されるように設定され、2つのカタログパラメーターを使用して機能します。設定すると、メンテナンスジョブは、指定された保存期間外のデータを削除します。

この保守ジョブは、internal.operationsからループで一度に10レコードを削除し、その後、ダウンストリームの多くのテーブルにカスケードします。この場合、毎日削除するオペレーションレコードは約3000個(一度に10個!)あり、これはinternal.operation_messagesから160万行に変換されます。ダウンストリームテーブルは1つだけです!このプロセス全体が、SELECT/INSERTデータからSSISDBデータベースを完全にロックします。

解決

MSが物事の仕組みを変更するまで、サポートされるオプションは

メンテナンスジョブスケジュールを環境に適した時間に移動する

現在のクライアントでは、データをロードするのはごくわずかであるため、SSISDBは営業時間中は静かです。

休止期間中にメンテナンスジョブを実行するオプションがない場合、独自の削除ステートメントを作成して、カスケード削除をsuck less

現在のクライアントでは、過去10か月間、毎晩約200のパッケージを実行しており、365日の歴史もあります。私たちの最大のテーブルは、桁違いです。

Schema    Table                   RowCount
internal  event_message_context   1,869,028
internal  operation_messages      1,500,811
internal  event_messages          1,500,803

そのデータすべてのドライバー、internal.operationsには3300行しかありません。これは、このデータが指数関数的に増大するというPhilのコメントと一致しています。

したがって、operation_idパージされ、コアに戻るリーフテーブルからの削除、internal.operations テーブル。

USE SSISDB;
SET NOCOUNT ON;
IF object_id('tempdb..#DELETE_CANDIDATES') IS NOT NULL
BEGIN
    DROP TABLE #DELETE_CANDIDATES;
END;

CREATE TABLE #DELETE_CANDIDATES
(
    operation_id bigint NOT NULL PRIMARY KEY
);

DECLARE @DaysRetention int = 100;
INSERT INTO
    #DELETE_CANDIDATES
(
    operation_id
)
SELECT
    IO.operation_id
FROM
    internal.operations AS IO
WHERE
    IO.start_time < DATEADD(day, -@DaysRetention, CURRENT_TIMESTAMP);

DELETE T
FROM
    internal.event_message_context AS T
    INNER JOIN
        #DELETE_CANDIDATES AS DC
        ON DC.operation_id = T.operation_id;

DELETE T
FROM
    internal.event_messages AS T
    INNER JOIN
        #DELETE_CANDIDATES AS DC
        ON DC.operation_id = T.operation_id;

DELETE T
FROM
    internal.operation_messages AS T
    INNER JOIN
        #DELETE_CANDIDATES AS DC
        ON DC.operation_id = T.operation_id;

-- etc
-- Finally, remove the entry from operations

DELETE T
FROM
    internal.operations AS T
    INNER JOIN
        #DELETE_CANDIDATES AS DC
        ON DC.operation_id = T.operation_id;

通常の注意事項が適用されます-インターネット上のランダムからのコードを信頼しないでください-ssistalkやシステムテーブルの図を使用してすべての依存関係を特定します-削除操作を小さな操作に分割するだけでよい場合があります-RI操作については、チェックオプションを使用して再度有効にし、信頼できるようにします。 -操作が4時間以上続く場合は、dbaに相談してください

参照資料

34
billinkc

同様のストアドプロシージャを作成して、以下のアーカイブを実行します。バグがあれば教えてください。私は何の保証も提供しませんが、私にとってはうまく機能します。このコードは決して洗練されていませんが、私は共有したいです(つまり、一時テーブルの使用は一度に1回しか実行できないことを意味し、おそらくセッションスコープのテーブルの方が良いでしょう)

MSが独自のクリーンプロシージャを実行するとLDFファイルが完全に削除され、テーブルが長期間ロックされ、サーバーのスペースが不足するという問題が発生しました。最初に大きなテーブルを削除してから操作テーブルを削除する独自のプログラムを作成することにしました。以下のこの手順では、ログスペースで1 GBを超えることは決してなく、非常に長い期間テーブルをロックしません。

まず、テーブルに記録します

CREATE TABLE [dbo].[ETL_SSIS_Operations_Archived](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [operation_id_str] [varchar](900) NOT NULL,
    [event_messages_context] [int] NULL,
    [event_messages] [int] NULL,
    [operation_messages] [int] NULL,
    [num_operators] [int] NULL,
    [chunksize] [int] NULL,
    [DateStarted] [datetime] NOT NULL,
    [DateFinished] [datetime] NULL,
    [executionSecs] [int] NULL,
    [DelOperationsDateStarted] [datetime] NULL,
    [DelOperationsDateFinished] [datetime] NULL,
    [DelOperationsExecutionSecs] [int] NULL
) ON [PRIMARY]
GO

一時テーブルを使用します

CREATE TABLE [dbo].[tmp_etl_operations_id](
    [operation_id] [int] NULL,
    [dateCreated] [datetime] NULL default getdate()
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[tmp_etl_operations_id] ADD  DEFAULT (getdate()) FOR [dateCreated]
GO

手順はこちら

    CREATE PROCEDURE [dbo].[sp_Archive_SSIDB_Catalogue]
AS
BEGIN

    DECLARE @MyCursor as CURSOR;
    DECLARE @l_operation_id int;
    declare @l_rows_del int = 1

    declare @l_operation_id_str varchar(8000) = ''
    declare @l_id int

    declare @l_event_message_context int = 0
    declare @l_event_messages        int = 0
    declare @l_operation_messages    int = 0

    declare @l_loop_num int = 1
    declare @C_BULK_NUM int = 100
    declare @C_CHUNK_SIZE int = 100000

    declare @l_last_rec char(1)

    SET @MyCursor = CURSOR FOR
       with params as
       (
           -- i round up the midnight that day, just so i know once it is done for the day it is done
           -- and if the real maintenance job was to run after this (just for the sake of it to double ensure nothing has been missed), but not actually need to do
           -- anything as its already done in here, no new operations would have snuck in due to the sliding system time
           SELECT cast(dateadd(day,1,GETDATE() - CONVERT(int,property_value)) as date)  ArchiveDate 
           FROM  ssisdb.[catalog].[catalog_properties]
           WHERE property_name = 'RETENTION_WINDOW'
       )
       select operation_id,iif(r=c,'Y','N') lastrec
       from
       (
           select operation_id,row_number() over (partition by null order by operation_id) r,count(*) over (partition by null) c
           FROM ssisdb.[internal].[operations] 
           WHERE ( [end_time] <= (select ArchiveDate from params)
           -- A special case when END_TIME is null, we will delete the records based on the created time 
           OR ([end_time] IS NULL AND [status] = 1 AND [created_time] <= (select ArchiveDate from params) ))
       ) x
       order by operation_id


    OPEN @MyCursor;
    FETCH NEXT FROM @MyCursor INTO @l_operation_id,@l_last_rec

    WHILE @@FETCH_STATUS = 0
    BEGIN
        set @l_operation_id_str = @l_operation_id_str+','+cast(@l_operation_id as varchar(100))

        if @l_loop_num = 1
        begin
           delete from tmp_etl_operations_id
           set @l_operation_id_str = cast(@l_operation_id as varchar(100))
        end

        insert into tmp_etl_operations_id (operation_id)  select @l_operation_id

        if @l_loop_num = @C_BULK_NUM or @l_last_rec='Y'
        begin
            set @l_loop_num = 1

            set @l_event_message_context = 0
            set @l_event_messages        = 0
            set @l_operation_messages    = 0

            insert into ETL_SSIS_Operations_Archived ([operation_id_str], num_operators,chunksize, event_messages_context, event_messages, operation_messages, datestarted)
            select @l_operation_id_str, @C_BULK_NUM,@C_CHUNK_SIZE,@l_event_message_context,@l_event_messages,@l_operation_messages,getdate()
            --where 0 = (select count(*) from ETL_SSIS_Operations_Archived where operation_id=@l_operation_id_str)

            set @l_id = Scope_Identity() 

            set @l_rows_del = @C_CHUNK_SIZE
            while (@l_rows_del >= @C_CHUNK_SIZE)
            begin
             delete top (@C_CHUNK_SIZE)
             from   ssisdb.internal.event_message_context
             where  operation_id in (select operation_id from etl..tmp_etl_operations_id)

             set @l_rows_del = @@ROWCOUNT
             set @l_event_message_context = @l_event_message_context+@l_rows_del

             update ETL_SSIS_Operations_Archived 
             set    event_messages_context = event_messages_context+@l_rows_del
             where  id = @l_id--operation_id = @l_operation_id_str

            end

            set @l_rows_del = @C_CHUNK_SIZE

            while (@l_rows_del >= @C_CHUNK_SIZE)
            begin
             delete top (@C_CHUNK_SIZE)
             from   ssisdb.internal.event_messages
             where  operation_id in (select operation_id from tmp_etl_operations_id)

             set @l_rows_del = @@ROWCOUNT
             set @l_event_messages = @l_event_messages+@l_rows_del

             update ETL_SSIS_Operations_Archived 
             set    event_messages = event_messages+@l_rows_del
             where  id = @l_id--operation_id = @l_operation_id_strwhere  operation_id = @l_operation_id_str 

            end

            set @l_rows_del = @C_CHUNK_SIZE
            while (@l_rows_del >= @C_CHUNK_SIZE)
            begin
             delete top (@C_CHUNK_SIZE)
             from   ssisdb.internal.operation_messages
             where  operation_id in (select operation_id from tmp_etl_operations_id)

             set @l_rows_del = @@ROWCOUNT
             set @l_operation_messages = @l_operation_messages+@l_rows_del

             update ETL_SSIS_Operations_Archived 
             set    operation_messages = operation_messages+@l_rows_del
             where  id = @l_id--operation_id = @l_operation_id_strwhere  operation_id = @l_operation_id_str -- 

            end

             update ETL_SSIS_Operations_Archived 
             set    DateFinished = getdate()
                   ,executionSecs =  Datediff(s, DateStarted, getdate())
                   ,DelOperationsDateStarted = getdate()
             where  id = @l_id--operation_id = @l_operation_id_strwhere  operation_id = @l_operation_id_str -- 


             -- lets delete the operations now
             delete --top (@C_CHUNK_SIZE)
             from   ssisdb.internal.operations
             where  operation_id in (select operation_id from tmp_etl_operations_id)

             update ETL_SSIS_Operations_Archived 
             set    DelOperationsDateFinished = getdate()
                   ,DelOperationsExecutionSecs =  Datediff(s, DelOperationsDateStarted, getdate())
             where  id = @l_id--operation_id = @l_operation_id_strwhere  operation_id = @l_operation_id_str -- 

        end
        else
        begin
            set @l_loop_num = @l_loop_num+1
        end

        FETCH NEXT FROM @MyCursor INTO @l_operation_id,@l_last_rec


    END

    CLOSE @MyCursor;
    DEALLOCATE @MyCursor;

END
1
Ab Bennett