MongoDB分片叢集部署
之前提到了mongodb複製集(副本集)的一些概念,在這篇博文介紹一下如何使用複製集在單機上進行分片叢集的部署。在這裡先介紹叢集部署複製集和分片的一些概念,然後再完成部署實現。
CAP
首先我們來了解一下什麼是CAP理論,其核心是:一個分散式系統不可能同時很好的滿足 一致性,可用性和分割槽容錯性這三個需求,最多隻能同時較好的滿足兩個。
- 一致性(Consistency) :所有節點在同一時間具有相同的資料;
- 可用性(Availability) :保證每個請求不管成功或者失敗都有響應;
- 分割槽容錯性(Partition tolerance) :系統中任意資訊的丟失或失敗不會 影響系統的繼續運作,即可靠性。
而MongoDB通過複製集和分片技術很好的滿足了CP原則。
複製集
MongoDB複製集是由一組mongod例項(程序)組成,包含一個Primary節點和多個Secondary節點,客戶端的所有資料都寫入Primary,Secondary從Primary同步寫入的資料,以保持複製集內所有成員儲存相同的資料集,提供資料的高可用。預設情況下,複製集的所有讀請求都發到Primary。
Primary選舉
複製集首先進行初始化,初始化後各個成員間開始傳送心跳訊息,併發起Priamry選舉操作,獲得『大多數』成員投票支援的節點,會成為Primary,其餘節點成為Secondary。
『大多數』:假設複製集內投票成員數量為N,則大多數為 N/2 + 1,當複製集記憶體活成員數量不足大多數時,整個複製集將無法選舉出Primary,複製集將無法提供寫服務,處於只讀狀態。通常建議將複製整合員數量設定為奇數。例如,當投票成員數為3或者4時,容忍失效數均為1。
特殊Secondary——Arbiter
正常情況下,複製集的Seconary會參與Primary選舉(自身也可能會被選為Primary),並從Primary同步最新寫入的資料,以保證與Primary儲存相同的資料。Secondary可以提供讀服務,增加Secondary節點可以提供複製集的讀服務能力,同時提升複製集的可用性。而Arbiter節點只參與投票,不能被選為Primary,並且不從Primary同步資料。它的目的是通過響應心跳來維持複製整合員選舉時要求的法定成員數。
異常處理和自動故障轉移
Primary重新選舉場景:
-複製集被reconfig;
-Secondary節點檢測到Primary宕機時;
-當有Primary節點主動stepDown(主動降級為Secondary)。
Primary的選舉受節點間心跳、優先順序、最新的oplog(資料同步)時間等多種因素影響。當Primary宕機時,如果有資料未同步到Secondary或當Primary重新加入時,如果新的Primary上已經發生了寫操作,則舊Primary需要回滾部分操作,以保證資料集與新的Primary一致。
複製整合員間預設每2s會發送一次心跳資訊,如果10s未收到某個節點的心跳,則認為該節點已宕機;如果宕機的節點為Primary,Secondary(前提是可被選為Primary)會發起新的Primary選舉。
Sharding分片
與mysql等關係資料庫的Cluster不同,mongodb支援自動分片。
為什麼需要分片?
當MongoDB複製集遇到下面的業務場景時,就需要考慮使用分片。
-儲存容量需求超出單機磁碟容量;
-活躍的資料集超出單機記憶體容量,導致很多請求都要從磁碟讀取資料,影響效能;
-寫IOPS超出單個MongoDB節點的寫服務能力。
Sharded cluster使得集合的資料可以分散到多個Shard(複製集或者單個Mongod節點)儲存,使得MongoDB具備了橫向擴充套件的能力。例如,將1TB的資料均分成4個256GB的片,從而使資料庫的負載均衡。
Sharded cluster架構
Sharded cluster由Shard、Mongos和Config server 3個元件構成。
-Mongos: Sharded cluster的訪問入口,所有的請求都通過mongos來路由、分發、合併,這些動作對客戶端透明。 Mongos會根據請求型別及shard key將請求路由到對應的Shard。在生產環境通常有多mongos,目的是防止其中一個掛掉後所有的mongodb請求都無法操作。
-Config server: 儲存所有資料庫元資訊(路由、分片)的配置。這個資料包含叢集資料集與片的對映。 Mongos使用這些元資料來定位到特定的片進行操作。mongos本身沒有物理儲存分片伺服器和資料路由資訊,只是快取在記憶體裡,配置伺服器則實際儲存這些資料。 同樣,在生產環境通常有多個配置伺服器,防止其中一個掛掉後還有後備的元資料,這樣 mongodb叢集就不會掛掉。
-Shard: 儲存資料。為了提供高可用性和資料的一致性,在生產分片叢集時,每個片都是一個複製集。
Hash片鍵策略
MongoDB分片有多種片鍵策略,這裡介紹一下博文叢集部署時採用的Hash片鍵策略。
對於Hash片鍵策略, MongoDB計算欄位的hash值(64bit整型),然後使用這些hash值來建立chunk。基於雜湊的分片,“臨近” 片鍵值的兩個文件不可能是相同chunk的一部分,這確保了集合在叢集中更隨機的分佈。
Hash分片能將文件隨機的分散到各個chunk ,充分地擴充套件寫能力,但不能高效地服務範圍查詢,所有的範圍查詢要分發到後端所有的Shard才能找出滿足條件的文件。
部署實現
在部署圖中mongos 3個, config server 3個,資料分3片 shard server 3個,每個shard 有一個Secondary和一個Arbiter也就是 3 * 2 = 6 個,總共需要部署15個mongod例項。將這些例項部署一臺機器上,通過配置不同的埠來實現。資料分為3個片,每個片都是一個複製集,分別為shard1,shard2,shard3。
伺服器環境:Ubuntu 14.04 LTS、MongoDB 3.2.0,單機的IP地址為localhost(127.0.0.1)
建立資料夾,存放mongodb資料檔案
sudo mkdir -p /data/mongodbtest1
sudo mkdir -p /data/mongodbtest2
sudo mkdir -p /data/mongodbtest3
為mongos 、config 、 shard1 、shard2、shard3 建立目錄,用來存放資料和日誌檔案,因為mongos不儲存資料,只需要建立日誌檔案目錄即可。
#/data/mongodbtest1檔案目錄
sudo mkdir -p /data/mongodbtest1/mongos/log
sudo mkdir -p /data/mongodbtest1/config/data
sudo mkdir -p /data/mongodbtest1/config/log
sudo mkdir -p /data/mongodbtest1/shard11/data
sudo mkdir -p /data/mongodbtest1/shard11/log
sudo mkdir -p /data/mongodbtest1/shard12/data
sudo mkdir -p /data/mongodbtest1/shard12/log
sudo mkdir -p /data/mongodbtest1/shard13/data
sudo mkdir -p /data/mongodbtest1/shard13/log
#/data/mongodbtest2檔案目錄
sudo mkdir -p /data/mongodbtest2/mongos/log
sudo mkdir -p /data/mongodbtest2/config/data
sudo mkdir -p /data/mongodbtest2/config/log
sudo mkdir -p /data/mongodbtest2/shard21/data
sudo mkdir -p /data/mongodbtest2/shard21/log
sudo mkdir -p /data/mongodbtest2/shard22/data
sudo mkdir -p /data/mongodbtest2/shard22/log
sudo mkdir -p /data/mongodbtest2/shard23/data
sudo mkdir -p /data/mongodbtest2/shard23/log
#/data/mongodbtest3檔案目錄
sudo mkdir -p /data/mongodbtest3/mongos/log
sudo mkdir -p /data/mongodbtest3/config/data
sudo mkdir -p /data/mongodbtest3/config/log
sudo mkdir -p /data/mongodbtest3/shard31/data
sudo mkdir -p /data/mongodbtest3/shard31/log
sudo mkdir -p /data/mongodbtest3/shard32/data
sudo mkdir -p /data/mongodbtest3/shard32/log
sudo mkdir -p /data/mongodbtest3/shard33/data
sudo mkdir -p /data/mongodbtest3/shard33/log
啟動配置伺服器,埠號分別為:10001,20001,30001
sudo /usr/local/mongodb/bin/mongod --configsvr --dbpath /data/mongodbtest1/config/data --port 10001 --logpath /data/mongodbtest1/config/log/config.log --fork
sudo /usr/local/mongodb/bin/mongod --configsvr --dbpath /data/mongodbtest2/config/data --port 20001 --logpath /data/mongodbtest2/config/log/config.log --fork
sudo /usr/local/mongodb/bin/mongod --configsvr --dbpath /data/mongodbtest3/config/data --port 30001 --logpath /data/mongodbtest3/config/log/config.log --fork
mongos伺服器,埠號分別為:10002,20002,30002
sudo /usr/local/mongodb/bin/mongos --configdb localhost:10001,localhost:20001,localhost:30001 --port 10002 --logpath /data/mongodbtest1/mongos/log/mongos.log --fork
sudo /usr/local/mongodb/bin/mongos --configdb localhost:10001,localhost:20001,localhost:30001 --port 20002 --logpath /data/mongodbtest2/mongos/log/mongos.log --fork
sudo /usr/local/mongodb/bin/mongos --configdb localhost:10001,localhost:20001,localhost:30001 --port 30002 --logpath /data/mongodbtest3/mongos/log/mongos.log --fork
啟動複製集shard1,shard2,shard3
#shard1,埠號分別為10003,10004,10005
sudo /usr/local/mongodb/bin/mongod --shardsvr --replSet shard1 --dbpath /data/mongodbtest1/shard11/data --logpath /data/mongodbtest1/shard11/log/shard1.log --port 10003 --oplogSize 1024 --fork
sudo /usr/local/mongodb/bin/mongod --shardsvr --replSet shard1 --dbpath /data/mongodbtest1/shard12/data --logpath /data/mongodbtest1/shard12/log/shard2.log --port 10004 --oplogSize 1024 --fork
sudo /usr/local/mongodb/bin/mongod --shardsvr --replSet shard1 --dbpath /data/mongodbtest1/shard13/data --logpath /data/mongodbtest1/shard13/log/shard3.log --port 10005 --oplogSize 1024 --fork
#shard2,埠號分別為20003,20004,20005
sudo /usr/local/mongodb/bin/mongod --shardsvr --replSet shard2 --dbpath /data/mongodbtest2/shard21/data --logpath /data/mongodbtest2/shard21/log/shard1.log --port 20003 --oplogSize 1024 --fork
sudo /usr/local/mongodb/bin/mongod --shardsvr --replSet shard2 --dbpath /data/mongodbtest2/shard22/data --logpath /data/mongodbtest2/shard22/log/shard2.log --port 20004 --oplogSize 1024 --fork
sudo /usr/local/mongodb/bin/mongod --shardsvr --replSet shard2 --dbpath /data/mongodbtest2/shard23/data --logpath /data/mongodbtest2/shard23/log/shard3.log --port 20005 --oplogSize 1024 --fork
#shard3,埠號分別為30003,30004,30005
sudo /usr/local/mongodb/bin/mongod --shardsvr --replSet shard3 --dbpath /data/mongodbtest3/shard31/data --logpath /data/mongodbtest3/shard31/log/shard1.log --port 30003 --oplogSize 1024 --fork
sudo /usr/local/mongodb/bin/mongod --shardsvr --replSet shard3 --dbpath /data/mongodbtest3/shard32/data --logpath /data/mongodbtest3/shard32/log/shard2.log --port 30004 --oplogSize 1024 --fork
sudo /usr/local/mongodb/bin/mongod --shardsvr --replSet shard3 --dbpath /data/mongodbtest3/shard33/data --logpath /data/mongodbtest3/shard33/log/shard3.log --port 30005 --oplogSize 1024 --fork
檢視mongod程序開啟狀態
新增複製集
任意登陸一個機器,比如登陸localhost:10003
sudo /usr/local/mongodb/bin/mongo localhost:10003
設定shard1複製集
use admin
config = { _id:"shard1", members:[
{_id:0,host:"localhost:10003"},
{_id:1,host:"localhost:10004"},
{_id:2,host:"localhost:10005",arbiterOnly:true}
]
}
rs.initiate(config);
登陸localhost:20003
sudo /usr/local/mongodb/bin/mongo localhost:20003
設定shard2複製集
use admin
config = { _id:"shard2", members:[
{_id:0,host:"localhost:20003"},
{_id:1,host:"localhost:20004"},
{_id:2,host:"localhost:20005",arbiterOnly:true}
]
}
rs.initiate(config);
登陸localhost:30003
sudo /usr/local/mongodb/bin/mongo localhost:30003
設定shard3複製集
use admin
config = { _id:"shard3", members:[
{_id:0,host:"localhost:30003"},
{_id:1,host:"localhost:30004"},
{_id:2,host:"localhost:30005",arbiterOnly:true}
]
}
rs.initiate(config);
shard1複製集配置結果,shard2,shard3類似
配置好複製集後,連線到其中一個mongos
sudo /usr/local/mongodb/bin/mongo localhost:10002
串聯路由伺服器並分配複製集
use admin
db.runCommand( { addshard : "shard1/localhost:10003,localhost:10004,localhost:10005"});
db.runCommand( { addshard : "shard2/localhost:20003,localhost:20004,localhost:20005"});
db.runCommand( { addshard : "shard3/localhost:30003,localhost:30004,localhost:30005"});
檢視分片伺服器的配置
db.runCommand( { listshards : 1 } );
在這裡,每個複製集的仲裁節點沒有顯示。
讓指定的資料庫和指定的集合分片生效
指定test集合分片生效
db.runCommand( { enablesharding :"test"});
對test資料庫中的userinfo集合進行分片,片鍵採用Hash片鍵
db.runCommand({shardcollection:"test.userinfo",key:{"_id": "hashed"}})
測試分片結果
連線mongos伺服器,之前已經連線,如果關閉了就重新連線
sudo /usr/local/mongodb/bin/mongo localhost:10002
插入資料
use test
for(var i=0;i<100000;i++){db.userinfo.insert({"username":"user"+i});}
檢視分片情況(只擷取展示關鍵資訊)
由於採用的是hash分片,將100000條資料比較均勻的分成3個片
db.userinfo.stats();
檢視叢集狀態
每個片有兩個chuck(塊)
sh.status();
到這裡整個分片叢集就搭建完了,希望對大家有所幫助,博文中不足的地方可以探討探討。