it-swarm.com.de

Was ist der schnellste Weg, um Daten zu löschen?

Szenario :

Wir haben zwei Tabellen Tbl1 Und Tbl2 Auf dem Abonnentenserver. Der Tbl1 Wird von Publisher Server A Repliziert und hat zwei Trigger - Einfügen und Aktualisieren. Die Trigger fügen die Daten in Tbl2 Ein und aktualisieren sie.

Jetzt müssen wir (ca. 900 Millionen Datensätze) aus Tbl2 Löschen, das insgesamt mehr als 1000 Millionen Datensätze enthält. Nachfolgend finden Sie die Datenverteilung für einen Monat bis eine Minute.

  • Ein Monat - 14986826 Zeilen
  • Ein Tag - 483446 Zeilen
  • Eine Stunde - 20143 Zeilen
  • Eine Minute - 335 Zeilen

Was ich suche;

Der schnellste Weg, um diese Daten ohne Produktionsprobleme, Datenkonsistenz und möglicherweise ohne Ausfallzeiten zu löschen. Ich denke also, die folgenden Schritte zu befolgen, stecke aber fest :(

Schritte :

  1. BCP Herausgabe der erforderlichen Daten aus der vorhandenen Tabelle Tbl2 (ca. 100 Millionen Datensätze können ca. 30 Minuten dauern).
    • Nehmen wir an, ich habe die Aktivität am 1Fab2018 um 22:00 Uhr begonnen und sie wurde um 1Fab2018 um 22:30 Uhr beendet. Wenn die Aktivität abgeschlossen ist, erhält die Tabelle Tbl2 neue Datensätze, die zu Delta werden
  2. Erstellen Sie eine neue Tabelle in der Datenbank mit dem Namen Tbl3
  3. BCP in den exportierten Daten in die neu erstellte Tabelle Tbl3 (ca. 100 Millionen Datensätze, es kann ca. 30 Minuten dauern)
  4. Beenden Sie den Replikationsjob
  5. Verwenden Sie nach Abschluss der BCP-Eingabe das tsql-Skript, um die neuen Delta-Daten einzufügen.

  6. Die Herausforderung ist - Wie gehe ich mit der Delta-Update-Anweisung um?

  7. Starten Sie die Replikation

Zusätzliche Frage :

Was ist der beste Weg, um mit Szenarien umzugehen?

18

Da Sie 90% der Zeilen löschen, würde ich empfehlen, die Zeilen, die Sie behalten müssen, in eine neue Tabelle mit derselben Struktur zu kopieren und dann mit ALTER TABLE ... SWITCH Die vorhandene Tabelle durch die neue Tabelle zu ersetzen Lass den alten Tisch fallen. Siehe diese Microsoft Docs-Seite für die Syntax.

Ein einfacher Prüfstand ohne Replikation, der das allgemeine Prinzip zeigt:

Zuerst erstellen wir eine Datenbank für unseren 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

Hier erstellen wir einige Tabellen mit einem Auslöser zum Verschieben von Zeilen von Tabelle "A" nach "B", um sich Ihrem Setup anzunähern.

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

Hier fügen wir 1.000.000 Zeilen in "A" ein, und aufgrund des Auslösers werden diese Zeilen auch in "B" eingefügt.

;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;

Löschen Sie das Transaktionsprotokoll, um zu vermeiden, dass der Platz knapp wird. Führen Sie dies in der Produktion NICHT aus, da die Transaktionsprotokolldaten an das Gerät "NUL" gesendet werden.

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

Dieser Code erstellt eine Transaktion, um sicherzustellen, dass keine der betroffenen Tabellen geschrieben werden kann, während Zeilen migriert werden:

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

Mit sp_getapplock Und sp_releaseapplock Wird verhindert, dass mehrere Instanzen dieses Codes gleichzeitig ausgeführt werden. Dies ist hilfreich, wenn Sie die Wiederverwendung dieses Codes über eine GUI aktivieren.

(Beachten Sie, dass App-Sperren nur dann wirksam sind, wenn jeder Prozess, der auf die Ressource zugreift, explizit dieselbe manuelle Ressourcen-Sperrlogik implementiert - es gibt keine Magie, die die Sperre "sperrt" Tabelle auf die gleiche Weise, wie SQL Server Zeilen, Seiten usw. während eines Einfüge-/Aktualisierungsvorgangs automatisch sperrt.)

Jetzt testen wir den Prozess des Einfügens von Zeilen in "A", um sicherzustellen, dass sie vom Trigger in "B" eingefügt werden.

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

SELECT *
FROM dbo.B
WHERE B.d = 'testRow'
+ --------- + --------- + ------------------------- + 
 | i | d | Zeilendatum | 
 + --------- + --------- + ---------------------- --- + 
 | 1000001 | testRow | 2018-04-13 03: 49: 53.343 | 
 + --------- + --------- + ------------- ------------ + 
26
Max Vernon