Quel est le moyen le plus rapide de purger les données?


18

Scénario:

Nous avons deux tables Tbl1et Tbl2sur le serveur d'abonné. Le Tbl1est en cours de réplication à partir de Publisher Server Aet il a deux déclencheurs - insérer et mettre à jour. Les déclencheurs insèrent et mettent à jour les données dans Tbl2.

Maintenant, nous devons purger (environ 900 millions d'enregistrements) Tbl2dont le total s'élève à plus de 1000 millions d'enregistrements. Vous trouverez ci-dessous la distribution des données d'un mois à une minute.

  • Un mois - 14986826 lignes
  • Un jour - 483446 lignes
  • Une heure - 20143 lignes
  • Une minute - 335 rangées

Ce que je recherche;

Le moyen le plus rapide de purger ces données sans aucun problème de production, cohérence des données et éventuellement aucun temps d'arrêt. Donc, je pense à suivre les étapes ci-dessous mais coincé :(

Pas:

  1. BCP Sortez les données requises de la table Tbl2 existante (environ 100 millions d'enregistrements, cela peut prendre environ 30 minutes).
    • Supposons que j'ai commencé l'activité sur 1Fab2018 22h00, elle s'est terminée à 1Fab2018 22h30. Au moment où l'activité sera terminée, la table Tbl2 obtiendra de nouveaux enregistrements qui deviendront delta
  2. Créer une nouvelle table dans la base de données avec le nom Tbl3
  3. BCP dans les données exportées dans la table Tbl3 nouvellement créée (environ 100 millions d'enregistrements, cela peut prendre environ 30 minutes)
  4. Arrêtez le travail de réplication
  5. Une fois BCP-in terminé, utilisez le script tsql pour insérer les nouvelles données delta.

  6. Le défi est - Comment gérer la déclaration de «mise à jour» delta?

  7. Lancer la réplication

Question supplémentaire:

Quelle est la meilleure façon de gérer un scénario?

Réponses:


26

Étant donné que vous supprimez 90% des lignes, je vous recommande de copier les lignes dont vous avez besoin pour conserver dans une nouvelle table avec la même structure, puis utilisez ALTER TABLE ... SWITCHpour remplacer la table existante par la nouvelle table, puis supprimez simplement l'ancienne table. Consultez cette page Microsoft Docs pour la syntaxe.

Un banc d'essai simple, sans réplication qui montre le principe général:

Tout d'abord, nous allons créer une base de données pour notre test:

USE master;
IF (SELECT 1 FROM sys.databases d WHERE d.name = 'SwitchTest') IS NOT NULL
BEGIN
    ALTER DATABASE SwitchTest SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
    DROP DATABASE SwitchTest;
END
CREATE DATABASE SwitchTest;
ALTER DATABASE SwitchTest SET RECOVERY FULL;
BACKUP DATABASE SwitchTest TO DISK = 'NUL:';
GO

Ici, nous créons quelques tables, avec un déclencheur pour déplacer les lignes de la table "A" vers "B", approximant votre configuration.

USE SwitchTest;
GO
CREATE TABLE dbo.A
(
    i int NOT NULL 
        CONSTRAINT PK_A
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , d varchar(300) NOT NULL
    , rowdate datetime NOT NULL
) ON [PRIMARY]
WITH (DATA_COMPRESSION = PAGE);

CREATE TABLE dbo.B
(
    i int NOT NULL 
        CONSTRAINT PK_B
        PRIMARY KEY CLUSTERED
    , d varchar(300) NOT NULL
    , rowdate datetime NOT NULL
) ON [PRIMARY]
WITH (DATA_COMPRESSION = PAGE);

GO
CREATE TRIGGER t_a
ON dbo.A
AFTER INSERT, UPDATE
AS
BEGIN
    SET NOCOUNT ON;
    DELETE
    FROM dbo.B
    FROM dbo.B b
        INNER JOIN deleted d ON b.i = d.i
    INSERT INTO dbo.B (i, d, rowdate)
    SELECT i.i
        , i.d
        , i.rowdate
    FROM inserted i;
END
GO

Ici, nous insérons 1 000 000 de lignes dans "A", et en raison du déclencheur, ces lignes seront également insérées dans "B".

;WITH src AS (
    SELECT i.n
    FROM (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9))i(n)
)
INSERT INTO dbo.A (d, rowdate)
SELECT d = CRYPT_GEN_RANDOM(300), DATEADD(SECOND, s6.n + (s5.n * 100000) + (s4.n * 10000) + (s3.n * 1000) + (s2.n * 100) + (s1.n * 10), '2017-01-01T00:00:00.000')
FROM src s1
    CROSS JOIN src s2
    CROSS JOIN src s3
    CROSS JOIN src s4
    CROSS JOIN src s5
    CROSS JOIN src s6;

Effacez le journal des transactions pour éviter de manquer de place. NE PAS EXÉCUTER cela en production car il envoie les données du journal des transactions au périphérique "NUL".

BACKUP LOG SwitchTest TO DISK = 'NUL:';
GO

Ce code crée une transaction pour garantir qu'aucune des tables affectées ne peut être écrite pendant la migration des lignes:

BEGIN TRANSACTION
EXEC sys.sp_getapplock @Resource = N'TableSwitcher', @LockMode = 'Exclusive', @LockOwner = 'Transaction', @LockTimeout = '1000', @DbPrincipal = N'dbo';
BEGIN TRY
    -- create a table to hold the rows we want to keep
    CREATE TABLE dbo.C
    (
        i int NOT NULL 
            CONSTRAINT PK_C
            PRIMARY KEY CLUSTERED
        , d varchar(300) NOT NULL
        , rowdate datetime NOT NULL
    ) ON [PRIMARY]
    WITH (DATA_COMPRESSION = PAGE);

    --copy the rows we want to keep into "C"
    INSERT INTO dbo.C (i, d, rowdate)
    SELECT b.i
        , b.d
        , b.rowdate
    FROM dbo.B
    WHERE b.rowdate >= '2017-01-11T10:00:00';

    --truncate the entire "B" table
    TRUNCATE TABLE dbo.B;

    --"switch" table "C" into "B"
    ALTER TABLE dbo.C SWITCH TO dbo.B;

    --drop table "C", since we no longer need it
    DROP TABLE dbo.C;

    --shows the count of rows in "B" which were retained.
    SELECT COUNT(1)
    FROM dbo.B
    WHERE b.rowdate >= '2017-01-11T10:00:00';

   --look for rows in "B" that should no longer exist.
    SELECT COUNT(1)
    FROM dbo.B
    WHERE b.rowdate < '2017-01-11T10:00:00';

    --release the applock and commit the transaction
    EXEC sys.sp_releaseapplock @Resource = N'TableSwitcher', @LockOwner = 'Transaction', @DbPrincipal = N'dbo';
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    DECLARE @message nvarchar(1000) = ERROR_MESSAGE();
    DECLARE @severity int = ERROR_SEVERITY();
    DECLARE @state int = ERROR_STATE();
    RAISERROR (@message, @severity, @state);
    EXEC sys.sp_releaseapplock @Resource = N'TableSwitcher', @LockOwner = 'Transaction', @DbPrincipal = N'dbo';
    ROLLBACK TRANSACTION;
END CATCH
GO

Les sp_getapplocket sp_releaseapplockprévenir les cas multiples de ce code en cours d' exécution en même temps. Cela serait utile si vous autorisez la réutilisation de ce code via une interface graphique.

(Notez que les verrous d'applications ne sont efficaces que si tous les processus accédant à la ressource implémente explicitement la même logique de verrouillage manuel des ressources - il n'y a pas de magie qui "verrouille" la table de la même manière que SQL Server verrouille automatiquement les lignes, les pages, etc. pendant un opération d'insertion / mise à jour.)

Maintenant, nous testons le processus d'insertion de lignes dans "A", pour nous assurer qu'elles sont insérées dans "B" par le déclencheur.

INSERT INTO dbo.A (d, rowdate)
VALUES ('testRow', GETDATE());

SELECT *
FROM dbo.B
WHERE B.d = 'testRow'
+ --------- + --------- + ------------------------- +
| je | d | rowdate |
+ --------- + --------- + ------------------------- +
| 1000001 | testRow | 2018-04-13 03: 49: 53.343 |
+ --------- + --------- + ------------------------- +
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.