一、集群的理論知識
1.1 集群成員
MongoDB的集群類似於GreenPlum集群,由一個入口節點負責任務分發與結果統計,分片結節負責執行任務。不同GP,多了一個config servers。
集群有三個組件:
A。shards:分片,即數據結點,存儲數據和執行計算。為了保證高可用和數據一致性,生產環境中shards應該做成
replicasets(防止丟失數據)。集群中有一個primary shards,執行非分片的任務。
B。mongos(query routers):查詢路由,負責client的連接,並把任務分給shards,然后收集結果。一個集群中可以有多個query
routers(replica sets),以分擔客戶端請求(負載均衡)。
C。config server:配置服務器。保存了集群的元數據(比如數據放在哪個shards上),query router通過config server中的配置信
息決定把任務分配到哪個shards上。從3.2開始,config servers可以做成replica sets。
集群架構圖:



1.2 數據的分片
MongoDB在集合層面做數據分片,以shard key來分片。
shard key應該是一個索引字段或者復合索引字段。
MongoDB根據shard key創建chunks,然后把chunks均衡的分布在shards上。
兩種分區方式:range和hash.結果全oracle里range分區和hash分區理解一下。
1.3 分片的性能
range分片:適合range query,能夠使用批量I/O。但是如果分區鍵不是均勻分布的的,則可能只用到集群中的少數據結點,不能發揮集群的性能。
hash分片:數據平均分布到各個節點上。但是范圍查詢時需要各個節點一起讀,效率比range分片低。
MongoDB支持數據標簽,用戶可以指定某個標簽的數據分布到特定節點上。
1.4數據的平衡
1.4.1 分裂(splite)
當一個chunk的大小超過chunk的最大值大時,這個chunk會分裂為兩個。該過程只修改元數據,不遷移數據。該過程是后台進程完成,不需要人工執行。chunk默認值64M。


分裂的特點:
1)默認的chunk是64M。小的chunk好處是數據平衡性,壞處是經常要做數據平衡。大的chunk則正好相反
2)只有在insert或者update時才會發生chunk的分裂。如果人為修改chunk的大小,是不會發生分裂的。
3)某個特定的shard key values的chunk可能大小默認的chunk大小,並且無法分裂
1.4.2 平衡(balance)
如果集群中的某個節點保存的數據太多,就會自動的把數據分配給其它節點。該過程是后台進程完成,不需要人工執行。
在平衡數據時,先從原始節點復制數據到新節點,然后更新復制的數據。只有在平衡完成后,才會刪除原始節點上的數據。如果發生了錯誤,則不影響原始節點數據。
增加和刪除節點都會造成數據的平衡。
注意:並非所有的數據不均勻都會自動平衡,一般來說,一個節點可以至少存儲數百個chunk(其它節點可以一個chunk都沒有)而不觸發平衡操作。 所以小數據量沒有必要使用集群。
數據平衡的特點:
1)自動化的,默認是開啟的
2)每次只移動一個chunk,但可能在源上的chunk沒有刪除前就開始新的移動
3)只有在節點的chunk數量差距非常大的時候才觸發。
觸發的閥值:


5)如果設置了shard可以使用的磁盤最大小值 ,如果節點的磁盤使用超過了這個值,將不會balance到這個節點上。
6)平衡完畢后,會修改confige server的信息
7)_secondaryThrottle默認為true,當定入至少一個secondary。
1.5 config server
1.5.1 config serve配置成replicatsion sets的限制
config servers配置成replicate sets有以下限制:
1)沒有仲裁節點
2)沒有延遲節點
3)build indexes必須為true
1.5.2 config server的讀寫
寫:只有集群的元數據發生變化時,才會更新config server的信息。比如加入、刪除節點、分裂chunk。
使用write majority方式寫。
讀:當mongos程序重啟或集群的元數據發生了變化mongos才會讀取元數據。
使用read majority方式讀。
1.6 系統架構
1.6.1 用於生產的架構
用於生產的架構必須要保證數據的冗余,mongos、shards、config server必須要做成replication sets。每個集群都必須有一個獨立的config server(一台config server不能多用)。生產的架構示意:


