การใช้ GUID เป็นคีย์หลัก


32

โดยทั่วไปฉันใช้รหัสการเพิ่มอัตโนมัติเป็นคีย์หลักในฐานข้อมูล ฉันพยายามเรียนรู้ประโยชน์ของการใช้ GUID ฉันได้อ่านบทความนี้: https://betterexplained.com/articles/the-quick-guide-to-guids/

ฉันรู้ว่า GUID เหล่านี้ใช้เพื่อระบุวัตถุในระดับแอปพลิเคชัน พวกเขายังเก็บไว้เป็นคีย์หลักในระดับฐานข้อมูล ตัวอย่างเช่นสมมติว่าฉันมีคลาสต่อไปนี้:

public class Person
{
public GUID ID;
public string Name;
..

//Person Methods follow
}

ว่าฉันต้องการสร้างคนใหม่ในหน่วยความจำแล้วใส่คนลงในฐานข้อมูล ฉันขอทำสิ่งนี้ได้ไหม

Person p1 = new Person();
p1.ID=GUID.NewGUID();
PersonRepository.Insert(p1);

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

ผมอ่านบทความนี้ก่อนหน้านี้: http://enterprisecraftsmanship.com/2014/11/15/cqs-with-database-generated-ids/ มันทำให้ฉันสับสนเล็กน้อยตามที่ปรากฏเพื่อแนะนำสื่อกลางที่มีความสุขระหว่าง GUID และจำนวนเต็มเป็นคีย์หลัก

แก้ไข 11/06/18

ฉันเชื่อว่า Guids นั้นเหมาะสมกว่า ints สำหรับความต้องการของฉัน ฉันใช้ CQRS มากกว่านี้ในวันนี้และ GUID นั้นเหมาะสมกว่า

ฉันสังเกตว่านักพัฒนาบางคนวาง GUID เป็นสตริงในรูปแบบโดเมนเช่นที่นี่: https://github.com/dotnet-architecture/eShopOnContainers/blob/dev/src/Services/OrderingDomain/AggregatesModel/BuyerAggregate/ Buyer.cs - ในกรณีนี้: IdentityGuid เป็น GUID ที่จำลองเป็นสตริง มีเหตุผลใดที่จะทำสิ่งนี้นอกเหนือจากที่ระบุไว้ที่นี่: ใช้วัตถุค่าที่กำหนดเองหรือ Guid เป็นตัวระบุเอนทิตีในระบบกระจาย . เป็น "ปกติ" ในการสร้างโมเดล GUID เป็นสตริงหรือฉันควรสร้างแบบจำลองเป็น GUID ในรูปแบบและฐานข้อมูลหรือไม่



7
ไม่รับประกันว่าจะไม่ซ้ำกัน แต่ก็ไม่น่าที่คุณจะเห็นการชนกัน stackoverflow.com/questions/1155008/how-unique-is-uuid/ …
icirellik

2
ดูเพิ่มเติมที่: การชน UUID
ริ้น

2
ดูเพิ่มเติมที่dba.stackexchange.com/questions/54690/ …รวมถึงคำถามอื่น ๆ อีกมากมาย - หัวข้อนี้ได้รับการถามและตอบและได้รับการถกเถียงกันบ่อยครั้ง
Greenstone Walker

1
ระบบที่ฉันใช้งานในขณะนี้ใช้ UUID คุณสมบัติที่ดีคือ ID ระบุเร็กคอร์ดที่ไม่ซ้ำกันซึ่งตรงข้ามกับ ID ลำดับที่ระบุเร็กคอร์ดในตารางนั้น
จัสติน

คำตอบ:


41

GUID นั้นใช้คำจำกัดความว่า มีแนวคิดที่คล้ายกัน แต่แตกต่างกันเล็กน้อยใน Java ที่เรียกว่า UUIDs "Universally Unique IDentifiers" ชื่อสามารถใช้แทนกันได้สำหรับการใช้งานจริงทั้งหมด

GUID เป็นศูนย์กลางของการจัดกลุ่มฐานข้อมูลของ Microsoft ให้ทำงานได้อย่างไรและหากคุณต้องการรวมข้อมูลจากแหล่งข้อมูลที่เชื่อมต่อบางครั้งพวกเขาจะช่วยป้องกันการชนกันของข้อมูล

ข้อเท็จจริง Pro-GUID บางส่วน:

  • GUID ป้องกันการชนที่สำคัญ
  • GUID ช่วยในการรวมข้อมูลระหว่างเครือข่ายเครื่อง ฯลฯ
  • SQL Server มีการสนับสนุน GUIDS กึ่งต่อเนื่องเพื่อช่วยลดการกระจายตัวของดัชนี ( อ้างอิงบางคำเตือน)

