ความเป็นไปได้ในการสร้าง Mongo ObjectId ซ้ำกันในสองคอลเล็กชั่นที่แตกต่างกันหรือไม่


187

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

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

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

เราได้เริ่มแปลงใบสมัครของเราจาก MySql เป็น Mongo เมื่อไม่กี่เดือนที่ผ่านมาและในขณะที่เราอยู่ในช่วงการเปลี่ยนภาพเราได้จัดเก็บ MySql id ดั้งเดิมสำหรับทั้งสองประเภทข้อมูลเหล่านี้และเราก็เริ่มเก็บ Mongo ObjectId เอกสารเพื่อทำแผนที่กลับไปยังข้อมูลทางการที่ได้รับการเลือกตั้ง

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

ขอบคุณสำหรับความเข้าใจของคุณ

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



1
ฉันเคยอ่านหน้านั้นมาก่อน แดกดันพอฉันเชื่อมโยงไปยังหน้าเดียวกันจริงในคำตอบก่อนหน้า และฉันได้เห็นข้อจำกัดความรับผิดชอบ "น่าจะเป็นของที่ไม่เหมือนใคร" แต่ไม่แน่ใจว่ามีการใส่ชุดสะสมเข้าไปในปัจจัยใด ฉันเดาว่าสิ่งที่ฉันไม่แน่ใจคือส่วน ID กระบวนการ 2 ไบต์ของ ObjectId หมายถึงอะไร หากมีบางสิ่งเกี่ยวกับการรวบรวมก็จะมีความเป็นเอกลักษณ์ระหว่างเอกสารสองฉบับที่สร้างขึ้นในเวลาเดียวกันบนเครื่องเดียวกันในคอลเลกชันที่แตกต่างกัน
Anthony Jack

1
id กระบวนการ 2byte คือ pid ของกระบวนการที่สร้าง ObjectID ตัวอย่างเช่นนี่คือรหัส pymongo ที่ใช้ในการสร้าง ObjectID: github.com/mongodb/mongo-python-driver/blob/master/bson/…
mstearn

gotcha หนึ่งที่ฉันพบเจอคือการใส่แบตช์ ฉันกำลังสร้างเอกสารขนาด 10k และเกิดการชนกันทุกครั้งเพราะส่วนของตัวนับกลิ้งไปทุกครั้ง
fawce

ฉันรู้ว่ามันใช้เวลานานแล้ว แต่เอกสาร 10K จะไม่พลิกข้ามตัวนับ ส่วนเคาน์เตอร์คือสามไบต์ไม่ใช่สามหลัก นั่นคือมากกว่า 16 ล้าน
Asya Kamsky

คำตอบ:


318

คำตอบสั้น ๆ

เพียงเพื่อเพิ่มการตอบกลับโดยตรงกับคำถามเริ่มต้นของคุณ: ใช่ถ้าคุณใช้การสร้างรหัสวัตถุ BSON จากนั้นสำหรับไดรเวอร์ส่วนใหญ่ ID นั้นเกือบจะไม่ซ้ำกันในคอลเลกชัน ดูด้านล่างสำหรับความหมาย "เกือบแน่นอน"

คำตอบยาว

รหัสวัตถุ BSON ที่สร้างขึ้นโดยไดรเวอร์ Mongo DB มีแนวโน้มสูงที่จะไม่เหมือนใครในคอลเลกชัน นี่เป็นสาเหตุหลักมาจาก 3 ไบต์สุดท้ายของ ID ซึ่งสำหรับไดรเวอร์ส่วนใหญ่จะถูกสร้างผ่านตัวนับการเพิ่มแบบคงที่ ตัวนับนั้นเป็นอิสระจากการรวบรวม มันเป็นสากล ตัวอย่างเช่นไดรเวอร์ Java ใช้ AtomicInteger เริ่มต้นแบบสุ่มเริ่มต้น

ดังนั้นทำไมในเอกสาร Mongo พวกเขาบอกว่ารหัสนั้น "มีแนวโน้มสูง" ที่จะไม่เหมือนใครแทนที่จะเป็นคนพูดทันทีว่าพวกเขาจะไม่ซ้ำกันหรือไม่ ความเป็นไปได้สามประการสามารถเกิดขึ้นได้โดยที่คุณจะไม่ได้รับ ID ที่ไม่ซ้ำกัน (โปรดแจ้งให้เราทราบหากมีมากกว่านั้น):

ก่อนการสนทนานี้ให้ระลึกไว้ว่ารหัสวัตถุ BSON ประกอบด้วย:

[4 ไบต์วินาทีนับตั้งแต่กาล, แฮชเครื่อง 3 ไบต์, ID กระบวนการ 2 ไบต์, ตัวนับ 3 ไบต์]

นี่คือความเป็นไปได้สามประการดังนั้นคุณจะตัดสินด้วยตัวคุณเองว่ามีโอกาสที่จะได้รับล่อ:

