シナリオ:
サブスクライバーサーバーに2つのテーブルTbl1
&Tbl2
があります。 Tbl1
はパブリッシャーServer A
から複製されており、挿入と更新の2つのトリガーがあります。トリガーはデータをTbl2
に挿入および更新しています。
ここで、合計で1億を超えるレコードがあるTbl2
から(約9億レコード)を削除する必要があります。以下は、1か月から1分のデータ分布です。
私が探しているもの;
本番の問題、データの整合性、および場合によってはダウンタイムなしでそのデータをパージする最も速い方法。だから、私は以下の手順に従うことを考えていますが、:(
手順:
BCP-inが完了したら、tsqlスクリプトを使用して新しいデルタデータを挿入します。
課題は-デルタの「更新」ステートメントの処理方法?
レプリケーションを開始する
追加の質問:
シナリオに対処する最良の方法は何ですか?
行の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
ここでは、「A」に1,000,000行を挿入します。トリガーにより、これらの行も「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_getapplock
およびsp_releaseapplock
は、このコードの複数のインスタンスが同時に実行されるのを防ぎます。これは、このコードをGUIから再利用できるようにする場合に役立ちます。
(アプリのロックは、リソースにアクセスするeveryプロセスが同じ手動のリソースロックロジックを明示的に実装している場合にのみ有効です-「ロック」する魔法はありませんSQL Serverが挿入/更新操作中に行やページなどを自動的にロックするのと同じ方法でテーブルを作成します。)
次に、行を「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 | + --------- + --------- + ------------- ------------ +