ความน่าเกลียดบางอย่างกับ GUIDs

  • พวกมันมีขนาดใหญ่ละ 16 ไบต์
  • พวกมันไม่เรียบร้อยดังนั้นคุณไม่สามารถเรียงลำดับบน ID และหวังว่าจะได้รับคำสั่งแทรกเช่นเดียวกับที่คุณสามารถทำได้บนรหัสที่เพิ่มขึ้นอัตโนมัติ
  • มีความยุ่งยากในการทำงานโดยเฉพาะกับชุดข้อมูลขนาดเล็ก (เช่นค้นหาตาราง)
  • การติดตั้ง GUID ใหม่นั้นมีประสิทธิภาพมากขึ้นบน SQL Server มากกว่าที่อยู่ในไลบรารี C # (คุณสามารถมี GUIDS ตามลำดับจาก SQL Server ใน C # เป็นแบบสุ่ม)

GUID จะทำให้ดัชนีของคุณใหญ่ขึ้นดังนั้นค่าใช้จ่ายพื้นที่ดิสก์ในการทำดัชนีคอลัมน์จะสูงขึ้น GUID แบบสุ่มจะแยกส่วนดัชนีของคุณ

หากคุณรู้ว่าคุณไม่ต้องการซิงโครไนซ์ข้อมูลจากเครือข่ายที่แตกต่างกัน GUID สามารถรองรับค่าใช้จ่ายได้มากกว่าค่าที่ได้รับ

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


18
ความเข้าใจของฉันคือ GUID นั้นมีความหมายเหมือนกันกับ UUID UUID เป็นชื่อมาตรฐาน GUID คือสิ่งที่ไมโครซอฟท์ประกาศเกียรติคุณพวกเขาก่อนที่จะRFC 4122
JimmyJames

13
"พวกมันไม่เรียบร้อยดังนั้นคุณไม่สามารถเรียงลำดับบน ID และหวังว่าจะได้รับคำสั่งแทรกเช่นคุณสามารถใช้รหัสที่เพิ่มขึ้นอัตโนมัติ" ตรงไปตรงมาฉันไม่สบายใจที่จะใช้รหัสประจำตัวด้วยเช่นกัน ในขณะที่เป็นไปได้ในกรณีที่ขอบสุดขีดสำหรับ id ต่ำกว่าที่จะได้รับการมุ่งมั่นที่จะดิสก์ในภายหลังฉันควรพึ่งพาข้อมูลการเรียงลำดับที่มีประโยชน์เช่นการประทับเวลาการแทรก รหัสควรได้รับการปฏิบัติเหมือนที่อยู่หน่วยความจำ - ทุกอย่างมี แต่ค่านั้นไม่มีความหมาย ใช้พวกเขาสำหรับ tiebreakers มากที่สุด โดยเฉพาะอย่างยิ่งหากคุณมีภาระจำนวนมากคำสั่งแทรกจะไม่รับประกัน
Clockwork-Muse

8
@CortAmmon ตามWikipediaและRFC 4122พวกเขามีความหมายเหมือนกัน P. Leach ของ Microsoft เป็นหนึ่งในผู้สร้าง RFC ฉันคิดว่าตั้งแต่ RFC ถูกสร้างขึ้นทั้งสองเหมือนกัน จาก RFC: "UUIDs (รหัสเฉพาะสากล) หรือที่รู้จักในชื่อ GUID (รหัสสากลซ้ำ) ฉันคิดว่ามันมีประโยชน์ที่จะทราบว่า GUID นั้นไม่ได้สร้างโดย MS พวกเขาเพิ่งสร้างชื่อใหม่สำหรับเทคโนโลยีที่นำมาใช้จากที่อื่น
JimmyJames

6
"SQL Server มีการปรับให้เหมาะสมสำหรับการจัดการกับ GUID ดังนั้นจึงไม่ควรส่งผลกระทบต่อประสิทธิภาพของแบบสอบถามมากนัก" -1 ไม่ได้รับการปรับให้เหมาะสมเพียงพอ ฉันทำงานกับฐานข้อมูลที่ PKs ทั้งหมดเป็นของมีค่าและเป็นหนึ่งในสาเหตุหลักของประสิทธิภาพที่ไม่ดี
แอนดี้

7
"SQL Server มีการปรับให้เหมาะสมสำหรับการจัดการกับ GUID ดังนั้นจึงไม่ควรส่งผลกระทบต่อประสิทธิภาพการสืบค้นมากนัก " ไม่เป็นความจริง คำสั่งนั้นจะถือว่าประเภทข้อมูลอื่นไม่ได้รับการปรับให้เหมาะสม เซิร์ฟเวอร์ฐานข้อมูลยังมีการปรับให้เหมาะสมสำหรับการจัดการกับค่า int อย่างง่ายเช่น GUIDs / UUID ช้ากว่าการใช้ค่า int 4 ไบต์ 16 ไบต์จะไม่เร็วเท่ากับ 4 ไบต์ - โดยเฉพาะอย่างยิ่งในเครื่องที่จัดการได้สูงสุด 4 หรือ 8 ไบต์โดยกำเนิด
Andrew Henle

28

สิ่งนี้จะไม่ซ้ำกันหรือไม่

