データをパージする最も簡単な方法は何ですか?


18

シナリオ:

我々は2つのテーブル持っているTbl1Tbl2加入者サーバ上に。Tbl1出版社から複製されServer A、それは2つのトリガーがある-挿入および更新を。トリガーはにデータを挿入および更新していますTbl2

今、私たちはパージしなければなりません(約9億件のレコード)からTbl2、合計で1億件以上のレコードがあります。以下は、1か月から1分間のデータ分布です。

  • 1か月-14986826行
  • 1日-483446行
  • 1時間-20143行
  • 1分-335行

私が探しているもの;

生産上の問題、データの一貫性、そしておそらくダウンタイムなしでそのデータを消去する最速の方法。だから、私は以下の手順に従うことを考えていますが、立ち往生しています:(

手順:

  1. BCP既存のテーブルTbl2から必要なデータを出力します(約1億レコード、約30分かかる場合があります)。
    • 1Fab2018の午後10:00にアクティビティを開始し、1Fab2018の午後10:30に終了したとします。アクティビティが完了するまでに、テーブルTbl2はデルタになる新しいレコードを取得します
  2. Tbl3という名前でデータベースに新しいテーブルを作成します
  3. 新しく作成されたテーブルTbl3にエクスポートされたデータのBCP(約1億レコード、約30分かかる場合があります)
  4. 複製ジョブを停止します
  5. BCP-inが完了したら、tsqlスクリプトを使用して新しいデルタデータを挿入します。

  6. 課題は-デルタ「更新」ステートメントに対処する方法ですか?

  7. レプリケーションを開始する

追加の質問:

シナリオに対処する最良の方法は何ですか?

回答:


26

行の90%を削除しているため、保持する必要がある行を同じ構造を持つ新しいテーブルにコピーしALTER TABLE ... SWITCH、既存のテーブルを新しいテーブルに置き換えて、古いテーブルを単に削除することをお勧めします。構文については、このMicrosoft Docsページを参照してください。

一般的な原則を示す、複製のない単純なテストベッド:

最初に、テスト用のデータベースを作成します。

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

ここでは、テーブル「A」から「B」に行を移動するトリガーを使用して、セットアップを概算するテーブルをいくつか作成します。

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

ここでは、1,000,000行を「A」に挿入します。トリガーのため、これらの行も「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;

トランザクションログを消去して、空き容量が不足しないようにします。トランザクションログデータを「NUL」デバイスに送信するため、本番環境ではこれを実行しないでください。

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

このコードはトランザクションを作成し、行の移行中に影響を受けるテーブルに書き込みができないようにします。

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_getapplocksp_releaseapplockは、このコードの複数のインスタンスが同時に実行されるのを防ぎます。これは、このコードをGUIで再利用できるようにする場合に役立ちます。

(アプリのロックは、リソースにアクセスするすべてのプロセスが同じ手動リソースロックロジックを明示的に実装している場合にのみ有効であることに注意してください。SQLServerが行、ページなどを挿入/更新操作。)

次に、行を「A」に挿入するプロセスをテストし、トリガーによって「B」に行が挿入されることを確認します。

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

SELECT *
FROM dbo.B
WHERE B.d = 'testRow'
+ --------- + --------- + ------------------------- +
| 私| d | rowdate |
+ --------- + --------- + ------------------------- +
| 1000001 | testRow | 2018-04-13 03:49:53.343 |
+ --------- + --------- + ------------------------- +
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.