¿Posibilidad de generar Mongo ObjectId duplicados en dos colecciones diferentes?

187

¿Es posible que se genere el mismo ObjectId Mongo exacto para un documento en dos colecciones diferentes? Me doy cuenta de que definitivamente es muy poco probable, pero ¿es posible?

Sin ser demasiado específico, la razón por la que pregunto es que con una aplicación en la que estoy trabajando mostramos perfiles públicos de funcionarios electos que esperamos convertir en usuarios completos de nuestro sitio. Tenemos colecciones separadas para los usuarios y los funcionarios electos que actualmente no son miembros de nuestro sitio. Hay varios otros documentos que contienen varios datos sobre los funcionarios electos que todos corresponden a la persona que usa su ObjectId oficial elegido.

Después de crear la cuenta, aún resaltamos los datos asociados al funcionario electo, pero ahora también forman parte de la colección de usuarios con un ObjectId de usuario correspondiente para asignar su perfil a las interacciones con nuestra aplicación.

Habíamos comenzado a convertir nuestra aplicación de MySql a Mongo hace unos meses y, mientras estamos en transición, almacenamos la identificación de MySql heredada para ambos tipos de datos y ahora también comenzamos a almacenar el ObjectId oficial de Mongo elegido en los usuarios. documento para asignar de nuevo a los datos oficiales elegidos.

Estaba pensando en especificar el nuevo ObjectId de usuario como el ObjectId oficial elegido anteriormente para simplificar las cosas, pero quería asegurarme de que no fuera posible tener una colisión con ningún ObjectId de usuario existente.

Gracias por tu perspicacia.

Editar: Poco después de publicar esta pregunta, me di cuenta de que mi solución propuesta no era una muy buena idea. Sería mejor simplemente mantener el esquema actual que tenemos en su lugar y simplemente vincular al '_id' oficial elegido en el documento de los usuarios.

Anthony Jack
fuente
1
He leído esa página antes. Irónicamente, en realidad me vinculé a la misma página en una respuesta anterior. Y sí vi la exención de responsabilidad "razonablemente alta probabilidad de ser único", pero no estaba seguro de si la colección que se inserta jugó algún factor en esto. Supongo que no estoy seguro de qué representa realmente la porción de ID de proceso de 2 bytes del ObjectId. Si tiene algo que ver con la colección, entonces habría unicidad entre dos documentos diferentes creados al mismo tiempo exactamente en la misma máquina en diferentes colecciones.
Anthony Jack
1
El id de proceso de 2bytes es el pid del proceso que genera el ObjectID. Como ejemplo, aquí está el código que usa pymongo para generar ObjectIDs: github.com/mongodb/mongo-python-driver/blob/master/bson/…
mstearn
Una de las cosas con las que me encontré es la inserción por lotes. Estaba construyendo lotes de documentos de 10k y colisionando cada vez porque la parte del mostrador se volcó cada vez.
fawce
Sé que ha pasado un tiempo, pero los documentos de 10K no pasarían por el mostrador. La parte del contador es de tres bytes, no de tres dígitos. Eso es más de 16 millones.
Asya Kamsky

Respuestas:

318

Respuesta corta

Solo para agregar una respuesta directa a su pregunta inicial: SÍ, si usa la generación de ID de objeto BSON, entonces para la mayoría de los controladores, las ID seguramente serán únicas en todas las colecciones. Vea a continuación lo que significa "casi seguro".

Respuesta larga

Es muy probable que las ID de objeto BSON generadas por los controladores de Mongo DB sean únicas en todas las colecciones. Esto se debe principalmente a los últimos 3 bytes de la ID, que para la mayoría de los controladores se genera a través de un contador de incremento estático. Ese contador es independiente de la colección; Es global. El controlador Java, por ejemplo, utiliza un AtomicInteger estático inicializado aleatoriamente.

Entonces, ¿por qué, en los documentos de Mongo, dicen que las identificaciones son "altamente probables" para ser únicas, en lugar de decir que SERÁN únicas? Pueden ocurrir tres posibilidades en las que no obtendrá una identificación única (avíseme si hay más):