1) Counter overflow: มี 3 ไบต์ในตัวนับ หากคุณเกิดการแทรกเอกสารมากกว่า 16,777,216 (2 ^ 24) ในวินาทีเดียวบนเครื่องเดียวกันในกระบวนการเดียวกันจากนั้นคุณอาจล้นตัวนับจำนวนไบต์ที่เพิ่มขึ้นและจบด้วย ID วัตถุสองอันที่แบ่งปันในเวลาเดียวกัน กระบวนการและค่าตัวนับ

2) ตัวนับที่ไม่เพิ่มจำนวน: ไดรเวอร์ Mongo บางตัวใช้หมายเลขสุ่มแทนการเพิ่มตัวเลขสำหรับตัวนับไบต์ ในกรณีเหล่านี้มีโอกาส 1 / 16,777,216 ในการสร้าง ID ที่ไม่ซ้ำกัน แต่เฉพาะถ้าสอง ID เหล่านั้นถูกสร้างขึ้นในวินาทีเดียวกัน (เช่นก่อนส่วนเวลาของ ID อัปเดตเป็นวินาทีถัดไป) ในเวลาเดียวกัน เครื่องในกระบวนการเดียวกัน

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

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


ตัวนับ 3 ไบต์ไม่ใช่ความสามารถในการยอมรับ 2 ^ 24 = 16777216 จำนวนเอกสารที่แทรกต่อวินาทีต่อกระบวนการต่อเครื่องหรือไม่
Forrest Ye

คุณพูดถูกจริงฉันตั้งใจลดจำนวนบิตลงเล็กน้อย - คำตอบถูกแก้ไข
Raj Advani

เนื่องจากฉันเพิ่งก้าวเข้าสู่สิ่งนี้ขอให้ฉันเพิ่มว่าไดรเวอร์บางตัว (เช่น C) ถึงแม้ว่าจะใช้การเพิ่มทีละก็ไม่ได้เพิ่มขึ้นแบบอะตอมดังนั้นเวลามันสร้างน้ำมันเดียวกันเนื่องจากสภาพการแข่งขัน
Pawel Veselov

39
คุณข้ามความจริงที่ว่าใน 136 ปีคุณมีช็อตอีกครั้งเพื่อสร้างสิ่งที่ObjectIdคุณเคยมีมาก่อนตราบเท่าที่แฮชของเครื่องจักร, ID กระบวนการและตัวนับกลับกลายเป็นเหมือนเดิม
jamylak

25
@jamylak เราจะดูแลปัญหานั้นเมื่อมันกลายเป็นเรื่องเร่งด่วน (คนที่ใช้รูปแบบวันที่ YYMMDD มาตรฐานในยุค 70 กล่าว)
Philipp

14

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

ตอนนี้ถ้าคุณอ้างถึงฟิลด์ _id โดยทั่วไปเราไม่ต้องการความเป็นเอกลักษณ์ในคอลเล็กชันดังนั้นจึงปลอดภัยที่จะใช้ _id เก่าอีกครั้ง ในฐานะที่เป็นตัวอย่างที่เป็นรูปธรรม, ถ้าคุณมีสองคอลเลกชันcolorsและfruitsทั้งสองจะไปพร้อม ๆ {_id: 'orange'}กันมีวัตถุเช่น

ในกรณีที่คุณต้องการทราบข้อมูลเพิ่มเติมเกี่ยวกับวิธีการสร้าง ObjectIds นี่คือข้อมูลจำเพาะ: http://www.mongodb.org/display/DOCS/Object+IDs#ObjectIDs-BSONObjectIDSpecification


11

ในกรณีที่ใครก็ตามมีปัญหากับ Mongo ObjectID ที่ซ้ำกันคุณควรรู้ว่าแม้จะมีความซ้ำซ้อนในการทำ Mongo อยู่ก็ตาม แต่ก็เป็นไปได้ที่จะมี _id ที่สร้างขึ้นด้วย PHP ใน Mongo

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

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

มีสามวิธีในการแก้ไขปัญหานี้:

  1. คุณสามารถunset()ฟิลด์ _id จากอาร์เรย์
  2. คุณสามารถกำหนดค่าเริ่มต้นอาร์เรย์ทั้งหมดใหม่array()ทุกครั้งที่คุณวนซ้ำชุดข้อมูลของคุณ
  3. คุณสามารถกำหนดค่า _id ได้อย่างชัดเจนด้วยตัวคุณเอง (โปรดระมัดระวังในการกำหนดด้วยวิธีที่คุณไม่ได้สร้างความซ้ำซ้อนด้วยตนเอง)

ฉันเดาว่านี่เป็นข้อผิดพลาดในอินเทอร์เฟซ PHP และไม่ใช่ปัญหากับ Mongo มากนัก แต่ถ้าคุณพบปัญหานี้เพียงแค่ยกเลิกการตั้งค่า _id และคุณควรจะดี


