1. 程式人生 > 資料庫 >MongoDB分片詳解

MongoDB分片詳解

分片是MongoDB的擴充套件方式,通過分片能夠增加更多的機器來用對不斷增加的負載和資料,還不影響應用.

1.分片簡介

分片是指將資料拆分,將其分散存在不同機器上的過程.有時也叫分割槽.將資料分散在不同的機器上,不需要功能

強大的大型計算機就可以儲存更多的資料,處理更大的負載.

使用幾乎所有資料庫軟體都能進行手動分片,應用需要維護與若干不同資料庫伺服器的連線,每個連線還是完全

獨立的.應用程式管理不同伺服器上的不同資料,儲存查村都需要在正確的伺服器上進行.這種方法可以很好的工作,但是也

難以維護,比如向叢集新增節點或從叢集刪除節點都很困難,調整資料分佈和負載模式也不輕鬆.

MongoDB支援自動分片,可以擺脫手動分片的管理.叢集自動切分資料,做負載均衡.

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":[email protected]})

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集合含有已經包含在片上的資料庫列表和一些相關資訊

db.databases.find()

返回的文件解釋:

"_id"

表示資料庫名

"partitioned"

表示是否啟用了分片功能

"primary"

這個值與"_id"相對應,表名這個資料的"大本營"在哪裡.不論分片與否,資料庫總會有個大本營.要是分片的話,建立資料庫時會

隨機選擇一個片.也就是說,大本營是開始建立資料庫文件的位置.雖然分片時資料庫也會用到很多別的伺服器,但會從這個片開始.

c.塊

塊資訊儲存在chunks集合中.這可以看到資料到底是怎麼切分到叢集中的

db.chunks.find()

分片命令

獲得概要

db.printShardingStatus()

刪除片

用removeshard就能從叢集中刪除片.removeshard會把給定片上的所有塊的資料都挪到其他片上

db.runCommand({"removeshard":"127.0.0.1:10001"})

在挪動過程中,removeshard會顯示程序