Apakah _id unik di mongodb?

Artikel blog ini telah diterbitkan ulang dengan izin saya oleh MongoDB di https. // www. mongodb. com/blog/post/generating-globally-unique-identifiers-for-use-with-mongodb

Motivasi

Secara default, MongoDB menghasilkan pengenal ObjectID unik yang ditetapkan ke bidang _id dalam dokumen baru sebelum menulis dokumen itu ke database. Dalam banyak kasus, pengidentifikasi unik default yang ditetapkan oleh MongoDB akan memenuhi persyaratan aplikasi. Namun, dalam beberapa kasus, aplikasi mungkin perlu membuat pengidentifikasi unik khusus, seperti.

  • Aplikasi mungkin memerlukan pengidentifikasi unik dengan jumlah digit yang tepat. Misalnya, pengidentifikasi 12 digit unik mungkin diperlukan untuk nomor rekening bank
  • Pengidentifikasi unik mungkin perlu dibuat dalam urutan sekuensial yang meningkat secara monoton dan terus menerus
  • Pengidentifikasi unik mungkin harus independen dari vendor database tertentu

Karena sifat aplikasi modern yang multi-utas dan terdistribusi, tidak selalu merupakan tugas yang mudah untuk menghasilkan pengidentifikasi unik yang memenuhi persyaratan aplikasi

Ringkasan

Posting ini mencakup topik-topik berikut

  • Bagaimana cara menjamin keunikan pengidentifikasi di tingkat basis data
  • Gunakan ObjectID sebagai pengidentifikasi unik
  • Gunakan dokumen penghitung tunggal untuk menghasilkan pengidentifikasi unik
  • Gunakan satu dokumen penghitung yang mengalokasikan kumpulan pengidentifikasi unik
  • Gunakan beberapa dokumen penghitung yang mengalokasikan kumpulan pengidentifikasi unik
  • Buat pengidentifikasi unik secara acak dan coba lagi jika sudah ditetapkan
  • Gunakan algoritme UUID standar untuk pembuatan pengidentifikasi unik tingkat aplikasi
Bagaimana cara menjamin keunikan pengidentifikasi di tingkat basis data

Semua pendekatan yang kami usulkan di sini menghasilkan pengidentifikasi yang unik secara global untuk semua tujuan praktis, namun tergantung pada pendekatan yang dipilih, mungkin ada kasus tepi jarak jauh di mana pengidentifikasi yang dihasilkan mungkin tidak benar-benar unik secara global

Jika risiko benturan pengidentifikasi unik dianggap cukup jauh atau konsekuensi dari benturan semacam itu kecil, maka seseorang dapat mempertimbangkan untuk menulis ke database tanpa pemeriksaan keunikan tambahan. Ini adalah pendekatan yang valid yang kemungkinan akan diadopsi oleh banyak aplikasi

Namun, jika sangat penting untuk memastikan bahwa pengidentifikasi yang dihasilkan unik secara global, kode dapat ditulis secara defensif untuk menjamin bahwa database tidak akan pernah menulis pengidentifikasi unik yang sama dua kali. Ini relatif mudah diterapkan pada koleksi non-sharded dengan menentukan indeks unik pada kolom tertentu, yang mencegah nilai duplikat untuk kolom tersebut agar tidak pernah ditulis ke koleksi. Perhatikan bahwa secara default kolom _id selalu mematuhi batasan indeks unik

Jika penulisan gagal karena tabrakan pada bidang yang memiliki indeks unik, maka aplikasi harus menangkap kesalahan ini dan membuat pengidentifikasi unik baru dan mencoba menulis dokumen ke database lagi

Jika koleksi dipecah, ada. Jika batasan untuk menggunakan indeks unik pada koleksi sharded tidak dapat dipenuhi, maka koleksi proxy dapat digunakan untuk menjamin keunikan sebelum menulis data ke database

Gunakan ObjectID sebagai pengidentifikasi unik

Keterangan

Driver database MongoDB secara default menghasilkan pengidentifikasi ObjectID yang ditetapkan ke bidang _id dari setiap dokumen. Jika dokumen masuk ke database tanpa nilai _id, maka database itu sendiri akan menetapkan ObjectID ke bidang _id. Dalam banyak kasus, ObjectID dapat digunakan sebagai pengidentifikasi unik dalam aplikasi

