Kemungkinan duplikasi ObjectId Mongo yang dihasilkan dalam dua koleksi yang berbeda?


187

Apakah mungkin untuk ObjectId Mongo yang sama persis dihasilkan untuk dokumen dalam dua koleksi yang berbeda? Saya menyadari bahwa itu sangat tidak mungkin, tetapi apakah itu mungkin?

Tanpa terlalu spesifik, alasan saya bertanya adalah bahwa dengan aplikasi yang saya kerjakan kami menunjukkan profil publik pejabat terpilih yang kami harap dapat dikonversi menjadi pengguna situs kami yang lengkap. Kami memiliki koleksi terpisah untuk pengguna dan pejabat terpilih yang saat ini tidak menjadi anggota situs kami. Ada berbagai dokumen lain yang berisi berbagai potongan data tentang pejabat terpilih yang semuanya memetakan kembali ke orang tersebut menggunakan ObjectId resmi terpilih mereka.

Setelah membuat akun, kami masih menyoroti data yang terkait dengan pejabat terpilih tetapi sekarang mereka juga merupakan bagian dari koleksi pengguna dengan ObjectId pengguna yang sesuai untuk memetakan profil mereka untuk berinteraksi dengan aplikasi kami.

Kami telah mulai mengonversi aplikasi kami dari MySql ke Mongo beberapa bulan yang lalu dan sementara kami dalam transisi, kami menyimpan id MySql lama untuk kedua tipe data ini dan kami juga mulai sekarang menyimpan ObjectId resmi Mongo yang terpilih di pengguna. mendokumentasikan untuk memetakan kembali ke data resmi terpilih.

Saya sedang merenungkan hanya menetapkan ObjectId pengguna baru sebagai ObjectId resmi terpilih sebelumnya untuk membuat hal-hal lebih sederhana tetapi ingin memastikan bahwa itu tidak mungkin untuk memiliki tabrakan dengan ObjectId pengguna yang ada.

Terima kasih atas wawasan Anda.

Sunting: Tidak lama setelah memposting pertanyaan ini, saya menyadari bahwa solusi yang saya usulkan bukanlah ide yang sangat bagus. Akan lebih baik untuk menjaga skema saat ini yang kami miliki dan hanya tautan ke '_id' resmi yang terpilih dalam dokumen pengguna.



1
Saya sudah membaca halaman itu sebelumnya. Ironisnya, saya sebenarnya terhubung ke halaman yang sama di jawaban sebelumnya. Dan saya memang melihat "kemungkinan cukup tinggi untuk menjadi unik" disclaimer tetapi tidak yakin apakah koleksi yang dimasukkan memainkan faktor apa pun dalam hal ini. Saya kira apa yang saya tidak yakin adalah apa yang sebenarnya mewakili 2 byte Proses ID dari ObjectId sebenarnya. Jika ada hubungannya dengan koleksi maka akan ada keunikan antara dua dokumen berbeda yang dibuat pada waktu yang sama pada mesin yang sama persis dalam koleksi yang berbeda.
Anthony Jack

1
Id proses 2byte adalah pid dari proses yang menghasilkan ObjectID. Sebagai contoh, berikut adalah kode yang digunakan pymongo untuk menghasilkan ObjectID: github.com/mongodb/mongo-python-driver/blob/master/bson/…
mstearn

Satu gotcha yang saya temui adalah memasukkan batch. Saya sedang membangun kumpulan dokumen 10k, dan bertabrakan setiap kali karena bagian counter berguling setiap waktu.
fawce

Saya tahu ini sudah lama, tetapi dokumen 10K tidak akan berguling. Bagian penghitungnya adalah tiga byte, bukan tiga digit. Itu lebih dari 16 juta.
Asya Kamsky

Jawaban:


318

Jawaban singkat

Hanya untuk menambahkan jawaban langsung ke pertanyaan awal Anda: YA, jika Anda menggunakan generasi ID Objek BSON, maka untuk sebagian besar driver ID hampir pasti akan menjadi unik di seluruh koleksi. Lihat di bawah untuk arti "hampir pasti".

Jawaban panjang

ID Objek BSON yang dihasilkan oleh driver Mongo DB sangat mungkin unik di seluruh koleksi. Ini terutama karena 3 byte terakhir dari ID, yang untuk sebagian besar driver dihasilkan melalui penghitung kenaikan statis. Penghitung itu tidak bergantung pada pengumpulan; ini global. Driver Java, misalnya, menggunakan AtomicInteger statis yang diinisialisasi secara acak.

Jadi mengapa, dalam dokumen Mongo, apakah mereka mengatakan bahwa ID "sangat mungkin" menjadi unik, bukannya langsung mengatakan bahwa mereka AKAN unik? Tiga kemungkinan dapat terjadi di mana Anda tidak akan mendapatkan ID unik (beri tahu saya jika ada lebih banyak):

