MongoDB
 sql >> Teknologi Basis Data >  >> NoSQL >> MongoDB

Cara Membagi Shard Key Berbasis GUID Secara Terprogram dengan MongoDB

Kami tahu ukuran data awal (120GB) dan kami tahu ukuran potongan maksimum default di MongoDB adalah 64MB. Jika kita membagi 64MB menjadi 120GB, kita mendapatkan 1920 - jadi itu adalah jumlah minimum potongan yang harus kita lihat untuk memulai. Kebetulan 2048 merupakan pangkat 16 dibagi 2, dan mengingat GUID (kunci shard kami) berbasis hex, itu angka yang jauh lebih mudah untuk ditangani daripada 1920 (lihat di bawah).

CATATAN: Pra-pemisahan ini harus dilakukan sebelum data apa pun ditambahkan ke koleksi. Jika Anda menggunakan perintah enableSharding() pada koleksi yang berisi data, MongoDB akan membagi data itu sendiri dan Anda kemudian akan menjalankannya saat potongan sudah ada - yang dapat menyebabkan distribusi potongan yang cukup aneh, jadi berhati-hatilah.

Untuk keperluan jawaban ini, mari kita asumsikan bahwa database akan disebut users dan koleksinya disebut userInfo . Mari kita asumsikan juga bahwa GUID akan ditulis ke dalam _id bidang. Dengan parameter tersebut kita akan terhubung ke mongos dan jalankan perintah berikut:

// first switch to the users DB
use users;
// now enable sharding for the users DB
sh.enableSharding("users"); 
// enable sharding on the relevant collection
sh.shardCollection("users.userInfo", {"_id" : 1});
// finally, disable the balancer (see below for options on a per-collection basis)
// this prevents migrations from kicking off and interfering with the splits by competing for meta data locks
sh.stopBalancer(); 

Sekarang, per perhitungan di atas, kita perlu membagi rentang GUID menjadi 2048 potongan. Untuk melakukan itu, kita membutuhkan setidaknya 3 digit heksagonal (16 ^ 3 =4096) dan kita akan menempatkannya dalam digit paling signifikan (yaitu 3 paling kiri) untuk rentang. Sekali lagi, ini harus dijalankan dari mongos cangkang

// Simply use a for loop for each digit
for ( var x=0; x < 16; x++ ){
  for( var y=0; y<16; y++ ) {
  // for the innermost loop we will increment by 2 to get 2048 total iterations
  // make this z++ for 4096 - that would give ~30MB chunks based on the original figures
    for ( var z=0; z<16; z+=2 ) {
    // now construct the GUID with zeroes for padding - handily the toString method takes an argument to specify the base
        var prefix = "" + x.toString(16) + y.toString(16) + z.toString(16) + "00000000000000000000000000000";
        // finally, use the split command to create the appropriate chunk
        db.adminCommand( { split : "users.userInfo" , middle : { _id : prefix } } );
    }
  }
}

Setelah selesai, mari kita periksa status permainan menggunakan sh.status() pembantu:

