ลำดับของคอลัมน์ในดัชนี PK มีความสำคัญหรือไม่?


33

ฉันมีตารางที่มีขนาดใหญ่มากไม่กี่แห่งที่มีโครงสร้างพื้นฐานแบบเดียวกัน แต่ละคนมีRowNumber (bigint)และDataDate (date)คอลัมน์ ข้อมูลถูกโหลดโดยใช้ SQLBulkImport ทุกคืนและไม่มีการโหลดข้อมูล "ใหม่" - บันทึกประวัติ (SQL Standard ไม่ใช่ Enterprise ดังนั้นจึงไม่มีการแบ่งพาร์ติชัน)

เนื่องจากข้อมูลแต่ละบิตจำเป็นต้องเชื่อมโยงกลับไปที่ระบบอื่น ๆ และการRowNumber/DataDateรวมกันแต่ละครั้งไม่ซ้ำกันนั่นคือคีย์หลักของฉัน

ฉันสังเกตเห็นว่าเนื่องจากวิธีที่ฉันกำหนด PK ใน SSMS Table Designer RowNumberแสดงรายการที่หนึ่งและDataDateสอง

ฉันยังสังเกตเห็นว่าการกระจายตัวของฉันมักจะสูงมาก ~ 99%

ตอนนี้เพราะแต่ละรายการDataDateปรากฏเพียงครั้งเดียวฉันคาดว่าเครื่องมือสร้างดัชนีจะเพิ่มไปยังหน้าเว็บในแต่ละวัน แต่ฉันสงสัยว่าจริง ๆ แล้วการจัดทำดัชนีอิงตามลำดับRowNumberแรกหรือไม่และต้องเปลี่ยนทุกอย่างอื่นหรือไม่


Rownumberไม่ใช่คอลัมน์ข้อมูลประจำตัว แต่เป็น int ที่สร้างขึ้นโดยระบบภายนอก (น่าเศร้า) DataDateมันรีเซ็ตในช่วงเริ่มต้นของแต่ละคน

ตัวอย่างข้อมูล

RowNumber | DataDate | a | b | c..... 
   1      |2013-08-01| x | y | z 
   2      |2013-08-01| x | y | z 
...
   1      |2013-08-02| x | y | z 
   2      |2013-08-02| x | y | z 
...

ข้อมูลกำลังถูกโหลดRowNumberตามลำดับหนึ่งรายการDataDateต่อการโหลด

กระบวนการนำเข้าเป็น bcp - ฉันได้ลองโหลดไปยังตารางชั่วคราวแล้วเลือกตามลำดับจากที่นั่น ( ORDER BY RowNumber, DataDate) แต่ยังคงมีการกระจายตัวสูง

คำตอบ:


50

ลำดับของคอลัมน์ในดัชนี PK มีความสำคัญหรือไม่?

ใช่แล้ว.

โดยค่าเริ่มต้นข้อ จำกัด คีย์หลักถูกบังคับใช้ใน SQL Server โดยดัชนีคลัสเตอร์ที่ไม่ซ้ำกัน ดัชนีคลัสเตอร์กำหนดลำดับตรรกะของแถวในตาราง อาจมีจำนวนหน้าดัชนีพิเศษที่เพิ่มเข้ามาเพื่อแสดงถึงระดับสูงสุดของดัชนี b-tree แต่ระดับต่ำสุด (leaf) ของดัชนีคลัสเตอร์เป็นเพียงลำดับตรรกะของข้อมูลเอง

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

ด้วยคีย์หลักที่เป็นคลัสเตอร์ของ(RowNumber, DataDate)แถวจะถูกจัดเรียงอย่างมีเหตุผลก่อนRowNumberแล้วจึงDataDate- ดังนั้นแถวทั้งหมดที่RowNumber = 1มีการจัดกลุ่มอย่างมีเหตุผลร่วมกันจากนั้นแถวที่RowNumber = 2เป็นต้น

เมื่อคุณเพิ่มข้อมูลใหม่ ( RowNumbersจาก 1 ถึง n) แถวใหม่อย่างมีเหตุผลอยู่ภายในหน้าเว็บที่มีอยู่ดังนั้น SQL Server จะต้องทำงานแยกหน้าเพื่อให้มีจำนวนมาก กิจกรรมทั้งหมดนี้สร้างงานพิเศษมากมาย (รวมถึงการบันทึกการเปลี่ยนแปลง) เพื่อไม่ให้เกิดผลกำไร