เสมอ? ไม่ไม่เสมอไป; มันเป็นลำดับที่แน่นอนของบิต

สมมติว่าฉันมีฐานข้อมูลที่ประกอบด้วยแถวนับล้านแถวที่มี GUID เป็นคีย์หลัก

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

ฉันสามารถทำสิ่งนี้ได้หรือไม่?

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

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

ดูคำอธิบายของ uuid ตามชื่อในRFC 4122

เป็น "ปกติ" ในการสร้างโมเดล GUID เป็นสตริงหรือฉันควรสร้างแบบจำลองเป็น GUID ในรูปแบบและฐานข้อมูลหรือไม่

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

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

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


20
+1 สำหรับ "คุณใช้พื้นที่ดิสก์หมดในเวลาที่เกิดขึ้น"
w0051977

2
ผมรู้สึกว่าแนวคิด " สร้าง deterministically UUID " เป็นสิ่งจำเป็น (ดูข้อมูล Vault 2)
alk

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

ล้านล้าน = 2 ^ 40 นั่นทำให้เกิดการชนกัน 2 ^ 79 คู่ GUID มี 2 ^ 128 บิตดังนั้นโอกาสคือหนึ่งใน 2 ^ 49 มีโอกาสมากขึ้นที่คุณจะมีข้อผิดพลาดที่นำ GUID เดียวกันมาใช้ซ้ำสำหรับสองเรคคอร์ดหรือเชื่อผิดว่ามีการชนกันที่ไม่มี
gnasher729

ฉันจะย้อนกลับไปยังคำถามประวัติศาสตร์ของฉัน ก่อนที่ฉันจะยอมรับ คุณช่วยดูการแก้ไขของฉันได้ไหม?
w0051977

11

GUID หรือUUIDน่าจะมีลักษณะเฉพาะเนื่องจากวิธีการสร้างและให้วิธีที่ปลอดภัยในการรับประกันความเป็นเอกลักษณ์โดยไม่ต้องสื่อสารกับหน่วยงานส่วนกลาง

ประโยชน์ของ GUID ที่เป็นคีย์หลัก:

  • คุณสามารถคัดลอกข้อมูลระหว่างส่วนต่าง ๆ ของคลัสเตอร์และไม่ต้องกังวลเกี่ยวกับการชนกันของ PK
  • จะช่วยให้คุณทราบคีย์หลักของคุณก่อนที่คุณจะได้แทรกระเบียนใด ๆ
  • ลดความซับซ้อนของตรรกะการทำธุรกรรมสำหรับการแทรกบันทึกเด็ก
  • ไม่สามารถเดาได้ง่าย

ในตัวอย่างที่คุณให้ไว้:

Person p1 = new Person();
p1.ID = GUID.NewGUID();
PersonRepository.Insert(p1);

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

Person p2 = new Person();
p2.ParentID = p1.ID
PersonRepository.Insert(p2);

detriments ไปยัง GUIDs เป็นคีย์หลัก:

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

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

ฐานข้อมูลจำนวนมากมีการใช้งานภายในของตนเองที่พยายามลดปัญหาการจัดเก็บที่เกิดจาก GUID และ SQL Server แม้จะมีฟังก์ชั่นใหม่ตามลำดับเพื่อช่วยในการจัดลำดับของ UUID ที่อนุญาตให้ใช้ดัชนีได้ดีขึ้น

นอกจากนี้จากมุมมองของผู้ทดสอบผู้ใช้หรือนักพัฒนาที่ทำงานกับแอปพลิเคชันการใช้ ID บน GUID จะช่วยปรับปรุงการสื่อสารอย่างมีนัยสำคัญ ลองนึกภาพว่าต้องอ่าน GUID ทางโทรศัพท์

ในท้ายที่สุดถ้าไม่มีการทำคลัสเตอร์ขนาดใหญ่หรือการทำให้ URL ยุ่งเหยิงก็เป็นข้อกำหนดที่จะต้องใช้ประโยชน์จาก ID ที่เพิ่มขึ้นอัตโนมัติ


1
สิ่งหนึ่งที่ต้องพิจารณาคือขึ้นอยู่กับประเภทของ UUIDซึ่งประกอบด้วยข้อมูลที่อาจใช้เพื่อระบุเครื่องที่สร้างขึ้น ตัวแปรสุ่มบริสุทธิ์อาจมีแนวโน้มที่จะชนกันโดยไม่มีเอนโทรปีเพียงพอ สิ่งนี้ควรพิจารณาก่อนใช้ใน URI
JimmyJames

ตกลง แต่ก็ไม่ควรเปิดเผยคีย์หลักใน URL วิธีการบางอย่างที่เหมาะสมมากขึ้นควรจะใช้เพื่อให้แน่ใจว่าไม่มีการรั่วไหลของข้อมูลที่ปลอดภัยเพื่อ system.s ภายนอก
icirellik