Antes de esta discusión, recuerde que el ID de objeto BSON consiste en:

[4 bytes segundos desde la época, hash de máquina de 3 bytes, ID de proceso de 2 bytes, contador de 3 bytes]

Estas son las tres posibilidades, para que juzgues por ti mismo la probabilidad de que te engañen:

1) Desbordamiento del contador: hay 3 bytes en el contador. Si inserta más de 16.777.216 (2 ^ 24) documentos en un solo segundo, en la misma máquina, en el mismo proceso, puede desbordar los bytes de contador incrementales y terminar con dos ID de objeto que comparten el mismo tiempo, máquina , proceso y valores de contador.

2) Contador sin incremento: algunos controladores Mongo usan números aleatorios en lugar de números incrementales para los bytes del contador. En estos casos, existe una posibilidad de 1 / 16,777,216 de generar una ID no única, pero solo si esas dos ID se generan en el mismo segundo (es decir, antes de que la sección de tiempo de la ID se actualice al siguiente segundo), en el mismo máquina, en el mismo proceso.

3) Máquina y proceso hash a los mismos valores. Los valores de ID de máquina e ID de proceso pueden, en un escenario altamente improbable, correlacionarse con los mismos valores para dos máquinas diferentes. Si esto ocurre, y al mismo tiempo, los dos contadores en las dos máquinas diferentes, durante el mismo segundo, generan el mismo valor, entonces terminará con una ID duplicada.

Estos son los tres escenarios a tener en cuenta. Los escenarios 1 y 3 parecen altamente improbables, y el escenario 2 es totalmente evitable si está utilizando el controlador correcto. Tendrá que verificar la fuente del controlador para saber con certeza.

Raj Advani
fuente
¿El contador de 3 bytes no es una capacidad de aceptar 2 ^ 24 = 16777216 número de documentos insertados por segundo por proceso por máquina?
Forrest Ye
Tienes toda la razón, accidentalmente reduje a la mitad el número de bits: la respuesta se ha modificado.
Raj Advani
Como acabo de entrar en esto, permítanme agregar que algunos conductores (por ejemplo, C), aunque usan incrementos, no se incrementan atómicamente, por lo que de vez en cuando, genera el mismo oido debido a la condición de la carrera
Pawel Veselov,
39
Olvidó por completo el hecho de que en 136 años tendría otra oportunidad para generar lo mismo ObjectIdque tenía antes, siempre que el hash de la máquina, la identificación del proceso y el contador resulten todos iguales
jamylak
25
@jamylak Nos ocuparemos de ese problema cuando sea urgente (dijeron aquellas personas que estandarizaron los formatos de fecha YYMMDD en los años 70)
Philipp
14

Los ObjectIds se generan del lado del cliente de manera similar al UUID pero con algunas propiedades más agradables para el almacenamiento en una base de datos, como aumentar el orden y codificar su tiempo de creación de forma gratuita. La clave para su caso de uso es que están diseñados para garantizar la unicidad a una alta probabilidad, incluso si se generan en máquinas diferentes.

Ahora, si se refería al campo _id en general, no requerimos unicidad entre las colecciones, por lo que es seguro reutilizar el antiguo _id. Como ejemplo concreto, si tiene dos colecciones, colorsy fruits, ambas podrían tener simultáneamente un objeto como {_id: 'orange'}.

En caso de que quiera saber más sobre cómo se crean ObjectIds, aquí está la especificación: http://www.mongodb.org/display/DOCS/Object+IDs#ObjectIDs-BSONObjectIDSpecification

mstearn
fuente
11

En caso de que alguien tenga problemas con los ObjectID de Mongo duplicados, debe saber que, a pesar de la improbabilidad de que ocurran dups en Mongo, es posible tener _id's duplicados generados con PHP en Mongo.

El caso de uso en el que esto ha sucedido con regularidad para mí es cuando estoy recorriendo un conjunto de datos e intentando inyectar los datos en una colección.

