一、集群的理論知識1.1 集群成員MongoDB的集群類似於GreenPlum集群,由一個入口節點負責任務分發與結果統計,分片結節負責執行任務。不同GP,多了一個config servers。集群有三個組件:A。shards:分片,即數據結點,存儲數據和執行計算。為了保證高可用和數據一致性,生產環境...
一、集群的理論知識
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數量差距非常大的時候才觸發。
觸發的閥值: 4)一直遷移到任何兩個節點上的chunk數量相差小於2為止。
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)沒有延遲節點
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。解決重啟的辦法是配置DNS1.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:270192.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) |
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集群搭建完畢。