Sebelum diskusi ini, ingat bahwa ID Objek BSON terdiri dari:

[4 byte detik sejak zaman, hash mesin 3 byte, ID proses 2 byte, penghitung 3 byte]

Berikut adalah tiga kemungkinan, jadi Anda menilai sendiri seberapa besar kemungkinan mendapatkan dupe:

1) Counter overflow: ada 3 byte di konter. Jika Anda memasukkan lebih dari 16.777.216 (2 ^ 24) dokumen dalam satu detik, pada mesin yang sama, dalam proses yang sama, maka Anda dapat melimpahi byte kenaikan counter dan berakhir dengan dua ID Obyek yang berbagi waktu yang sama, mesin , memproses, dan melawan nilai.

2) Counter non-incrementing: beberapa driver Mongo menggunakan angka acak alih-alih menambah angka untuk byte counter. Dalam kasus ini, ada peluang 1 / 16.777.216 untuk menghasilkan ID yang tidak unik, tetapi hanya jika kedua ID tersebut dihasilkan dalam detik yang sama (yaitu sebelum bagian waktu dari ID diperbarui ke detik berikutnya), pada saat yang sama mesin, dalam proses yang sama.

3) Mesin dan proses hash dengan nilai yang sama. ID mesin dan nilai ID proses dapat, dalam beberapa skenario yang sangat tidak mungkin, dipetakan ke nilai yang sama untuk dua mesin yang berbeda. Jika ini terjadi, dan pada saat yang sama kedua penghitung pada dua mesin yang berbeda, pada detik yang sama, menghasilkan nilai yang sama, maka Anda akan berakhir dengan duplikat ID.

Ini adalah tiga skenario yang harus diperhatikan. Skenario 1 dan 3 tampaknya sangat tidak mungkin, dan skenario 2 benar-benar dapat dihindari jika Anda menggunakan driver yang tepat. Anda harus memeriksa sumber driver untuk mengetahui dengan pasti.


Bukankah penghitung 3 byte mewakili kemampuan menerima 2 ^ 24 = 16777216 jumlah dokumen yang dimasukkan per detik per proses per mesin?
Forrest Ye

Anda memang benar, saya tidak sengaja membagi dua bit - jawaban telah diubah.
Raj Advani

Karena saya baru saja melangkah ke ini, izinkan saya menambahkan bahwa beberapa driver (misalnya C), meskipun menggunakan kenaikan, tidak bertambah secara atom, jadi dari waktu ke waktu, ini menghasilkan oid yang sama karena kondisi balapan
Pawel Veselov

39
Anda benar-benar melewatkan fakta bahwa dalam 136 tahun Anda akan memiliki kesempatan lain untuk menghasilkan yang sama ObjectIdseperti sebelumnya selama hash mesin, memproses ID, dan melawan semuanya ternyata sama
jamylak

25
@jamylak Kami akan mengurus masalah itu ketika menjadi mendesak (kata orang-orang yang membuat format tanggal YYMMDD standar pada tahun 70-an)
Philipp

14

ObjectIds dihasilkan sisi klien dengan cara yang mirip dengan UUID tetapi dengan beberapa properti yang lebih baik untuk penyimpanan dalam database seperti peningkatan pesanan secara kasar dan penyandian waktu pembuatannya secara gratis. Kuncinya untuk use case Anda adalah mereka dirancang untuk menjamin keunikan dengan probabilitas tinggi bahkan jika mereka dihasilkan pada mesin yang berbeda.

Sekarang jika Anda merujuk ke bidang _id secara umum, kami tidak memerlukan keunikan di seluruh koleksi sehingga aman untuk menggunakan kembali _id yang lama. Sebagai contoh konkret, jika Anda memiliki dua koleksi, colorsdan fruits, keduanya secara bersamaan dapat memiliki objek seperti {_id: 'orange'}.

Jika Anda ingin tahu lebih banyak tentang bagaimana ObjectIds dibuat, berikut adalah spesifikasinya: http://www.mongodb.org/display/DOCS/Object+IDs#ObjectIDs-BSONObjectIDSpecification


11

Jika ada yang mengalami masalah dengan duplikat Objecto Mongo, Anda harus tahu bahwa meskipun kemungkinan dups terjadi di Mongo itu sendiri, ada kemungkinan duplikat _id dihasilkan dengan PHP dalam bahasa Mongo.

Kasus penggunaan di mana hal ini terjadi dengan keteraturan bagi saya adalah ketika saya mengulang-ulang data dan mencoba menyuntikkan data ke dalam koleksi.

