การจัดทำดัชนี PK GUID ใน SQL Server 2012


13

นักพัฒนาของฉันได้ติดตั้งแอปพลิเคชันของพวกเขาเพื่อใช้ GUID เป็น PK สำหรับตารางทั้งหมดของพวกเขาและโดยค่าเริ่มต้น SQL Server ได้ตั้งค่าดัชนีคลัสเตอร์บน PK เหล่านี้

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

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

นี่จะทำให้คีย์การทำคลัสเตอร์กว้างเกินไปและจะเพิ่มประสิทธิภาพสำหรับการเขียนหรือไม่ การอ่านมีความสำคัญเช่นกัน แต่การเขียนอาจเป็นปัญหาที่ใหญ่กว่าในตอนนี้


1
GUID นั้นถูกสร้างขึ้นมาอย่างไร NEWID หรือ NEWSEQUENTIALID
swasheck

6
Guid ที่เป็นกลุ่มและประสิทธิภาพในการแทรกควรอยู่ในประโยคหากคำที่นำหน้า "ประสิทธิภาพ" ถูกย่อให้เล็กที่สุด
billinkc

2
นำนักพัฒนาเหล่านั้นออกไปทานอาหารกลางวันและอธิบายให้พวกเขาทราบว่าหากพวกเขาใช้ NEWID () อีกครั้งเป็นคีย์หลักคุณจะโทษประสิทธิภาพที่แย่ของพวกเขา พวกเขาจะถามคุณอย่างรวดเร็วว่าจะทำอย่างไรเพื่อป้องกันไม่ให้ ณ จุดนี้คุณบอกว่าใช้ตัวตน (1,1) แทน (อาจเป็นการใช้งานเกินขนาดเล็กน้อย แต่ 9 เท่าจาก 10 ที่ใช้งานได้)
Max Vernon

3
เหตุผลสำหรับความเกลียดชังของเราคือว่าพวกเขามีความกว้าง (16 ไบต์) และเมื่อไม่ได้สร้างด้วยnewsequentialidจะสุ่ม คีย์แบบคลัสเตอร์จะดีที่สุดเมื่อปุ่มนั้นแคบและเพิ่มขึ้น GUID ตรงกันข้าม: ไขมันและสุ่ม ลองนึกภาพชั้นหนังสือที่เกือบเต็มไปด้วยหนังสือ ใน OED มาและเนื่องจากการสุ่มของ guids มามันแทรกอยู่ตรงกลางของชั้นวาง เพื่อให้สิ่งต่าง ๆ ได้รับคำสั่งหนังสือที่ถูกต้องครึ่งหนึ่งต้องถูกเขวี้ยงไปยังที่ใหม่ซึ่งเป็นงานที่ต้องใช้เวลามาก นั่นคือสิ่งที่ GUID ทำกับฐานข้อมูลของคุณและประสิทธิภาพการฆ่า
billinkc

7
วิธีการแก้ไขปัญหาของการใช้ uniqueidentifiers คือการกลับไปที่กระดานวาดภาพและไม่ใช้ uniqueidentifiers มันไม่ได้แย่มากถ้าระบบมีขนาดเล็ก แต่ถ้าคุณมีตาราง + แถวอย่างน้อย 2-3 ล้านแถว (หรือตารางที่ใหญ่กว่านั้น) คุณจะถูกแบนโดยใช้เครื่องมือระบุตัวตน
Jon Seigel

คำตอบ:


20

ปัญหาหลักของ GUID โดยเฉพาะปัญหาที่ไม่ต่อเนื่องคือ:

  • ขนาดของคีย์ (16 ไบต์เทียบกับ 4 ไบต์สำหรับ INT): หมายความว่าคุณเก็บข้อมูลจำนวน 4 เท่าในคีย์ของคุณพร้อมกับพื้นที่เพิ่มเติมนั้นสำหรับดัชนีใด ๆ หากนี่เป็นดัชนีคลัสเตอร์ของคุณ
  • การแตกแฟรกเมนต์ของดัชนี: เป็นไปไม่ได้จริง ๆ ที่จะคงการจัดระเบียบคอลัมน์ GUID ที่ไม่ต่อเนื่องไว้เนื่องจากลักษณะที่สุ่มโดยสมบูรณ์ของค่าคีย์