1
มีอีกหนึ่งกรณีการใช้งาน: ใส่ฐานข้อมูล OLTP จำนวนมากซึ่งการล็อกลำดับเป็นคอขวด ตามที่เพื่อน Oracle DBA ของฉันนี้ไม่ได้หายากอย่างที่มันฟังคุณไม่จำเป็นต้องมีขนาดใหญ่หรือกลุ่มสำหรับสิ่งนั้น •ในที่สุดชั่งน้ำหนักข้อดีข้อเสีย (และอย่าสับสนข้อดี / ข้อเสียของ UUID กับข้อดี / ข้อเสียที่ไม่เฉพาะเจาะจงกับ UUID เช่นเดียวกับผู้โพสต์บางราย) และตัวชี้วัด
mirabilos

1
หากคุณใช้หมายเลขที่ใหม่กว่าคุณต้องไปที่ db เพื่อรับ id (เช่นเดียวกับ identity int) ใช่ไหม ประโยชน์ที่นี่คืออะไร
w0051977

1
@ mirabilos มีความชัดเจนเมื่อฉันบอกว่าน่ากลัวเราได้มีส่วนแทรกที่ใช้เวลาไม่กี่นาทีต่อแถว มันเริ่มจากตกลง แต่หลังจากที่มี 10 แถวเป็นพันแถวมันไปด้านข้างเร็วมาก ถ้ามันไม่ชัดเจน 10s ของแถวเป็นตารางขนาดเล็กมาก
JimmyJames

4

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

12 ไบต์พิเศษเพิ่มขึ้นอย่างรวดเร็ว โปรดจำไว้ว่า PKs ส่วนใหญ่จะเป็น FK ในตารางอื่น ๆ และเพียงสาม FKs ในตารางตอนนี้คุณมี 48 ไบต์พิเศษสำหรับทุกแถว ที่เพิ่มขึ้นในตารางและในดัชนี นอกจากนี้ยังเพิ่มขึ้นในดิสก์ I / O 12 ไบต์พิเศษเหล่านั้นจำเป็นต้องอ่านและเขียน

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

หากคุณต้องการตัวระบุที่ไม่ซ้ำบางประเภทสำหรับการซิงค์หรือบางอย่างให้เพิ่มคอลัมน์ guid อย่าทำให้เป็น PK


4
Person p1 = new Person();
p1.ID=GUID.NewGUID();
PersonRepository.Insert(p1);

นี่คือเหตุผลที่สำคัญที่สุดสำหรับการใช้ GUID

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

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

คุณสามารถติดมันในฐานข้อมูลประเภทใดก็ได้ rdb หรือ no-sql, ไฟล์, ส่งไปยัง webservice หรือโยนทิ้งทันทีโดยที่ไม่ต้องมีผู้ดูแล

ไม่คุณจะไม่ได้รับการชนกัน

ใช่เม็ดมีดอาจช้าลงเล็กน้อยเนื่องจากดัชนีอาจต้องเล่นซอ

ใช่มันใหญ่กว่า int

  • แก้ไข ต้องยิงก่อนที่จะจบ

ฉันรู้ว่าหลายคนรู้สึกอย่างมากเกี่ยวกับ auto inc ints และนี่เป็นหัวข้อที่ถกเถียงกันกับ DBAs

แต่ฉันไม่สามารถระบุได้อย่างชัดเจนว่าแนวทางที่ดีกว่าเป็นอย่างไร คุณควรใช้ guids เป็นค่าเริ่มต้นในแอปพลิเคชันใด ๆ

int อัตโนมัติมีข้อบกพร่องมากมาย

  • คุณใช้ฐานข้อมูลแบบกระจาย No-Sql คุณไม่สามารถคุยกับอินสแตนซ์อื่น ๆ ทั้งหมดเพื่อค้นหาว่าหมายเลขถัดไปคืออะไร

  • คุณใช้ระบบคิวข้อความ สิ่งที่ต้องการรหัสก่อนที่พวกเขาจะตีฐานข้อมูล

  • คุณกำลังสร้างหลายรายการและแก้ไขก่อนบันทึก แต่ละคนต้องการรหัสก่อนที่คุณจะกดฐานข้อมูล

  • คุณต้องการลบและแทรกแถวอีกครั้ง ตรวจสอบให้แน่ใจว่าคุณไม่ได้นับรหัสอัตโนมัติของคุณและหมด!

  • คุณไม่ต้องการเปิดเผยจำนวนคำสั่งซื้อที่คุณได้รับในปีนี้ให้กับผู้ใช้ทุกคน

  • คุณต้องการย้ายข้อมูลที่ไม่ระบุชื่อจากการผลิตเพื่อทดสอบและรักษาความสัมพันธ์ที่ไม่เปลี่ยนแปลง แต่ไม่ลบข้อมูลการทดสอบที่มีอยู่ทั้งหมด

  • คุณต้องการรวมผลิตภัณฑ์ผู้เช่ารายเดียวของคุณเข้ากับฐานข้อมูลที่มีผู้เช่าหลายคน แต่ทุกคนมีใบสั่งซื้อ 56

  • คุณสร้างวัตถุที่คงอยู่ แต่ไม่ยั่งยืน (คำสั่งซื้อที่ไม่สมบูรณ์) อีกครั้งอย่าใช้ ints ของคุณทั้งหมดกับสิ่งที่ไม่มีอยู่อีกต่อไป

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

