ลบประสิทธิภาพสำหรับข้อมูล LOB ใน SQL Server


16

คำถามนี้เกี่ยวข้องกับกระทู้ในฟอรัมนี้

ใช้ SQL Server 2008 Developer Edition บนเวิร์กสเตชันของฉันและคลัสเตอร์เครื่องเสมือนสองโหนด Enterprise Edition ซึ่งฉันอ้างถึง "อัลฟ่าคลัสเตอร์"

เวลาที่ใช้ในการลบแถวที่มีคอลัมน์ varbinary (สูงสุด) เกี่ยวข้องโดยตรงกับความยาวของข้อมูลในคอลัมน์นั้น นั่นอาจฟังดูเป็นเรื่องง่ายในตอนแรก แต่หลังจากการตรวจสอบมันขัดแย้งกับความเข้าใจของฉันเกี่ยวกับวิธีที่ SQL Server ลบแถวทั่วไปและจัดการกับข้อมูลประเภทนี้จริง ๆ

ปัญหาเกิดจากปัญหาการหมดเวลาในการลบ (> 30 วินาที) ที่เราเห็นในเว็บแอปพลิเคชัน. NET ของเรา แต่ฉันได้ทำให้มันง่ายขึ้นเพื่อการอภิปรายนี้

เมื่อลบเรกคอร์ด SQL Server จะทำเครื่องหมายว่าเป็นโกสต์ที่จะล้างข้อมูลโดย Ghost Cleanup Task ในเวลาต่อมาหลังจากการทำธุรกรรม (ดูที่บล็อกของ Paul Randal ) ในการทดสอบการลบสามแถวที่มีข้อมูล 16 KB, 4 MB และ 50 MB ในคอลัมน์ varbinary (สูงสุด) ตามลำดับฉันเห็นสิ่งนี้เกิดขึ้นบนหน้าเว็บที่มีส่วนของข้อมูลในแถวรวมทั้งในธุรกรรม เข้าสู่ระบบ

สิ่งที่ดูเหมือนแปลกสำหรับฉันคือการวาง X ล็อคในหน้าข้อมูล LOB ทั้งหมดในระหว่างการลบและหน้าถูกจัดสรรคืนใน PFS ฉันเห็นสิ่งนี้ในบันทึกธุรกรรมเช่นเดียวกับsp_lockและผลลัพธ์ของdm_db_index_operational_statsDMV ( page_lock_count)

สิ่งนี้จะสร้างคอขวด I / O บนเวิร์กสเตชันและคลัสเตอร์อัลฟาของเราหากหน้าเหล่านั้นไม่ได้อยู่ในแคชแคช ในความเป็นจริงแล้วpage_io_latch_wait_in_msDMV เดียวกันนั้นเป็นระยะเวลาทั้งหมดของการลบและpage_io_latch_wait_countสอดคล้องกับจำนวนหน้าที่ถูกล็อก สำหรับไฟล์ขนาด 50 MB บนเวิร์กสเตชันของฉันแปลได้นานกว่า 3 วินาทีเมื่อเริ่มต้นด้วยแคชบัฟเฟอร์ ( checkpoint/ dbcc dropcleanbuffers) ที่ว่างเปล่าและฉันไม่สงสัยเลยว่ามันจะนานกว่าสำหรับการแตกแฟรกเมนต์หนักและภายใต้การโหลด

ฉันพยายามทำให้แน่ใจว่าไม่เพียงจัดสรรพื้นที่ในแคชเท่านั้น ฉันอ่านข้อมูลในแถว 2 GB จากแถวอื่นก่อนที่จะทำการลบแทนcheckpointวิธีการซึ่งมากกว่าที่กำหนดไว้ในกระบวนการ SQL Server ไม่แน่ใจว่าเป็นการทดสอบที่ถูกต้องหรือไม่เนื่องจากฉันไม่รู้ว่า SQL Server สับเปลี่ยนข้อมูลได้อย่างไร ฉันคิดว่ามันจะผลักดันคนเก่าให้เข้ากับคนใหม่เสมอ

นอกจากนี้ยังไม่ได้ปรับเปลี่ยนหน้า dm_os_buffer_descriptorsฉันนี้สามารถมองเห็นได้ด้วย หน้าสะอาดหลังจากลบในขณะที่จำนวนหน้าที่แก้ไขน้อยกว่า 20 สำหรับการลบทั้งเล็กกลางและใหญ่ทั้งสาม ฉันยังได้เปรียบเทียบผลลัพธ์ของDBCC PAGEการสุ่มตัวอย่างของหน้าที่ค้นหาและไม่มีการเปลี่ยนแปลง (เฉพาะALLOCATEDบิตที่ถูกลบออกจาก PFS) มันเป็นเพียงการจัดสรรคืนพวกเขา

เพื่อพิสูจน์เพิ่มเติมว่าการค้นหาหน้า / deallocations ทำให้เกิดปัญหาฉันลองทดสอบเดียวกันโดยใช้คอลัมน์ filestream แทน vanilla varbinary (สูงสุด) การลบเป็นเวลาคงที่โดยไม่คำนึงถึงขนาด LOB