ObjectID adalah angka 96-bit yang disusun sebagai berikut

  • nilai 4-byte yang mewakili detik sejak zaman Unix (yang tidak akan kehabisan detik hingga tahun 2106)
  • pengidentifikasi mesin 3-byte (biasanya berasal dari alamat MAC),
  • id proses 2-byte, dan
  • counter 3-byte, dimulai dengan nilai acak

Manfaat

  • ObjectID dihasilkan secara otomatis oleh driver database atau dalam beberapa kasus oleh database, dan akan ditetapkan ke kolom _id dari setiap dokumen
  • ObjectID dapat dianggap unik secara global untuk sebagian besar tujuan praktis
  • ObjectID mengkodekan stempel waktu waktu pembuatannya, yang dapat digunakan untuk kueri atau mengurutkan berdasarkan waktu pembuatan
  • ObjectID sebagian besar meningkat secara monoton,
  • ObjectID adalah 96-bit yang kurang dari beberapa implementasi UUID lainnya, yang akan menghasilkan penggunaan disk dan RAM yang sedikit lebih sedikit daripada solusi alternatif ini

Kekurangan

  • Pada 96 bit, ObjectId lebih panjang dari beberapa solusi alternatif, yang berarti mungkin memerlukan lebih banyak ruang disk dan RAM daripada alternatif ini
  • ObjectID dihasilkan oleh driver MongoDB atau oleh database itu sendiri. Beberapa bisnis mungkin enggan menautkan logika aplikasi mereka ke ID yang dibuat oleh database
  • ObjectID dapat dianggap unik secara global untuk sebagian besar tujuan praktis, tetapi ada kasus ekstrem yang mungkin tidak benar-benar unik secara global
Solusi dokumen penghitung tunggal

Penafian

Pendekatan yang dijelaskan di bagian ini umumnya tidak disarankan karena potensi dokumen penghitung tunggal menjadi hambatan dalam aplikasi

Keterangan

Pengidentifikasi unik mungkin diperlukan dalam urutan sekuensial yang meningkat secara monoton dan terus menerus, yang mirip dengan fungsionalitas urutan yang diimplementasikan oleh beberapa RDBMS.

Ini dapat dicapai dengan menerapkan solusi yang didasarkan pada dokumen penghitung terpusat yang berada dalam kumpulan yang kami sebut uniqueIdentifierCounter. Dokumen penghitung terpusat ini adalah satu-satunya dokumen dalam koleksi uniqueIdentifierCounter, dan kolom COUNT-nya akan melacak nilai pengidentifikasi unik saat ini

Setiap kali pengidentifikasi unik baru diperlukan, findAndModify akan digunakan pada dokumen penghitung untuk . Nilai COUNT kemudian dapat digunakan oleh aplikasi sebagai pengidentifikasi unik. Misalnya aplikasi dapat memberikan nilai COUNT ke bidang _id dari dokumen yang akan ditulis ke koleksi tertentu.

Contoh implementasi

Dokumen penghitung untuk pembuatan pengidentifikasi unik dapat terlihat sebagai berikut

{
    "_id"   : "UNIQUE COUNT DOCUMENT IDENTIFIER",
    "COUNT" : 0,
    "NOTES" : “Increment COUNT using findAndModify to ensure that the COUNT field will be incremented atomically with the fetch of this document",
}

Dan dokumen pembuatan pengenal unik dapat diminta dan ditambahkan secara atomik sebagai berikut. Perhatikan bahwa secara default dokumen yang dikembalikan dari findAndModify adalah dokumen pra-modifikasi

db.uniqueIdentifierCounter.findAndModify({
    query: { _id: "UNIQUE COUNT DOCUMENT IDENTIFIER" },
    update: {
        $inc: { COUNT: 1 },
    },
    writeConcern: 'majority'
})
_

Manfaat

  • Mudah diimplementasikan
  • Pengidentifikasi unik dihasilkan secara terus menerus dan meningkat secara monoton

Kekurangan

  • Pendekatan ini kemungkinan besar akan menghasilkan kemacetan serius dalam sistem, karena akan ada perselisihan yang disebabkan oleh banyak utas yang secara bersamaan mengakses dokumen penghitung tunggal.
  • Bergantung pada kelambatan replikasi dan waktu untuk menyiram dokumen penghitung ke disk, teknik ini akan membatasi kecepatan pembuatan pengenal unik. Jika kita berasumsi bahwa dibutuhkan 25ms untuk dokumen penghitung untuk bertahan dan direplikasi ke database maka metode ini hanya akan dapat menghasilkan 40 pengidentifikasi unik baru per detik. Jika aplikasi menunggu nilai pengidentifikasi unik sebelum dokumen baru dapat dimasukkan ke dalam koleksi tertentu, maka penyisipan ini akan memiliki kecepatan tulis maksimum 40 dokumen per detik. Tanpa hambatan seperti itu, kami berharap database yang berfungsi dengan baik dapat menulis puluhan ribu dokumen per detik