ในที่สุดปัญหาใหญ่ที่มี ints คือคุณหมดพวกเขา !!! ตกลงในทางทฤษฎีคุณไม่ได้มีการโหลด แต่ในทางปฏิบัติคุณทำเพราะคนไม่ปฏิบัติต่อพวกเขาเช่นตัวเลขสุ่มที่ไม่มีความหมาย พวกเขาทำสิ่งต่าง ๆ เช่น

  • โอ้ฉันไม่ต้องการให้ลูกค้าคิดว่าเราใหม่ เริ่มต้นที่ 10,000

  • ฉันต้องนำเข้าข้อมูลจำนวนมากดังนั้นฉันเพิ่งเติมเมล็ดไปที่ 1 ม. เพื่อให้เรารู้ว่าอะไรที่นำเข้า

  • เราต้องการหมวดหมู่ของข้อมูล ทุกช่วงเวลาเริ่มต้นที่ล้านถัดไปเพื่อให้เราสามารถใช้ตัวเลขตัวแรกเป็นตัวเลขเวทย์มนตร์

  • ฉันลบและนำเข้าข้อมูลทั้งหมดอีกครั้งด้วยรหัสใหม่ ใช่แม้กระทั่งบันทึกการตรวจสอบ

  • ใช้หมายเลขนี้ซึ่งเป็นคีย์ผสมเป็นรหัสของสิ่งอื่นนี้


1
ไม่มีอะไรผิดจริงกับคำตอบนี้ แต่ฉันจะ (ที่จะขับไล่ downvotes เพิ่มเติม) อาจทำให้ข้อแม้ชัดเจนว่าแม้ว่าการใช้งานในชีวิตจริงจะไม่เผชิญหน้ากับการชนก็เป็นไปได้ในทางทฤษฎี (หรือบางทีอาจมีฐานข้อมูล exabyte มากกว่า 45 แห่งที่แพร่หลายกว่าที่ฉันคิด ... ) แม้ว่าฉันจะคิดว่าภาษา "เหตุผลที่สำคัญที่สุด" ค่อนข้างแข็งแกร่ง แต่นี่คือสิ่งที่ฉันพบว่ามีประโยชน์มากที่สุด
BurnsBA

2
มีแนวโน้มว่า int อัตโนมัติจะชนกันมากกว่า guid
Ewan

4
-1 สำหรับ "คุณควรใช้ guids เป็นค่าเริ่มต้นในแอปพลิเคชันใด ๆ " มันขึ้นอยู่กับ™ และอย่างที่คนอื่น ๆ ได้แสดง GUIDs / UUID นั้นไม่ได้รับประกันว่าจะไม่ซ้ำกัน
Max Vernon

3
คำตอบ "ขึ้นอยู่กับ" ไม่มีประโยชน์แน่นอนว่าจะมีแอปพลิเคชันแปลก ๆ ที่ int ดีกว่า แต่โอกาสที่แอปพลิเคชันของคุณจะไม่ใช่หนึ่งในนั้น GUIDs เป็นสิ่งที่พิเศษที่สุดที่คุณจะได้รับ
Ewan

2
ฉันคิดว่าจะมีแอปพลิเคชั่นแปลก ๆ ที่ guids ดีกว่า ไม่ซ้ำกันไม่ใช่สิ่งสำคัญที่สุดที่ต้องพิจารณา "ข้อบกพร่อง" ของ ints ของคุณนั้นเต็มไปด้วยความหนาแน่นและคุณไม่ได้พิจารณาข้อบกพร่องหลายประการ
แอนดี้

2

ฉันรู้ว่า GUID เหล่านี้ใช้เพื่อระบุวัตถุในระดับแอปพลิเคชัน พวกเขายังเก็บไว้เป็นคีย์หลักในระดับฐานข้อมูล

นั่นคือสิ่งที่คุณควรหยุดตรงนั้นและคิดใหม่

คีย์หลักของฐานข้อมูลของคุณไม่ควรมีความหมายทางธุรกิจ มันควรจะไร้ความหมายตามคำนิยาม

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

แน่นอนว่ามันเป็นทฤษฎีฐานข้อมูลที่พูดถึง แต่ก็เป็นการฝึกฝนที่ดีเช่นกัน ฉันจัดการกับฐานข้อมูลที่คีย์หลักมีความหมายทางธุรกิจ (ลูกค้ารายหนึ่งคิดว่าจะประหยัดทรัพยากรฐานข้อมูลโดยใช้เป็นหมายเลขพนักงานหมายเลขลูกค้า ฯลฯ ฯลฯ ) และมันมักจะนำไปสู่ปัญหา


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

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

