การแตกแฟรกเมนต์ดัชนีขณะประมวลผลอย่างต่อเนื่อง


10

SQL Server 2005

ฉันต้องสามารถประมวลผลระเบียน 350M อย่างต่อเนื่องในตารางบันทึก 900M แบบสอบถามที่ฉันใช้เพื่อเลือกระเบียนที่จะดำเนินการจะแยกส่วนอย่างไม่ดีตามที่ฉันดำเนินการและฉันจำเป็นต้องหยุดการประมวลผลเพื่อสร้างดัชนีใหม่ จำลองข้อมูล & คิวรีแบบหลอก ...

/**************************************/
CREATE TABLE [Table] 
(
    [PrimaryKeyId] [INT] IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
    [ForeignKeyId] [INT] NOT NULL,
    /* more columns ... */
    [DataType] [CHAR](1) NOT NULL,
    [DataStatus] [DATETIME] NULL,
    [ProcessDate] [DATETIME] NOT NULL,
    [ProcessThreadId] VARCHAR (100) NULL
);

CREATE NONCLUSTERED INDEX [Idx] ON [Table] 
(
    [DataType],
    [DataStatus],
    [ProcessDate],
    [ProcessThreadId]
);
/**************************************/

/**************************************/
WITH cte AS (
    SELECT TOP (@BatchSize) [PrimaryKeyId], [ProcessThreadId]
    FROM [Table] WITH ( ROWLOCK, UPDLOCK, READPAST )
    WHERE [DataType] = 'X'
    AND [DataStatus] IS NULL
    AND [ProcessDate] < DATEADD(m, -2, GETDATE()) -- older than 2 months
    AND [ProcessThreadId] IS NULL
)
UPDATE cte
SET [ProcessThreadId] = @ProcessThreadId;

SELECT * FROM [Table] WITH ( NOLOCK )
WHERE [ProcessThreadId] = @ProcessThreadId;
/**************************************/

เนื้อหาข้อมูล ...
ในขณะที่คอลัมน์ [ประเภทข้อมูล] ถูกพิมพ์เป็น CHAR (1) ประมาณ 35% ของระเบียนทั้งหมดเท่ากับ 'X' กับส่วนที่เหลือเท่ากับ 'A'
เฉพาะในเร็กคอร์ดที่ [DataType] เท่ากับ 'X' ประมาณ 10% จะมีค่า NOT NULL [DataStatus]

คอลัมน์ [ProcessDate] และ [ProcessThreadId] จะได้รับการอัปเดตสำหรับทุกระเบียนที่ประมวลผล
คอลัมน์ [ประเภทข้อมูล] ได้รับการอัปเดต ('X' เปลี่ยนเป็น 'A') ประมาณ 10% ของเวลา
คอลัมน์ [DataStatus] ได้รับการอัปเดตน้อยกว่า 1% ของเวลา

สำหรับตอนนี้โซลูชันของฉันคือการเลือกคีย์หลักของระเบียนทั้งหมดเพื่อประมวลผลลงในตารางการประมวลผลแยกต่างหาก ฉันลบปุ่มต่าง ๆ ในขณะที่ฉันประมวลผลเพื่อให้เป็นชิ้นส่วนดัชนีที่ฉันจัดการกับระเบียนน้อยลง

อย่างไรก็ตามนี่ไม่เหมาะกับเวิร์กโฟลว์ที่ฉันต้องการเพื่อให้ข้อมูลเหล่านี้ได้รับการประมวลผลอย่างต่อเนื่องโดยไม่มีการแทรกแซงด้วยตนเองและการหยุดทำงานที่สำคัญ ฉันคาดว่าจะหยุดทำงานเป็นประจำทุกไตรมาสสำหรับงานบ้านทำความสะอาด แต่ตอนนี้หากไม่มีตารางการประมวลผลแยกกันฉันไม่สามารถผ่านการประมวลผลได้แม้แต่ครึ่งหนึ่งของชุดข้อมูลโดยที่การแตกแฟรกเมนต์ไม่ดีเท่าที่จำเป็นในการหยุดและสร้างดัชนีใหม่

