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


188

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

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

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

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

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

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

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



1
Я читал эту страницу раньше. По иронии судьбы в предыдущем ответе я действительно ссылался на ту же страницу. И я действительно видел отказ от ответственности «достаточно высокая вероятность быть уникальным», но не был уверен, влияет ли это на вставку коллекции в игру. Я предполагаю, что я не уверен, что именно представляет собой 2-байтовая часть идентификатора процесса в ObjectId. Если это как-то связано с коллекцией, то между двумя разными документами, созданными в одно и то же время на одной и той же машине в разных коллекциях, будет уникальность.
Энтони Джек,

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

Одна проблема, с которой я столкнулся, - это пакетная вставка. Я собирал пакеты по 10 тыс. Документов и каждый раз сталкивался, потому что счетчик каждый раз перекатывался.
fawce

Я знаю, что прошло много времени, но 10К документов не перевернутся. Счетчик состоит из трех байтов, а не трех цифр. Это более 16 миллионов.
Ася Камский

Ответы:


326

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

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

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

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

Так почему же в документах Mongo говорится, что идентификаторы «весьма вероятно» будут уникальными, вместо того, чтобы прямо сказать, что они БУДУТ уникальными? Возможны три варианта, при которых вы не получите уникальный идентификатор (сообщите мне, если есть другие):

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

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

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

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

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

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

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


Разве трехбайтовый счетчик не представляет собой возможность принимать 2 ^ 24 = 16777216 количество документов, вставляемых в секунду на процесс на машину?
Forrest Ye

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

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

42
Вы полностью упустили тот факт, что через 136 лет у вас будет еще один шанс сгенерировать то же самое, ObjectIdчто и раньше, пока машинный хэш, идентификатор процесса и счетчик будут одинаковыми
jamylak

29
@jamylak Мы позаботимся об этой проблеме, когда она станет неотложной (сказали те люди, которые стандартизировали формат даты YYMMDD в 70-х годах)
Филипп

15

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

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

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


12

Если у кого-то возникают проблемы с дублированием идентификаторов Mongo ObjectID, вы должны знать, что, несмотря на маловероятность дублирования, происходящего в самом Mongo, в Mongo возможно создание дубликатов _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
Я не понимаю сценарий, который вы здесь описываете; возможно, вы могли бы показать какой-нибудь код, который обнаруживает ошибку?
Марк Эмери

-8

Нет никаких гарантий относительно уникальности 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

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

1
@pst: ObjectID MongoDB включают в себя как pid процесса генерации, так и некоторые байты на основе хэша имени хоста. Они в сочетании с меткой времени и увеличивающимся счетчиком делают чрезвычайно вероятным, что любые два отдельно сгенерированных ObjectID будут глобально / универсально уникальными. Конечно, как вы сказали, это применимо только к недавно сгенерированным ObjectID.
mstearn

1
Я имею в виду тип ObjectId. Без указания строкового значения для '_id'. Конечно, они будут одинаковыми и конфликтующими, если вы вручную установите для них одну и ту же строку.
Энтони Джек,

Да, я кое-что прояснил в своем посте. _id, безусловно, не уникальны, и, поскольку вы не контролируете функцию генерации ObjectId, полагаться на нее, вероятно, будет плохой идеей.
slacy
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.