วิธีล้างข้อมูลเร็วที่สุดคืออะไร


18

สถานการณ์:

เรามีสองตารางTbl1& Tbl2บนเซิร์ฟเวอร์สมาชิก Tbl1จะถูกจำลองแบบจากสำนักพิมพ์Server Aและมันมีสองทริกเกอร์ - แทรกและการปรับปรุง Tbl2ทริกเกอร์แทรกและปรับปรุงข้อมูลลงใน

ตอนนี้เราต้องกำจัด (ประมาณ 900 ล้านบันทึก) จากTbl2ที่มีทั้งหมด 1,000+ ล้านบันทึก ด้านล่างคือการกระจายข้อมูลสำหรับหนึ่งเดือนถึงหนึ่งนาที

  • หนึ่งเดือน - 14986826 แถว
  • หนึ่งวัน - 483446 แถว
  • หนึ่งชั่วโมง - 20143 แถว
  • หนึ่งนาที - 335 แถว

สิ่งที่ฉันกำลังมองหา

วิธีที่เร็วที่สุดในการล้างข้อมูลนั้นโดยไม่มีปัญหาการผลิตความสอดคล้องของข้อมูลและอาจไม่มีการหยุดทำงาน ดังนั้นฉันคิดว่าจะทำตามขั้นตอนด้านล่าง แต่ติดอยู่ :(

ขั้นตอน:

  1. BCP จากข้อมูลที่ต้องการจากตาราง Tbl2 ที่มีอยู่ (ประมาณ 100 ล้านบันทึกอาจใช้เวลาประมาณ 30 นาที)
    • สมมติว่าฉันเริ่มทำกิจกรรมในวันที่ 1Fab2018 10:00 PM ก็เสร็จสิ้นที่ 1Fab2018 22:30 น ตามกิจกรรมเวลาจะเสร็จสมบูรณ์ตาราง Tbl2 จะได้รับระเบียนใหม่ที่กลายเป็นเดลต้า
  2. สร้างตารางใหม่ในฐานข้อมูลที่มีชื่อ Tbl3
  3. BCP ในข้อมูลที่ส่งออกไปยังตาราง Tbl3 ที่สร้างขึ้นใหม่ (ประมาณ 100 ล้านระเบียนอาจใช้เวลาประมาณ 30 นาที)
  4. หยุดงานการจำลองแบบ
  5. เมื่อ BCP-in เสร็จสมบูรณ์ให้ใช้สคริปต์ tsql เพื่อแทรกข้อมูลเดลต้าใหม่

  6. The Challenge is -วิธีจัดการกับคำสั่งเดลต้า“ อัพเดต”?

  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_getapplockและsp_releaseapplockป้องกันไม่ให้เกิดกรณีหลายรหัสนี้ทำงานในเวลาเดียวกัน สิ่งนี้จะเป็นประโยชน์หากคุณเปิดใช้งานรหัสนี้เพื่อนำกลับมาใช้ใหม่ผ่าน GUI

(โปรดทราบว่าแอปล็อคจะมีผลก็ต่อเมื่อทุกกระบวนการเข้าถึงทรัพยากรใช้ตรรกะการล็อกทรัพยากรด้วยตนเองอย่างชัดเจน - ไม่มีเวทมนต์ที่ "ล็อค" ตารางในลักษณะเดียวกับที่ 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 |
+ --------- + --------- + + -------------------------
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.