Возможность создания дубликата Mongo ObjectId в двух разных коллекциях?

133

Возможно ли, чтобы один и тот же точный Mongo ObjectId был сгенерирован для документа в двух разных коллекциях? Я понимаю, что это определенно очень маловероятно, но возможно ли это?

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

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

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

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

Спасибо за понимание.

Редактировать: Вскоре после публикации этого вопроса я понял, что мое предлагаемое решение было не очень хорошей идеей. Было бы лучше просто сохранить существующую схему, которая у нас есть, и просто связать с избранным официальным "_id" в документе пользователей.

  • 0
    См. Mongodb.org/display/DOCS/Object+IDs.
  • 1
    Я читал эту страницу раньше. По иронии судьбы, я уже ссылался на ту же страницу в предыдущем ответе. И я видел «разумно высокую вероятность быть уникальным» отказ от ответственности, но не был уверен, сыграло ли какая-либо роль в этом добавление коллекции. Я предполагаю, что я не уверен в том, что именно представляет собой 2-байтовая часть идентификатора процесса ObjectId. Если это как-то связано с коллекцией, тогда будет уникальность между двумя разными документами, созданными в одно и то же время на одном и том же компьютере в разных коллекциях.
Показать ещё 3 комментария
Теги:
database
nosql

4 ответа

233
Лучший ответ

Короткий ответ

Просто добавьте прямой ответ на свой начальный вопрос: ДА, если вы используете генерацию идентификатора объекта BSON, то для большинства драйверов идентификаторы почти наверняка будут уникальными для разных коллекций. См. Ниже, что означает "почти наверняка".

Длинный ответ

Идентификатор объекта BSON, созданный драйверами Mongo DB, скорее всего, будет уникальным для разных коллекций. Это происходит главным образом из-за последних 3 байтов идентификатора, который для большинства драйверов генерируется через счетчик статического приращения. Этот счетчик не зависит от коллекции; он глобальный. Драйвер Java, например, использует случайно инициализированный статический AtomicInteger.

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

Перед этим обсуждением напомните, что идентификатор объекта BSON состоит из:

[4 байта секунд с эпохи, 3 байта машинного хэша, 2 байта ИД процесса, счетчик 3 байта]

Вот три возможности, поэтому вы сами судите о том, насколько вероятно получить обман:

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

2) Счетчик без инкремента: некоторые монго-драйверы используют случайные числа вместо инкрементирующих чисел для байтов счетчика. В этих случаях существует вероятность получения уникального идентификатора 1/16,777,216, но только если эти два идентификатора генерируются за одну секунду (т.е. До временного раздела обновлений ID до следующей секунды), на том же машина в том же процессе.

3) Хэш машинного и технологического процессов с теми же значениями. Идентификатор машины и значения идентификатора процесса могут в некоторых маловероятных сценариях сопоставляться с одинаковыми значениями для двух разных машин. Если это происходит, и в то же время два счетчика на двух разных машинах в течение той же секунды генерируют одно и то же значение, тогда вы получите дубликат ID.

Это три сценария, на которые нужно следить. Сценарии 1 и 3 кажутся маловероятными, и сценарий 2 полностью можно избежать, если вы используете правильный драйвер. Вам обязательно нужно проверить источник драйвера.

  • 0
    Разве счетчик 3 байта не представляет возможность приема 2 ^ 24 = 16777216 количества документов, вставленных в секунду на процесс на машину?
  • 0
    Вы абсолютно правы, я случайно наполовину уменьшил количество бит - ответ был изменен.
Показать ещё 5 комментариев
13

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

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

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

10

В случае, если у кого-то возникают проблемы с дублирующимися идентификаторами Mongo ObjectID, вы должны знать, что, несмотря на маловероятность дублирования, происходящего в самом Mongo, можно создать дубликат _id, сгенерированный PHP в Mongo.

Случай использования, где это произошло с регулярностью для меня, - это когда я перебираю набор данных и пытаюсь вставить данные в коллекцию.

Массив, содержащий данные инъекции, должен быть явно reset на каждой итерации - даже если вы не указали значение _id. По какой-то причине процесс INSERT добавляет Mongo _id в массив, как если бы он был глобальной переменной (даже если массив не имеет глобальной области). Это может повлиять на вас, даже если вы вызываете вставку в отдельный вызов функции, где вы обычно ожидаете, что значения массива не будут сохраняться до вызывающей функции.

Существует три решения:

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

Я предполагаю, что это ошибка в интерфейсе PHP, а не проблема с Mongo, но если вы столкнулись с этой проблемой, просто отключите _id, и все будет в порядке.

  • 0
    см. здесь: php.net/manual/en/mongocollection.insert.php : «Примечание. Если параметр не имеет ключа или свойства _id, будет создан новый экземпляр MongoId и назначен ему. Это специальное поведение не означает что параметр передается по ссылке. ", это особенность, а не ошибка, это должно быть так
  • 1
    Я не понимаю сценарий, который вы здесь описываете; возможно, вы могли бы показать какой-то код, который показывает ошибку?
-1

Нет никакой гарантии об уникальности ObjectId в коллекциях. Даже если это вероятностно очень маловероятно, это будет очень плохой дизайн приложения, основанный на уникальности уникальных коллекций.

Можно легко проверить это в оболочке монго:

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, и если вы сделаете это вручную, вы можете лучше гарантировать уникальность.

Помните, что вы можете поместить объекты разных "типов" в одну коллекцию, поэтому почему бы просто не поместить ваши две "таблицы" в одну коллекцию. Они будут разделять одно и то же пространство и, таким образом, будут гарантированы уникальными. Переход от "проспективного" к "зарегистрированному" будет простым переворотом поля...

  • 1
    Я думаю, что вы можете путать поле _id в целом с типом ObjectID. Тип ObjectID был специально разработан для уникальности с целью, чтобы его можно было рассматривать как UUID. Однако поле _id может быть любого типа и гарантирует уникальность только для одной коллекции, если вы используете для ключа другие типы, например строку в вашем примере.
  • 0
    @mstearn (Nitpick) Представление о том, что UUID по своей сути уникален, ошибочно. Хорошая стратегия генерации UUID / последовательности может сделать коллизию маловероятной, но она должна учитывать уникальные генераторы (например, уникальные местоположения), чтобы гарантировать абсолютную уникальность между генераторами. Конечно, у большинства есть настолько низкие вероятности, что это не имеет отношения к делу :-) GUID . Тем не менее, возникает проблема дублирования / копирования идентификаторов, а не нового поколения.
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню