Scale MongoDB dengan Sharding

Konten Halaman
TL;DR Proses Horizontal Partitioning pada MongoDB melibatkan suatu konsep Chunk dimana data dibagi menjadi beberapa chunk sesuai dengan suatu shard key. Artikel ini membahas bagaimana melakukan sharding pada mongodb

Introduksi

MongoDB adalah salah satu NoSQL database yang cukup populer. MongoDB merupakan document store database, dimana data disimpan dalam format BSON atau mirip dengan JSON. Keunggulan mongoDB antara lain adalah automatic sharding. Artinya apabila kita memiliki beberapa server database, kita dapat melakukan partisi terhadap data tersebut dan mongo akan melakukan load balancing terhadap data tersebut secara automatis

Sebelum mengeksplorasi mongodb, ada beberapa konsep yang harus kita pahami tentang mongo. yaitu:

  • Mongod
  • Replicas
  • Shards
  • Shard Keys
  • Chunks
  • Config DB Process (config server)
  • Routing Process (mongos)

mongod

mongod adalah core dari mongoDB. mongod adalah satu instance server mongo. Saya sendiri tidak tau kepanjangan dari mongod tapi kalau menebak, mungkin yang dimaksud adalah mongo daemon. Kalau di analogikan, 1 mongod adalah 1 server database (misalkan sama dengan 1 server mysql). Sehingga satu aplikasi sedehana cukup dibuat dengan 1 application server yang berkomunikasi dengan 1 mongod

Replica

Replica set adalah suatu bentuk Master/slave database. 1 replika terdiri dari 2 atau lebih proses mongod yang memiliki data yang sama. Replica bertujuan untuk failover, reducancy juga sebagai distribution read load . Artinya apabila terdapat beberapa request read terhadap data yang sama, maka request tersebut bisa di handle oleh lebih dari satu server

Contoh, misalkan kita mempunyai 3 data center: Jakarta, Bandung dan Surabaya. maka kita dapat membentuk replika sebagai berikut.

Replica-1Replica-2Replica-3
Jakarta mongod mongod mongod
Bandung mongod mongod mongod
Surabaya mongod mongod mongod
### Shards ### Dalam database kita dapat melakukan partisi terhadap data. Partisi tersebut bisa dilakukan dalam dua arah: Vertikal dan Horizontal. Vertical dilakukan dengan membagi data berdasarkan kolom, misalkan melalui proses denormalisasi. Sedangkan *horizontal partitioning* dilakukan dengan membagi data secara horizontal.

Contoh horizontal partitioning misalkan kita memiliki data center seperti diatas. Maka kita dapat menyimpan semua data user jakarta ke data center jakarta, bandung ke bandung dan lainya ke surabaya. Apabila terdapat request terhadap data bandung maka cukup server bandung yang melayani data tersebut.

Shard dalam mongoDB teridiri dari beberapa server (mongod). Beberapa server tersebut dalam shard membentuk replika set. sehingga dapat kita katakan suatu replika set berada pada suatu sharding.

shard key

Untuk membagi-bagi suatu data, maka dibutuhkan suatu key yang menentukan data tersebut akan masuk ke shard yang mana. Key tersebut disebut dengan shard key. untuk contoh diatas shard key adalah lokasi. Pada contoh lain kita dapat membuat email sebagai shard key. sehingga data suatu user akan dikelompokkan pada suatu shard yang sama.

Pemilihan shard key sangat bergantung dengan bagaimana cara aplikasi berintarksi untuk mengakses data. Sehingga pemilihan shard key dapat mempengaruhi performa dari sistem. Pemilihan shard key yang salah dapat menyebabkan database beroperasi lebih lambat dari single instance database.

Untuk kriteria pemilihan shard key dapat dilihat pada artikel Choosing a shard key

Chunks