Dokumen penghitung tunggal dengan alokasi rentang

Keterangan

Pendekatan ini mirip dengan pendekatan sebelumnya, dengan perbedaan bahwa alih-alih menambah nilai COUNT dengan 1, kami mungkin ingin menambahkannya dengan angka yang lebih besar yang akan mewakili kumpulan pengidentifikasi unik yang akan dialokasikan oleh database ke

Misalnya, jika aplikasi mengetahui bahwa ia memerlukan 1000 pengidentifikasi unik baru, maka aplikasi akan menggunakan findAndModify() untuk secara atomis mendapatkan COUNT saat ini dan menambah nilai COUNT sebesar 1000. Dokumen yang dikembalikan dari perintah findAndModify akan berisi nilai awal untuk sekumpulan pengidentifikasi unik, dan aplikasi akan mengulang lebih dari 1000 nilai dari titik awal tersebut

Perhatikan bahwa dengan pendekatan ini aplikasi dapat mengirimkan nilai apa pun yang diinginkannya untuk menambah nilai COUNT, dan oleh karena itu pendekatan ini dapat dibuat fleksibel. Untuk batch besar, kenaikan ini akan menjadi angka yang besar, dan untuk pengenal tunggal, ini akan disetel ke 1

Contoh implementasi

Berikut ini menunjukkan perintah shell javascript yang secara atomik akan menambah COUNT sebanyak 1000 dan mengembalikan dokumen penghitung sebelumnya (sebelum kenaikan)

var seq_increment = 1000;
db.uniqueIdentifierCounter.findAndModify({
    query: { _id: "UNIQUE COUNT DOCUMENT IDENTIFIER" },
    update: {
        $inc: {COUNT: seq_increment },
    }
    writeConcern: 'majority'
})

Manfaat

  • Relatif mudah diimplementasikan
  • Pengidentifikasi unik dihasilkan oleh database dengan cara yang meningkat secara monoton, dan kemungkinan akan digunakan oleh aplikasi dengan cara yang meningkat secara monoton.
  • Pendekatan ini memiliki potensi untuk secara dramatis mengurangi jumlah permintaan dan pembaruan dokumen penghitung, yang dapat menghilangkan kemacetan yang dijelaskan dalam pendekatan sebelumnya

Kekurangan

  • Ada potensi kemacetan jika aplikasi meminta rentang pengidentifikasi unik yang kecil dengan setiap permintaan
  • Aplikasi harus memahami bahwa angka yang diterima dari database dimaksudkan sebagai rentang nilai
  • Beberapa utas yang meminta kumpulan pengidentifikasi unik pada saat yang sama dapat menyebabkan pengidentifikasi yang dialokasikan digunakan oleh aplikasi dalam urutan yang tidak meningkat secara monoton, karena setiap utas membutuhkan waktu untuk bekerja melalui kumpulan yang dialokasikan
  • Jika aplikasi bertingkah buruk, maka itu bisa membakar sejumlah besar pengidentifikasi unik tanpa benar-benar menggunakannya
Beberapa dokumen penghitung pengalokasi rentang

Keterangan

Pendekatan ini mirip dengan pendekatan sebelumnya, tetapi alih-alih memiliki satu dokumen penghitung, kita dapat memiliki banyak dokumen penghitung yang disimpan dalam koleksi uniqueIdentifierCounter

Misalnya, mungkin ada 1000 dokumen counter (bernomor 0 sampai 999) masing-masing bertanggung jawab untuk mengalokasikan 1 miliar nomor unik yang diambil dari rentang tertentu yang telah dialokasikan ke setiap counter. Dalam hal ini, penghitung 499 akan bertanggung jawab untuk mengalokasikan nilai dari 499000000000 ke 499999999999

Perhatikan bahwa contoh khusus ini menghasilkan angka unik mulai dari 0 hingga 999.999.999.999 yang merupakan angka 12 digit

Contoh implementasi

