mongodb分片集群
Sharding cluster是一種可以水平擴展的模式,在數據量很大時特給力,實際大規模應用一般會采用這種架構去構建。sharding分片很好的解決了單台服務器磁盤空間、內存、cpu等硬件資源的限制問題,把數據水平拆分出去,降低單節點的訪問壓力。每個分片都是一個獨立的數據庫,所有的分片組合起來構成一個邏輯上的完整的數據庫。因此,分片機制降低了每個分片的數據操作量及需要存儲的數據量,達到多台服務器來應對不斷增加的負載和數據的效果。
1)Sharding分區概念
分片 (sharding)是指將數據庫拆分,將其分散在不同的機器上的過程。將數據分散到不同的機器上,不需要功能強大的服務器就可以存儲更多的數據和處理更大的負載。
分片的基本思想就是:
將集合切成小塊,這些塊分散到若干片里,每個片只負責總數據的一部分。通過一個名為 mongos 的路由進程進行操作,mongos 知道數據和片的對應
關系(通過配置服務器)。 大部分使用場景都是解決磁盤空間的問題,對於寫入有可能會變差(+++里面的說明+++),查 詢則盡量避免跨分片查詢。使用分片的時機:
使用場景:
1)機器的磁盤不夠用了。使用分片解決磁盤空間的問題。
2)單個mongod已經不能滿足寫數據的性能要求。通過分片讓寫壓力分散到各個分片上面,使用分片服務器自身的資源。
3)想把大量數據放到內存里提高性能。和上面一樣,通過分片使用分片服務器自身的資源。
要構建一個MongoDB Sharding Cluster(分片集群),需要三種角色:
1)分片服務器(Shard Server)
mongod 實例,用於存儲實際的數據塊,實際生產環境中一個 shard server 角色可由幾台機器組個一個 relica
set
承擔,防止主機單點故障
這是一個獨立普通的mongod進程,保存數據信息。可以是一個副本集也可以是單獨的一台服務器。
2)配置服務器(Config Server)
mongod 實例,存儲了整個 Cluster Metadata,其中包括 chunk 信息。
這是一個獨立的mongod進程,保存集群和分片的元數據,即各分片包含了哪些數據的信息。最先開始建立,啟用日志功能。像啟動普通的 mongod 一樣啟動
配置服務器,指定configsvr 選項。不需要太多的空間和資源,配置服務器的 1KB 空間相當於真是數據的 200MB。保存的只是數據的分布表。
3)路由服務器(Route Server)
mongos實例,前端路由,客戶端由此接入,且讓整個集群看上去像單一數據庫,前端應用
起到一個路由的功能,供程序連接。本身不保存數據,在啟動時從配置服務器加載集群信息,開啟 mongos 進程需要知道配置服務器的地址,指定configdb選項。
片鍵的意義
一個好的片鍵對分片至關重要。 片鍵必須是一個索引 ,通 過 sh.shardCollection 加會自動創建索引。一個自增的片鍵對寫入和數據均勻分布就不是很好, 因為自增
的片鍵總會在一個分片上寫入,后續達到某個閥值可能會寫到別的分片。但是按照片鍵查詢會非常高效。隨機片鍵對數據的均勻分布效果很好。注意盡量避免在多個分片上進行查詢。
在所有分片上查詢,mongos 會對結果進行歸並排序
水平數據分片
分片集群主要由三種組件組成:mongos,config server,shard 1) mongos (路由進程, 應用程序接入 mongos 再查詢到具體分片) 數據庫集群請求的入口,所有的請求都通過 mongos 進行協調,不需要在應用程序添加一個路由選擇器,mongos 自己就是一個請求分發中心,它負責把對應的數據請求 請求轉發到對應的 shard 服務器上。在生產環境通常有多個 mongos 作為請求的入口,防止其中一個掛掉所有的 mongodb 請求都沒有辦法操作。 2) config server (路由表服務。 每一台都具有全部 chunk 的路由信息) 顧名思義為配置服務器,存儲所有數據庫元信息(路由、分片)的配置。mongos 本身沒有物理存儲分片服務器和數據路由信息,只是緩存在內存里,配置服務器則實際存儲 這些數據。mongos 第一次啟動或者關掉重啟就會從 config server 加載配置信息,以后如果配置服務器信息變化會通知到所有的 mongos 更新自己的狀態,這樣 mongos 就能繼續准確路由。在生產環境通常有多個 config server 配置服務器,因為它存儲了分片路由的元數據,這個可不能丟失!就算掛掉其中一台,只要還有存貨, mongodb 集群就不會掛掉。 3) shard (為數據存儲分片。 每一片都可以是復制集(replica set)) 這就是傳說中的分片了。如圖所示,一台機器的一個數據表 Collection1 存儲了 1T 數據,壓力太大了!在分給 4 個機器后, 每個機器都是 256G,則分攤了集中在一台 機器的壓力。事實上,上圖4個分片如果沒有副本集(replica set)是個不完整架構,假設其中的一個分片掛掉那四 分之一的數據就丟失了,所以在高可用性的分片架構還 需要對於每一個分片構建 replica set 副本集保 證分片的可靠性。生產環境通常是 2 個副本 + 1 個仲裁。
2)Sharding分區的原理
分片,是指將數據拆分,將其分散到不同的機器上。這樣的好處就是,不需要功能強大的大型計算機也可以存儲更多的數據,處理更大的負載。mongoDB 的分片,
是將collection 的數據進行分割,然后將不同的部分分別存儲到不同的機器上。當 collection 所占空間過大時,我們需要增加一台新的機器,分片會自動
將 collection 的數據分發到新的機器上。
mongos:
首先我們要了解”片鍵“的概念,也就是說拆分集合的依據是什么?按照什么鍵值進行拆分集合。mongos就是一個路由服務器,它會根據管理員設置的
"片鍵"
將數據分攤到自己管理的mongod集群,數據和片的對應關系以及相應的配置信息保存在
"config服務器"
上。
客戶端只需要對 mongos 進行操作就行了,至於如何進行分片,不需要 客戶端參與,由 mongos 和 config 來完成。
mongod:
一個普通的數據庫實例或者副本集,如果不分片的話,我們會直接連上mongod。
分片是指將數據拆分,將其分散存在不同機器上的過程.有時也叫分區.將數據分散在不同的機器上MongoDB支持自動分片,可以擺脫手動分片的管理.集群自動切分數據,做負載均衡