2

ใช้ฐานข้อมูลหลักที่สร้างขึ้นและเพิ่มระดับอัตโนมัติ (PKs) ทุกครั้ง

เหตุใดจึงใช้การเพิ่มอัตโนมัติแทน GUID / UUID

  • GUID (UUID) ไม่ได้ป้องกันการชนที่สำคัญเนื่องจากมันไม่ซ้ำกันและไม่มีวิธีที่จะทำให้มันไม่ซ้ำกันเนื่องจากมันถูกสร้างขึ้นจากแหล่งต่าง ๆ มากมาย
  • GUID ไม่ได้ช่วยในการผสานเนื่องจากจะเพิ่มกระบวนการผสานที่ใช้เวลานานมากแล้วด้วยคอลัมน์ PK และ FK ที่มีความยาวและไม่จำนวนเต็มซึ่งใช้เวลาในการประมวลผลนานมาก โปรดจำไว้ว่าสำหรับ PKs ส่วนใหญ่จะมีอย่างน้อย 1 ตารางที่มีขนาดอย่างน้อย 2 คีย์: มี PK และ FK ของตนเองกลับไปที่ตารางแรก ทั้งหมดจะต้องได้รับการแก้ไขในการรวม

แต่ถ้าอย่างนั้นจะจัดการกับเศษชิ้นส่วน ฯลฯ ได้อย่างไร?

  • สร้าง PKs แบบหลายคอลัมน์ซึ่งประกอบด้วยคอลัมน์แยกกันเพื่อระบุแต่ละ shard / cluster / database / อะไรก็ตามที่จัดการมันเป็นคีย์การเพิ่มอัตโนมัติ ตัวอย่างเช่น...

PK 3 คอลัมน์สำหรับตารางคลัสเตอร์อาจเป็น ...

 DB | SH | KEY     |
----|----|---------|
 01 | 01 | 1234567 |

แต่แล้ว ...

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

  • ประสิทธิภาพ - คอมพิวเตอร์สามารถประมวลผลจำนวนเต็มง่ายได้เร็วกว่าสิ่งอื่นใดเนื่องจากโดเมนมีขนาดใหญ่กว่ามากหากค่าที่เป็นไปได้ต่อองค์ประกอบใน GUID (37) เทียบกับจำนวนเต็ม (10) จำไว้ด้วยว่าอักขระแต่ละตัวใน GUID นั้นจะต้องถูกแปลงเป็นตัวเลขเพื่อให้ CPU จัดการ

การใช้งานคีย์หลักทั่วไป PKs มีจุดประสงค์เดียวเท่านั้น ... เพื่อระบุแถวในตารางอย่างไม่ซ้ำกัน สิ่งอื่นใดคือการใช้ในทางที่ผิดเหมือนกัน

กำลังตรวจหาบันทึกที่หายไป

  • ไม่พบระเบียนที่หายไปโดยดูที่ PKs Bless QA อย่างน้อยก็พยายามรับรองคุณภาพข้อมูล อย่างไรก็ตามพวกเขาและโปรแกรมเมอร์ขาดความเข้าใจว่ากุญแจในระบบฐานข้อมูลที่ทันสมัยได้รับมอบหมายบ่อยครั้งทำให้พวกเขาเชื่อว่าตัวเลขที่ขาดหายไปใน PK ที่เพิ่มขึ้นอัตโนมัติหมายถึงข้อมูลที่ขาดหายไป มันไม่ได้เพราะ ...
  • สำหรับประสิทธิภาพระบบฐานข้อมูลจะจัดสรรบล็อกตัวเลขใน 'ลำดับ' (แบตช์ช่วง) เพื่อลดการเดินทางไปยังฐานข้อมูลจริงในหน่วยเก็บข้อมูล ขนาดของลำดับตัวเลขเหล่านี้มักอยู่ภายใต้การควบคุมของ DBA แต่อาจไม่สามารถปรับได้ตามตารางต่อตาราง
  • สิ่งที่สำคัญคือ ... หมายเลขที่ไม่ได้ใช้จากลำดับเหล่านี้จะไม่ถูกส่งกลับไปยังฐานข้อมูลดังนั้นจึงมีช่องว่างในหมายเลข PK เสมอ
  • ทำไมคุณถึงถามเบอร์ที่ไม่ได้ใช้ เนื่องจากการดำเนินการบำรุงรักษาฐานข้อมูลที่หลากหลายอาจทำให้ลำดับถูกทอดทิ้ง สิ่งเหล่านี้คือการรีสตาร์ทการโหลดซ้ำจำนวนมากของตารางการกู้คืนบางประเภทจากการสำรองข้อมูลและการดำเนินการอื่น ๆ

