mongodb是目前使用很是普遍的nosql(not only sql)之一,在db engines上排名很是靠前,下圖是5月份的排名:python
MongoDB is an open-source document database that provides high performance, high availability, and automatic scaling.
開源、基於文檔(document oriented)、高性能、高可用、自動伸縮。mysql
開源:sql
這個好處就不用多說了,GitHub上有源碼。mongodb
應用程序經過驅動與Primary鏈接,全部的寫操做都在Primary上進行,同時primary會將這些操做寫到oplog(operation log)中,secondary經過異步複製oplog,而後在本地數據集上執行oplog中的操做,這樣就達到了數據的一致性。從這裏能夠看到,雖然secondary和primary維護的上同一份數據,可是其變動是要遲於primary的。數據庫
自動的failover 雖然保證了mongodb的高可用性,可是在primary到secondary的切換過程當中,這一段時間,mongodb是沒法提供寫操做的。表現就是對於應用程序的數據庫操做請求會返回一些錯誤,這個時候應用程序須要識別這些錯誤,而後作重試。編程
除了Primary和Secondary,在replica set中還能夠存在存在另一種節點:Arbiter。Arbiter與Secondary節點的區別在於,Arbiter不持久化數據(do not bearing data), 天然也不可能在Primary掛掉的時候被選舉。Arbiter的做用在於投票:爲了選出新的primary,secondary投票規則是少數服從多數,若是replica set中的節點數目是偶數,那麼就可能出現「平局」的狀況,因此加入一個Arbiter就能夠以最小的代價解決這個問題。數組
所謂sharding就是將同一個集合的不一樣子集分發存儲到不一樣的機器(shard)上,Mongodb使用sharding機制來支持超大數據量,將不一樣的CRUD路由到不一樣的機器上執行,提到了數據庫的吞吐性能。因而可知,sharding是很是常見的scale out方法。bash
如上圖所示,一個集合(Collection1)有1T的數據,本來放在一個單獨的數據庫中,經過sharding,將這個集合的數據放在四個獨立的shard中,每個shard存儲這個集合256G的數據。每一個shard物理上是獨立的數據庫,但邏輯上共同組成一個數據庫。服務器
一個sharded cluster由一下三部分組成:config server,shards,router。如圖所示:網絡
shards:
存儲數據,能夠是單個的mongod,也能夠是replica set。在生產環境中,爲了提升高可用性,都會使用replica set。存儲在mongod上的數據以chunk爲基本單位,默認的大小爲64M,後面會介紹shard上數據的分裂(split)與遷移(migration)
config server:
存儲集羣的元數據(metadata),即數據的哪一部分放在哪個shard上,router將會利用這些元數據將請求分發到對應的shards上,shards上chunk的遷移也是config server來控制的。
router:
mongos實例,在一個集羣中直接爲應用程序提供服務,利用config server上的元數據來制定最佳的查詢計劃。
數據分割(data partition):
從前文知道,MongoDB在collection這個級別進行數據的切塊,稱之爲sharding。塊的最小粒度是chunk,其大小(chunkSize)默認爲64M。
當一個集合的數據量超過chunkSize的時候,就會被拆分紅兩個chunk,這個過程稱爲splitting。那麼按照什麼原則將一個chunk上的數據拆分紅兩個chunk,這就是Sharding key的做用,Sharding key是被索引的字段,經過sharding key,就能夠把數據均分到兩個chunk,每個document在哪個chunk上,這就是元數據信息。元數據信息存放在config server上,方便router使用。
若是sharding cluster中有多個shard,那麼不一樣shard上的chunk數目多是不一致的,這個時候會有一個後臺進程(balancer)來遷移(migrate)chunk,從chunk數目最多的shard遷移到chunk數目最少的chunk,直到達到均衡的狀態。遷移的過程對應用程序來講是透明的。
以下圖所示,遷移以前ShardA ShardB上都有3個chunk,而Shard C上只有一個Chunk。經過從ShardB上遷移一個chunk到ShardC,就達到了一個均衡的狀態。
splitting和migration 的目的是爲了讓數據在shards之間均勻分佈,其根本目標是爲了將對數據的CRUD操做均衡地分發到各個shard,提升集羣的併發性能。
1 #!/bin/bash 2 export BIN_HOME=/usr/local/mongodb/bin 3 export DB_PATH=/home/mongo_db/data 4 export LOG_PATH=/home/mongo_db/log 5 6 LOCAL=127.0.0.1 7 8 #config rs 9 export RS1_1_DB_PATH=$DB_PATH/rs1_1 10 export RS1_2_DB_PATH=$DB_PATH/rs1_2 11 export RS1_3_DB_PATH=$DB_PATH/rs1_3 12 export RS2_1_DB_PATH=$DB_PATH/rs2_1 13 export RS2_2_DB_PATH=$DB_PATH/rs2_2 14 export RS2_3_DB_PATH=$DB_PATH/rs2_3 15 16 export RS1_1_DB_LOG=$LOG_PATH/rs1_1.log 17 export RS1_2_DB_LOG=$LOG_PATH/rs1_2.log 18 export RS1_3_DB_LOG=$LOG_PATH/rs1_3.log 19 export RS2_1_DB_LOG=$LOG_PATH/rs2_1.log 20 export RS2_2_DB_LOG=$LOG_PATH/rs2_2.log 21 export RS2_3_DB_LOG=$LOG_PATH/rs2_3.log 22 23 export RS1_1_PORT=27018 24 export RS1_2_PORT=27019 25 export RS1_3_PORT=27020 26 export RS2_1_PORT=27021 27 export RS2_2_PORT=27022 28 export RS2_3_PORT=27023 29 30 export RS1=rs1 31 export RS2=rs2 32 33 #config config_server 34 export CONF1_DB_PATH=$DB_PATH/db_conf1 35 export CONF2_DB_PATH=$DB_PATH/db_conf2 36 export CONF3_DB_PATH=$DB_PATH/db_conf3 37 38 export CONF1_DB_LOG=$LOG_PATH/conf1.log 39 export CONF2_DB_LOG=$LOG_PATH/conf2.log 40 export CONF3_DB_LOG=$LOG_PATH/conf3.log 41 42 export CONF1_PORT=40000 43 export CONF2_PORT=40001 44 export CONF3_PORT=40002 45 46 export CONF1_HOST=$LOCAL:$CONF1_PORT 47 export CONF2_HOST=$LOCAL:$CONF2_PORT 48 export CONF3_HOST=$LOCAL:$CONF3_PORT 49 50 #config route_server 51 export ROUTE_DB_LOG=$LOG_PATH/route.log 52 53 export ROUTE_PORT=27017
能夠在會話窗口中將這些命令執行一遍,不過更好的方式是將其保存在一個文件中(如mongodb_define.sh),而後執行這個文件就好了:source mongodb_define.sh
$BIN_HOME/mongod --port $RS1_1_PORT --dbpath $RS1_1_DB_PATH --fork --logpath $RS1_1_DB_LOG --replSet $RS1 --smallfiles --nojournal
$BIN_HOME/mongod --port $RS1_2_PORT --dbpath $RS1_2_DB_PATH --fork --logpath $RS1_2_DB_LOG --replSet $RS1 --smallfiles --nojournal
$BIN_HOME/mongod --port $RS1_3_PORT --dbpath $RS1_3_DB_PATH --fork --logpath $RS1_3_DB_LOG --replSet $RS1 --smallfiles --nojournal
_id : "rs1",
members : [
{_id : 0, host : "127.0.0.1:27018"},
{_id : 1, host : "127.0.0.1:27019"},
{_id : 2, host : "127.0.0.1:27020", arbiterOnly: true},
]
}
>rs.initiate(config)
mkdir -p $RS2_1_DB_PATH
mkdir -p $RS2_2_DB_PATH
mkdir -p $RS2_3_DB_PATH
$BIN_HOME/mongod --port $RS1_1_PORT --dbpath $RS1_1_DB_PATH --fork --logpath $RS1_1_DB_LOG --replSet $RS1 --smallfiles --nojournal
$BIN_HOME/mongod --port $RS1_2_PORT --dbpath $RS1_2_DB_PATH --fork --logpath $RS1_2_DB_LOG --replSet $RS1 --smallfiles --nojournal
$BIN_HOME/mongod --port $RS1_3_PORT --dbpath $RS1_3_DB_PATH --fork --logpath $RS1_3_DB_LOG --replSet $RS1 --smallfiles --nojournal
mongo --port $RS2_1_PORT
>config = {
_id : "rs2",
members : [
{_id : 0, host : "127.0.0.1:27021"},
{_id : 1, host : "127.0.0.1:27022"},
{_id : 2, host : "127.0.0.1:27023", arbiterOnly: true},
]
}
>rs.initiate(config)
mongodb官方建議config server須要三個mongod實例組成,每個mongod最好部署在不一樣的物理機器上。這個三個mongod並非複製集的關係,
step1:建立db目錄
mkdir -p $CONF1_DB_PATH
mkdir -p $CONF2_DB_PATH
mkdir -p $CONF3_DB_PATH
step2:啓動三個mongod實例:
$BIN_HOME/mongod --port $CONF1_PORT --dbpath $CONF1_DB_PATH --fork --logpath $CONF1_DB_LOG --configsvr --smallfiles --nojournal
$BIN_HOME/mongod --port $CONF2_PORT --dbpath $CONF2_DB_PATH --fork --logpath $CONF2_DB_LOG --configsvr --smallfiles --nojournal
$BIN_HOME/mongod --port $CONF3_PORT --dbpath $CONF3_DB_PATH --fork --logpath $CONF3_DB_LOG --configsvr --smallfiles --nojournal
一樣啓動參數中nojournal只是爲了節省存儲空間,在生產環境中必定要使用journaling。與建立replica set時mongod的啓動不一樣的是,這裏有一個configsvr 選項,代表這些節點都是做爲config server存在。
再啓動這三個mongod以後,不會有相似replica set那樣講三個mongod綁定之類的操做,也說明了config server之間是相互獨立的
在上面截圖藍色框中能夠看出,如今尚未任何shard的信息,緣由是到如今爲止,config servers與replica set尚未任何關係
mongos> sh.addShard('rs1/127.0.0.1:27018')
mongos> sh.addShard('rs2/127.0.0.1:27021')
PS:爲何須要在rs1後面指定一個mongod的ip port,這個是用來找到對應的mongod,繼而找到相應rs
再次查看結果:
爲了演示,咱們假設添加一個db叫test_db, 其中有兩個collection,一個是須要sharding的,叫sharded_col;另外一個暫時不用sharding,叫non_sharded_col, 固然以後也能夠增長新的集合,或者把原來沒有sharding的集合改爲sharding。
一下操做都須要登陸到router進行: mongo --port $ROUTE_PORT
step1:首先得告知mongodb test_db這個數據庫支持sharding
mongos> sh.enableSharding("test_db")
{ "ok" : 1 }
這個時候能夠查看數據庫的狀態,注意,是在config這個db下面的databases集合
mongos> use config
sh.status()反應的內容事實上也是來自config整個數據庫的內容,只不過作了必定程度的整合。從上面能夠看到,有兩個shard,rs1, rs2;test_db容許sharding,test_db.sharded_col整個collection的sharding key爲{"_id": 1},且目前只有一個chunk在rs1整個shard上。
到目前爲止,咱們已經搭建了一個有三個config server,兩個shard的sharded cluster,其中每個shard包含三個節點的replica set,且都包含一個Arbiter。咱們能夠查看一下剛建立好以後各個mongod實例持久化的數據大小:
能夠看到,兩個Arbiter(rs1_3, rs2_3)所佔的空間要小得多。
對於應用程序來講,集羣(sharded cluster)和單點(standalone)是有必定差別的,若是須要發揮sharded cluster高性能、高可用的特色,須要根據應用場景精心選擇好sharding key,而sharding key的選擇跟索引的創建以及CRUD語句息息相關,這一部分之後再聊。對於目前搭建的這個實例,簡單測試的話,往sharded_col插入足夠多條document就能看到chunks的拆分和遷移。
references: