Verileri temizlemenin en hızlı yolu nedir?


18

Senaryo:

Biz iki tablo var Tbl1ve Tbl2Abone Server. Tbl1Publisher çoğaltılan ediliyor Server Ave iki tetikler - insert ve güncelleştirme. Tetikleyiciler verileri ekler ve içine günceller Tbl2.

Şimdi, Tbl2toplam 1000+ milyon kaydı bulunan (yaklaşık 900 milyon kayıt) tasfiye etmek zorundayız . Aşağıda bir aydan bir dakikaya kadar olan veri dağılımı verilmiştir.

  • Bir Ay - 14986826 satır
  • Bir gün - 483446 satır
  • Bir saat - 20143 satır
  • Bir dakika - 335 satır

Aradığım şey;

Herhangi bir üretim sorunu, veri tutarlılığı ve muhtemelen kesinti olmadan bu verileri temizlemenin en hızlı yolu. Yani, aşağıdaki adımları izlemeyi düşünüyorum ama sıkışmış :(

Adımlar:

  1. BCP Mevcut tablo Tbl2'den gerekli verileri çıkarın (yaklaşık 100 milyon kayıt, yaklaşık 30 dakika sürebilir).
    • 1Fab2018 22: 00'da etkinliği yapmaya başladığımı varsayalım, 1Fab2018 22: 30'da bitti. Etkinlik tamamlanınca Tbl2 tablosu delta haline gelen yeni kayıtları alacak
  2. Veritabanında Tbl3 adıyla yeni bir tablo oluşturun
  3. Yeni oluşturulan tablo Tbl3'e aktarılan verilerdeki BCP (yaklaşık 100 milyon kayıt, yaklaşık 30 dakika sürebilir)
  4. Çoğaltma işini durdurma
  5. BCP girişi tamamlandığında, yeni delta verilerini eklemek için tsql komut dosyasını kullanın.

  6. Zorluk - Delta “update” ifadesiyle nasıl başa çıkılır?

  7. Çoğaltmayı Başlat

Ek Soru:

Senaryo ile başa çıkmanın en iyi yolu nedir?

Yanıtlar:


26

Satırların% 90'ını sildiğiniz için, saklamanız gereken satırları aynı yapıya sahip yeni bir tabloya kopyalamanızı, ardından ALTER TABLE ... SWITCHmevcut tabloyu yeni tabloyla değiştirmek için kullanmanızı ve ardından eski tabloyu bırakmanızı öneririm . Sözdizimi için bu Microsoft Dokümanlar sayfasına bakın .

Genel prensibi gösteren çoğaltma olmadan basit bir test yatağı:

İlk olarak, testimiz için bir veritabanı oluşturacağız:

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

Burada, "A" tablosundan "B" ye satırları taşımak için bir tetikleyici olan ve kurulumunuza yaklaşan birkaç tablo oluşturuyoruz.

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

Burada, "A" ya 1.000.000 satır ekliyoruz ve tetikleme nedeniyle bu satırlar da "B" ye eklenecek.

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

Odada yer kalmaması için işlem günlüğünü temizleyin. İşlem günlüğü verilerini "NUL" cihaza gönderdiğinden bunu üretimde ÇALIŞTIRMAYIN.

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

Bu kod, satırları taşırken etkilenen tabloların hiçbirinin yazılamayacağından emin olmak için bir işlem oluşturur:

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

sp_getapplockVe sp_releaseapplockaynı anda çalışan bu kodun birden çok örneğini önler. Bu kodun bir GUI aracılığıyla yeniden kullanılmasını etkinleştirirseniz bu yararlı olacaktır.

(Uygulama kilitlerinin yalnızca kaynağa erişen her işlem aynı manuel kaynak kilitleme mantığını açıkça uyguladığında etkili olduğunu unutmayın - SQL Server'ın bir satır sırasında otomatik olarak satırları, sayfaları vb. Kilitlediği şekilde tabloyu "kilitleyen" bir sihir yoktur. ekleme / güncelleme işlemi.)

Şimdi, tetikleyici tarafından "B" ye yerleştirildiklerinden emin olmak için satırları "A" ya ekleme işlemini test ediyoruz.

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

SELECT *
FROM dbo.B
WHERE B.d = 'testRow'
+ --------- + --------- + ------------------------- +
| i | d | satır tarihi |
+ --------- + --------- + ------------------------- +
| 1000001 | testRow | 2018-04-13 03: 49: 53.343 |
+ --------- + --------- + ------------------------- +
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.