web-dev-qa-db-fra.com

Comment nettoyer la SSISDB?

Lorsque j'ai configuré cela, j'ai ignoré la période de rétention. Ma base de données est devenue assez volumineuse, donc je veux réduire sa taille. Si je modifie simplement la période de rétention (c'était 365), cela provoque des problèmes avec SSIS lors de l'exécution de mes packages. Je l'ai même changé par petits incréments, mais l'instruction de suppression créerait des verrous qui empêcheraient l'exécution de nouveaux travaux.

Des idées pour contourner cela? J'ai pensé à créer un nouveau SSISDB.

21
Paul Riker

Phil Brammer a rencontré cela et une foule d'autres choses liées aux soins et à l'alimentation du catalogue SSIS, qu'il couvre dans son article Catalog Indexing Recommendations .

Problème racine

Le problème racine est que MS a tenté de concevoir le SSIS avec RI à l'esprit, mais ils étaient paresseux et ont permis que les suppressions en cascade se produisent par rapport à leur manipulation explicite.

Prête à l'emploi, la nouvelle base de données du catalogue SSIS 2012 (SSISDB) a une indexation de base appliquée, avec l'intégrité référentielle définie pour effectuer des suppressions en cascade entre la plupart des tables.

Entrez le travail de l'Agent SQL, "Travail de maintenance du serveur SSIS". Ce travail par défaut est configuré pour s'exécuter à minuit quotidiennement et utilise deux paramètres de catalogue pour fonctionner: "Nettoyer les journaux périodiquement" et "Période de rétention (jours)". défini, le travail de maintenance purge toutes les données en dehors de la période de conservation indiquée.

Ce travail de maintenance supprime, 10 enregistrements à la fois dans une boucle, des opérations internes, puis cascades dans de nombreuses tables en aval. Dans notre cas, nous avons environ 3000 enregistrements d'opérations à supprimer quotidiennement (10 à la fois!), Ce qui se traduit par 1,6 million de lignes de internal.operation_messages. C’est juste une table en aval! L'ensemble de ce processus verrouille complètement et complètement la base de données SSISDB de toutes les données SELECT/INSERT

Résolution

Jusqu'à ce que MS change la façon dont les choses fonctionnent, l'option prise en charge est

déplacer le calendrier des travaux de maintenance à un moment plus approprié pour votre environnement

Je sais que chez mon client actuel, nous ne chargeons les données que dans les petites heures, donc le SSISDB est silencieux pendant les heures de bureau.

Si l'exécution du travail de maintenance pendant une période silencieuse n'est pas une option, vous envisagez de créer vos propres instructions de suppression pour essayer d'obtenir les suppressions en cascade pour aspirer moins .

Chez mon client actuel, nous avons exécuté environ 200 packages tous les soirs au cours des 10 derniers mois et avons également 365 jours d'historique. Nos plus grandes tables, par ordre de grandeur, sont.

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

Le pilote de toutes ces données, internal.operations ne contient que 3300 lignes, ce qui correspond au commentaire de Phil sur la croissance exponentielle de ces données.

Identifiez donc le operation_id à purger et la suppression des tables foliaires remontant au noyau, internal.operations table.

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;

Les mises en garde habituelles s'appliquent - ne faites pas confiance au code de Randoms sur Internet - utilisez les diagrammes de ssistalk et/ou des tables système pour identifier toutes les dépendances - vous devrez peut-être uniquement segmenter vos opérations de suppression en opérations plus petites - vous pourriez bénéficier en supprimant RI pour les opérations, mais assurez-vous de les réactiver avec l'option de vérification afin qu'ils soient fiables. - consultez votre dba si les opérations durent plus de 4 heures

Les références

34
billinkc

J'ai créé une procédure stockée similaire pour faire l'archivage ci-dessous. Faites-moi savoir s'il y a des bugs. Je ne donne aucune garantie, mais cela fonctionne bien pour moi. Ce code n'est en aucun cas raffiné mais je voulais le partager (c'est-à-dire que l'utilisation de la table temporaire ne peut s'exécuter qu'une seule fois à la fois, peut-être qu'une table à portée de session serait meilleure)

J'ai eu un problème où, par la propre procédure de nettoyage de MS, je ferais sauter le fichier LDF et verrouillerait les tables pendant de longues périodes, et une fois, le serveur aurait manqué d'espace. J'ai décidé d'écrire le mien pour supprimer d'abord les plus grandes tables, puis supprimer la table d'opération. Cette procédure ci-dessous n'utilise jamais plus de 1 Go d'espace journal et ne verrouille pas les tables pendant de très longues périodes, ce qui est un problème lorsque les travaux SSIS doivent s'exécuter toute la journée.

Tout d'abord, il se connecte à une table

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

et utilise une table temporaire

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

voici la procédure

    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