Apa cara tercepat untuk membersihkan data?

18

Skenario:

Kami memiliki dua tabel Tbl1& Tbl2di Server Pelanggan. Ini Tbl1sedang direplikasi dari Penerbit Server Adan memiliki dua pemicu - masukkan dan perbarui. Pemicu memasukkan dan memperbarui data ke dalam Tbl2.

Sekarang, kita harus membersihkan (sekitar 900 juta catatan) dari Tbl2yang memiliki total 1000+ juta catatan. Di bawah ini adalah distribusi data selama satu bulan hingga satu menit.

  • Satu Bulan - 14986826 baris
  • Suatu hari - 483446 baris
  • Satu jam - baris 20143
  • Satu menit - 335 baris

Apa yang saya cari;

Cara tercepat untuk membersihkan data tersebut tanpa masalah produksi, konsistensi data, dan mungkin tanpa downtime. Jadi, saya berpikir untuk mengikuti langkah-langkah di bawah ini tetapi terjebak :(

Langkah:

  1. BCP Keluar data yang diperlukan dari tabel Tbl2 yang ada (sekitar 100 juta catatan, mungkin butuh sekitar 30 menit).
    • Mari kita asumsikan saya mulai melakukan aktivitas pada 1Fab2018 10:00, selesai pada 1Fab2018 10.30 PM. Pada saat aktivitas akan selesai, tabel Tbl2 akan mendapatkan catatan baru yang menjadi delta
  2. Buat tabel baru di database dengan nama Tbl3
  3. BCP dalam data yang diekspor ke tabel Tbl3 yang baru dibuat (sekitar 100 juta catatan, mungkin butuh sekitar 30 menit)
  4. Hentikan pekerjaan replikasi
  5. Setelah BCP-in selesai, gunakan skrip tsql untuk memasukkan data delta baru.

  6. Tantangannya adalah - Bagaimana cara menangani pernyataan "pembaruan" delta?

  7. Mulai Replikasi

Pertanyaan tambahan:

Apa cara terbaik untuk menghadapi skenario?

Dharmedra Keshari
sumber

Jawaban:

26

Karena Anda menghapus 90% dari baris, saya akan merekomendasikan menyalin baris yang Anda butuhkan untuk tetap berada di tabel baru dengan struktur yang sama, kemudian gunakan ALTER TABLE ... SWITCHuntuk mengganti tabel yang ada dengan tabel baru, kemudian cukup letakkan tabel lama. Lihat halaman Microsoft Documents ini untuk sintaksisnya.

Test-bed sederhana, tanpa replikasi yang menunjukkan prinsip umum:

Pertama, kami akan membuat database untuk pengujian kami:

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

Di sini, kami membuat beberapa tabel, dengan pemicu untuk memindahkan baris dari tabel "A" ke "B", mendekati pengaturan Anda.

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

Di sini, kami memasukkan 1.000.000 baris ke "A", dan karena pemicunya, baris-baris itu juga akan dimasukkan ke dalam "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;

Kosongkan log transaksi, untuk menghindari kehabisan ruang. JANGAN LARI ini dalam produksi karena mengirimkan data log transaksi ke perangkat "NUL".

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

Kode ini menciptakan transaksi untuk memastikan tidak ada tabel yang terpengaruh yang dapat ditulis saat kami memigrasikan baris:

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

The sp_getapplockdan sp_releaseapplockmencegah beberapa contoh kode ini berjalan pada saat yang bersamaan. Ini akan sangat membantu jika Anda mengaktifkan kode ini untuk digunakan kembali melalui GUI.

(Perhatikan bahwa kunci aplikasi hanya efektif jika setiap proses mengakses sumber daya mengimplementasikan logika penguncian sumber daya manual yang sama secara eksplisit - tidak ada keajaiban yang "mengunci" tabel dengan cara yang sama seperti SQL Server secara otomatis mengunci baris, halaman, dll selama masukkan / perbarui operasi.)

Sekarang, kami menguji proses memasukkan baris ke "A", untuk memastikan mereka dimasukkan ke "B" oleh pemicu.

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

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