mongos> sh.status()
--- Sharding Status ---
  sharding version: {
        "_id" : 1,
        "version" : 3,
        "minCompatibleVersion" : 3,
        "currentVersion" : 4,
        "clusterId" : ObjectId("527056b8f6985e1bcce4c4cb")
}
  shards:
        {  "_id" : "shard0000",  "host" : "localhost:30000" }
        {  "_id" : "shard0001",  "host" : "localhost:30001" }
        {  "_id" : "shard0002",  "host" : "localhost:30002" }
        {  "_id" : "shard0003",  "host" : "localhost:30003" }
  databases:
        {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
        {  "_id" : "users",  "partitioned" : true,  "primary" : "shard0001" }
                users.userInfo
                        shard key: { "_id" : 1 }
                        chunks:
                                shard0001       2049
                        too many chunks to print, use verbose if you want to force print

Kami memiliki 2048 potongan kami (ditambah satu tambahan berkat potongan min/maks), tetapi semuanya masih di pecahan asli karena penyeimbang tidak aktif. Jadi, mari aktifkan kembali penyeimbang:

sh.startBalancer();

Ini akan segera mulai menyeimbangkan, dan itu akan relatif cepat karena semua potongan kosong, tetapi masih akan memakan waktu agak lama (jauh lebih lambat jika bersaing dengan migrasi dari koleksi lain). Setelah beberapa waktu berlalu, jalankan sh.status() lagi dan di sana Anda (harus) memilikinya - 2048 bongkahan semuanya terbelah dengan baik menjadi 4 pecahan dan siap untuk memuat data awal:

mongos> sh.status()
--- Sharding Status ---
  sharding version: {
        "_id" : 1,
        "version" : 3,
        "minCompatibleVersion" : 3,
        "currentVersion" : 4,
        "clusterId" : ObjectId("527056b8f6985e1bcce4c4cb")
}
  shards:
        {  "_id" : "shard0000",  "host" : "localhost:30000" }
        {  "_id" : "shard0001",  "host" : "localhost:30001" }
        {  "_id" : "shard0002",  "host" : "localhost:30002" }
        {  "_id" : "shard0003",  "host" : "localhost:30003" }
  databases:
        {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
        {  "_id" : "users",  "partitioned" : true,  "primary" : "shard0001" }
                users.userInfo
                        shard key: { "_id" : 1 }
                        chunks:
                                shard0000       512
                                shard0002       512
                                shard0003       512
                                shard0001       513
                        too many chunks to print, use verbose if you want to force print
        {  "_id" : "test",  "partitioned" : false,  "primary" : "shard0002" }

Anda sekarang siap untuk mulai memuat data, tetapi untuk benar-benar menjamin bahwa tidak ada pemisahan atau migrasi yang terjadi hingga pemuatan data Anda selesai, Anda perlu melakukan satu hal lagi - matikan penyeimbang dan pemisahan otomatis selama impor:

  • Untuk menonaktifkan semua penyeimbangan, jalankan perintah ini dari mongos:sh.stopBalancer()
  • Jika Anda ingin membiarkan operasi penyeimbangan lainnya tetap berjalan, Anda dapat menonaktifkannya pada koleksi tertentu. Menggunakan namespace di atas sebagai contoh:sh.disableBalancing("users.userInfo")
  • Untuk menonaktifkan pemisahan otomatis selama pemuatan, Anda harus memulai ulang setiap mongos Anda akan menggunakan untuk memuat data dengan --noAutoSplit pilihan.

Setelah impor selesai, balikkan langkah-langkah yang diperlukan (sh.startBalancer() , sh.enableBalancing("users.userInfo") , dan mulai ulang mongos tanpa --noAutoSplit ) untuk mengembalikan semuanya ke pengaturan default.

**

Pembaruan:Mengoptimalkan Kecepatan

**

Pendekatan di atas baik-baik saja jika Anda tidak terburu-buru. Seperti yang terjadi, dan seperti yang akan Anda temukan jika Anda menguji ini, penyeimbang tidak terlalu cepat - bahkan dengan potongan kosong. Karenanya, saat Anda menambah jumlah potongan yang Anda buat, semakin lama waktu yang dibutuhkan untuk menyeimbangkan. Saya melihat butuh lebih dari 30 menit untuk menyelesaikan penyeimbangan 2048 bongkahan meskipun ini akan bervariasi tergantung pada penerapannya.

Itu mungkin OK untuk pengujian, atau untuk cluster yang relatif tenang, tetapi menonaktifkan penyeimbang dan tidak memerlukan pembaruan lain yang mengganggu akan jauh lebih sulit untuk dipastikan pada cluster yang sibuk. Jadi, bagaimana kita mempercepatnya?

Jawabannya adalah melakukan beberapa gerakan manual lebih awal, lalu membagi potongan setelah berada di pecahannya masing-masing. Perhatikan bahwa ini hanya diinginkan dengan kunci pecahan tertentu (seperti UUID yang didistribusikan secara acak), atau pola akses data tertentu, jadi berhati-hatilah agar Anda tidak berakhir dengan distribusi data yang buruk.

Dengan menggunakan contoh di atas, kami memiliki 4 pecahan, jadi daripada melakukan semua pemisahan, lalu menyeimbangkan, kami membagi menjadi 4 sebagai gantinya. Kami kemudian menempatkan satu bongkahan di setiap pecahan dengan memindahkannya secara manual, dan akhirnya kami membagi bongkahan itu ke dalam jumlah yang diperlukan.

Rentang dalam contoh di atas akan terlihat seperti ini:

$min --> "40000000000000000000000000000000"
"40000000000000000000000000000000" --> "80000000000000000000000000000000"
"80000000000000000000000000000000" --> "c0000000000000000000000000000000"
"c0000000000000000000000000000000" --> $max     

Hanya 4 perintah untuk membuat ini, tetapi karena kami memilikinya, mengapa tidak menggunakan kembali loop di atas dalam bentuk yang disederhanakan/dimodifikasi:

for ( var x=4; x < 16; x+=4){
    var prefix = "" + x.toString(16) + "0000000000000000000000000000000";
    db.adminCommand( { split : "users.userInfo" , middle : { _id : prefix } } ); 
} 

Begini tampilan pemikiran sekarang - kami memiliki 4 bagian, semuanya di shard0001:

mongos> sh.status()
--- Sharding Status --- 
  sharding version: {
    "_id" : 1,
    "version" : 4,
    "minCompatibleVersion" : 4,
    "currentVersion" : 5,
    "clusterId" : ObjectId("53467e59aea36af7b82a75c1")
}
  shards:
    {  "_id" : "shard0000",  "host" : "localhost:30000" }
    {  "_id" : "shard0001",  "host" : "localhost:30001" }
    {  "_id" : "shard0002",  "host" : "localhost:30002" }
    {  "_id" : "shard0003",  "host" : "localhost:30003" }
  databases:
    {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
    {  "_id" : "test",  "partitioned" : false,  "primary" : "shard0001" }
    {  "_id" : "users",  "partitioned" : true,  "primary" : "shard0001" }
        users.userInfo
            shard key: { "_id" : 1 }
            chunks:
                shard0001   4
            { "_id" : { "$minKey" : 1 } } -->> { "_id" : "40000000000000000000000000000000" } on : shard0001 Timestamp(1, 1) 
            { "_id" : "40000000000000000000000000000000" } -->> { "_id" : "80000000000000000000000000000000" } on : shard0001 Timestamp(1, 3) 
            { "_id" : "80000000000000000000000000000000" } -->> { "_id" : "c0000000000000000000000000000000" } on : shard0001 Timestamp(1, 5) 
            { "_id" : "c0000000000000000000000000000000" } -->> { "_id" : { "$maxKey" : 1 } } on : shard0001 Timestamp(1, 6)                    

Kami akan meninggalkan $min potongan di mana itu, dan pindahkan tiga lainnya. Anda dapat melakukan ini secara terprogram, tetapi ini bergantung pada lokasi awal bongkahan, bagaimana Anda menamai pecahan Anda, dll. jadi saya akan meninggalkan manual ini untuk saat ini, tidak terlalu berat - cukup 3 moveChunk perintah:

mongos> sh.moveChunk("users.userInfo", {"_id" : "40000000000000000000000000000000"}, "shard0000")
{ "millis" : 1091, "ok" : 1 }
mongos> sh.moveChunk("users.userInfo", {"_id" : "80000000000000000000000000000000"}, "shard0002")
{ "millis" : 1078, "ok" : 1 }
mongos> sh.moveChunk("users.userInfo", {"_id" : "c0000000000000000000000000000000"}, "shard0003")
{ "millis" : 1083, "ok" : 1 }          

Mari kita periksa kembali, dan pastikan bahwa potongan berada di tempat yang kita harapkan:

mongos> sh.status()
--- Sharding Status --- 
  sharding version: {
    "_id" : 1,
    "version" : 4,
    "minCompatibleVersion" : 4,
    "currentVersion" : 5,
    "clusterId" : ObjectId("53467e59aea36af7b82a75c1")
}
  shards:
    {  "_id" : "shard0000",  "host" : "localhost:30000" }
    {  "_id" : "shard0001",  "host" : "localhost:30001" }
    {  "_id" : "shard0002",  "host" : "localhost:30002" }
    {  "_id" : "shard0003",  "host" : "localhost:30003" }
  databases:
    {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
    {  "_id" : "test",  "partitioned" : false,  "primary" : "shard0001" }
    {  "_id" : "users",  "partitioned" : true,  "primary" : "shard0001" }
        users.userInfo
            shard key: { "_id" : 1 }
            chunks:
                shard0001   1
                shard0000   1
                shard0002   1
                shard0003   1
            { "_id" : { "$minKey" : 1 } } -->> { "_id" : "40000000000000000000000000000000" } on : shard0001 Timestamp(4, 1) 
            { "_id" : "40000000000000000000000000000000" } -->> { "_id" : "80000000000000000000000000000000" } on : shard0000 Timestamp(2, 0) 
            { "_id" : "80000000000000000000000000000000" } -->> { "_id" : "c0000000000000000000000000000000" } on : shard0002 Timestamp(3, 0) 
            { "_id" : "c0000000000000000000000000000000" } -->> { "_id" : { "$maxKey" : 1 } } on : shard0003 Timestamp(4, 0)  

Itu cocok dengan rentang yang kami usulkan di atas, jadi semuanya terlihat bagus. Sekarang jalankan loop asli di atas untuk membaginya "di tempat" pada setiap pecahan dan kita harus memiliki distribusi yang seimbang segera setelah loop selesai. Satu lagi sh.status() harus mengkonfirmasi beberapa hal:

mongos> for ( var x=0; x < 16; x++ ){
...   for( var y=0; y<16; y++ ) {
...   // for the innermost loop we will increment by 2 to get 2048 total iterations
...   // make this z++ for 4096 - that would give ~30MB chunks based on the original figures
...     for ( var z=0; z<16; z+=2 ) {
...     // now construct the GUID with zeroes for padding - handily the toString method takes an argument to specify the base
...         var prefix = "" + x.toString(16) + y.toString(16) + z.toString(16) + "00000000000000000000000000000";
...         // finally, use the split command to create the appropriate chunk
...         db.adminCommand( { split : "users.userInfo" , middle : { _id : prefix } } );
...     }
...   }
... }          
{ "ok" : 1 }
mongos> sh.status()
--- Sharding Status --- 
  sharding version: {
    "_id" : 1,
    "version" : 4,
    "minCompatibleVersion" : 4,
    "currentVersion" : 5,
    "clusterId" : ObjectId("53467e59aea36af7b82a75c1")
}
  shards:
    {  "_id" : "shard0000",  "host" : "localhost:30000" }
    {  "_id" : "shard0001",  "host" : "localhost:30001" }
    {  "_id" : "shard0002",  "host" : "localhost:30002" }
    {  "_id" : "shard0003",  "host" : "localhost:30003" }
  databases:
    {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
    {  "_id" : "test",  "partitioned" : false,  "primary" : "shard0001" }
    {  "_id" : "users",  "partitioned" : true,  "primary" : "shard0001" }
        users.userInfo
            shard key: { "_id" : 1 }
            chunks:
                shard0001   513
                shard0000   512
                shard0002   512
                shard0003   512
            too many chunks to print, use verbose if you want to force print    

Dan begitulah - tidak menunggu penyeimbang, distribusinya sudah merata.




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Mencetak output kueri Mongo ke file saat berada di shell mongo

  2. Mongo, temukan melalui daftar id

  3. NodeJS + MongoDB:Mendapatkan data dari koleksi dengan findOne ()

  4. Tutorial MongoDB untuk 2022 – Pelajari Apa itu MongoDB?

  5. MongoDB:Temukan elemen minimum dalam array dan hapus itu