คำแนะนำสำหรับการจัดทำดัชนีหรือรูปแบบข้อมูลอื่น จำเป็นต้องมีรูปแบบการวิจัยหรือไม่?
ฉันมีการควบคุมเต็มรูปแบบของข้อมูลและซอฟต์แวร์กระบวนการดังนั้นจึงไม่มีอะไรอยู่นอกตาราง


มีใครคิดเช่นกัน: ดัชนีของคุณดูเหมือนจะผิดลำดับ: ควรจะเลือกได้มากที่สุดถึงเลือกน้อยที่สุด ดังนั้น ProcessThreadId, ProcessDate, DataStatus, DataType อาจจะ?
gbn

เราได้โฆษณามันในการแชทของเรา คำถามที่ดีมาก chat.stackexchange.com/rooms/179/the-heap
gbn

ฉันอัปเดตแบบสอบถามเพื่อให้เป็นตัวแทนที่ถูกต้องของการเลือก ฉันมีหลายเธรดที่ทำงานพร้อมกันนี้ ฉันได้สังเกตคำแนะนำคำสั่งที่เลือก ขอบคุณ
Chris Gallucci

@ChrisGallucci มาแชทถ้าคุณสามารถ ...
JNK

คำตอบ:


4

สิ่งที่คุณทำคือคุณใช้ตารางเป็นคิว การอัปเดตของคุณเป็นวิธีการถอนพิษ แต่ดัชนีคลัสเตอร์บนโต๊ะเป็นตัวเลือกที่ไม่ดีสำหรับคิว การใช้ตารางเป็นคิวจริง ๆ กำหนดข้อกำหนดที่ค่อนข้างเข้มงวดในการออกแบบตาราง ดัชนีคลัสเตอร์ของคุณต้องเป็นลำดับ dequeue ในกรณีนี้น่าจะ([DataType], [DataStatus], [ProcessDate])เป็น คุณสามารถใช้คีย์หลักเป็นข้อ จำกัดnonclustered ดร็อปดัชนีที่ไม่ทำคลัสเตอร์Idxเนื่องจากคีย์ที่ทำคลัสเตอร์มีบทบาทอยู่

อีกส่วนที่สำคัญของตัวต่อคือการรักษาขนาดของแถวให้คงที่ระหว่างการประมวลผล คุณได้ประกาศสิ่งที่บอกProcessThreadIdเป็นVARCHAR(100)นัยถึงแถวที่เพิ่มขึ้นและลดขนาดเช่นเดียวกับที่ 'ถูกประมวลผล' เนื่องจากค่าเขตข้อมูลเปลี่ยนจาก NULL เป็นไม่ใช่ null รูปแบบที่ขยายและย่อขนาดบนแถวทำให้เกิดการแยกหน้าและการแยกส่วน ฉันไม่สามารถจินตนาการรหัสเธรดที่เป็น 'VARCHAR (100)' ได้ INTใช้ชนิดที่มีความยาวคงที่บางทีอาจจะเป็น

ในฐานะที่เป็นบันทึกย่อด้านข้างคุณไม่จำเป็นต้อง dequeue ในสองขั้นตอน (UPDATE ตามด้วย SELECT) คุณสามารถใช้คำสั่ง OUTPUT ตามที่อธิบายไว้ในบทความด้านบน:

/**************************************/
CREATE TABLE [Table] 
(
    [PrimaryKeyId] [INT] IDENTITY(1,1) NOT NULL PRIMARY KEY NONCLUSTERED,
    [ForeignKeyId] [INT] NOT NULL,
    /* more columns ... */
    [DataType] [CHAR](1) NOT NULL,
    [DataStatus] [DATETIME] NULL,
    [ProcessDate] [DATETIME] NOT NULL,
    [ProcessThreadId] INT NULL
);