ดังนั้นสิ่งนี้หมายถึงสถานการณ์ของคุณ? มันมากับการออกแบบของคุณ หากระบบของคุณเกี่ยวกับการเขียนและคุณไม่ต้องกังวลเกี่ยวกับการดึงข้อมูลแสดงว่า Thomas K นั้นถูกต้อง อย่างไรก็ตามคุณต้องจำไว้ว่าด้วยการใช้กลยุทธ์นี้คุณจะสร้างปัญหาที่อาจเกิดขึ้นมากมายสำหรับการอ่านข้อมูลและจัดเก็บ ในฐานะที่เป็นJon Seigelชี้ให้เห็นคุณจะได้ครอบครองพื้นที่มากขึ้นและเป็นหลักมีหน่วยความจำขยายตัว

คำถามหลักเกี่ยวกับ GUID คือความจำเป็น นักพัฒนาอย่างพวกเขาเพราะพวกเขารับประกันความเป็นเอกลักษณ์ระดับโลก แต่เป็นโอกาสที่หาได้ยากที่ความเป็นเอกลักษณ์ประเภทนี้จำเป็น แต่ให้พิจารณาว่าหากจำนวนสูงสุดของคุณน้อยกว่า 2,147,483,647 (ค่าสูงสุดของจำนวนเต็ม 4 ไบต์ที่ลงนามแล้ว) คุณอาจไม่ได้ใช้ประเภทข้อมูลที่เหมาะสมสำหรับคีย์ของคุณ แม้จะใช้ BIGINT (8 ไบต์) ค่าสูงสุดของคุณคือ 9,223,372,036,854,775,807 โดยทั่วไปจะเพียงพอสำหรับฐานข้อมูลที่ไม่ใช่สากล (และฐานข้อมูลโกลบอลจำนวนมาก) หากคุณต้องการค่าที่เพิ่มขึ้นอัตโนมัติสำหรับคีย์เฉพาะ

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

ข้อมูลเพิ่มเติม:


13

ไม่มีอะไรผิดปกติกับ GUID ในฐานะคีย์และคลัสเตอร์ในระบบ OLTP (ยกเว้นว่าคุณมีดัชนีจำนวนมากบนโต๊ะที่ประสบปัญหาจากขนาดที่เพิ่มขึ้นของคลัสเตอร์) ตามจริงแล้วพวกมันสามารถปรับขนาดได้มากกว่าคอลัมน์ประจำตัว

มีความเชื่ออย่างกว้างขวางว่า GUID เป็นปัญหาที่ยิ่งใหญ่ใน SQL Server ซึ่งส่วนใหญ่เป็นเรื่องที่ผิด ตามความเป็นจริง GUID สามารถปรับขนาดได้อย่างมีนัยสำคัญบนกล่องที่มีมากกว่า 8 แกน:

ฉันขอโทษ แต่นักพัฒนาซอฟต์แวร์ของคุณถูกต้อง กังวลเกี่ยวกับสิ่งอื่น ๆ ก่อนที่คุณจะกังวลเกี่ยวกับ GUID

อ้อและสุดท้าย: ทำไมคุณต้องการดัชนีคลัสเตอร์ตั้งแต่แรก? หากความกังวลของคุณเป็นระบบ OLTP ที่มีดัชนีขนาดเล็กจำนวนมากคุณน่าจะดีกว่าด้วยกอง

ให้เราพิจารณาว่าการกระจายตัวของข้อมูลใดบ้าง (ซึ่ง GUID จะแนะนำ) ให้อ่านของคุณ มีสามปัญหาที่สำคัญกับการกระจายตัวของ:

  1. หน้าแยก I / O ดิสก์ค่าใช้จ่าย
  2. ครึ่งหน้าเต็มไม่ได้เป็นหน่วยความจำที่มีประสิทธิภาพเท่ากับเต็มหน้า
  3. มันทำให้หน้าถูกจัดเก็บนอกลำดับซึ่งทำให้ I / O ตามลำดับมีโอกาสน้อยลง

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

