Можливість створення двох копій Mongo ObjectId у двох різних колекціях?


187

Чи можливо для одного і того ж точного Mongo ObjectId генеруватися для документа у двох різних колекціях? Я усвідомлюю, що це, безумовно, дуже малоймовірно, але чи можливо це?

Не надто конкретно, тому я прошу, що за допомогою програми, над якою я працюю, ми показуємо загальнодоступні профілі обраних чиновників, яких ми сподіваємося перетворити на повноправних користувачів нашого сайту. У нас є окремі колекції для користувачів та обраних посадових осіб, які наразі не є членами нашого сайту. Існують різні інші документи, що містять різні відомості про обраних посадових осіб, які всі відображають особу, використовуючи обраний офіційний ObjectId.

Після створення облікового запису ми все ще виділяємо дані, пов’язані з обраним чиновником, але вони також є частиною колекції користувачів з відповідними користувачами ObjectId, щоб відобразити їх профіль на взаємодію з нашим додатком.

Ми почали конвертувати наш додаток з MySql в Монго кілька місяців тому, і поки ми переходимо, ми зберігаємо спадковий MySql id для обох цих типів даних, і ми також починаємо зберігати обраний офіційний Mongo ObjectId у користувачах документ для відображення обраних офіційних даних.

Я розмірковував, просто вказуючи нового користувача ObjectId як попереднього обраного офіційного ObjectId, щоб зробити його простішим, але хотів переконатися, що не вдалося зіткнутись з будь-яким наявним користувачем ObjectId.

Дякуємо за ваше розуміння.

Редагувати: Незабаром після публікації цього питання я зрозумів, що запропоноване нами рішення не дуже вдала ідея. Було б краще просто зберегти поточну схему, яку ми маємо, і просто посилання на обрану посадову особу '_id' в документі користувачів.



1
Я читав цю сторінку раніше. Як не дивно, я фактично посилався на ту саму сторінку в попередній відповіді. І я бачив "досить високу ймовірність бути унікальним" відмови від відповідальності, але не був впевнений, що колекція, яка вставляється в якийсь фактор, відіграє в цьому якийсь фактор. Я думаю, що я не впевнений у тому, що саме являє собою 2-байтна частина ідентифікатора Process ObjectId. Якщо це має щось спільне з колекцією, то між двома різними документами, створеними в той самий час, на одній і тій же машині в різних колекціях буде унікальність.
Ентоні Джек

1
Ідентифікатор 2-байтного процесу - це підсистема процесу, що генерує ObjectID. Як приклад, ось код, який pymongo використовує для створення ObjectID: github.com/mongodb/mongo-python-driver/blob/master/bson/…
mstearn

Один готч, на який я зіткнувся, - це вставка партії. Я будував партії з 10-тисячним документами, і щоразу стикався, бо частина зустрічної перекидалася щоразу.
fawce

Я знаю, що минуло деякий час, але 10-тисячні документи не перекидалися на прилавок. Лічильна частина - три байти, а не три цифри. Це понад 16 мільйонів.
Ася Камський

Відповіді:


318

Коротка відповідь

Просто для додання прямої відповіді на ваше початкове запитання: Так, якщо ви використовуєте BSON Object ID-генерацію, то для більшості драйверів ідентифікатори майже напевно будуть унікальними для колекцій. Дивіться нижче, що означає «майже напевно».

Довга відповідь

Ідентифікатор об'єкта BSON, сформований драйверами DB Mongo, є великою ймовірністю бути унікальним у колекціях. Це головним чином через останні 3 байти ідентифікатора, які для більшості драйверів генеруються за допомогою статичного збільшення лічильника. Цей лічильник не залежить від колекції; це глобально. Наприклад, драйвер Java використовує випадково ініціалізований статичний AtomicInteger.

То чому, в документах Mongo, вони кажуть, що ідентифікатори "з великою ймовірністю" будуть унікальними, а не прямо говорять, що вони будуть унікальними? Три можливості можуть виникнути там, де ви не отримаєте унікальний ідентифікатор (будь ласка, повідомте мене, якщо їх більше):

Перед цим обговоренням нагадайте, що BSON Object ID складається з:

[4 байти секунди з епохи, 3 байти машинного хешу, 2 байти ідентифікатора процесу, 3 байтового лічильника]

Ось три можливості, тож ви самі судіть, наскільки ймовірно отримати дупа:

1) Переповнення лічильника: у лічильнику є 3 байти. Якщо вам вдається вставити понад 16 777 216 (2 ^ 24) документів за одну секунду, на тій же машині, в одному і тому ж процесі, ви можете переповнити байти збільшення лічильника і в кінцевому підсумку з двома ідентифікаторами об'єктів, які поділяють той самий час, машина , обробляти та лічити значення.

2) Лічильник непримноження: деякі водії Монго використовують випадкові числа замість збільшення чисел для лічильників байтів. У цих випадках є 1 / 16,777,216 шанс генерувати не унікальний ідентифікатор, але лише в тому випадку, якщо ці два ідентифікатори генеруються в одну і ту ж секунду (тобто до того, як розділ часу ідентифікатора оновиться на наступну секунду), на ту ж саму машина, в тому ж процесі.

3) Машина та обробка хеша до однакових значень. Значення ідентифікатора машини та ідентифікатора процесу можуть у деяких дуже малоймовірних сценаріях відображати однакові значення для двох різних машин. Якщо це відбувається, і в той же час два лічильника на двох різних машинах протягом однієї секунди генерують одне і те ж значення, тоді ви отримаєте повторюваний ідентифікатор.