Array yang menyimpan data injeksi harus secara eksplisit diatur ulang pada setiap iterasi - bahkan jika Anda tidak menentukan nilai _id. Untuk beberapa alasan, proses INSERT menambahkan Mongo _id ke array seolah-olah itu adalah variabel global (bahkan jika array tidak memiliki cakupan global). Ini dapat mempengaruhi Anda bahkan jika Anda memanggil penyisipan dalam panggilan fungsi terpisah di mana Anda biasanya mengharapkan nilai-nilai array tidak bertahan kembali ke fungsi panggilan.

Ada tiga solusi untuk ini:

  1. Anda dapat unset()bidang _id dari array
  2. Anda dapat menginisialisasi ulang seluruh array dengan array()setiap kali Anda mengulang dataset
  3. Anda dapat menentukan sendiri nilai _id secara eksplisit (berhati-hatilah untuk mendefinisikannya sedemikian rupa sehingga Anda tidak menghasilkan dups sendiri).

Dugaan saya adalah bahwa ini adalah bug di antarmuka PHP, dan bukan masalah dengan Mongo, tetapi jika Anda mengalami masalah ini, hapus saja _id dan Anda harus baik-baik saja.


lihat di sini: php.net/manual/en/mongocollection.insert.php : "Catatan: Jika parameter tidak memiliki kunci atau properti _id, instance MongoId baru akan dibuat dan ditugaskan padanya. Perilaku khusus ini tidak berarti bahwa parameter dilewatkan dengan referensi. ", itu adalah fitur, bukan bug, itu dimaksudkan untuk menjadi seperti itu
Oliver Konig

1
Saya tidak mengerti skenario yang Anda gambarkan di sini; mungkin Anda bisa menunjukkan beberapa kode yang menunjukkan bug?
Mark Amery

-7

Tidak ada jaminan apa pun tentang keunikan ObjectId di seluruh koleksi. Bahkan jika secara probabilistik sangat tidak mungkin, itu akan menjadi desain aplikasi yang sangat buruk yang mengandalkan keunikan _id di koleksi.

Seseorang dapat dengan mudah menguji ini di 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" }

Jadi, benar-benar tidak bergantung pada _id yang unik di seluruh koleksi, dan karena Anda tidak mengontrol fungsi pembuatan ObjectId, jangan bergantung padanya.

Dimungkinkan untuk membuat sesuatu yang lebih seperti uuid, dan jika Anda melakukannya secara manual, Anda bisa memiliki jaminan keunikan yang lebih baik.

Ingatlah bahwa Anda dapat meletakkan objek "jenis" yang berbeda dalam koleksi yang sama, jadi mengapa tidak hanya menempatkan dua "tabel" Anda di koleksi yang sama. Mereka akan berbagi ruang _id yang sama, dan dengan demikian, akan dijamin unik. Beralih dari "calon" ke "terdaftar" akan menjadi membalik sederhana bidang ...


1
Saya pikir Anda mungkin membingungkan bidang _id secara umum dengan tipe ObjectID. Jenis ObjectID dirancang khusus untuk keunikan dengan tujuan bahwa itu dapat diperlakukan seperti UUID. Namun bidang _id dapat berupa jenis apa pun dan hanya menjamin keunikan pada satu koleksi jika Anda menggunakan jenis lain untuk kunci tersebut, seperti string dalam contoh Anda.
belajar

@mstearn (Nitpick) Gagasan bahwa UUID secara inheren unik cacat. UUID / strategi pembuatan urutan yang baik dapat membuat tumbukan tidak mungkin tetapi perlu mempertimbangkan generator unik (misalnya lokasi unik) untuk menjamin keunikan absolut antara generator. Memang, sebagian besar memiliki probabilitas sangat rendah sehingga tidak menjadi masalah yang berlaku :-) GUID . Salah satu isu yang tidak datang meskipun, adalah duplikasi / menyalin id bukannya generasi baru.

1
@ pst: MongoDBs ObjectIDs termasuk pid dari proses pembuatan dan beberapa byte berdasarkan hash dari nama host. Ini dikombinasikan dengan timestamp dan penghitung tambahan membuatnya sangat mungkin bahwa setiap dua ObjectID yang dihasilkan secara terpisah akan unik secara global / universal. Tentu saja seperti yang Anda katakan itu hanya berlaku untuk ObjectID yang baru dibuat.
belajar

1
Saya mengacu pada tipe ObjectId. Tidak menentukan nilai string untuk '_id'. Tentu saja mereka akan sama dan bertentangan jika Anda mengaturnya ke string yang sama persis secara manual.
Anthony Jack

Ya, saya mengklarifikasi hal-hal di posting saya. _id tentu tidak unik, dan karena Anda tidak mengontrol fungsi pembuatan ObjectId, mungkin ide yang buruk untuk mengandalkannya.
slacy
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.