1.分片簡介 分片是指將數據拆分,將其分散存在不一樣機器上的過程.有時也叫分區.將數據分散在不一樣的機器上,不須要功能 強大的大型計算機就能夠存儲更多的數據,處理更大的負載.使用幾乎全部數據庫軟件都能進行手動分片,應用須要維護與若干不一樣數據庫服務器的鏈接,每一個鏈接 仍是徹底獨立的.應用程序管理不一樣服務器上的不一樣數據,存儲查村都須要在正確的服務器上進行.這種方法能夠很好的工做,可是也難以維護,好比向集羣添加節 點或從集羣刪除節點都很困難,調整數據分佈和負載模式也不輕鬆.
MongoDB支持自動分片,能夠擺脫手動分片的管理.集羣自動切分數據,作負載均衡. shell
2.MongoDB的自動分片 MongoDB分片的基本思想就是將集合切分紅小塊.這些塊分散到若干片裏面,每一個片只負責總數據 的 一部分.應用程序沒必要知道哪片對應哪些數據,甚至不須要知道數據已經被拆分了,因此在分片以前要運行一個路由進程,進程名mongos,這個路由器知道全部數據的存放位置,因此應用能夠鏈接它來正常發送請求.對應用來講,它僅知道鏈接了一個普通的mongod.路由器知道和片的對應關係,可以轉發請求到正確的片上.若是請求有了迴應,路由器將其收集起來回送給應用.
在沒有分片的時候,客戶端鏈接mongod進程,分片時客戶端會鏈接mongos進程.mongos對應用隱藏了分片的細節.
從應用的角度看,分片和不分片沒有區別.因此須要擴展的時候,沒必要修改應用程序的代碼.不分片的客戶端鏈接:
數據庫
何時須要分片:
a.機器的磁盤不夠用了
b.單個mongod已經不能知足些數據的性能須要了
c.想將大量數據放在內存中提升性能
通常來講,先要從不分片開始,而後在須要的時候將其轉換成分片.緩存
3.片鍵
設置分片時,須要從集合裏面選一個鍵,用該鍵的值做爲數據拆分的依據.這個鍵成爲片鍵.
假設有個文檔集合表示的是人員,若是選擇名字"name"作爲片鍵,第一篇可能會存放名字以A-F開頭的文檔.
第二片存G-P開頭的文檔,第三篇存Q-Z的文檔.隨着增長或刪除片,MongoDB會從新平衡數據,是每片的流量比較
均衡,數據量也在合理範圍內(如流量較大的片存放的數據或許會比流量下的片數據要少些)
服務器
4.將已有的集合分片
假設有個存儲日誌的集合,如今要分片.咱們開啓分片功能,而後告訴MongoDB用"timestamp"做爲片鍵,就要全部數據放到
了一個片上.能夠隨意插入數據,但總會是在一個片上.
而後,新增一個片.這個片建好並運行了之後,MongoDB就會把集合拆分紅兩半,成爲塊.每一個塊中包含片鍵值在必定
範圍內的全部文檔,假設其中一塊包含時間戳在2011.11.11前的文檔,則另外一塊含有2011.11.11之後的文檔.其中
一塊會被移動到新片上.若是新文檔的時間戳在2011.11.11以前,則添加到第一塊,不然添加到第二塊.負載均衡
5.遞增片鍵仍是隨機片鍵
片鍵的選擇決定了插入操做在片之間的分佈.
若是選擇了像"timestamp"這樣的鍵,這個值可能不斷增加,並且沒有太大的間斷,就會將全部數據發送到一個片上
(含有2011.11.11之後日期的那片).若是有添加了新片,再拆分數據,仍是會都導入到一臺服務器上.添加了新片,
MongoDB肯能會將2011.11.11之後的拆分紅2011.11.11-2021.11.11.若是文檔的時間大於2021.11.11之後,
全部的文檔還會以最後一片插入.這就不適合寫入負載很高狀況,但按照片鍵查詢會很是高效.
若是寫入負載比較高,想均勻分散負載到各個片,就得選擇分佈均勻的片鍵.日誌例子中時間戳的散列值,沒有模式的"logMessage"
都是複合這個條件的.
不論片鍵隨機跳躍仍是穩定增長,片鍵的變化很重要.如,若是有個"logLevel"鍵的值只有3種值"DEBUG","WARN","ERROR",
MongoDB不管如何也不能把它做爲片鍵將數據分紅多於3片(由於只有3個值).若是鍵的變化太少,但又想讓其做爲片鍵,
能夠把這個鍵與一個變化較大的鍵組合起來,建立一個複合片鍵,如"logLevel"和"timestamp"組合.
選擇片鍵並建立片鍵很像索引,覺得兩者原理類似.事實上,片鍵也是最經常使用的索引.
6.片鍵對操做的影響
最終用戶應該沒法區分是否分片,可是要了解選擇不一樣片鍵狀況下的查詢有何不一樣.
假設仍是那個表示人員的集合,按照"name"分片,有3個片,其名字首字母的範圍是A-Z.下面以不一樣的方式查詢:
db.people.find({"name":"Refactor"})
mongos會將這個查詢直接發送給Q-Z片,得到響應後,直接轉發給客戶端
db.people.find({"name":{"$lt":"L"}})
mongos會將其先發送給A-F和G-P片,而後將結果轉發給客戶端.
db.people.find().sort({"email":1})
mongos會在全部片上查詢,返回結果時還會作歸併排序,確保結果順序正確.
mongos用遊標從各個服務器上獲取數據,因此沒必要等到所有數據都拿到才向客戶端發送批量結果.
db.people.find({"email":"refactor@msn.cn"})
mongos並不追蹤"email"鍵,因此也不知道應該將查詢發給那個片.因此他就向全部片順序發送查詢.
若是是插入文檔,mongos會依據"name"鍵的值,將其發送到相應的片上.
7.創建分片
創建分片有兩步:啓動實際的服務器,而後決定怎麼切分數據.
分片通常會有3個組成部分:
a.片
片就是保存子集合數據的容器,片但是單個的mongod服務器(開發和測試用),也能夠是副本集(生產用).因此一片
有多臺服務器,也只能有一個主服務器,其餘的服務器保存相同的數據.
b.mongos
mongos就是MongoDB配的路由器進程.它路由全部的請求,而後將結果聚合.它自己並不存儲數據或者配置信息
但會緩存配置服務器的信息.
c.配置服務器
配置服務器存儲了集羣的配置信息:數據和片的對應關係.mongos不永久存房數據,因此須要個地方存放分片的配置.
它會從配置服務器獲取同步數據.
8.啓動服務器
首先要啓動配置服務器和mongos.配置服務器須要先啓動.由於mongos會用到其上的配置信息.
配置服務器的啓動就像普通的mongod同樣
mongod --dbpath "F:\mongo\dbs\config" --port 20000 --logpath "F:\mongo\logs\config\MongoDB.txt" --rest
配置服務器不須要不少的空間和資源(200M實際數據大約佔用1kB的配置空間)
創建mongos進程,一共應用程序鏈接.這種路由服務器鏈接數據目錄都不須要,但必定要指明配置服務器的位置:
mongos --port 30000 --configdb 127.0.0.1:20000 --logpath "F:\mongo\logs\mongos\MongoDB.txt"
分片管理一般是經過mongos完成的.
添加片
片就是普通的mongod實例(或副本集)
mongod --dbpath "F:\mongo\dbs\shard" --port 10000 --logpath "F:\mongo\logs\shard\MongoDB.txt" --rest
mongod --dbpath "F:\mongo\dbs\shard1" --port 10001 --logpath "F:\mongo\logs\shard1\MongoDB.txt" --rest
鏈接剛纔啓動的mongos,爲集羣添加一個片.啓動shell,鏈接mongos:
肯定鏈接的是mongos而不是mongod,經過addshard命令添加片:
>mongo 127.0.0.1:30000
mongos> db.runCommand(
... {
... "addshard":"127.0.0.1:10000",
... "allowLocal":true
... }
... )
Sat Jul 21 10:46:38 uncaught exception: error { "$err" : "can't find a shard to
put new db on", "code" : 10185 }
mongos> use admin
switched to db admin
mongos> db.runCommand(
... {
... "addshard":"127.0.0.1:10000",
... "allowLocal":1
... }
... )
{ "shardAdded" : "shard0000", "ok" : 1 }
mongos> db.runCommand(
... {
... "addshard":"127.0.0.1:10001",
... "allowLocal":1
... }
... )
{ "shardAdded" : "shard0001", "ok" : 1 }
當在本機運行片的時候,得設定allowLocal鍵爲1.MongoDB儘可能避免因爲錯誤的配置,將集羣配置到本地,
因此得讓它知道這僅僅是開發,並且咱們很清楚本身在作什麼.若是是生產環境中,則要將其部署在不一樣的機器上.
想添加片的時候,就運行addshard.MongoDB會負責將片集成到集羣.
切分數據
MongoDB不會將存儲的每一條數據都直接發佈,得先在數據庫和集合的級別將分片功能打開.
若是是鏈接配置服務器,
E:\mongo\bin>mongo 127.0.0.1:20000
MongoDB shell version: 2.0.6
connecting to: 127.0.0.1:20000/test
> use admin
switched to db admin
> db.runCommand({"enablesharding":"test"})
{
"errmsg" : "no such cmd: enablesharding",
"bad cmd" : {
"enablesharding" : "test"
},
"ok" : 0
}
應該是鏈接 路由服務器:
db.runCommand({"enablesharding":"test"})//將test數據庫啓用分片功能.
對數據庫分片後,其內部的集合便會存儲到不一樣的片上,同時也是對這些集合分片的前置條件.
在數據庫級別啓用了分片之後,就可使用shardcollection命令堆積和進行分片:
db.runCommand({"shardcollection":"test.refactor","key":{"name":1}})//對test數據庫的refactor集合進行分片,片鍵是name
若是如今對refactor集合添加數據,就會依據"name"的值自動分散到各個片上.
9.生產配置
進入生產環境後,須要更健壯的分片方案,成功的構建分片須要以下條件:
多個配置服務器
多個mongos服務器
每一個片都是副本集
正確的設置w
健壯的配置
設置多個配置服務器是很簡單的.
設置多個配置服務器和設置一個配置服務器同樣
mongod --dbpath "F:\mongo\dbs\config" --port 20000 --logpath "F:\mongo\logs\config\MongoDB.txt" --rest
mongod --dbpath "F:\mongo\dbs\config1" --port 20001 --logpath "F:\mongo\logs\config1\MongoDB.txt" --rest
mongod --dbpath "F:\mongo\dbs\config2" --port 20002 --logpath "F:\mongo\logs\config2\MongoDB.txt" --rest
啓動mongos的時候應將其鏈接到3個配置服務器上:
mongos --port 30000 --configdb 127.0.0.1:20000,127.0.0.1:20001,127.0.0.1:20002 --logpath "F:\mongo\logs\mongos\MongoDB.txt"
配置服務器使用的是兩步提交機制,而不是普通的MongoDB的異步複製,來維護集羣配置的不一樣副本.這樣能保證集羣的狀態
的一致性.這意味着,某臺配置服務器宕機後,集羣的配置信息是隻讀的.客戶端仍是可以讀寫,可是隻有全部配置服務器備份了
之後才能從新均衡數據.
多個mongos
mongos的數量不受限制,建議針對一個應用服務器只運行一個mongos進程.這樣每一個應用服務器就能夠與mongos進行
本地回話,若是服務器不工做了,就不會有應用試圖與不存的mongos通話了
健壯的片
生產環境中,每一個片都應是副本集,這樣單個服務器壞了,就不會致使整個片失效.用addshard命令就能夠將副本集做爲片添加,
添加時,只要指定副本集的名稱和種子就好了.
如要添加副本集refactor,其中包含一個服務器127.0.0.1:10000(還有別的服務器),就能夠用下列命令將其添加到集羣中:
db.runCommand({"addshard":"refactor/127.0.0.1:10000"})
若是127.0.0.1:10000服務器掛了,mongos會知道它所鏈接的是一個副本集,並會使用新的主節點.
10.管理分片
分片信息主要存放在config數據庫上,這樣就能被任何鏈接到mongos的進程訪問到了.
配置集合
在shell中鏈接了mongos,並使用了use config數據庫
a.片
能夠在shareds集合中查到全部的片
db.shards.find()
b.數據庫
databases集合含有已經包含在片上的數據庫列表和一些相關信息
b.databases.find()
返回的文檔解釋:
"_id"
表示數據庫名
"partitioned"
表示是否啓用了分片功能
"primary"
這個值與"_id"相對應,表名這個數據的"大本營"在哪裏.不論分片與否,數據庫總會有個大本營.要是分片的話,建立數據庫時會
隨機選擇一個片.也就是說,大本營是開始建立數據庫文檔的位置.雖然分片時數據庫也會用到不少別的服務器,但會從這個片開始.
c.塊
塊信息存儲在chunks集合中.這能夠看到數據究竟是怎麼切分到集羣中的
db.chunks.find()
分片命令異步
得到概要
db.printShardingStatus()
刪除片
用removeshard就能從集羣中刪除片.removeshard會把給定片上的全部塊的數據都挪到其餘片上
db.runCommand({"removeshard":"127.0.0.1:10001"})
性能