說明:
不做replications set的話,shard節點是沒有數據冗余的功能,如果數據丟失了,就找不回來(此時其它節點可以正常工作)。因此對於生產環境,做復制是十分必要的。同樣,confige server也是一樣,也要做復制。
1.6.2. 用於測試的架構
測試的架構可以不做replication sets,以節省機器為目的,可以把mongos和config server和shards放一起。測試架構示意:


1.7 分片鍵shards key
分片鍵用於決定數據分布在哪個節點上。
分片鍵中的列必須是index key或者組合index key,分片鍵也可以組合的(比如key(c1,c2))。分片鍵在數據插入后不能改變。
1.7.1 hash 分片
hash key能夠很好的將數據均勻的分布在不同的節點上。選擇做為hash key的鍵應該有很大的基數,一般將object_id或者timestamp列作為hash 鍵。
對於空集合,hash 分片會自動的在每個節點上創建兩個chunk,通過修改shardCollection的numInitialChunks參數來決定空集合的chunk個數。
1.7.2 分片鍵對性能的影響
不同的分片鍵到性能有不同的影響。
1)使用object_id這樣唯一性或者基數很大的鍵做為分布鍵,則所有數據被離散的分布到各個節點上,這樣能顯著的提高集群的寫性能,適合存儲大量數據的場合。
2)
查詢集群中的數據時,最快的方式是查詢中包含了分片鍵,這樣就能直接定位的到數據所在的節點,否則就要在所有節點上執行全表掃描。對於讀操作,考慮以下兩點:
A.確定最常用被查詢的列
B.考慮這些列哪個對於性能影響最大
如果最后選定的這個列基數比較小,那么在分片皺中加入二級鍵,做一二級鍵組成的集合基數小即可(對比oracle中的組合索引)。
1.8 集群的高可用
1.8.1 shards節點
如果shars節點沒有做replication sets,該節點如果宕機,這部分數據就不可訪問。因此為了業務的持續性,對於shards節點有必要做成replication sets.
1.8.2 config server
1) 和shards一樣,如果沒有做replication set此時又宕機的話,整個集群都不能用。如果做了replications sets,當其中的一台機宕機后,會自動選出主節點,整個集群還可以用。如果宕機的過多無法選出主節點,整個集群依然可以用(讀寫),但是不能有chunk操作(chunk的創建、移動)。
2)如果不用replication set用mirror的方式來做配置config server,在confige server宕機后,則需要重啟所有的集群成員來連接鏡像confige server。解決重啟的辦法是配置DNS
1.8.3 mongos
mongos是應用程序的入口,如果mongos宕機了,應用程序就無法使用集群。因此一般也做也replication Set。mongos使用的資源相對於shards和confige server是最小的,因此可以和應用服務器放一台機上。當mongos宕機修復后,會自動從config server讀取數據.
1.9 集群的查詢
1.9.1 查詢路徑
mongos是應用程序的接口。mongos通過config server中的信息查詢數據在哪個節點從而分配任務。如果結果有中sort(),primary shard會合並shards的數據然后排序返回給mongos再返回給client.limit()操作直接在shards上完成。skip()不會發送給shards來執行。
1.9.2 mongos標識
應用程序連接到集群后,執行isMaster()命令,返回:
{
"ismaster" : true,
"msg" : "isdbgrid",
"maxBsonObjectSize" : 16777216,
"ok" : 1
}
則表示是mongos。如果msg不是 isdbgrid則不是mongos。
二、集群的搭建
2.1集群搭建的步驟
2.1.1 配置confige server
以下代碼是搭建一個三個結點的replication sets的config server:
1).創建replications sets
mongod --configsvr --replSet configReplSet --port <port> --dbpath <path>
或者使用配置文件
sharding:
clusterRole: configsvr
replication:
replSetName: configReplSet
net:
port: <port>
storage:
dbpath: <path>
2).初始化。進入其中一個的mongo shell:
rs.initiate( {
_id: "configReplSet",
configsvr: true,
members: [
{ _id: 0, host: "<host1>:<port1>" },
{ _id: 1, host: "<host2>:<port2>" },
{ _id: 2, host: "<host3>:<port3>" }
]
} )
2.1.2 創建mongos實例(路由實例)
mongos --configdb configReplSet/<cfgsvr1:port1>,<cfgsvr2:port2>,<cfgsvr3:port3>
2.1.3 加入 shards
1)連接mongos實例:
mongo --host <hostname of machine running mongos> --port <port mongos listens on>
2)在其中一台mongos上加入節點:
sh.addShard( "rs1/mongodb0.example.net:27017" ) (replications sets只需要加入rs中一個節點即可)
sh.addShard( "mongodb0.example.net:27017" ) (單機)
可能需要一段時間來遷移數據
2.1.4 設置分片
2.1.4.1 設置數據庫分片
在設置集合分片之前,必須設置要分片的數據庫。連接mongos:
mongo --host <hostname of machine running mongos> --port <port mongos listens on>
執行:
sh.enableSharding("<database>")或者db.runCommand( { enableSharding: <database> } )
2.1.4.2 設置集合分片
1)確定集合的shard key。如果集合已經有數據,那么在shard key上創建index。如果沒有數據,集群會自動為shard key創建索引
2)將集合加入分片
sh.shardCollection("<database>.<collection>", shard-key-pattern)
如:
sh.shardCollection("records.people", { "zipcode": 1, "name": 1 } ) shard key 為zipcode,如果有相同的zipcode再根據name來分
sh.shardCollection("people.addresses", { "state": 1, "_id": 1 } ) 同上
sh.shardCollection("assets.chairs", { "type": 1, "_id": 1 } ) 同上
sh.shardCollection("events.alerts", { "_id": "hashed" } ) hash分片
2.1.5 配置鏡像config server
注意:不推薦使用鏡像,請使用replication sets。
在每個config server上啟動mongod實例:
mongod --configsvr --dbpath /data/configdb --port 27019
每個路由節點啟動mongs,--configdb后面的連接字符串要一致
mongos --configdb cfg0.example.net:27019,cfg1.example.net:27019,cfg2.example.net:27019
2.2 實驗
2.2.1實驗環境
node1 | 192.168.75.10 | config server1(configRS 37017) mongos1( 27017) shard1(47017) |
node2 | 192.168.75.11 | config server2(configRS 37017) mongos2(27017) shard2(47017) |
node3 | 192.168.75.12 | config server3(configRS 37017) shard3( 47017) |
在三台主機上,分別安mongoConfig、 mongoShard 、mongoRouter三個實例
2.2.2.配置config server
1)配置文件
在三台主機配置configution文件:
[root@node1 mongoConfig]# cat mongodb.config
dbpath=/usr/local/mongoConfig/data
logpath=/usr/local/mongoConfig/log/mongo.log
port=37017
fork=true
#master=true
replSet=configRS
configsvr=true
2)在三台機器上啟動config server實例
[root@node1 bin]# ./mongod -f /usr/local/mongoConfig/mongodb.conf
about to fork child process, waiting until server is ready for connections.
forked process: 3032
child process started successfully, parent exiting
注意,打開防火牆的37017端口
3)初始化config server
連接到其中一台configer server:
[root@node1 bin]# ./mongo --port 37017
執行以下初始化:
> rs.initiate( {
... _id: "configRS",
... configsvr: true,
... members: [
... { _id: 0, host: "192.168.75.10:37017" },
... { _id: 1, host: "192.168.75.11:37017" },
... { _id: 2, host: "192.168.75.12:37017" }
... ]
... } );
{ "ok" : 1 }
2.2.3.配置mongos
在每台機器上執行:
./mongos --configdb configRS/192.168.75.10:37017,192.168.75.11:37017,192.168.75.12:37017 --port 27017 --fork --logpath=/usr/local/mongoRouter/log/mongo.log
2.2.4.啟動三個shard實例
在三台機器修改配置文件:
[root@node1 mongoShard]# vi mongodb.config
dbpath=/usr/local/mongoShard/data
logpath=/usr/local/mongoShard/log/mongo.log
port=47017
fork=true
在三台機器上啟動實例:
[root@node1 bin]# ./mongod -f /usr/local/mongoShard/mongodb.config
about to fork child process, waiting until server is ready for connections.
forked process: 17508
child process started successfully, parent exiting
2.2.5.將shards加入集群
在一台機器上連接mongos實例
./mongo --port 27017
執行:
sh.addShard( "192.168.75.10:47017" )
sh.addShard( "192.168.75.11:47017" )
sh.addShard( "192.168.75.12:47017" )
2.2.6.將數據庫加入分片
sh.enableSharding("testShards")
2.2.7.將集合加入分片
sh.shardCollection("testShards.test", { "_id": "hashed" });
2.2.8.插入數據
在某個mongos上執行:
mongos> use testShards
switched to db testShards
mongos> show collections;
test
mongos> db.test.insert({"name":"testshrads","msg":"ok"});
WriteResult({ "nInserted" : 1 })
mongos> db.test.insert({"name":"testshrads2","msg":"ok"});
WriteResult({ "nInserted" : 1 })
mongos> db.test.insert({"name":"testshrads3","msg":"ok"});
WriteResult({ "nInserted" : 1 })
mongos> db.test.insert({"name":"testshrads4","msg":"ok"});
WriteResult({ "nInserted" : 1 })
mongos> db.test.insert({"name":"testshrads5","msg":"ok"});
WriteResult({ "nInserted" : 1 })
mongos> db.test.insert({"name":"testshrads6","msg":"ok"});
WriteResult({ "nInserted" : 1 })
mongos> db.test.insert({"name":"testshrads7","msg":"ok"});
WriteResult({ "nInserted" : 1 })
mongos> db.test.insert({"name":"testshrads8","msg":"ok"});
WriteResult({ "nInserted" : 1 })
mongos> db.test.insert({"name":"testshrads9","msg":"ok"});
WriteResult({ "nInserted" : 1 })
查看數據:
mongos> db.test.find(); { "_id" : ObjectId("56815a0617de6d7dfc1051b5"), "name" : "testshrads", "msg" : "ok" } { "_id" : ObjectId("56815a0e17de6d7dfc1051b6"), "name" : "testshrads2", "msg" : "ok" } { "_id" : ObjectId("56815a1717de6d7dfc1051b8"), "name" : "testshrads4", "msg" : "ok" } { "_id" : ObjectId("56815a1b17de6d7dfc1051b9"), "name" : "testshrads5", "msg" : "ok" } { "_id" : ObjectId("56815a1e17de6d7dfc1051ba"), "name" : "testshrads6", "msg" : "ok" } { "_id" : ObjectId("56815a2617de6d7dfc1051bc"), "name" : "testshrads8", "msg" : "ok" } { "_id" : ObjectId("56815a1217de6d7dfc1051b7"), "name" : "testshrads3", "msg" : "ok" } { "_id" : ObjectId("56815a2117de6d7dfc1051bb"), "name" : "testshrads7", "msg" : "ok" } { "_id" : ObjectId("56815a2917de6d7dfc1051bd"), "name" : "testshrads9", "msg" : "ok" }
2.2.9.測試數據
連接到某個shards上:
#./mongod --port 47017
執行:
> use testShards
switched to db testShards
> db.test.find();
{ "_id" : ObjectId("56815a1217de6d7dfc1051b7"), "name" : "testshrads3", "msg" : "ok" }
{ "_id" : ObjectId("56815a2117de6d7dfc1051bb"), "name" : "testshrads7", "msg" : "ok" }
{ "_id" : ObjectId("56815a2917de6d7dfc1051bd"), "name" : "testshrads9", "msg" : "ok" }
發現數據分片正常.
mongoDB集群搭建完畢。