分片集群由以下3個服務組成:
Shards Server: 每個shard由一個或多個mongod進程組成,用於存儲數據
Config Server: 用於存儲集群的Metadata信息,包括每個Shard的信息和chunks信息
Route Server: 用於提供路由服務,由Client連接,使整個Cluster看起來像單個DB服務器
部署分片集群:
分別在3台機器運行一個mongod實例(稱為mongod shard11,mongod shard12,mongod shard13)組織replica set1,作為cluster的shard1
分別在3台機器運行一個mongod實例(稱為mongod shard21,mongod shard22,mongod shard23)組織replica set2,作為cluster的shard2
每台機器運行一個mongod實例,作為3個config server
每台機器運行一個mongos進程,用於客戶端連接
1)安裝mongodb (3台機器都要操作) 下載壓縮包, 安裝可以參考前邊的安裝
# tar -zxvf mongodb-linux-x86_64-enterprise-rhel70-4.4.0.tgz
[root@localhost ~]# cd /usr/local/src/
[root@localhost src]# mv /root/mongodb-linux-x86_64-enterprise-rhel70-4.4.0 ./
[root@localhost src]# mv mongodb-linux-x86_64-enterprise-rhel70-4.4.0 mongodb
# yum install net-snmp -y
2)創建sharding數據目錄
根據本例sharding架構圖所示,在各台sever上創建shard數據文件目錄
2)創建sharding數據目錄
根據本例sharding架構圖所示,在各台sever上創建shard數據文件目錄
slave1
[root@slave1 src]
# mkdir /home/services/
[root@slave1 src]
# mv mongodb /home/services/
[root@slave1 src]
# cd /home/services/mongodb/
[root@slave1 mongodb]
# mkdir -p data/shard11
[root@slave1 mongodb]
# mkdir -p data/shard21
slave2
[root@slave2 src]
# mkdir /home/services/
[root@slave2 src]
# mv mongodb /home/services/
[root@slave2 src]
# cd /home/services/mongodb/
[root@slave2 mongodb]
# mkdir -p data/shard12
[root@slave2 mongodb]
# mkdir -p data/shard22
slave3
[root@slave3 src]
# mkdir /home/services/
[root@slave3 src]
# mv mongodb /home/services/
[root@slave3 src]
# cd /home/services/mongodb/
[root@slave3 mongodb]
# mkdir -p data/shard13
[root@slave3 mongodb]
# mkdir -p data/shard23
3)配置relica sets
3.1)配置shard1所用到的replica sets 1:
slave1
[root@slave1 ~]
# /home/services/mongodb/bin/mongod --shardsvr --replSet shard1 --port 27018 --dbpath /home/services/mongodb/data/shard11 --oplogSize 100 --logpath /home/services/mongodb/data/shard11.log --logappend --fork --bind_ip 192.168.88.191
slave2
[root@slave2 ~]
# /home/services/mongodb/bin/mongod --shardsvr --replSet shard1 --port 27018 --dbpath /home/services/mongodb/data/shard12 --oplogSize 100 --logpath /home/services/mongodb/data/shard12.log --logappend --fork --bind_ip 192.168.88.192
slave3
[root@slave3 ~]
# /home/services/mongodb/bin/mongod --shardsvr --replSet shard1 --port 27018 --dbpath /home/services/mongodb/data/shard13 --oplogSize 100 --logpath /home/services/mongodb/data/shard13.log --logappend --fork --bind_ip 192.168.88.194
檢測各個機器上的mongod進程是否正常起來了(
ps
-ef|
grep
mongod),27018端口是否正常起來了
3.2)初始化replica
set
1
從3台機器中任意找一台,連接mongod
[root@slave1 ~]
# /home/services/mongodb/bin/mongo --port 27018 --host 192.168.88.191
......
> config = {
"_id"
:
"shard1"
,
"members"
: [{
"_id"
: 0,
"host"
:
"192.168.88.191:27018"
},{
"_id"
: 1,
"host"
:
"192.168.88.192:27018"
},{
"_id"
: 2,
"host"
:
"192.168.88.194:27018"
}]}
> rs.initiate(config);
{
"ok"
: 1 }
3.3)配置shard2所用到的replica sets 2:
slave1
[root@slave1 ~]
# /home/services/mongodb//bin/mongod --shardsvr --replSet shard2 --port 27019 --dbpath /home/services/mongodb/data/shard21 --oplogSize 100 --logpath /home/services/mongodb/data/shard21.log --logappend --fork --bind_ip 192.168.88.191
slave2
[root@slave2 ~]
# /home/services/mongodb//bin/mongod --shardsvr --replSet shard2 --port 27019 --dbpath /home/services/mongodb/data/shard22 --oplogSize 100 --logpath /home/services/mongodb/data/shard22.log --logappend --fork --bind_ip 192.168.88.192
slave3
[root@slave3 ~]
# /home/services/mongodb//bin/mongod --shardsvr --replSet shard2 --port 27019 --dbpath /home/services/mongodb/data/shard23 --oplogSize 100 --logpath /home/services/mongodb/data/shard23.log --logappend --fork --bind_ip 192.168.88.194
3.4)初始化replica
set
2
從3台機器中任意找一台,連接mongod
[root@slave1 ~]
# /home/services/mongodb/bin/mongo --port 27019
......
> config = {
"_id"
:
"shard2"
,
"members"
: [{
"_id"
: 0,
"host"
:
"192.168.88.191:27019"
},{
"_id"
: 1,
"host"
:
"192.168.88.192:27019"
},{
"_id"
: 2,
"host"
:
"192.168.88.194:27019"
}]}
> rs.initiate(config);
{
"ok"
: 1 }
4)配置三台config server
slave1
[root@slave1 ~]
# mkdir -p /home/services/mongodb/data/config
[root@slave1 ~]
# /home/services/mongodb//bin/mongod --configsvr --dbpath /home/services/mongodb/data/config --port 20000 --logpath /home/services/mongodb/data/config.log --logappend --fork --bind_ip 192.168.88.191
slave2
[root@slave2 ~]
# mkdir -p /home/services/mongodb/data/config
[root@slave2 ~]
# /home/services/mongodb//bin/mongod --configsvr --dbpath /home/services/mongodb/data/config --port 20000 --logpath /home/services/mongodb/data/config.log --logappend --fork --bind_ip 192.168.88.192
slave3
[root@slave3 ~]
# mkdir -p /home/services/mongodb/data/config
[root@slave3 ~]
# /home/services/mongodb//bin/mongod --configsvr --dbpath /home/services/mongodb/data/config --port 20000 --logpath /home/services/mongodb/data/config.log --logappend --fork --bind_ip 192.168.88.194
5)配置mongs
在三台機器上分別執行: slave1 [root@slave1 ~]# /home/services/mongodb/bin/mongos --configdb shard1/192.168.88.191:20000,192.168.88.192:20000,192.168.88.194:20000 --port 27017 --chunkSize 5 --logpath /home/services/mongodb/data/mongos.log --logappend --fork --bind_ip 192.168.88.191 slave2 [root@slave2 ~]# /home/services/mongodb/bin/mongos --configdb shard1/192.168.88.191:20000,192.168.88.192:20000,192.168.88.194:20000 --port 27017 --chunkSize 5 --logpath /home/services/mongodb/data/mongos.log --logappend --fork
--bind_ip 192.168.88.192
slave3 [root@slave3 ~]# /home/services/mongodb/bin/mongos --configdb shard1/192.168.88.191:20000,192.168.88.192:20000,192.168.88.194:20000 --port 27017 --chunkSize 5 --logpath /home/services/mongodb/data/mongos.log --logappend --fork
--bind_ip 192.168.88.194
注意:新版版的mongodb的mongos命令里就不識別--chunkSize參數了
報錯信息:
--logpath /home/services/mongodb/data/mongos.log --logappend --fork --bind_ip 192.168.88.192
FailedToParse: mirrored config server connections are not supported; for config server replica sets be sure to use the replica set connection string
try '/home/services/mongodb/bin/mongos --help' for more information
需要加上副本名稱
{"t":{"$date":"2020-08-17T17:16:12.133+08:00"},"s":"I", "c":"NETWORK", "id":4712102, "ctx":"ReplicaSetMonitor-TaskExecutor","msg":"Host failed in replica set","attr":{"replicaSet":"shard1","host":"192.169.88.192:20000","error":{"code":202,"codeName":"NetworkInterfaceExceededTimeLimit","errmsg":"Couldn't get a connection within the time limit of 483ms"},"action":{"dropConnections":false,"requestImmediateCheck":false,"outcome":{"host":"192.169.88.192:20000","success":false,"errorMessage":"NetworkInterfaceExceededTimeLimit: Couldn't get a connection within the time limit of 483ms"}}}}
about to fork child process, waiting until server is ready for connections
尚未解決
6)配置分片集群(Configuring the Shard Cluster)
從3台機器中任意找一台,連接mongod,並切換到admin數據庫做以下配置
[root@slave1 ~]# /home/services/mongodb/bin/mongo 182.48.115.236:27017/admin6.2)加入shards分區
如里shard是單台服務器,用
"db.runCommand( { addshard : “[:]” } )"
這樣的命令加入
如果shard是replica sets,用
"replicaSetName/[:port][,serverhostname2[:port],…]"
這樣的格式表示,例如本例執行:
mongos> db.runCommand( { addshard:
"shard1/192.168.88.191:27018,192.168.88.192:27018,192.168.88.194:27018"
,name:
"s1"
,maxsize:20480});
{
"shardAdded"
:
"s1"
,
"ok"
: 1 }
mongos> db.runCommand( { addshard:
"shard2/1192.168.88.191:27019,192.168.88.192:27019,192.168.88.194:27019"
,name:
"s2"
,maxsize:20480});
{
"shardAdded"
:
"s2"
,
"ok"
: 1 }
注意:
可選參數
Name:用於指定每個shard的名字,不指定的話系統將自動分配
maxSize:指定各個shard可使用的最大磁盤空間,單位megabytes
6.3)Listing shards
mongos> db.runCommand( { listshards : 1 } )
6.4)激活數據庫分片
命令:
db.runCommand( { enablesharding : “” } );
通過執行以上命令,可以讓數據庫跨shard,如果不執行這步,數據庫只會存放在一個shard,一旦激活數據庫分片,數據庫中不同的collection將被存放在不同的shard上,
但一個collection仍舊存放在同一個shard上,要使單個collection也分片,還需單獨對collection作些操作
Collecton分片
要使單個collection也分片存儲,需要給collection指定一個分片key,通過以下命令操作:
db.runCommand( { shardcollection : “”,key : });
注意:
a)分片的collection系統會自動創建一個索引(也可用戶提前創建好)
b)分片的collection只能有一個在分片key上的唯一索引,其它唯一索引不被允許
本案例:
mongos> db.runCommand({enablesharding:
"test2"
});
{
"ok"
: 1 }
mongos> db.runCommand( { shardcollection :
"test2.books"
, key : {
id
: 1 } } );
{
"collectionsharded"
:
"test2.books"
,
"ok"
: 1 }
mongos> use test2
switched to db test2
mongos> db.stats();
{
"raw"
: {
"shard1/182.48.115.236:27018,182.48.115.237:27018,182.48.115.238:27018"
: {
"db"
:
"test2"
,
"collections"
: 3,
"objects"
: 6,
"avgObjSize"
: 69.33333333333333,
"dataSize"
: 416,
"storageSize"
: 20480,
"numExtents"
: 3,
"indexes"
: 2,
"indexSize"
: 16352,
"fileSize"
: 67108864,
"nsSizeMB"
: 16,
"extentFreeList"
: {
"num"
: 0,
"totalSize"
: 0
},
"dataFileVersion"
: {
"major"
: 4,
"minor"
: 22
},
"ok"
: 1,
"$gleStats"
: {
"lastOpTime"
: Timestamp(0, 0),
"electionId"
: ObjectId(
"586286596422d63aa9f9f000"
)
}
},
"shard2/182.48.115.236:27019,182.48.115.237:27019,182.48.115.238:27019"
: {
"db"
:
"test2"
,
"collections"
: 0,
"objects"
: 0,
"avgObjSize"
: 0,
"dataSize"
: 0,
"storageSize"
: 0,
"numExtents"
: 0,
"indexes"
: 0,
"indexSize"
: 0,
"fileSize"
: 0,
"ok"
: 1
}
},
"objects"
: 6,
"avgObjSize"
: 69,
"dataSize"
: 416,
"storageSize"
: 20480,
"numExtents"
: 3,
"indexes"
: 2,
"indexSize"
: 16352,
"fileSize"
: 67108864,
"extentFreeList"
: {
"num"
: 0,
"totalSize"
: 0
},
"ok"
: 1
}
mongos> db.books.stats();
{
"sharded"
:
true
,
"paddingFactorNote"
:
"paddingFactor is unused and unmaintained in 3.0. It remains hard coded to 1.0 for c ompatibility only."
,
"userFlags"
: 1,
"capped"
:
false
,
"ns"
:
"test2.books"
,
"count"
: 0,
"numExtents"
: 1,
"size"
: 0,
"storageSize"
: 8192,
"totalIndexSize"
: 16352,
"indexSizes"
: {
"_id_"
: 8176,
"id_1"
: 8176
},
"avgObjSize"
: 0,
"nindexes"
: 2,
"nchunks"
: 1,
"shards"
: {
"s1"
: {
"ns"
:
"test2.books"
,
"count"
: 0,
"size"
: 0,
"numExtents"
: 1,
"storageSize"
: 8192,
"lastExtentSize"
: 8192,
"paddingFactor"
: 1,
"paddingFactorNote"
:
"paddingFactor is unused and unmaintained in 3.0. It remains hard co ded to 1.0 for compatibility only."
,
"userFlags"
: 1,
"capped"
:
false
,
"nindexes"
: 2,
"totalIndexSize"
: 16352,
"indexSizes"
: {
"_id_"
: 8176,
"id_1"
: 8176
},
"ok"
: 1,
"$gleStats"
: {
"lastOpTime"
: Timestamp(0, 0),
"electionId"
: ObjectId(
"586286596422d63aa9f9f000"
)
}
}
},
"ok"
: 1
}
7)測試
mongos>
for
(var i = 1; i <= 20000; i++) db.books.save({
id
:i,name:
"12345678"
,sex:
"male"
,age:27,value:
"test"
});
WriteResult({
"nInserted"
: 1 })
mongos> db.books.stats();
{
"sharded"
:
true
,
"paddingFactorNote"
:
"paddingFactor is unused and unmaintained in 3.0. It remains hard coded to 1.0 for compatibility only."
,
"userFlags"
: 1,
"capped"
:
false
,
"ns"
:
"test2.books"
,
"count"
: 20000,
"numExtents"
: 10,
"size"
: 2240000,
"storageSize"
: 5586944,
"totalIndexSize"
: 1250928,
"indexSizes"
: {
"_id_"
: 670432,
"id_1"
: 580496
},
"avgObjSize"
: 112,
"nindexes"
: 2,
"nchunks"
: 5,
"shards"
: {
"s1"
: {
"ns"
:
"test2.books"
,
"count"
: 12300,
"size"
: 1377600,
"avgObjSize"
: 112,
"numExtents"
: 5,
"storageSize"
: 2793472,
"lastExtentSize"
: 2097152,
"paddingFactor"
: 1,
"paddingFactorNote"
:
"paddingFactor is unused and unmaintained in 3.0. It remains hard coded to 1.0 for compatibility only."
,
"userFlags"
: 1,
"capped"
:
false
,
"nindexes"
: 2,
"totalIndexSize"
: 760368,
"indexSizes"
: {
"_id_"
: 408800,
"id_1"
: 351568
},
"ok"
: 1,
"$gleStats"
: {
"lastOpTime"
: Timestamp(0, 0),
"electionId"
: ObjectId(
"586286596422d63aa9f9f000"
)
}
},
"s2"
: {
"ns"
:
"test2.books"
,
"count"
: 7700,
"size"
: 862400,
"avgObjSize"
: 112,
"numExtents"
: 5,
"storageSize"
: 2793472,
"lastExtentSize"
: 2097152,
"paddingFactor"
: 1,
"paddingFactorNote"
:
"paddingFactor is unused and unmaintained in 3.0. It remains hard coded to 1.0 for compatibility only."
,
"userFlags"
: 1,
"capped"
:
false
,
"nindexes"
: 2,
"totalIndexSize"
: 490560,
"indexSizes"
: {
"_id_"
: 261632,
"id_1"
: 228928
},
"ok"
: 1,
"$gleStats"
: {
"lastOpTime"
: Timestamp(0, 0),
"electionId"
: ObjectId(
"58628704f916bb05014c5ea7"
)
}
}
},
"ok"
: 1
}