ดังนั้นก่อนอื่นคำถามทางวิชาการของฉัน:

  1. ทำไม SQL Server จำเป็นต้องค้นหาหน้าข้อมูล LOB ทั้งหมดเพื่อล็อค X นั่นเป็นเพียงรายละเอียดว่าล็อคถูกแสดงในหน่วยความจำได้อย่างไร (เก็บไว้ที่หน้าอย่างใด)? สิ่งนี้ทำให้ผลกระทบของ I / O ขึ้นอยู่กับขนาดข้อมูลเป็นอย่างมากหากไม่ได้แคชอย่างสมบูรณ์
  2. ทำไม X ถึงล็อคอยู่เพียงเพื่อยกเลิกการจัดสรรพวกเขา? มันไม่เพียงพอที่จะล็อคเพียงแค่ดัชนีใบไม้ที่มีส่วนในแถวเนื่องจากการจัดสรรคืนไม่จำเป็นต้องแก้ไขหน้าตัวเอง? มีวิธีอื่นในการรับข้อมูล LOB ที่ล็อคป้องกันหรือไม่
  3. เหตุใดจึงยกเลิกการจัดสรรหน้าใหม่เลยเนื่องจากมีงานพื้นหลังที่อุทิศให้กับงานประเภทนี้อยู่แล้ว?

และที่สำคัญกว่านั้นคำถามที่ปฏิบัติได้ของฉัน:

  • มีวิธีใดที่จะทำให้การลบทำงานแตกต่างกันหรือไม่? เป้าหมายของฉันคือการลบเวลาคงที่โดยไม่คำนึงถึงขนาดคล้ายกับ filestream ที่การล้างข้อมูลใด ๆ เกิดขึ้นในพื้นหลังหลังจากความจริง มันเป็นสิ่งที่กำหนดค่าหรือไม่? ฉันกำลังเก็บของแปลก ๆ อยู่หรือเปล่า?

นี่คือวิธีการทำซ้ำการทดสอบที่อธิบาย (ดำเนินการผ่านหน้าต่างแบบสอบถาม SSMS):

CREATE TABLE [T] (
    [ID] [uniqueidentifier] NOT NULL PRIMARY KEY,
    [Data] [varbinary](max) NULL
)

DECLARE @SmallID uniqueidentifier
DECLARE @MediumID uniqueidentifier
DECLARE @LargeID uniqueidentifier

SELECT @SmallID = NEWID(), @MediumID = NEWID(), @LargeID = NEWID()
-- May want to keep these IDs somewhere so you can use them in the deletes without var declaration

INSERT INTO [T] VALUES (@SmallID, CAST(REPLICATE(CAST('a' AS varchar(max)), 16 * 1024) AS varbinary(max)))
INSERT INTO [T] VALUES (@MediumID, CAST(REPLICATE(CAST('a' AS varchar(max)), 4 * 1024 * 1024) AS varbinary(max)))
INSERT INTO [T] VALUES (@LargeID, CAST(REPLICATE(CAST('a' AS varchar(max)), 50 * 1024 * 1024) AS varbinary(max)))

-- Do this before test
CHECKPOINT
DBCC DROPCLEANBUFFERS
BEGIN TRAN

-- Do one of these deletes to measure results or profile
DELETE FROM [T] WHERE ID = @SmallID
DELETE FROM [T] WHERE ID = @MediumID
DELETE FROM [T] WHERE ID = @LargeID

-- Do this after test
ROLLBACK

นี่คือผลลัพธ์บางส่วนจากการทำโปรไฟล์การลบในเวิร์กสเตชันของฉัน:

| ประเภทคอลัมน์ | ลบขนาด | ระยะเวลา (ms) | อ่าน | เขียน | ซีพียู |
-------------------------------------------------- ------------------
| VarBinary | 16 KB | 40 | 13 | 2 | 0 |
| VarBinary | 4 MB | 952 | 2318 | 2 | 0 |
| VarBinary | 50 MB | 2976 | 28594 | 1 | 62 |
-------------------------------------------------- ------------------
| FileStream | 16 KB | 1 | 12 | 1 | 0 |
| FileStream | 4 MB | 0 | 9 | 0 | 0 |
| FileStream | 50 MB | 1 | 9 | 0 | 0 |

เราไม่สามารถใช้ filestream แทนได้เพราะ:

  1. การกระจายขนาดข้อมูลของเราไม่รับประกัน
  2. ในทางปฏิบัติเราเพิ่มข้อมูลในกลุ่มข้อมูลจำนวนมากและ filestream ไม่สนับสนุนการอัปเดตบางส่วน เราจะต้องออกแบบสิ่งนี้

อัปเดต 1

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

SELECT MAX([Current LSN]) FROM fn_dblog(NULL, NULL)
--0000002f:000001d9:0001

BEGIN TRAN
DELETE FROM [T] WHERE ID = @ID

SELECT
    SUM(
        DATALENGTH([RowLog Contents 0]) +
        DATALENGTH([RowLog Contents 1]) +
        DATALENGTH([RowLog Contents 3]) +
        DATALENGTH([RowLog Contents 4])
    ) [RowLog Contents Total],
    SUM(
        DATALENGTH([Log Record])
    ) [Log Record Total]