การเรียงลำดับ

  • การเรียงลำดับโดย PK นั้นเกิดข้อผิดพลาดได้ง่ายมากเนื่องจากคนส่วนใหญ่คิดว่าเรียงแถวตามลำดับที่สร้างขึ้นและสอดคล้องกับเวลาของนาฬิกา ส่วนใหญ่ แต่ไม่จำเป็น
  • เอ็นจิ้นฐานข้อมูลได้รับการปรับให้เหมาะสมเพื่อประสิทธิภาพสูงสุดและนั่นอาจหมายถึงการล่าช้าในการแทรกผลลัพธ์ของธุรกรรมที่ซับซ้อนที่ต้องใช้เวลานานเพื่อที่จะแทรกสั้น ๆ ง่ายๆ ๆ "out-of-turn" เพื่อพูด

คุณคิดอย่างไรกับสคีมาของตารางที่มีเพียงคอลัมน์เดียวที่ไม่ซ้ำกันคือคีย์หลักที่เพิ่มขึ้นโดยอัตโนมัติ โดยเฉพาะอย่างยิ่งสำหรับตารางที่ไม่มีคีย์ต่างประเทศ แต่มีคีย์หลักคือคีย์ต่างประเทศสำหรับตารางที่เกี่ยวข้องหลายรายการหรือไม่
RibaldEddie

ฉันได้เพิ่มคำตอบลงในบรรทัดเหล่านั้นมากขึ้น คำตอบเดิมไม่สมบูรณ์เนื่องจากแอพ Android SE ที่ฉันใช้งานอยู่ ฉันคิดว่าการเขียนแอปที่สำคัญกำลังอยู่ในระหว่างการพัฒนา
DocSalvager

ดังนั้นในมุมมองของคุณมันจะโอเคสำหรับตารางที่จะมีจำนวนแถวใด ๆ ที่มีการบันทึกเหมือนกันสำหรับคีย์หลักที่เพิ่มขึ้นอัตโนมัติ?
RibaldEddie

@RibaldEddie - เท่าที่ฐานข้อมูลถูกออกแบบมาเพื่อให้ ... อย่างแน่นอน การลบนั้นง่าย เมื่อสถานการณ์ของคุณเกิดขึ้นฉันจะถือว่าเป็นข้อบกพร่องที่จะแก้ไขในซอฟต์แวร์แล้วลบแถวใดแถวหนึ่ง กรณีที่พบบ่อยมากคือบันทึกสองรายการสำหรับสิ่งเดียวกันที่มีข้อมูลแตกต่างกันเล็กน้อยดังนั้นจึงต้องรวมเข้าด้วยกัน หากคอลัมน์ว่างเปล่าในระเบียนหนึ่งและมีค่าอยู่ในอีกคอลัมน์หนึ่งตัวเลือกนั้นชัดเจนและสามารถเป็นแบบอัตโนมัติได้ บ่อยครั้งที่ datetimestamp สามารถใช้ในการตัดสินโดยอัตโนมัติแบบผสาน รายการที่ซ้ำบางรายการต้องการบุคคลที่จะเสร็จสิ้นและตรวจสอบการผสานตามกฎเกณฑ์ทางธุรกิจ
DocSalvager

1

ชอบอะไรก็ได้มีข้อดีและข้อเสียในการทำเช่นนี้:

ดี:

  1. คีย์ของคุณมีความยาวเท่ากันเสมอ (ฐานข้อมูลขนาดใหญ่มากสามารถมีคีย์ขนาดใหญ่มาก)

  2. มีการรับประกันความเป็นเอกลักษณ์ค่อนข้างมาก - แม้ว่าคุณจะสร้างจากระบบแยกต่างหากและ / หรือยังไม่ได้อ่าน ID ล่าสุดจากฐานข้อมูล

เลว:

  1. ดังกล่าวข้างต้นมาก - ดัชนีขนาดใหญ่และแหล่งข้อมูล

  2. คุณไม่สามารถสั่งซื้อด้วย ID คุณต้องสั่งซื้ออย่างอื่น ดัชนีมากขึ้นอาจมีประสิทธิภาพน้อยลง

  3. พวกเขาอ่านได้น้อยกว่ามนุษย์ จำนวนเต็มโดยทั่วไปจะง่ายกว่าในการแยกจำและพิมพ์สำหรับคน การใช้ GUID เป็น ID ในส่วนคำสั่ง WHERE ในหลาย ๆ ตารางที่เข้าร่วมสามารถทำให้หัวคุณละลาย

เช่นเดียวกับทุกสิ่งให้ใช้มันตามความเหมาะสมอย่าเชื่อมั่นในหลาย ๆ สถานการณ์การเพิ่มจำนวนอัตโนมัติจะดีกว่าบางครั้ง GUID นั้นยอดเยี่ยม


0

ใช่คุณสามารถใช้ GUID เป็นคีย์หลักได้ ข้อเสียคือขนาดและการกระจายตัวของดัชนีอย่างรวดเร็ว

ยกเว้นว่าคุณต้องการความแตกต่างของฐานข้อมูล (เช่นคลัสเตอร์) จำนวนเต็ม