Chunk adalah continuous range of data for a particular collection. Misalkan kita melakukan partisi data berdasarkan ID. dan ID tersebut merupakan bilangan integer antara 1..1000. Kita dapat membagi data tersebut dalam beberapa bagian (chunk) dengan ukuran yang sama. Secara default ukuran 1 chunk dalam mongoDB adalah 64MB (meskipun bisa dikonfigurasi). Untuk contoh kasus data tersebut bisa dibagi menjadi 10 chunk. dengan:

  • chunk-1: 1..100
  • chunk-2: 101..200
  • chunk-10: 901..1000

Suatu Chunk disimpan pada suatu shard tertentu. Keistimewaan mongoDB salah satunya adalah auto sharding. Artinya suatu chunk dapat di transfer dari suatu shard ke shard yang lain.

Misalkan contoh diatas, 3 shard. Pada shard pertama terdapat 10 chunk. MongoDB akan melakukan balancing dengan melakukan transfer ke 2 shard yang lain sampai ukuran chunk pada tiap shard menjadi seimbang. Hal ini dilakukan secara automatis, meskipun kita juga dapat melakukannya secara manual.

Config DB Process (Config server)

Selain mongod, dalam proses sharding dibutuhkan suatu prosess (program) yang bertugas untuk menyimpan informasi mengenai shard-server dan chunk didalam shard tersebut. informasi tersebut dikenal dengan meta-data. Kita dapat menjalankan 1 atau 3 Config Server. Tentu pada production dibutuhkan 3 config server. Ketika satu mati maka masih tersisa 2 cadangan Config server.

Tidak seperti mongod, Config Server tidak berkomunikasi antara satu dengan yang lain. Ketika satu proses mati, biasanya dihidupkan dengan melakukan restore terhadap dump dari server yang lain. Ketika server ini mati.

Routing Process (mongos)

Selain mongod dan config server, element lain yang penting adalah mongos. Fungsi utama dari mongos adalah melakukan routing terhadap request ke shard yang memiliki data tersebut. Pada saat aplikasi ini dijalankan. ia akan mengambil data dari config server. Aplikasi yang kita miliki berkomunikasi layaknya dengan server database biasa. Misalkan sebelumnya aplikasi kita setup ke url mongod, sekarang setelah kita melakukan konfigurasi pada sharding, kita cukup mengarahkan url ke proses mongos.

Proses mongos ini relativ lebih kecil. Kadang kita dapat menjalankan mongos pada server aplikasi kita. Ada juga yang menjalankan proses mongos di mesin yang sama dengan mongod.

Implementasi

Ok, setelah kita mengerti beberapa konsep mengenai sharding, mari kita membuat suatu contoh kasus bagaimana melakukan implementasi terhadap sharding berikut. Untuk kemudahan, maka semua instance server ktia letakkan pada server yang sama (localhost). Hal ini berlaku untuk mongod, Config Server, mongos dan server aplikasi.

Pada contoh kasus ini kita akan menjalankan beberpa server sebagai berikut

  • 2 Monogod. port 10000 & 10001
  • 1 Config Server. port 20000
  • 1 Mongos

Mongod

Dalam kasus ini, saya berasumsi anda telah melakukan instalasi terhadap mongo server. Untuk menjalankan 2 proses mongod, jalankan perintah berikut

1  $ mkdir a b
2  # jalankan instance pertama
3  $ mongod --shardsvr --dbpath $PWD/a --port 10000 > /tmp/sharda.log &
4  $ cat /tmp/sharda.log # pastikan process tersebut berjalan
5  # jalankan instance kedua
6  $ mongod --shardsvr --dbpath $PWD/b --port 10001 > /tmp/shardb.log &
7  $ cat /tmp/shardb.log # pastikan process tersebut berjalan

Configuration Server & Mongos

Berikutnya yang kita lakukan adalah menjalankan Configuration server dan Mongos dengan perintah berikut

1  $ mkdir config
2  # start config server
3  $ mongod --configsvr --dbpath $PWD/config --port 20000 > /tmp/configdb.log &
4  $ cat /tmp/configdb.log # pastikan server berjalan
5  # start mongos
6  $ mongos --configdb localhost:20000 --chunkSize 1 > /tmp/mongos.log &
7  $ cat /tmp/mongos.log # pastikan server tersebut berjalan

