Scale MongoDB dengan Sharding
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-1 | Replica-2 | Replica-3 | |
Jakarta | mongod | mongod | mongod |
Bandung | mongod | mongod | mongod |
Surabaya | mongod | mongod | mongod |
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
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