Di bawah ini kami menunjukkan format dan nilai awal yang ditetapkan ke dokumen penghitung di koleksi uniqueIdentifierCounter

/* The following would be the initial state of the 0th counter document, which is responsible for the range of unique identifiers from 0 to 999,999,999 */
{
    "_id"  : "COUNTER DOCUMENT NUMBER 0",
    "MAX_VALUE": 999999999,
    "COUNT"  : 0,
    "NOTES" : "Increment COUNT using findAndModify to ensure that the COUNT field will be incremented atomically with the fetch of this document",
}
 
/* The following would be the initial state of 499th counter document, which is responsible for the range of unique identifiers from 499,000,000,000 to 499,999,999,999 */
{
    "_id"  : "COUNTER DOCUMENT NUMBER 499"),
    "MAX_VALUE": 499999999999,
    "COUNT"  : 499000000000,
    "NOTES" : "Increment COUNT using findAndModify to ensure that the COUNT field will be incremented atomically with the fetch of this document",
}
/* Etc… */

Dengan pendekatan ini, setiap kali aplikasi membutuhkan nomor unik baru atau rentang nomor unik, aplikasi akan secara acak menghasilkan angka antara 0 dan 999 yang akan digunakan untuk melakukan kueri terhadap atribut _id di koleksi uniqueIdentifierCounter. Ini akan memilih dokumen penghitung tertentu untuk mengambil pengidentifikasi unik

Ini ditunjukkan dalam contoh berikut, di mana kami secara acak memilih salah satu dokumen penghitung, dan meminta kumpulan 100 nomor unik dari penghitung itu

var which_counter_to_query = Math.floor((Math.random()*1000));
var seq_increment = 100;
db.Unique Identifier_counter.findAndModify({
    query: { _id:  "COUNTER DOCUMENT NUMBER " + which_counter_to_query},
    update: {
        $inc: {COUNT: seq_increment },
    },
    writeConcern: 'majority'
})
_

Manfaat

  • Dibandingkan dengan pendekatan sebelumnya, pendekatan ini akan mengurangi perselisihan dengan memiliki utas yang relatif lebih sedikit yang secara bersamaan mengakses setiap dokumen penghitung

Kekurangan

  • Ini lebih rumit dari implementasi sebelumnya
  • Masih ada potensi kemacetan jika pendekatan ini digunakan untuk menghasilkan batch kecil dan memiliki sejumlah kecil dokumen tandingan
  • Jumlah dokumen penghitung harus ditentukan sebelumnya dan rentang pengidentifikasi unik yang ditetapkan untuk setiap dokumen penghitung perlu dialokasikan terlebih dahulu
  • Kehati-hatian harus dilakukan untuk memastikan bahwa rentang yang ditentukan sebelumnya yang ditetapkan untuk setiap dokumen penghitung tidak berguling
Pilih pengenal unik secara acak di aplikasi dan coba lagi jika sudah digunakan

Keterangan

Pendekatan ini bergantung pada fakta bahwa jika indeks unik didefinisikan dalam koleksi, bahwa setiap dokumen yang ditulis ke koleksi tersebut harus memiliki nilai unik yang ditetapkan ke bidang tersebut agar berhasil ditulis. Oleh karena itu, kami dapat secara acak membuat nomor pengidentifikasi unik dalam aplikasi, menetapkannya ke kolom unik dalam dokumen, dan mencoba menulis dokumen tersebut ke koleksi. Jika penulisan berhasil, maka kita tahu bahwa nilai yang kita berikan padanya unik. Jika penulisan gagal, maka aplikasi harus menangkap kegagalan tersebut dan secara acak membuat pengidentifikasi unik baru yang kemudian dapat digunakan untuk menulis dokumen ke koleksi. Jika jumlah tabrakan rendah, maka ini bisa menjadi cara yang efisien untuk menulis dokumen ke database dengan setiap dokumen memiliki pengidentifikasi unik yang dijamin

Contoh implementasi

