데이터를 가장 빠르게 제거하는 방법은 무엇입니까?


18

대본:

우리는 두 개의 테이블이 Tbl1Tbl2구독자 서버를. 은 Tbl1게시자에서 복제되고 Server A있으며 두 개의 트리거가 - 삽입 및 업데이트를. 트리거가에 데이터를 삽입하고 업데이트하고 있습니다 Tbl2.

이제 우리 Tbl2는 총 1000+ 백만 레코드를 가진 (약 9 억 레코드)를 제거해야합니다 . 다음은 1 개월에서 1 분 동안의 데이터 배포입니다.

  • 1 개월-14986826 행
  • 하루-483446 행
  • 1 시간-20143 행
  • 1 분-335 행

내가 찾고있는 것;

프로덕션 문제, 데이터 일관성 및 다운 타임없이 데이터를 가장 빠르게 제거하는 방법입니다. 따라서 아래 단계를 수행하려고하지만 붙어 있습니다. (

단계 :

  1. BCP 기존 테이블 Tbl2에서 필요한 데이터를 출력합니다 (약 1 억 개의 레코드, 약 30 분이 소요될 수 있음).
    • 내가 1Fab2018 10:00 PM에 활동을 시작했다고 가정 해 봅시다 .1Fab2018 10:30 PM에 끝났습니다. 활동이 완료 될 때까지 테이블 Tbl2는 델타가되는 새 레코드를 가져옵니다.
  2. 이름이 Tbl3 인 데이터베이스에 새 테이블을 작성하십시오.
  3. 내 보낸 데이터의 새로 작성된 테이블 Tbl3에 BCP (약 1 억 개의 레코드, 약 30 분이 소요될 수 있음)
  4. 복제 작업 중지
  5. BCP-in이 완료되면 tsql 스크립트를 사용하여 새 델타 데이터를 삽입하십시오.

  6. 도전은- 델타“업데이트”진술을 어떻게 처리 하는가?

  7. 복제 시작

추가 질문 :

시나리오를 다루는 가장 좋은 방법은 무엇입니까?

답변:


26

행의 90 %를 삭제하므로 동일한 구조의 새 테이블에 보관해야하는 행을 복사 한 다음 ALTER TABLE ... SWITCH기존 테이블을 새 테이블로 바꾸고 이전 테이블을 삭제하는 것이 좋습니다. 구문 은 이 Microsoft 문서 페이지 를 참조하십시오 .

복제가없는 간단한 테스트 베드로 일반적인 원칙을 보여줍니다.

먼저 테스트를위한 데이터베이스를 만듭니다.

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를 통해이 코드를 재사용 할 수 있도록하는 경우 유용합니다.

응용 프로그램 잠금은 리소스에 액세스하는 모든 프로세스가 동일한 수동 리소스 잠금 논리를 명시 적으로 구현하는 경우에만 유효합니다 . 삽입 / 업데이트 작업)

이제 트리거에 의해 행이 "B"에 삽입되도록 "A"에 행을 삽입하는 프로세스를 테스트합니다.

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.