เครื่องกำเนิดไฟฟ้า GUID อาจสร้าง GUID เดียวกันมากกว่าหนึ่งครั้ง ไม่ว่าพวกเขาจะหรือไม่ขึ้นอยู่กับความละเอียดของพวกเขาส่วนใหญ่ขึ้นอยู่กับช่วงเวลาระหว่างเห็บนาฬิกา เช่นเครื่องกำเนิดไฟฟ้าที่ใช้สัญญาณนาฬิกาสามารถทำเครื่องหมายทุก ๆ 100ms นำไปสู่ ​​2 GUID ที่ร้องขอภายใน 100ms บนเครื่องนั้นเหมือนกัน มีวิธีการหลีกเลี่ยงที่ส่วนใหญ่ แต่กำเนิด GUID จำนวนมากทำงานออกจากที่อยู่ IP และ / หรือที่อยู่ MAC และการประทับเวลา
jwenting

0

นี่คือปัญหาของฉันในเรื่องนี้ - การแก้ปัญหาคือบ้านครึ่งทางระหว่างค่า GUID และค่า int โดยใช้ทั้งสองอย่างให้ดีที่สุด

ชั้นสร้างหลอกสุ่ม ( แต่เพิ่มขึ้นเมื่อเวลาผ่านไป) ค่า ID ซึ่งจะคล้ายกับหวี GUID

ข้อได้เปรียบที่สำคัญคือช่วยให้สามารถสร้างค่า Id บนไคลเอนต์ได้มากกว่าการใช้ค่าที่เพิ่มขึ้นอัตโนมัติที่สร้างขึ้นบนเซิร์ฟเวอร์ (ซึ่งต้องมีการปัดเศษ) ด้วยความเสี่ยงเกือบเป็นศูนย์ของค่าที่ซ้ำกัน

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

public static class LongIdGenerator
{
    // set the start date to an appropriate value for your implementation 
    // DO NOT change this once any application that uses this functionality is live, otherwise existing Id values will lose their implied date
    private static readonly DateTime PeriodStartDate = new DateTime(2017, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    private static readonly DateTime PeriodEndDate = PeriodStartDate.AddYears(100);
    private static readonly long PeriodStartTicks = PeriodStartDate.Ticks;
    private static readonly long PeriodEndTicks = PeriodEndDate.Ticks;
    private static readonly long TotalPeriodTicks = PeriodEndTicks - PeriodStartTicks;

    // ensures that generated Ids are always positve
    private const long SEQUENCE_PART_PERMUTATIONS = 0x7FFFFFFFFFFF; 

    private static readonly Random Random = new Random();

    private static readonly object Lock = new object();
    private static long _lastSequencePart;

    public static long GetNewId()
    {
        var sequencePart = GetSequenceValueForDateTime(DateTime.UtcNow);

        // extra check, just in case we manage to call GetNewId() twice before enough ticks have passed to increment the sequence 
        lock (Lock)
        {
            if (sequencePart <= _lastSequencePart)
                sequencePart = _lastSequencePart + 1;

            _lastSequencePart = sequencePart;
        }

        // shift so that the sequence part fills the most significant 6 bytes of the result value
        sequencePart = (sequencePart << 16);

        // randomize the lowest 2 bytes of the result, just in case two different client PCs call GetNewId() at exactly the same time
        var randomPart = Random.Next() & 0xFFFF;

        return sequencePart + randomPart;
    }

    // used if you want to generate an Id value for a historic time point (within the start and end dates)
    // there are no checks, compared to calls to GetNewId(), but the chances of colliding values are still almost zero
    public static long GetIdForDateTime(DateTime dt)
    {
        if (dt < PeriodStartDate || dt > PeriodStartDate)
            throw new ArgumentException($"value must be in the range {PeriodStartDate:dd MMM yyyy} - {PeriodEndDate:dd MMM yyyy}");

        var sequencePart = GetSequenceValueForDateTime(dt.ToUniversalTime());
        var randomPart = Random.Next() & 0xFFFF;
        return ( sequencePart << 16 ) + randomPart;
    }

    // Get a 6 byte sequence value from the specified date time - startDate => 0 --> endDate => 0x7FFFFFFFFFFF
    // For a 100 year time period, 1 unit of the sequence corresponds to about 0.022 ms
    private static long GetSequenceValueForDateTime(DateTime dt)
    {
        var ticksFromStart = dt.ToUniversalTime().Ticks - PeriodStartTicks;
        var proportionOfPeriod = (decimal)ticksFromStart / TotalPeriodTicks;
        var result = proportionOfPeriod * SEQUENCE_PART_PERMUTATIONS;
        return (long)result;
    }

    public static DateTime GetDateTimeForId(long value)
    {
        // strip off the random part - the two lowest bytes
        var timePart = value >> 16;
        var proportionOfTotalPeriod = (decimal) timePart / SEQUENCE_PART_PERMUTATIONS;
        var ticks = (long)(proportionOfTotalPeriod * TotalPeriodTicks);
        var result = PeriodStartDate.AddTicks(ticks);
        return result;
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.