ทำไมข้อความที่สองINSERT
~ 5x ช้ากว่าประโยคแรก
จากจำนวนข้อมูลบันทึกที่สร้างขึ้นฉันคิดว่าข้อมูลที่สองไม่ผ่านการรับรองสำหรับการบันทึกขั้นต่ำ อย่างไรก็ตามเอกสารประกอบในคู่มือประสิทธิภาพการโหลดข้อมูลระบุว่าส่วนแทรกทั้งสองควรสามารถบันทึกได้น้อยที่สุด ดังนั้นหากการบันทึกขั้นต่ำเป็นความแตกต่างของประสิทธิภาพที่สำคัญทำไมจึงเป็นไปได้ว่าการสืบค้นครั้งที่สองไม่มีคุณสมบัติสำหรับการบันทึกขั้นต่ำ สิ่งที่สามารถทำได้เพื่อปรับปรุงสถานการณ์?
Query # 1: การแทรกแถว 5 มม. โดยใช้ INSERT ... with (TABLOCK)
พิจารณาแบบสอบถามต่อไปนี้ซึ่งแทรกแถว 5MM ลงในกอง แบบสอบถามนี้ดำเนินการใน1 second
และสร้างข้อมูลล็อกธุรกรรมตามการรายงานของ64MB
sys.dm_tran_database_transactions
CREATE TABLE dbo.minimalLoggingTest (n INT NOT NULL)
GO
INSERT INTO dbo.minimalLoggingTest WITH (TABLOCK) (n)
SELECT n
-- Any table/view/sub-query that correctly estimates that it will generate 5MM rows
FROM dbo.fiveMillionNumbers
-- Provides greater consistency on my laptop, where other processes are running
OPTION (MAXDOP 1)
GO
Query # 2: การแทรกข้อมูลเดียวกัน แต่ SQL จะประเมิน # ของแถวต่ำกว่า
ตอนนี้ให้ลองพิจารณาคำถามที่คล้ายกันนี้ซึ่งทำงานกับข้อมูลเดียวกัน แต่เกิดขึ้นจากตาราง (หรือSELECT
คำสั่งที่ซับซ้อนที่มีการรวมหลายรายการในกรณีการผลิตจริงของฉัน) ซึ่งการประเมิน cardinality ต่ำเกินไป แบบสอบถามนี้ดำเนินการ5.5 seconds
และสร้าง461MB
ข้อมูลบันทึกธุรกรรม
CREATE TABLE dbo.minimalLoggingTest (n INT NOT NULL)
GO
INSERT INTO dbo.minimalLoggingTest WITH (TABLOCK) (n)
SELECT n
-- Any table/view/sub-query that produces 5MM rows but SQL estimates just 1000 rows
FROM dbo.fiveMillionNumbersBadEstimate
-- Provides greater consistency on my laptop, where other processes are running
OPTION (MAXDOP 1)
GO
สคริปต์เต็ม
ดูPastebin นี้เพื่อดูชุดสคริปต์เพื่อสร้างข้อมูลการทดสอบและดำเนินการตามสถานการณ์เหล่านี้ ทราบว่าคุณต้องใช้ฐานข้อมูลที่อยู่ในที่รูปแบบการกู้คืนSIMPLE
บริบททางธุรกิจ
เราเคลื่อนย้ายข้อมูลไปรอบ ๆ แถวข้อมูลหลายล้านแถวและสิ่งสำคัญคือการให้การดำเนินการเหล่านี้มีประสิทธิภาพมากที่สุดเท่าที่จะเป็นไปได้ทั้งในแง่ของเวลาดำเนินการและโหลดดิสก์ I / O เริ่มแรกเราอยู่ภายใต้ความประทับใจว่าการสร้างตารางฮีปและการใช้INSERT...WITH (TABLOCK)
เป็นวิธีที่ดีในการทำสิ่งนี้ แต่ตอนนี้เรามีความมั่นใจน้อยลงเนื่องจากเราสังเกตเห็นสถานการณ์ที่แสดงข้างต้นในสถานการณ์การผลิตจริง (แม้ว่าจะมีแบบสอบถามที่ซับซ้อนมากกว่า เวอร์ชั่นที่เรียบง่ายที่นี่)
SELECT
INSERT
การรวมเหล่านี้สร้างการประมาณค่าความเป็นหัวใจที่ไม่ดีสำหรับตัวดำเนินการแทรกตารางสุดท้าย (ซึ่งฉันได้จำลองไว้ในสคริปต์ repro ผ่านการUPDATE STATISTICS
โทรที่ไม่ดี) และดังนั้นจึงไม่ใช่เรื่องง่ายเหมือนการออกUPDATE STATISTICS
คำสั่งเพื่อแก้ไขปัญหา ฉันเห็นด้วยอย่างยิ่งว่าการทำให้แบบสอบถามง่ายขึ้นเพื่อให้เข้าใจว่า Cardinality Estimator เข้าใจได้ง่ายขึ้นอาจเป็นวิธีที่ดี แต่ไม่ใช่วิธีการที่ซับซ้อนในการใช้ตรรกะทางธุรกิจที่ซับซ้อน