pada contoh diatas, mongos tidak membutuhkan --dbpath, karena mongo tidak memerlukan persistance (penyimpanan data). Perintah mongos diatas akan menjalankan mongos pada port default (sama serperti port default mongod) yaitu 27017

--chinkSize 1 merupakan opsi untuk menentukan ukuran chunk 1MB (defaultnya 64MB). Hal ini bertujuan untuk explorasi saja. sehingga kita tidak perlu memassukkan data berukuran 64MB untuk membuat satu chuck baru.

Setup Cluster

Sampai tahap ini yang telah kita lakukan adalah mejalankan 2 process mongod, 1 process config server dan 1 process mongos. Berikutnya yang kita lakukan adalah melakukan setup agar server tersebut dapat saling berkomunikasi.

Konfigurasi Shard Server pada mongos

Berikutnya yang kita lakukan adalah setup mongos agar menambahkan 2 shard server yang berjalan pada port 10000 & 10001.

 1$ mongo
 2MongoDB shell version: 1.8.2
 3connecting to: test
 4> use admin
 5switched to db admin
 6> db.runCommand( { addshard: "localhost:10000"} )
 7{ "shardAdded" : "shard0000", "ok" : 1 }
 8> db.runCommand( { addshard: "localhost:10001"} )
 9{ "shardAdded" : "shard0001", "ok" : 1 }
10>

Berikutnya, kita set agar server tersebut dalam mode sharding pada database test. Setelah itu kita akan menambahkan sharding pada collection people dengan shard key email. Meskipun pada tahap ini kita belum memiliki people Collection

1  > db.runCommand( { enablesharding: "test" } )
2  { "ok" : 1 }
3  > db.runCommand( { shardcollection: "test.people", key: {email: 1} } )
4  { "collectionsharded" : "test.people", "ok" : 1 }
5  > use test
6  > show collections

Oke kita cek dulu apakah ukuran chuck sudah 1 MB, apabila belum kita ubah menjadi 1 MB

Entah mengapa parameter --chunkSize 1 tidak mengubah ukuran chunk, oleh karena itu kita ubah dari database
 1  > use config
 2  switched to db config
 3  > show collections
 4  changelog
 5  chunks
 6  collections
 7  databases
 8  lockpings
 9  locks
10  mongos
11  settings
12  shards
13  system.indexes
14  version
15  > db.settings.find()
16  { "_id" : "chunksize", "value" : 64 }
17  > db.settings.save({_id:"chunksize", value: 1})
18  > db.settings.find()
19  { "_id" : "chunksize", "value" : 1 }
20  >

Perhatikan bahwa perintah terakhir tidak menghasilkan apa-apa karena kita memang belum memiliki collection.

Ok, sampai tahap ini kita telah selesai melakukan shard terhadap server tersebut

Let’s Play

Sekarang saatnya kita melakukan percobaan dengan shard dan chunk.

Tambahkan data pada collection

Pertama-tama kita coba untuk menambahkan 1 collection dan melihat ukuran collection tersebut.

1  > use test
2  switched to db test
3  > db.people.save( { name: "Person test", email: "test@foo.com" } )
4  > db.people.find()
5  { "_id" : ObjectId("4f5742cc580bd77a9d9ec6d6"), "email" : "test@foo.com", "name" : "Person test" }
6  > db.people.dataSize()
7  68
8  > db.people.totalSize()
9  24576

Dapat dilihat pada hasil diatas bahwa kita telah memasukkan 1 dokumen pada collection people dan untuk 1 dokumen kita memerlukan 68 byte. Ingat bahwa pada contoh diatas, kita menetapkan ukuran 1 chunk adalah 1MB (1024 byte). Skarang kita coba untuk memasukan data sehingga ukuran data menjadi 3 chuck

Mari kita tambahkan record (dokumen) sebanyak 20000.

 1  > for (var i = 1; i <= 20000; i++) {
 2  ...   var person_name = "Person #" + i;
 3  ...   var person_email = "email-" + i + "@foo.com";
 4  ...   db.people.save ({ name: person_name, email: person_email });
 5  ... }
 6  > db.people.totalSize()
 7  6776064
 8  > db.people.dataSize()
 9  1559608