Jika kita tahu bahwa kita akan memiliki maksimal satu miliar catatan dalam basis data kita, agar memiliki kemungkinan rendah untuk memilih pengidentifikasi yang telah ditetapkan (alias tabrakan), kita dapat secara acak memilih angka antara 0 dan 999.999.999.999 ( . Untuk contoh ini, rentang yang kami gunakan untuk memilih nomor acak adalah 1000 kali lebih besar dari jumlah dokumen yang kami harapkan untuk ditulis ke koleksi ini, yang menghasilkan kasus terburuk yang diharapkan 0. 1% kemungkinan tabrakan

Kami kemudian akan menetapkan nomor yang dibuat secara acak ke bidang yang memiliki indeks unik, dan menulis dokumen itu ke database. Jika penulisan berhasil, maka kita tahu bahwa pengidentifikasi itu unik. Jika penulisan gagal, maka aplikasi harus memilih pengenal lain secara acak dan mencoba lagi hingga penulisan berhasil. Perhatikan bahwa ada beberapa saat digunakan dalam cluster yang terfragmentasi. Jika batasan untuk menggunakan indeks unik pada koleksi sharded tidak dapat dipenuhi, maka koleksi proxy dapat digunakan untuk membantu menghasilkan indeks unik

Manfaat

  • Pendekatan ini tidak sulit diterapkan

Kekurangan

  • Beberapa bisnis tidak menyukai sifat acak dari pendekatan pembuatan pengenal unik ini
  • Menguji tabrakan pengidentifikasi unik mungkin rumit karena bergantung pada angka acak
  • Jika penghasil angka acak tidak baik maka mungkin ada potensi terjadinya beberapa tabrakan dan beberapa percobaan ulang yang diperlukan sebelum berhasil menulis dokumen
Hasilkan pengidentifikasi unik dalam aplikasi

Keterangan

RFC 4122 menentukan standar untuk menghasilkan UUID 128-bit. RFC menentukan berbagai algoritme untuk membuat UUID, yang disebut versi UUID.

Ada pustaka standar  yang akan menghasilkan UUID 128-bit pada tingkat aplikasi. Spesifikasi BSON mendefinisikan UUID sebagai subtipe yang valid, dan dapat ditemukan di. http. //bsonspec. org/spesifikasi. html.

Manfaat

  • Pendekatan ini secara efektif mengatasi kemacetan terkait UUID
  • UUID dibuat sepenuhnya dalam kode aplikasi, yang menyimpan perjalanan bolak-balik ke database yang diperlukan dengan beberapa pendekatan alternatif
  • Ini adalah implementasi standar yang bergantung pada perpustakaan yang ada
  • Ini tidak menggunakan pengidentifikasi basis data yang dibuat secara internal untuk fungsi bisnis
  • Untuk semua tujuan praktis, UUID yang dihasilkan dengan pendekatan ini bersifat unik secara global
  • Jika RFC 4122  versi 1 atau versi 2 digunakan dan diterapkan dengan benar, maka UUID yang dihasilkan dijamin unik secara global.

Kekurangan

  • Pengidentifikasi unik ini berukuran 128-bit, yang lebih panjang dari beberapa pendekatan alternatif. Ini menghasilkan dokumen yang sedikit lebih besar, dan karena itu menggunakan lebih banyak ruang disk dan memori
  • Untuk versi algoritma RFC 4122 selain versi 1 dan versi 2, secara teori dimungkinkan untuk menghasilkan UUID yang tidak sepenuhnya unik secara global. Namun, untuk semua

    Haruskah saya menggunakan _id di MongoDB?

    Field _id MongoDB sangat mendasar untuk setiap koleksi di MongoDB , dan, secara default, ia memiliki beberapa properti berguna yang dapat dimanfaatkan pengguna saat mengetahui cara _id dibuat.

    Apa perbedaan antara _id dan ID di MongoDB?

    Field _id dalam database dokumen MongoDB dianggap sebagai field default untuk ObjectId BSON dan, secara default, diindeks. _id dan id tidak sama . Anda mungkin juga lebih suka menambahkan bidang yang disebut id jika Anda mau, tetapi itu tidak akan menjadi indeks kecuali Anda menambahkan indeks.

    Apa bidang _id di MongoDB?

    Bidang _id . Jika dokumen yang dimasukkan menghilangkan bidang _id, driver MongoDB secara otomatis menghasilkan ObjectId untuk bidang _id. each document stored in a collection requires a unique _id field that acts as a primary key. If an inserted document omits the _id field, the MongoDB driver automatically generates an ObjectId for the _id field.

    Bisakah dua dokumen memiliki ID yang sama di MongoDB?

    Jika Anda tidak ingin MongoDB menetapkan id unik untuk dokumen Anda, Anda dapat menentukan bidang _id saat Anda memasukkan dokumen. Ingatlah bahwa nilainya harus unik. Dua dokumen tidak boleh memiliki _id yang sama .