參考:http://www.lanceyan.com/tech/arch/mongodb_shard1.html
一、mongodb分片簡介
在系統早期,資料量還小的時候不會引起太大的問題,但是随着資料量持續增多,後續遲早會出現一台機器硬體瓶頸問題的。而mongodb主打的就是海量資料架構,他不能解決海量資料怎麼行!不行!“分片”就用這個來解決這個問題。 傳統資料庫怎麼做海量資料讀寫?其實一句話概括:分而治之。上圖看看就清楚了,如下 taobao嶽旭強在infoq中提到的 架構圖:
上圖中有個TDDL,是taobao的一個資料通路層元件,他主要的作用是SQL解析、路由處理。根據應用的請求的功能解析目前通路的sql判斷是在哪個業務資料庫、哪個表通路查詢并傳回資料結果。具體如圖: 說了這麼多傳統資料庫的架構,那Nosql怎麼去做到了這些呢?mysql要做到自動擴充需要加一個資料通路層用程式去擴充,資料庫的增加、删除、備份還需要程式去控制。一但資料庫的節點一多,要維護起來也是非常頭疼的。不過mongodb所有的這一切通過他自己的内部機制就可以搞定!還是上圖看看mongodb通過哪些機制實作路由、分片: 從圖中可以看到有四個元件:mongos、config server、shard、replica set。- mongos,資料庫叢集請求的入口,所有的請求都通過mongos進行協調,不需要在應用程式添加一個路由選擇器,mongos自己就是一個請求分發中心,它負責把對應的資料請求請求轉發到對應的shard伺服器上。在生産環境通常有多mongos作為請求的入口,防止其中一個挂掉所有的mongodb請求都沒有辦法操作。
- config server,顧名思義為配置伺服器,存儲所有資料庫元資訊(路由、分片)的配置。mongos本身沒有實體存儲分片伺服器和資料路由資訊,隻是緩存在記憶體裡,配置伺服器則實際存儲這些資料。mongos第一次啟動或者關掉重新開機就會從 config server 加載配置資訊,以後如果配置伺服器資訊變化會通知到所有的 mongos 更新自己的狀态,這樣 mongos 就能繼續準确路由。在生産環境通常有多個 config server 配置伺服器,因為它存儲了分片路由的中繼資料,這個可不能丢失!就算挂掉其中一台,隻要還有存貨, mongodb叢集就不會挂掉。
- shard,這就是傳說中的分片了。上面提到一個機器就算能力再大也有天花闆,就像軍隊打仗一樣,一個人再厲害喝血瓶也拼不過對方的一個師。俗話說三個臭皮匠頂個諸葛亮,這個時候團隊的力量就凸顯出來了。在網際網路也是這樣,一台普通的機器做不了的多台機器來做,如下圖:
一台機器的一個資料表 Collection1 存儲了 1T 資料,壓力太大了!在分給4個機器後,每個機器都是256G,則分攤了集中在一台機器的壓力。也許有人問一台機器硬碟加大一點不就可以了,為什麼要分給四台機器呢?不要光想到存儲空間,實際運作的資料庫還有硬碟的讀寫、網絡的IO、CPU和記憶體的瓶頸。在mongodb叢集隻要設定好了分片規則,通過mongos操作資料庫就能自動把對應的資料操作請求轉發到對應的分片機器上。在生産環境中分片的片鍵可要好好設定,這個影響到了怎麼把資料均勻分到多個分片機器上,不要出現其中一台機器分了1T,其他機器沒有分到的情況,這樣還不如不分片!
分片的一些名詞解釋:
片鍵 :當設定分片時,需要從集合裡面選擇一個或幾個鍵,把選擇出來的鍵作為資料拆分的依據,這個鍵叫做片鍵或複合片鍵。 選好片鍵後,MongoDB将不允許插入沒有片鍵的文檔,但是允許不同文檔的片鍵類型不一樣。
塊(chunk) :在一個shard server内部,MongoDB還是會把資料分為chunks,每個chunk代表這個shard server内部一部分資料。chunk的産生,會有以下兩個用途:
- Splitting: 當一個chunk的大小超過配置中的chunk size時,MongDB的背景程序會把這個chunk切分成更小的chunk,進而避免chunk過大的情況。
- Balancing: 在MongoDB中,balancer是一個背景程序,負責chunk的遷移,進而均衡各個shard server的負載。
二、環境搭建
1、實驗環境介紹
2、同步時間
[[email protected] ~]# ntpdate 202.120.2.101
[root@hpf-linux ~]# ntpdate 202.120.2.101
[[email protected] ~]# ntpdate 202.120.2.101
[[email protected] ~]# ntpdate 202.120.2.101
3、安裝mongodb
注意剩下三台前面已經安裝了mongodb這裡就不列舉了。
[root@hpf-linux ~]#
yum localinstall -y mongo-10gen-2.4.14-mongodb_1.x86_64.rpm mongo-10gen-server-2.4.14-mongodb_1.x86_64.rpm
4、配置config server
[root@hpf-linux ~]# vim /etc/mongod.conf
dbpath=/data/mongodb
configsvr = true
[root@hpf-linux ~]# mkdir -p /data/mongodb
[root@hpf-linux ~]# chown -R mongod.mongod /data/mongodb
[root@hpf-linux ~]# service mongod start
Starting mongod: about to fork child process, waiting until server is ready for connections.
forked process: 130046
all output going to: /var/log/mongo/mongod.log
child process started successfully, parent exiting
[确定]
[root@hpf-linux ~]# ss -tnlp |grep mongod
LISTEN 0 128 *:28019 *:* users:(("mongod",130046,10))
LISTEN 0 128 *:27019 *:* users:(("mongod",130046,9))
5、配置mongos
注意在前面做副本集實驗時已經使用該機器,這裡需要将在做副本集實驗時的配置删除,并按以下配置。
[[email protected] ~]# vim /etc/mongod.conf
#dbpath=/data/mongodb
configdb = 192.168.1.6:27019
[[email protected] ~]# mongos -f /etc/mongod.conf
Sat Sep 26 21:50:57.282 warning: running with 1 config server should be done only for testing purposes and is not recommended for production
about to fork child process, waiting until server is ready for connections.
forked process: 7485
all output going to: /var/log/mongo/mongod.log
child process started successfully, parent exiting
[[email protected] ~]# ss -tnlp |grep mongos
LISTEN 0 128 *:28017 *:* users:(("mongos",7485,8))
LISTEN 0 128 *:27017 *:* users:(("mongos",7485,6))
6、shard節點配置并啟動
node1和node2節點也是在前面做副本集的實驗中使用過,這裡需要将在前面所增加的配置給删除,并按以下配置。
[[email protected] ~]# vim /etc/mongod.conf
dbpath=/data/mongodb
[[email protected] ~]# service mongod start
[[email protected] ~]# vim /etc/mongod.conf
dbpath=/data/mongodb
[[email protected] ~]# service mongod start
7、連入mongos
[[email protected] ~]# mongo --host 192.168.1.8
MongoDB shell version: 2.4.14
connecting to: 192.168.1.8:27017/test
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"version" : 3,
"minCompatibleVersion" : 3,
"currentVersion" : 4,
"clusterId" : ObjectId("5606a2c30b2c688bc754432d")
}
shards:
databases:
{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
添加shard節點:
mongos> sh.addShard("192.168.1.9:27017")
{ "shardAdded" : "shard0000", "ok" : 1 }
mongos> sh.addShard("192.168.1.10:27017")
{ "shardAdded" : "shard0001", "ok" : 1 }
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"version" : 3,
"minCompatibleVersion" : 3,
"currentVersion" : 4,
"clusterId" : ObjectId("5606a2c30b2c688bc754432d")
}
shards:
{ "_id" : "shard0000", "host" : "192.168.1.9:27017" }
{ "_id" : "shard0001", "host" : "192.168.1.10:27017" }
databases:
{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
{ "_id" : "test", "partitioned" : false, "primary" : "shard0000" }
将testdb庫分片:
mongos> sh.enableSharding("testdb")
{ "ok" : 1 }
mongos> use testdb
switched to db testdb
mongos> sh.shardCollection("testdb.testcoll",{Age: 1,Name: 1})
{ "collectionsharded" : "testdb.testcoll", "ok" : 1 }
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"version" : 3,
"minCompatibleVersion" : 3,
"currentVersion" : 4,
"clusterId" : ObjectId("5606a2c30b2c688bc754432d")
}
shards:
{ "_id" : "shard0000", "host" : "192.168.1.9:27017" }
{ "_id" : "shard0001", "host" : "192.168.1.10:27017" }
databases:
{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
{ "_id" : "test", "partitioned" : false, "primary" : "shard0000" }
{ "_id" : "testdb", "partitioned" : true, "primary" : "shard0001" }
testdb.testcoll
shard key: { "Age" : 1, "Name" : 1 }
chunks:
shard0001 1
{ "Age" : { "$minKey" : 1 }, "Name" : { "$minKey" : 1 } } -->> { "Age" : { "$maxKey" : 1 }, "Name" : { "$maxKey" : 1 } } on : shard0001 Timestamp(1, 0)
增加資料用來檢視是否能分片:
mongos> for(i=1;i<=1000000;i++) db.testcoll.insert({Name: "User"+i,Age:(i%150),Address: "xinyang"})
另一台機器接入mongos,檢視輸入存放狀态:
[root@hpf-linux ~]# mongo --host 192.168.1.8
MongoDB shell version: 2.4.14
connecting to: 192.168.1.8:27017/test
mongos> use testdb
switched to db testdb
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"version" : 3,
"minCompatibleVersion" : 3,
"currentVersion" : 4,
"clusterId" : ObjectId("5606a2c30b2c688bc754432d")
}
shards:
{ "_id" : "shard0000", "host" : "192.168.1.9:27017" }
{ "_id" : "shard0001", "host" : "192.168.1.10:27017" }
databases:
{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
{ "_id" : "test", "partitioned" : false, "primary" : "shard0000" }
{ "_id" : "testdb", "partitioned" : true, "primary" : "shard0001" }
testdb.testcoll
shard key: { "Age" : 1, "Name" : 1 }
chunks:
shard0000 1
shard0001 3
{ "Age" : { "$minKey" : 1 }, "Name" : { "$minKey" : 1 } } -->> { "Age" : 1, "Name" : "User1" } on : shard0000 Timestamp(2, 0)
{ "Age" : 1, "Name" : "User1" } -->> { "Age" : 64, "Name" : "User92614" } on : shard0001 Timestamp(2, 2)
{ "Age" : 64, "Name" : "User92614" } -->> { "Age" : 149, "Name" : "User899" } on : shard0001 Timestamp(2, 3)
{ "Age" : 149, "Name" : "User899" } -->> { "Age" : { "$maxKey" : 1 }, "Name" : { "$maxKey" : 1 } } on : shard0001 Timestamp(1, 4)
mongos> db.testcoll.find({Age: {$gt: 140}}).limit(3)
{ "_id" : ObjectId("5606b672ef76b4db97a7d713"), "Name" : "User100041", "Age" : 141, "Address" : "xinyang" }
{ "_id" : ObjectId("5606b672ef76b4db97a7d7a9"), "Name" : "User100191", "Age" : 141, "Address" : "xinyang" }
{ "_id" : ObjectId("5606b672ef76b4db97a7d83f"), "Name" : "User100341", "Age" : 141, "Address" : "xinyang" }
mongos> sh.getBalancerState()
true
總結:
這裡隻是簡單的示範了mongodb的分片機制,在該實驗中有幾點問題需要解決:
- shard節點出現故障就會出現資料丢失,故shard節點需要做副本集來增加高可用。
- 同樣config server也需要做到高可用。
以上就是我所想到的問題點,然而實際中還是有很多問題需要解決,本人水準有限,這裡就不多說了,下面一個連接配接講的架構就是利用三台機器做到的shard的高可用,但實際中還是不建議這麼玩,在所有的分布式系統中建議還是增加主機使用,若機器較少,還是使用集中式的解決方案。畢竟關系型資料庫技術是很成熟的,而所有的nosql也是這幾年火起來的,若想使用還是要看公司的研發能力。個人認為在使用任何一個新技術術時一定不要盲目使用,需要看其缺點,而不是隻看優點,同時根據自身的需求選擇軟體。隻有最合适的沒有最好的!我在使用四台虛拟機做該實驗時本本是4G的記憶體,CPU是i5-5200U,可是還是卡的要死,是以夥伴們還是根據自身的硬體在玩mongodb吧,我這隻是抛磚引玉。