10  >

Melihat informasi jumlah shard

untuk melihat informasi shard dapat kita lakukan dalam perintah berikut.

 1  > use admin
 2  switched to db admin
 3  > db.runCommand({ listshards: 1})
 4  {
 5  	"shards" : [
 6  		{
 7  			"_id" : "shard0000",
 8  			"host" : "localhost:10000"
 9  		},
10  		{
11  			"_id" : "shard0001",
12  			"host" : "localhost:10001"
13  		}
14  	],
15  	"ok" : 1
16  }

Melihat informasi Chunk

Untuk melihat informasi chunk, dan di shard mana chunk itu disimpan, lakukan perintah berikut

 1  > use config
 2  switched to db config
 3  > db.chunks.find()
 4  {
 5     "_id":"test.people-email_MinKey",
 6     "lastmod":{
 7        "t":2000,
 8        "i":0
 9     },
10     "ns":"test.people",
11     "min":{
12        "email":{
13           $minKey:1
14        }
15     },
16     "max":{
17        "email":"email-10000@foo.com"
18     },
19     "shard":"shard0001"
20  }{
21     "_id":"test.people-email_\"email-10000@foo.com\"",
22     "lastmod":{
23        "t":2000,
24        "i":2
25     },
26     "ns":"test.people",
27     "min":{
28        "email":"email-10000@foo.com"
29     },
30     "max":{
31        "email":"email-9@foo.com"
32     },
33     "shard":"shard0000"
34  }{
35     "_id":"test.people-email_\"email-9@foo.com\"",
36     "lastmod":{
37        "t":2000,
38        "i":3
39     },
40     "ns":"test.people",
41     "min":{
42        "email":"email-9@foo.com"
43     },
44     "max":{
45        "email":{
46           $maxKey:1
47        }
48     },
49     "shard":"shard0000"
50  }

dari data diatas kita dapat melihat bahwa terdapat 3 chunk:

  • test.people-email_MinKey berada pada shard0001
  • test.people-email_"email-10000@foo.com" berada pada shard0000
  • test.people-email_"email-9@foo.com" berada pada shard0000

MongoDB ternyata melakukan partisi terhadap shard key dengan memperlakukan key tersebut sebagai string.

Memindahkan Chunk ke shard lain

Misalkan pada contoh diatas kita ingin memindahkan chunk test.people-email_\"email-9@foo.com\" berada pada shard0000 dari shard0000 je shard0001. Hal tersebut dapat dilakukan dengan perintah berikut

 1  > db.adminCommand({ moveChunk: "test.people", find: {email: "email-9@foo.com"}, to: 'shard0001'})
 2  { "millis" : 1038, "ok" : 1 }
 3  > db.chunks.find()
 4  {
 5     "_id":"test.people-email_MinKey",
 6     "lastmod":{
 7        "t":2000,
 8        "i":0
 9     },
10     "ns":"test.people",
11     "min":{
12        "email":{
13           $minKey:1
14        }
15     },
16     "max":{
17        "email":"email-10000@foo.com"
18     },
19     "shard":"shard0001"
20  }{
21     "_id":"test.people-email_\"email-10000@foo.com\"",
22     "lastmod":{
23        "t":3000,
24        "i":1
25     },
26     "ns":"test.people",
27     "min":{
28        "email":"email-10000@foo.com"
29     },
30     "max":{
31        "email":"email-9@foo.com"
32     },
33     "shard":"shard0000"
34  }{
35     "_id":"test.people-email_\"email-9@foo.com\"",
36     "lastmod":{
37        "t":3000,
38        "i":0
39     },
40     "ns":"test.people",
41     "min":{
42        "email":"email-9@foo.com"
43     },
44     "max":{
45        "email":{
46           $maxKey:1
47        }
48     },
49     "shard":"shard0001"
50  }
51  >

Dari data diatas kita bisa melihat bahwa chunk tersebut sudah pindah dari shard0000 ke sahrd00001