โฆษณา 1) หากคุณต้องการเพิ่มขนาดคุณสามารถซื้อ I / O ได้ แม้แต่ Samsung / Intel 512GB SSD ราคาถูก (เพียงไม่กี่ USD / GB) คุณจะได้รับมากกว่า 100K IOPS คุณจะไม่ต้องเสียเวลาอีกต่อไปในระบบซ็อกเก็ต 2 ตัว และถ้าคุณควรเจอสิ่งนั้นให้ซื้อเพิ่มอีกหนึ่งชุด

โฆษณา 2) หากคุณลบตารางคุณจะมีครึ่งหน้าเต็มอยู่แล้ว และแม้ว่าคุณจะทำไม่ได้หน่วยความจำราคาถูกและสำหรับทุกคน แต่เป็นระบบ OLTP ที่ใหญ่ที่สุด - ข้อมูลน่าสนใจควรจะอยู่ที่นั่น การหาแพ็คข้อมูลลงในหน้าต่างๆเป็นการเพิ่มประสิทธิภาพย่อยเมื่อคุณกำลังมองหาเครื่องชั่ง

โฆษณา 3) ตารางที่สร้างจากการแบ่งหน้าบ่อย ๆ ข้อมูลที่มีการแยกส่วนอย่างมากจะสุ่ม I / O ที่ความเร็วเดียวกับที่ตารางเต็มตามลำดับ

สำหรับการเข้าร่วมนั้นมีประเภทการเข้าร่วมที่สำคัญสองประเภทที่คุณน่าจะเห็นใน OLTP เช่นปริมาณงาน: แฮชและลูป ให้ดูแต่ละด้าน:

การเข้าร่วมแฮช:การแฮชการรวมเข้าจะถือว่าตารางเล็ก ๆ ถูกสแกนและโดยทั่วไปจะเป็นการค้นหาที่ใหญ่กว่า ตารางขนาดเล็กมีแนวโน้มที่จะอยู่ในหน่วยความจำดังนั้น I / O ไม่ได้เป็นความกังวลของคุณที่นี่ เราได้สัมผัสกับความจริงที่ว่าการค้นหามีค่าใช้จ่ายเท่ากันในดัชนีการแยกส่วนเช่นเดียวกับในดัชนีที่ไม่มีการแยกส่วน

การเข้าร่วมแบบวนซ้ำ: ค้นหาตารางด้านนอก ราคาเดียวกัน

คุณอาจมีการสแกนตารางที่ไม่ดีจำนวนมากเกิดขึ้น - แต่ GUID นั้นไม่ใช่ความกังวลของคุณอีกแล้วการจัดทำดัชนีที่เหมาะสมคือ

ตอนนี้คุณอาจมีช่วงการสแกนที่ถูกต้องเกิดขึ้น (โดยเฉพาะอย่างยิ่งเมื่อเข้าร่วมในคีย์ต่างประเทศ) และในกรณีนี้ข้อมูลที่อยู่อย่างกระจัดกระจายจะน้อยกว่า "บรรจุ" เมื่อเปรียบเทียบกับข้อมูลที่ไม่ได้แยกส่วน แต่ให้เราพิจารณาสิ่งที่คุณจะได้เห็นจากการรวมเข้าด้วยกันเป็นดัชนีข้อมูล 3NF คือ:

  1. การเข้าร่วมจากตารางที่มีการอ้างอิงคีย์ต่างประเทศกับคีย์หลักของตารางที่อ้างอิง

  2. วิธีอื่น ๆ

โฆษณา 1) ในกรณีนี้คุณกำลังมองหาคีย์หลักเพียงครั้งเดียว - เข้าร่วม n to 1 การกระจายตัวหรือไม่ค่าใช้จ่ายเท่ากัน (หนึ่งค้นหา)