FROM fn_dblog(NULL, NULL)
WHERE [Current LSN] > '0000002f:000001d9:0001'

สำหรับไฟล์กว่า 5 1651 | 171860ล้านบาทในขนาดนี้กลับมา

เพิ่มเติมฉันคาดหวังว่าหน้าตัวเองจะสกปรกหากข้อมูลถูกเขียนลงในบันทึก ดูเหมือนว่าจะมีการบันทึกการจัดสรรคืนเท่านั้นซึ่งตรงกับสิ่งที่สกปรกหลังจากลบ

อัปเดต 2

ฉันได้รับการตอบกลับจาก Paul Randal เขายืนยันความจริงที่ว่ามันต้องอ่านทุกหน้าเพื่อสำรวจต้นไม้และค้นหาว่าหน้าไหนที่จะจัดสรรคืนและระบุว่าไม่มีทางอื่นที่จะค้นหาหน้าไหน นี่เป็นคำตอบครึ่งหนึ่งสำหรับ 1 & 2 (แต่ไม่ได้อธิบายความจำเป็นในการล็อคข้อมูลนอกแถว แต่นั่นเป็นมันฝรั่งขนาดเล็ก)

คำถามที่ 3 ยังคงเปิดอยู่: เหตุใดจึงยกเลิกการจัดสรรหน้าใหม่หากมีงานพื้นหลังที่ต้องทำการล้างข้อมูลเพื่อลบ

และแน่นอนคำถามที่สำคัญทั้งหมด: มีวิธีลดพฤติกรรมการลบตามขนาดนี้โดยตรงหรือไม่ ฉันคิดว่านี่จะเป็นปัญหาที่พบบ่อยกว่านี้เว้นแต่เราจะเป็นคนเดียวที่จัดเก็บและลบแถว 50 MB ใน SQL Server ทุกคนที่ทำงานที่นี่มีงานเก็บขยะหรือไม่


ฉันหวังว่าจะมีทางออกที่ดีกว่า แต่ยังไม่พบ ฉันมีสถานการณ์ของการบันทึกจำนวนมากของแถวขนาดที่แตกต่างกันสูงสุด 1MB + และฉันมีกระบวนการ "ล้างข้อมูล" เพื่อลบระเบียนเก่า เนื่องจากการลบช้ามากฉันจึงต้องแยกมันออกเป็นสองขั้นตอนก่อนอื่นให้ลบการอ้างอิงระหว่างตาราง (ซึ่งเร็วมาก) จากนั้นจึงลบแถวกำพร้า งานลบเฉลี่ย ~ 2.2 วินาที / MB เพื่อลบข้อมูล ดังนั้นฉันต้องลดความขัดแย้งดังนั้นฉันจึงมีกระบวนงานที่เก็บไว้ด้วย "DELETE TOP (250)" ภายในวงจนกว่าจะไม่มีแถวถูกลบอีกต่อไป
Abacus

คำตอบ:


5

ฉันไม่สามารถบอกได้ว่าทำไมมันไม่มีประสิทธิภาพมากกว่าในการลบ VARBINARY (MAX) มากไปกว่าการสตรีมไฟล์ แต่มีแนวคิดหนึ่งที่คุณควรพิจารณาหากคุณกำลังพยายามหลีกเลี่ยงเวลาจากเว็บแอปพลิเคชันของคุณเมื่อลบ LOBS เหล่านี้ คุณสามารถเก็บค่า VARBINARY (MAX) ในตารางแยกต่างหาก (ให้เรียกมันว่า tblLOB) ที่อ้างอิงโดยตารางต้นฉบับ (เรียกว่า tblParent นี้)

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


ขอบคุณ นั่นคือหนึ่งในตัวเลือกของเราบนกระดาน ตารางเป็นระบบไฟล์และขณะนี้เรากำลังอยู่ระหว่างการแยกข้อมูลไบนารี่ออกเป็นฐานข้อมูลที่แยกจากเมตาลำดับชั้นอย่างสมบูรณ์ เราสามารถทำตามที่คุณพูดและลบแถวลำดับชั้นและให้กระบวนการ GC ล้างแถว LOB ที่ไม่ได้ใช้งาน หรือมีการประทับเวลาลบพร้อมข้อมูลเพื่อให้บรรลุเป้าหมายเดียวกัน นี่คือเส้นทางที่เราอาจใช้หากไม่มีคำตอบที่น่าพอใจสำหรับปัญหา
Jeremy Rosenberg

1
ฉันจะระมัดระวังการมีเวลาประทับเพื่อระบุว่ามันถูกลบ มันจะใช้งานได้ แต่ในที่สุดคุณจะมีพื้นที่ใช้งานมากมายในแถวที่ใช้งาน คุณจะต้องมีกระบวนการ gc บางประเภทขึ้นอยู่กับว่าถูกลบไปมากน้อยเพียงใดและจะมีผลกระทบน้อยกว่าในการลบน้อยลงเป็นประจำ
Ian Chamberland
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.