ดูที่นี่: php.net/manual/en/mongocollection.insert.php : "หมายเหตุ: หากพารามิเตอร์ไม่มีคีย์ _id หรือคุณสมบัติอินสแตนซ์ MongoId ใหม่จะถูกสร้างและกำหนดให้กับมันพฤติกรรมพิเศษนี้ไม่ได้หมายความว่า . ว่าพารามิเตอร์ผ่านอ้างอิง "มันเป็นคุณสมบัติไม่ได้เป็นปัญหาก็หมายความว่าจะเป็นวิธีการที่
โอลิเวอร์ Konig

1
ฉันไม่เข้าใจสถานการณ์ที่คุณกำลังอธิบายที่นี่; บางทีคุณสามารถแสดงรหัสที่แสดงข้อผิดพลาดบางอย่าง?
Mark Amery

-7

ไม่มีการรับประกันใด ๆ เกี่ยวกับ ObjectId ที่ไม่ซ้ำใครในคอลเลกชัน แม้ว่ามันจะไม่น่าจะเป็นไปได้ แต่มันก็เป็นการออกแบบแอพพลิเคชั่นที่แย่มากซึ่งอาศัยเอกลักษณ์ของ _id ในคอลเล็กชัน

หนึ่งสามารถทดสอบในเปลือก mongo:

MongoDB shell version: 1.6.5
connecting to: test
> db.foo.insert({_id: 'abc'})
> db.bar.insert({_id: 'abc'})
> db.foo.find({_id: 'abc'})
{ "_id" : "abc" }
> db.bar.find({_id: 'abc'})
{ "_id" : "abc" }
> db.foo.insert({_id: 'abc', data:'xyz'})
E11000 duplicate key error index: test.foo.$_id_  dup key: { : "abc" }

ดังนั้นอย่าพึ่งพา _id ว่าเป็นสิ่งที่ไม่ซ้ำใครในคอลเลกชันและเนื่องจากคุณไม่ได้ควบคุมฟังก์ชั่นการสร้าง ObjectId อย่าพึ่งพามัน

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

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


1
ฉันคิดว่าคุณอาจสร้างความสับสนให้กับฟิลด์ _id โดยทั่วไปกับประเภท ObjectID ประเภท ObjectID ได้รับการออกแบบมาโดยเฉพาะเพื่อความเป็นเอกลักษณ์โดยมีเป้าหมายที่จะได้รับการปฏิบัติเหมือนเป็น UUID อย่างไรก็ตามฟิลด์ _id สามารถเป็นประเภทใดก็ได้และรับประกันได้เฉพาะในคอลเลกชันเดียวหากคุณใช้ประเภทอื่น ๆ สำหรับคีย์เช่นสตริงในตัวอย่างของคุณ
พฤศจิกายน

@mstearn (Nitpick) ความคิดที่ว่า UUID นั้นมีความเป็นเอกลักษณ์นั้นมีข้อบกพร่อง กลยุทธ์รุ่นที่ดี UUID / ลำดับอาจทำให้การปะทะกันน่า แต่จะต้องใช้เครื่องกำเนิดไฟฟ้าที่ไม่ซ้ำกัน (เช่นสถานที่ไม่ซ้ำกัน) เข้าบัญชีเพื่อรับประกันเอกลักษณ์แน่นอนระหว่างเครื่องกำเนิดไฟฟ้า ที่ได้รับส่วนใหญ่จะมีความน่าจะเป็นต่ำเพื่อที่ว่ามันเป็นความกังวลบังคับไม่มี :-) GUID ปัญหาหนึ่งที่ไม่เกิดขึ้น แต่เป็นซ้ำ / การคัดลอกรหัสแทนของคนรุ่นใหม่

1
@pst: MongoDBs ObjectIDs รวมทั้ง pid ของกระบวนการสร้างและบางไบต์ตามแฮชของชื่อโฮสต์ สิ่งเหล่านี้รวมกับการประทับเวลาและตัวนับที่เพิ่มขึ้นทำให้เป็นไปได้อย่างมากว่า ObjectID ที่สร้างขึ้นสองอันใด ๆ ที่แยกจากกันจะมีความเป็นสากล / เป็นสากล แน่นอนว่าคุณบอกว่าจะใช้กับ ObjectID ที่สร้างขึ้นใหม่เท่านั้น
พฤศจิกายน

1
ฉันหมายถึงประเภท ObjectId ไม่ได้ระบุค่าสตริงสำหรับ '_id' แน่นอนว่าพวกเขาจะเหมือนกันและขัดแย้งกันหากคุณตั้งให้เป็นสายอักขระเดียวกันแน่นอนด้วยตนเอง
Anthony Jack

ใช่ฉันชี้แจงสิ่งต่าง ๆ ในโพสต์ของฉัน แน่นอนว่า _id นั้นไม่ซ้ำกันและเนื่องจากคุณไม่ได้ควบคุมฟังก์ชั่นการสร้าง ObjectId มันอาจเป็นความคิดที่ดีที่จะพึ่งพามัน
slacy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.