CREATE CLUSTERED INDEX [Cdx] ON [Table] 
(
    [DataType],
    [DataStatus],
    [ProcessDate]
);
/**************************************/

declare @BatchSize int, @ProcessThreadId int;

/**************************************/
WITH cte AS (
    SELECT TOP (@BatchSize) [PrimaryKeyId], [ProcessThreadId] , ... more columns 
    FROM [Table] WITH ( ROWLOCK, UPDLOCK, READPAST )
    WHERE [DataType] = 'X'
    AND [DataStatus] IS NULL
    AND [ProcessDate] < DATEADD(m, -2, GETDATE()) -- older than 2 months
    AND [ProcessThreadId] IS NULL
)
UPDATE cte
SET [ProcessThreadId] = @ProcessThreadId
OUTPUT DELETED.[PrimaryKeyId] , ... more columns ;
/**************************************/

นอกจากนี้ฉันจะพิจารณาย้ายรายการที่ประมวลผลสำเร็จลงในตารางเก็บถาวรที่แตกต่างกัน คุณต้องการให้คิวคิวของคุณลอยอยู่ใกล้กับขนาดศูนย์คุณไม่ต้องการให้ตารางเติบโตขึ้นเนื่องจากพวกเขาเก็บ 'ประวัติ' จากรายการเก่าที่ไม่ต้องการ คุณอาจพิจารณาการแบ่งพาร์ติชัน[ProcessDate]เป็นทางเลือก (เช่นหนึ่งพาร์ติชันปัจจุบันที่ใช้งานอยู่ซึ่งทำหน้าที่เป็นคิวและเก็บข้อมูลรายการด้วย NULL ProcessDate และอีกพาร์ติชันสำหรับทุกอย่างที่ไม่ใช่ null หรือหลายพาร์ติชันสำหรับ non-null ถ้าคุณต้องการใช้ประสิทธิภาพ ลบ (ปิด) สำหรับข้อมูลที่ผ่านช่วงเวลาเก็บรักษาที่กำหนดถ้าสิ่งต่างๆเริ่มร้อนคุณสามารถแบ่งพาร์ติชันเพิ่มเติมได้[DataType] ถ้ามันมีการเลือกที่เพียงพอ แต่การออกแบบนั้นจะซับซ้อนมากเนื่องจากต้องมีการแบ่งพาร์ติชันโดยคอลัมน์ที่คำนวณแล้ว (คอลัมน์คอมโพสิตที่ติดกัน [DataType] และ [ProcessingDate]


3

ฉันจะเริ่มต้นด้วยการย้ายProcessDateและProcessthreadidเขตข้อมูลไปยังตารางอื่น

ตอนนี้ทุกแถวที่คุณเลือกจากดัชนีหน้ากว้างนี้ต้องได้รับการอัปเดตด้วย

หากคุณย้ายทั้งสองฟิลด์ไปยังตารางอื่นปริมาณการอัพเดทของคุณบนตารางหลักจะถูกตัด 90% ซึ่งควรดูแลการกระจายตัวส่วนใหญ่

คุณจะยังคงมีการแตกแฟรกเมนต์ในตารางใหม่ แต่จะง่ายต่อการจัดการบนตารางที่แคบกว่าโดยมีข้อมูลน้อยลง


สิ่งนี้และการแยกข้อมูลตาม [DataType] ควรทำให้ฉันได้ในที่ที่ฉันต้องการ ขณะนี้ฉันอยู่ในขั้นตอนการออกแบบ (ออกแบบใหม่จริง ๆ ) ดังนั้นจึงต้องใช้เวลาสักครู่ก่อนที่ฉันจะมีโอกาสทดสอบการเปลี่ยนแปลงนี้
Chris Gallucci
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.