โฆษณา 2) ในกรณีนี้คุณกำลังเข้าร่วมกับคีย์เดียวกัน แต่อาจดึงข้อมูลได้มากกว่าหนึ่งแถว (ค้นหาช่วง) การเข้าร่วมในกรณีนี้คือ 1 ถึง n อย่างไรก็ตามตารางต่างประเทศที่คุณค้นหาคุณกำลังค้นหาคีย์ SAME ซึ่งมีแนวโน้มที่จะอยู่ในหน้าเดียวกันในดัชนีแยกส่วนเช่นเดียวกับที่ไม่ได้แยกส่วน

พิจารณากุญแจต่างประเทศเหล่านั้นสักครู่ แม้ว่าคุณจะมีลำดับ "สมบูรณ์" ในการวางคีย์หลักของเรา - สิ่งที่ชี้ไปที่คีย์นั้นจะยังคงไม่เรียงตามลำดับ

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


1
ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม การสนทนานี้ได้รับการย้ายไปแชท
พอลไวท์ 9

5

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

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

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

แต่ฉันมักจะใช้ทางเลือกกับ GUID การตั้งค่าส่วนบุคคลของฉันสำหรับประเภทข้อมูลที่นี่เป็น BIGINT ที่ไม่เหมือนใครทั่วโลกซึ่งสร้างโดยแอป เราจะทำสิ่งนี้ได้อย่างไร? ในตัวอย่างที่น่าสนใจที่สุดคุณเพิ่มฟังก์ชั่นที่มีน้ำหนักเบามาก ๆ ลงในแอพเพื่อแฮช GUID สมมติว่าฟังก์ชันแฮชของคุณนั้นรวดเร็วและค่อนข้างรวดเร็ว (ดู CityHash จาก Google สำหรับตัวอย่างหนึ่ง: http://google-opensource.blogspot.in/2011/04/introducing-cityhash.html - ตรวจสอบให้แน่ใจว่าคุณได้รวบรวมขั้นตอนทั้งหมดเรียบร้อยแล้ว หรือตัวแปร FNV1a ของhttp://tools.ietf.org/html/draft-eastlake-fnv-03สำหรับรหัสง่าย ๆ ) สิ่งนี้จะทำให้คุณได้รับประโยชน์จากทั้งแอปพลิเคชั่นที่สร้างตัวระบุเฉพาะและค่าคีย์ 64 บิตที่ซีพียูทำงานได้ดีขึ้นด้วย .

มีวิธีอื่นในการสร้าง BIGINT และใน algos ทั้งสองนี้มีโอกาสที่จะเกิดการชนกันของข้อมูล - อ่านและตัดสินใจอย่างมีสติ


2
ฉันขอแนะนำให้คุณแก้ไขคำตอบของคุณเป็นคำตอบสำหรับคำถามของ OP ไม่ใช่คำตอบของโทมัส คุณยังสามารถเน้นความแตกต่างระหว่าง Thomas (, MikeFal's) และข้อเสนอแนะของคุณ
ypercubeᵀᴹ

2
โปรดตอบคำถามของคุณ ถ้าคุณทำไม่ได้เราจะลบมันให้คุณ
JNK

2
ขอบคุณสำหรับความคิดเห็นที่ทำเครื่องหมาย เมื่อคุณแก้ไขคำตอบของคุณ (ซึ่งฉันคิดว่ามีบริบทที่ดีมาก) ฉันจะเปลี่ยนสิ่งหนึ่ง: IDENTITY ไม่ต้องการไปกลับไปยังเซิร์ฟเวอร์เพิ่มเติมหากคุณระมัดระวัง INSERT คุณสามารถส่งคืน SCOPE_IDENTITY () ในชุดที่เรียก INSERT ..
Thomas Kejser

1
เกี่ยวกับ "มันช้ามากเพราะต้องใช้การเดินทางไปกลับเพื่อสร้างคีย์" - คุณสามารถคว้าได้มากเท่าที่คุณต้องการในการเดินทางไปกลับ
AK

เกี่ยวกับ "คุณสามารถคว้าได้มากเท่าที่คุณต้องการในการเดินทางไป - กลับ" - คุณไม่สามารถทำได้ด้วยคอลัมน์ข้อมูลประจำตัวหรือวิธีอื่นใดที่คุณใช้ DEFAULT ในระดับฐานข้อมูล
Avi Cherry
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.