La matriz que contiene los datos de inyección debe restablecerse explícitamente en cada iteración, incluso si no está especificando el valor _id. Por alguna razón, el proceso INSERT agrega Mongo _id a la matriz como si fuera una variable global (incluso si la matriz no tiene un alcance global). Esto puede afectarlo incluso si está llamando a la inserción en una llamada de función separada donde normalmente esperaría que los valores de la matriz no persistan de nuevo a la función de llamada.

Hay tres soluciones para esto:

  1. Puede unset()el campo _id de la matriz
  2. Puede reinicializar toda la matriz array()cada vez que recorre su conjunto de datos
  3. Puede definir explícitamente el valor _id usted mismo (teniendo cuidado de definirlo de tal manera que no genere duplicados usted mismo).

Supongo que esto es un error en la interfaz de PHP, y no es tanto un problema con Mongo, pero si se encuentra con este problema, simplemente desarme el _id y debería estar bien.

DenverMatt
fuente
ver aquí: php.net/manual/en/mongocollection.insert.php : "Nota: Si el parámetro no tiene una clave o propiedad _id, se creará una nueva instancia de MongoId y se le asignará. Este comportamiento especial no significa que el parámetro se pasa por referencia. ", es una característica, no un error, está destinado a ser así
Oliver Konig
1
No entiendo el escenario que estás describiendo aquí; ¿quizás podrías mostrar algún código que exhiba el error?
Mark Amery
-7

No hay garantía alguna sobre la unicidad de ObjectId en las colecciones. Incluso si es probabilísticamente muy improbable, sería un diseño de aplicación muy pobre que se basara en _id unicidad en todas las colecciones.

Uno puede probar esto fácilmente en el shell 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" }

Por lo tanto, no confíe en que _id sea único en todas las colecciones, y dado que no controla la función de generación de ObjectId, no confíe en ella.

Es posible crear algo más parecido a un líquido, y si lo hace manualmente, podría tener una mejor garantía de singularidad.

Recuerde que puede colocar objetos de diferentes "tipos" en la misma colección, entonces, ¿por qué no simplemente poner sus dos "tablas" en la misma colección? Compartirían el mismo espacio _id, y por lo tanto, se les garantizaría un espacio único. Cambiar de "prospectivo" a "registrado" sería un simple cambio de campo ...

pizarroso
fuente
1
Creo que puede estar confundiendo el campo _id en general con el tipo ObjectID. El tipo ObjectID fue diseñado específicamente para la unicidad con el objetivo de que pueda tratarse como un UUID. Sin embargo, el campo _id puede ser de cualquier tipo y solo garantiza la unicidad en una sola colección si usa otros tipos para la clave, como una cadena en su ejemplo.
mstearn
@mstearn (Nitpick) La noción de que un UUID es inherentemente único es errónea. Una buena estrategia de generación de secuencia / UUID puede hacer que la colisión sea poco probable, pero necesita tener en cuenta generadores únicos (por ejemplo, ubicaciones únicas) para garantizar la unicidad absoluta entre los generadores. Por supuesto, la mayoría tiene probabilidades tan bajas que no es una preocupación aplicable :-) GUID . Sin embargo, un problema que surge es la duplicación / copia de identificadores en lugar de una nueva generación.
1
@pst: MongoDBs ObjectIDs incluyen tanto el pid del proceso de generación como algunos bytes basados ​​en un hash del nombre de host. Estos combinados con una marca de tiempo y un contador incremental hacen que sea extremadamente probable que cualquiera de los dos ObjectID generados por separado sean global / universalmente únicos. Por supuesto, como dijiste, eso solo se aplica a los ObjectID recién generados.
mstearn
1
Me refiero al tipo ObjectId. No se especifica un valor de cadena para '_id'. Por supuesto, serán los mismos y entrarán en conflicto si los configura en la misma cadena de forma manual.
Anthony Jack
Sí, aclaré cosas en mi publicación. Los identificadores ciertamente no son únicos, y dado que usted no controla la función de generación de ObjectId, probablemente sea una mala idea confiar en ella.
Slacy