หน้าแยกยังเริ่มว่างประมาณ 50% ดังนั้นการแบ่งมากเกินไปอาจส่งผลให้ความหนาแน่นของหน้าต่ำ (แถวน้อยกว่าที่ดีที่สุดต่อหน้า) เช่นกัน ไม่เพียง แต่เป็นข่าวดีสำหรับการอ่านจากดิสก์ (ความหนาแน่นต่ำ = อ่านเพิ่มเติมหน้า), หน้าความหนาแน่นต่ำยังใช้พื้นที่มากขึ้นในหน่วยความจำเมื่อแคช

การเปลี่ยนดัชนีคลัสเตอร์เป็น(DataDate, RowNumber) หมายความว่าข้อมูลใหม่ (ที่มีอยู่สูงDataDatesกว่าที่เก็บไว้ในปัจจุบัน) จะถูกผนวกเข้ากับท้ายตรรกะของดัชนีคลัสเตอร์ในหน้าใหม่ การดำเนินการนี้จะลบค่าโสหุ้ยที่ไม่จำเป็นของการแยกหน้าและทำให้โหลดเร็วขึ้น ข้อมูลที่กระจัดกระจายน้อยลงก็หมายความว่ากิจกรรมการอ่านล่วงหน้า (การอ่านหน้าจากดิสก์ก่อนที่พวกเขาต้องการสำหรับการสืบค้นที่อยู่ระหว่างดำเนินการ) จะมีประสิทธิภาพมากขึ้น

ถ้าไม่มีอะไรคำสั่งของคุณมีมากมีแนวโน้มการค้นหาบนกว่าDataDate RowNumberดัชนีคลัสเตอร์บน(DataDate, RowNumber) รองรับการค้นหาดัชนีDataDate(และจากนั้นRowNumber) การจัดเรียงที่มีอยู่รองรับการค้นหาเท่านั้นRowNumber(และจากนั้นอาจเปิดDataDateเท่านั้น) คุณอาจจะสามารถลบดัชนี nonclustered ที่มีอยู่DataDateเมื่อคีย์หลักถูกเปลี่ยน ดัชนีคลัสเตอร์จะกว้างกว่าดัชนีที่ไม่ใช่คลัสเตอร์ซึ่งจะมาแทนที่ดังนั้นคุณควรทดสอบเพื่อให้แน่ใจว่าประสิทธิภาพยังคงเป็นที่ยอมรับ

เมื่อนำเข้าข้อมูลใหม่ด้วยbcpคุณอาจได้รับประสิทธิภาพที่สูงขึ้นหากข้อมูลภายในไฟล์นำเข้าถูกจัดเรียงโดยคีย์ดัชนีคลัสเตอร์ (นึกคิด(DataDate, RowNumber) และคุณระบุbcpตัวเลือก:

-h "ORDER(DataDate,RowNumber), TABLOCK"

เพื่อประสิทธิภาพในการโหลดข้อมูลที่ดีที่สุดคุณอาจลองแทรกเม็ดมีดน้อยที่สุด สำหรับข้อมูลเพิ่มเติมดู:


4
คำตอบที่ยอดเยี่ยม - ตอนนี้ฉันรู้ว่าฉันควรทำอย่างไรและทำไม ฉันเคยคิดแบบนั้น แต่ไม่รู้จักเลย! ขอขอบคุณ.
BlueChippy

ลองใช้ LOOOOONG ในขณะที่นำ DB ไปไว้ใน SQL Server ของฉันเพื่อทำการทดสอบ: ก่อนที่จะทำการเปลี่ยนแปลงดัชนีโหลดใช้เวลา 45 นาที ... หลังจากนั้นใช้เวลาเพียง 5 !!!
BlueChippy

13

ใช่คำสั่งนั้นสำคัญ ฉันสงสัยอย่างมากว่าคุณเคยสอบถามโดย RowNumber (เช่นWHERE RowNumber=1) โด่งอนุกรมเวลาจะมีการสอบถามตามวันที่ ( WHERE DataDate BEWEEN @start AND @end) DataDateและแบบสอบถามดังกล่าวจะต้องมีองค์กรคลัสเตอร์โดย

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


ฉันยังมีดัชนีที่ไม่ใช่คลัสเตอร์ใน DataDate ซึ่งตามที่คุณพูดมักจะเป็นWHEREประโยคในแบบสอบถาม
BlueChippy

1
หาก ORDER ของคอลัมน์มีความสำคัญผลกระทบของคำสั่งรวมจะเห็น I / O ของฉันเพิ่มขึ้นหรือไม่ ความคิดของฉันคือการสั่งซื้อโดย RowNumber และดังนั้นจึงต้องทำงานมากกับดัชนีทุกครั้งในขณะที่มันควรจะขึ้นอยู่กับ DataDate?
BlueChippy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.