Це три сценарії, на які слід стежити. Сценарії 1 та 3 здаються малоймовірними, а сценарій 2 цілком уникнути, якщо ви використовуєте правильний драйвер. Вам доведеться точно перевірити джерело драйвера.


Хіба 3-байтний лічильник не представляє собою можливість приймати 2 ^ 24 = 16777216 кількість документів, що вставляються в секунду за процес на машину?
Форест Є.

Ви абсолютно праві, я випадково вдвічі зменшив кількість бітів - відповідь була змінена.
Радж Адвані

Оскільки я щойно вступив у це, дозвольте додати, що деякі драйвери (наприклад, C), хоч і використовують кроки, не збільшуються атомно, тож час від часу він генерує таку саму дію через стан перегонів
Павло Веселов

39
Ви повністю пропустили той факт, що через 136 років у вас з’явиться ще один знімок, щоб створити те саме, що ObjectIdви мали раніше, доки хеш машин, ідентифікатор обробки та лічильник
виявляться

25
@jamylak Ми піклуємося про цю проблему, коли вона стане нагальною (сказали ті люди, які стандартизували формати дати YYMMDD у 70-х роках)
Філіпп,

14

ObjectIds генеруються на стороні клієнта таким чином, як у UUID, але з деякими приємнішими властивостями для зберігання в базі даних, наприклад, приблизно збільшуючи порядок та безкоштовно кодуючи час їх створення. Ключовим для вашого випадку використання є те, що вони створені для того, щоб гарантувати унікальність з високою ймовірністю, навіть якщо вони створюються на різних машинах.

Тепер якщо ви посилалися на поле _id взагалі, нам не потрібна унікальність для колекцій, тому безпечно повторно використовувати старий _id. Як конкретний приклад, якщо у вас є дві колекції, colorsі fruitsобидві можуть одночасно мати такий предмет {_id: 'orange'}.

Якщо ви хочете дізнатися більше про те, як створюються ObjectIds, ось специфікація: http://www.mongodb.org/display/DOCS/Object+IDs#ObjectIDs-BSONObjectIDSpecification


11

Якщо у когось виникають проблеми з дублікатами Mongo ObjectID, ви повинні знати, що незважаючи на ймовірність того, що дупи відбудуться в самому Монго, можливо, з дублікатом _id генерується з PHP в Монго.

Випадок використання, коли це сталося з регулярністю для мене, - це коли я перебираю набір даних і намагаюся ввести дані в колекцію.

Масив, який містить дані ін'єкції, повинен бути явно скинутий під час кожної ітерації - навіть якщо ви не вказуєте значення _id. З певних причин процес INSERT додає до масиву Mongo _id так, ніби це глобальна змінна (навіть якщо масив не має глобальної області застосування). Це може вплинути на вас, навіть якщо ви викликаєте вставку в окремому виклику функції, де зазвичай очікуєте, що значення масиву не зберігатимуться до функції виклику.

Для цього є три рішення:

  1. unset()Поле _id можна використовувати з масиву
  2. Ви можете повторно ініціалізувати весь масив array()кожного разу, коли ви перебираєте свій набір даних
  3. Ви можете чітко визначити значення _id самостійно (подбаючи про те, щоб визначити його таким чином, щоб ви самі не створювали дублі).

Я здогадуюсь, що це помилка в інтерфейсі PHP, і це не стільки проблема з Mongo, але якщо ви зіткнулися з цією проблемою, просто вимкніть _id і вам слід добре.


дивіться тут: php.net/manual/en/mongocollection.insert.php : "Примітка. Якщо параметр не має _id ключа або властивості, буде створений і призначений новий екземпляр MongoId. Це особлива поведінка не означає що параметр передається посиланням. ", це особливість, а не помилка. Це мається на увазі саме так
Олівер Коніг

1
Я не розумію сценарій, який ви тут описуєте; можливо, ви могли б показати якийсь код, який демонструє помилку?
Марк Амерді

-7

Про унікальність ObjectId у колекціях немає жодної гарантії. Навіть якщо це ймовірно дуже малоймовірно, це був би дуже поганий дизайн програми, який спирався на унікальність _id у колекціях.

Можна легко перевірити це в оболонці монго:

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

@mstearn (Nitpick) Поняття про те, що UUID за своєю суттю є унікальним, є помилковим. Хороша стратегія генерації UUID / послідовностей може зробити зіткнення малоймовірним, але для цього потрібно врахувати унікальні генератори (наприклад, унікальні місця), щоб гарантувати абсолютну унікальність між генераторами. Зрозуміло, більшість імовірностей настільки низькі, що не викликає жодних проблем :-) GUID . Одне з питань, яке виникає , - це копіювання / копіювання ідентифікаторів замість нового покоління.

1
@pst: MongoDB ObjectID включають як pid процесу генерації, так і деякі байти на основі хешу імені хоста. Це в поєднанні з лічильником часу та збільшенням лічильника робить надзвичайно ймовірним, що будь-які два окремо створених ObjectID будуть унікальними у всьому світі / універсально. Звичайно, як ви вже говорили, це стосується тільки щойно створених ObjectID.
mstearn

1
Я маю на увазі тип ObjectId. Не вказується значення рядка для '_id'. Звичайно, вони будуть однаковими і конфліктуватимуть, якщо встановити їх у тот же строці вручну.
Ентоні Джек

Так, я уточнив речі у своєму посту. _id, звичайно, не унікальний, і оскільки ви не контролюєте функцію генерації ObjectId, напевно, це погана ідея.
шило
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.