9.分片(Sharding)
Mongodb Manual閱讀筆記:CH2 Mongodb CRUD 操作
Mongodb Manual閱讀筆記:CH3 數據模型(Data Models)
Mongodb Manual閱讀筆記:CH4 管理
Mongodb Manual閱讀筆記:CH5 安全性
Mongodb Manual閱讀筆記:CH6 聚合
Mongodb Manual閱讀筆記:CH7 索引
Mongodb Manual閱讀筆記:CH8 復制集
Mongodb Manual閱讀筆記:CH9 Sharding
隨着數據增長,shard是多機器處理數據存儲的方式。shard是橫向擴展的解決方案。
9.1 Sharding說明
9.1.1 Shard的目的
當數據庫數據增大,應用程序高吞吐量會對單服務的能力進行挑戰。查詢過大cpu使用過大,數據過大要要求存儲能力,working set 過大要求RAM和IO能力。
為了解決這個問題,有2個基本的方案:水平擴展,垂直擴展。
水平擴展:添加更多的cpu,存儲資源來這更加處理能力。大cpu大內存的設備價格會遠遠大於小設備。
Sharding:是一種水平擴展,把數據分片到各個設備上。每個分片都是一個獨立的數據庫。
sharding處理大數據,大吞吐量的好處:
1.減少了在每個shard上的操作
2.減少了每個shard上的數據保存量。
9.1.2 Mongodb中的Shard
shard集群由以下幾個組件:shard,query routers和config server
Shard:用來存儲數據。提供高可用和數據一致性,在生產環境下一個shard是一個復制集
Query Routers:或者mongos實例,是客戶端的接口然后在合適的shard中型操作然后返回數據。對於shard集群可以包含多余一個router來分流客戶端請求。一個客戶端發送請求到一個router,大多數shard集群由很多query router。
Config Server:保存集群元數據,query router根據config server上的元數據來決定路由到哪個shard。
9.1.3數據分區
mongodb分發數據或者分配是在collection級別的,同歸shard key對collection進行分片。
9.1.3.1 Shard Keys
Shard Keys必須每個文檔都有,並且要不是索引的字段,要不是組合索引的字段。mongodb把shard key划分為chunks,然后發布這些chunk到各個shard。可以使用range分區也可以是hash分區。
9.1.3.2 Range分區
Mongodb通過shard key把數據分為若干個區間。給定一個Range,key近的理論上在同一個文檔上。
9.1.3.3 Hash分區
hash分區是根據文檔中的字段計算hash,然后使用這些hash來創建chunk。那么即使shard key值相近也可能不在同一個chunk中。
9.1.3.4 Range分區和Hash分區的性能區別
對於Range分區,區間查詢更加有效,路由可以很容易的決定哪些chunk可以覆蓋這些查詢,然后路由到想要的shard上。
Range分區會導致數據不均勻,會否定shard帶來的好處,會導致大量數據堆積在同一個shard上,弱化了shard的優勢。
Hash分區確保了數據的均勻分布,但是對於區間查詢就不可用對一些shard進行炒作,會對所有shard產生影響。
9.1.4 維護數據均衡發布
新增的數據或者新增的服務會導致數據發布不均衡。有一些chunk會包含比其他chunk大的多的數據,或者比其他chunk的數據更加重要。
9.1.4.1 分離(Splitting)
Splitting是一個后台進程,不讓chunk變得太大,當chunk超過閥值是就會splits,split會影響元數據,split不會遷移也不會影響shard。
9.1.4.2均衡(Balancing)
均衡器也是后台進程用來管理chunk遷移。
當發布數據不均衡的時候,均衡器會從多的shard遷移到小的shard.
1.chunk遷移的時候,目標shard會獲取chunk上的所有文檔。
2.然后目標shard獲取在遷移過程中所有的修改
3.最后shard修改config中相應的location
4.然后刪除源中的chunk。但是要遷移大於1個chunk的時候,不會先刪除,而是進入下一個chunk的遷移。
9.1.4.3 增加和刪除Shard
增加和刪除shard,都要遷移chunk。
9.2 Sharding 概述
9.2.1 Shard集群組件
Shard集群有一下幾個組件:
Shard:一個shard是一個mongodb實例,保存了collection數據的一個子集,Shard是單實例,也可以是復制集。
Config Server:每個config server是一個mongod實例,保存了元數據。
Routing Instances:路由是mongos實例,路由從應用程序到shard的讀寫。應用程序不能直接路由到shard。
9.2.1.1 Shards
Shard可以是復制集也可以是一個實例,保存了shard集群的部分數據。通常shard是一個復制集,復制集為每個shard提供了冗余和可用性。
Primary Shard
每個數據庫都有一個primary shard用來保存非shard collection。
可以使用movePrimary命令來修改shard primary 數據庫。當部署新的shard集群時,第一個shard會變成primary。
Shard狀態
使用sh.status()來查看shard狀態。
9.2.1.2 Config服務
Config服務比較特殊,保存了shard集群內的所有的元數據。Config服務之間使用二階段提交的方式來保證一致性和可靠性。Config服務不能使用復制集,所有config服務必須是可用的,來保證元數據的修改。
一般生產環境需要3個config服務。當只有一個config 服務,出現單點故障后,config數據被恢復之前是不能訪問集群的。
Config數據庫
Config服務把元數據存放在config數據庫中,mongos會緩存這些數據,來路由數據庫操作。
Config服務的讀寫
只有在一下情況下會寫config 服務:
1.在已存在的chunk上創建split
2.在shard之前遷移chunk
再有在一下情況下會讀config服務:
1.mongos第一次啟動
2.遷移chunk后,mongos更新自己的cache。
Config服務可用性
當有3個config 服務其中1個或2個不可用的時候,集群的元數據變為只讀,可以讀數據,但是不能遷移chunk或者創建split。
如果3個config服務都不可用,如果不重啟mongos那么任然可以使用集群,如果在config服務可用之前mongos重啟了,mongos就不能再路由。
當沒有元數據的時候集群變得不可操作,所以保持一個可用的完好的config十分重要。備份起到一個很關鍵作用。Config服務的數據和整個集群比很小,負荷也少,並且config服務並不要求一直可用來支持shard集群。
當shard集群使用的config服務修改了名字或者地址,需要重啟每一個mongod和mongos,為了避免這種情況,可以使用CNAME來唯一標示衣蛾ocnfig服務。
9.2.2 Shard集群體系結構
介紹:shard集群要求,生產環境shard集群結構體系,shard集群測試環境的結構體系。
9.2.2.1 Shard集群要求
Shard是很給力的功能,但shard集群有很多設備的要求,增加了整體部署的復雜性。
對於某些情況來說shard是唯一的解決方法:
1.數據集超過了單個實例處理的能力
2.數據集需要的內存超過了單個實例可以提供的內存
3.大量寫的需求,使用其他方法無法解決。
如果沒有以上的情況,使用shard只會影響整體的復雜性。
Shard的部署比較花時間,如果系統已經到達了處理能力的邊界,再部署shard肯定會影響應用程序的使用。
Shard的部署應該在設計數據結構的時候就應該考慮。
數據量需求
集群會管理大量的數據,數據以chunk為單位進行管理,chunk的大小默認是64MB,當shard之間chunk不均衡的時候(超過了閥值),均衡器會在shard之間遷移chunk知道均衡為止。對於小的collection來說shard是不合算的,光增加了復雜性,除非有寫入性能的要求。Chunk默認大小64MB,是可以配置的
9.2.2.2生產環境集群結構
在生產環境下,要保證數據冗余和系統高可用。
組件
Config Server:3個Config Server,每個config server有獨立的設備,一個shard獨占使用config。也就是說config 不能被shard集群共享。
Shards:2個以上的shard,這些shard要求是復制集
Query Rounters(mongos):一個或多個mongos,mongos用來路由,一般是每個應用程序服務都有一個mongos實例。
當然,可以在mongos和應用程序之間使用代理和負載均衡,這種情況下使用client affinity來配置負載均衡,這樣同一個鏈接可以一直使用同一個mongos。
9.2.2.3測試環境的體系結構
測試環境下:1個config server,1個以上shard,一個mongos實例。
9.2.3 Shard集群特性
介紹:shard key,shard集群高可用性,shard集群路由
9.2.3.1 Shard Key
Shard key確定了collection的數據分布,shard key是索引字段,或者是組合索引字段。
Mongodb使用shard key來切分collection,每個區間或者chunk,不能重疊。然后mongodb分發這些chunks。
Hash過的Shard key
也就是說shard key 可以是一個hash值。
選擇字段的時候最好,字段有很高的選擇性(相同值數量很少)。Hash key在單調遞增的字段上也很不錯比如objectid或者時間戳。
如果在一個空的collection上使用hash作為shard key,mongodb會自動創建和遷移chunk,所以每個shard會有2個chunk,當然這個可以通過shardCollection命令中的numInitChunks參數來控制,也可以通過split命令手動創建chunk。
Shard key對集群的影響
Shard key通過確定數據的分區和mongos對集群的有效操作來影響性能。
寫能力擴展:shard key會增加寫入的性能,以下objectid為例。
Mongodb為每個文檔創建一個objectid,但是有個問題是,是單調增長的,所有的插入都會在一個chunk上一個shard上,那么這個shard寫入能力就是整個集群的寫入能力。
只有當單調的增加為shard key的很少有插入,這樣對性能的影響比較少。
通常使用shard key需要一點隨機性。可以讓集群擴展寫能力。但同時這樣的shard key不能提供查詢隔離,這個也是shard key重要特性之一。
查詢:mongos提供了應用程序到shard集群的接口,mongos接受應用程序的請求,使用config server的元數據進行路由,路由到有相應數據的mongod。當mongos路由所有查詢操作時,shard key會對查詢有很大的影響。
查詢隔離:在shard環境中,最快的查詢是通過shard key 和元數據被路由到一個shard上的查詢。對不包含shard key 過濾的查詢會被路由到所有的shard會比較慢。若shard key是組合的,查詢對shard key 前綴進行過濾,那么可以被路由到某些shard。
Shard key的選擇:
1.查詢中最常用到的字段
2.哪些操作對性能有很高的依賴
如果一個字段選擇度很差,那么就再加個字段提高選擇度。
排序:在shard環境下,mongos執行合並排序法來排序從shard中出來的結果
9.2.3.2 Shard集群高可用性
在生產環境下,不能產生單點故障
應用程序或者mongos不可用
如果每個應用程序服務都有自己的mongos,當不可用時,其他的應用可以使用自己的mongos。Mongos自己不保存數據,重啟后不會有數據丟失。
一個shard不可用
Shard級別,復制集提供了可用性,如果primary變得不可用,復制集就會投票生成另一個primary。如果是secondary不可用不影響使用。如果系統變得不可恢復,那么增加新的成員來彌補冗余。
復制集所有成員不可用
如果整個復制集不可用,shard內的數據也變得不可用,但是其他shard的數據還是可用的。就有可能讀寫到其他成員。所有要能夠處理這部分數據,調查清楚問題並恢復。
一個以上Config 數據庫不可用
如果有3個不同的config數據,config 數據庫之間是使用二階段提交的。整個集群還是可用的,但是chunk遷移,chunk分離。如果所有config不可用,那么整個集群就不可用。
重命名Config服務和集群可用性
如果連接到config的名字和地址被修改,必須重啟所有的mongod和mongos。使用CNAME(DNS)可以完成換服務器,但是可以不修改連接字符串。
Shard key和集群可用性
選擇Shard Key要考慮:
1.保證mongod可以均勻的分布數據
2.可以擴展寫能力
3.保證mongos在大多數查詢上可以查詢隔離,也就是路由到指定的mongod。
跟多:
1.每個shard應該是一個復制集
2.如果可以隔離大多數操作,shard不可用,只會影響部分數據。
3.如果每個操作都要發布到整個集群,當一個shard不可用會導致整個集群不可用。
9.2.3.3 Shard集群查詢路由
Shard對應用程序來說是透明的。Mongos緩存了config的數據,進行路由工作。一般的做法mongos都是在應用程序服務器上,也可以在shard上或者其他專用設備上。
路由進程
Mongos使用一下過程路由到結果:
決定哪些Shard接收查詢:
1.確定哪些shard必須接收查詢
2.在這些shard上創建游標。
如果查詢中使用shard key或者shard key前綴過濾可以只查詢shard的子集。
Mongos如何處理查詢Modifiers:
如果查詢的結果不要求是排序的,mongos會打開一個游標然后循環shard上來的結果。如果指定了排序,那么把排序給shard,mongos在shard基礎上執行合並排序。如果有limit會把limit傳給shard,再在結果上limit。若有skip那么不會傳,如果有limit和skip那么會傳limit,傳給shard的limit的個數為skip個數加上limit的個數。
診斷到Mongos的連接
如果連接到的是mongos,使用ismaster命令會返回:
{
"ismaster":true,
"msg":"isdbgrid",
"maxBsonObjectSize":16777216,
"ok":1
}
其中msg為isdbgrid,如果連接到的是mongod不會包含isdbgrid。
廣播操作和目標操作
在shard集群中操作有2種:
1.會廣播到所有shard的操作,如remove
2.以一部分shard為目標的操作,如insert。
Shard和非Shard數據
Shard是在collection級別的,可以在多個數據庫的多個collection上shard。不管集群的結構,所有的查詢和操作都使用mongos路由到數據中心。
9.2.4 Shard機制
介紹,shard collection均衡,chunk遷移,chunk split,Shard key所有,shard集群元數據。
9.2.4.1 Shard Collection均衡
Mongodb使用均衡器來平衡shard之間的數據。當一個shard的數據過多,均衡器會啟動分發給其他shard。
集群均衡器
均衡器是用來均衡shard之間的數據量,默認均衡器是開啟的。
任何mongos實例都可以啟動均衡流程。當均衡器被激活,mongos要修改config數據庫中的lock collection,獲得鎖。
均衡器一旦啟動,只有當chunk均衡的時候才會停止。
Chunk遷移會導致負荷上升,影響應用的性能。有2個方法減少影響:
1.一次只遷移一個chunk
2.或者只有當最大chunks個數和最小chunks個數只差超過一個閥值。
當然也可以臨時關閉均衡器
遷移閥值
Number of Chunks |
Migration Threshold |
Fewer than 20 |
2 |
21-80 |
4 |
Greater than 80 |
8 |
一旦均衡器啟動只有當2個shard的chunk差為2或者遷移失敗才會停止。
Shard 大小
默認mongodb會試圖占滿所有的可用空間,為了保證還有性能和空間繼續擴展,要監視磁盤可用空間和磁盤性能。
當添加shard的時候可以設置shard最大大小。可以方式shard往里面移動數據。
9.2.4.2 Shard間Chunk遷移
Chunk遷移
Chunk遷移可以:1.手動遷移,2.自動遷移(通過均衡器)
所有的遷移都是一下過程:
1.均衡器發送moveChunk命令道源shard
2.源Shard使用內部的moveChunk命令。源Shard負責處理寫入
3.目標Shard開始復制數據
4.接受到之后一個文檔之后,目標sahrd開始同步,保證被修改的文檔可以被同步過來
5.當都被同步完成,目標shard連接到config數據庫,修改集群元數據
6.當修改完元數據,並沒有游標,刪除chunk上的文檔。
若要進行另外一個chunk遷移,不用等待當前chunk刪除完成,直接進行。
Chunk遷移隊列
為了讓遷移速度更快,均衡器並不會等待當前遷移刪除chunk,就啟動下一個遷移。
Chunk遷移寫注意
刪除chunk的時候等待發布到secondary,會導致遷移速度下降,但是保證了大量chunk遷移的時候集群的可用性。具體看為chunk遷移修改復制集行為
9.2.4.3 Shard集群下Chunk的分離
當Chunk的大小超過了指定的chunk大小,mongos會把這個chunk分成2半
Chunk大小
默認Chunk的大小為64MB,也可以通過配置增加減少chunk的大小。
1.小的chunk大小,會讓數據更加平均,但是遷移量變大,成本增加。
2.大的chunk會讓遷移變少,但是數據不均衡。
限制
Chunk大小的修改也會影響chunk分離。
1.自動分離只會發生在插入和修改
2.分離不能被undoen,如果增加了chunk的大小,chunk只有在insert和update的時候增長才會變成新的大小。
9.2.4.4 Shard Key索引
所有的shard collection都要有個索引用於shard key,如果沒有文檔沒有索引,shardCollection命令會在shard key上創建索引。如果已經有文檔了,那么要在shardCollection之前創建好索引。
Shard key不能是multikey索引。
如果shard collection在zipcode為shard key可以使用如下方法來替換索引:
1.創建一個索引zipcode,username
2.刪除zipcode這個索引
刪除了最后一個可用索引之后,可以重新在shard key創建索引來恢復。
9.2.4.5Shard集群元數據
Config服務保存了集群的元數據,元數據反映了狀態和shard數據集和系統的組合。mongos實例緩存了這些數據並使用它來路由讀寫操作。config數據庫包含了以下collection:
9.3 Shard集群教程
主要介紹如何管理shard集群
9.3.1 Shard集群部署教程
部署shard集群,考慮shard key,使用hash shard key分片collection,shard集群驗證,添加shard到集群,為生產環境部署3個config server,把復制集轉化為shard集群,把shard轉化為復制集。
9.3.1.1部署shard集群
啟動Config服務
config服務是保存了集群元數據的mongod,啟動的時候標記configsvr,每個config保存了一份完整的集群元數據。
生產環境下最好在不同的服務器上部署3個config服務。
1.為config服務創建數據文件夾
mkdir /data/configdb
2.啟動mongod服務
mongod --configsvr --dbpath <path> --port <port>
默認端口是27019,當然可以通過--port來指定端口。
啟動mongos實例
mongos是很輕量的不要求數據文件夾。默認端口是27017。啟動mongos的時候需要指定config服務,可以在命令行也可以在配置文件下。最好給每個config服務一個DNS,這樣換服務器的時候就不會有下線時間。
mongos --configdb cfg0.example.net:27019,cfg1.example.net:27019,cfg2.example.net:27019
每個mongos啟動的時候必須要有相同的configdb配置字符串。
增加Shard到集群中
shard可以是復制集也可以是單個實例,對於生產環境應該要求都是復制集。
1.使用mongo連接到mongos
mongo --host mongos0.example.net --port 27017
2.使用sh.addShard添加shard到集群
添加一個復制集,添加一個成員就可以了,以前的版本要添加所有的成員。
sh.addShard("rs1/mongodb0.example.net:27017")
添加一個單實例shard
sh.addShard("mongodb0.example.net:27017")
啟動數據庫中的Shard
在真正shard collection之前要先啟用數據庫的shard。
1.連接到mongos
mongo --host mongos0.example.net --port 27017
2.使用sh.enableShard()命令對數據庫啟用shard
sh.enableSharding("<database>")
也可以使用命令:db.runCommand({enableSharding:<database>})
啟動Collection的Shard
1.確定shard key
2.如果collection已經有數據庫的要先在shard key上創建索引使用ensureIndex(),如果是空的,Mongodb會通過sh.shardCollection創建。
3.使用sh.shardCollection() shard一個collection
sh.shardCollection("<database>.<collection>",shard-key-pattern)
shard-key-pattern表示shard key
如:
sh.shardCollection("records.people",{"zipcode":1,"name":1})
sh.shardCollection("people.addresses",{"state":1,"_id":1})
sh.shardCollection("assets.chairs",{"type":1,"_id":1})
db.alerts.ensureIndex({_id:"hashed"})
sh.shardCollection("events.alerts",{"_id":"hashed"})
9.3.1.2考慮Shard Key
選擇Shard Key
以下是幫助你找到合適的shard key的策略:
1.在應用程序層先計算好理想的shard key,然后保存到文檔中
2.使用組合shard key使用,前綴來提高寫能力和讀隔離
3.在這些情況下,不好的shard key 並沒有很大的影響:有限的寫入,預期的數據大小,應用程序查詢方式。
4.使用hash shard key,選一個選擇度高的然后創建hahs索引。然后使用這個hash索引為shard key的值。
考慮Shard Key的選擇性:shard key的選擇對性能,選擇度,集群的功能影響重大。
創建一個易切分(divisible)的shard key
一個易切分的shard key讓mongodb分發數據比較簡單,如果只有有限的幾個值會讓chunk無法分割。
創建一個高隨機的shard key
一個高隨機的shard key不會讓任何一個shard變成瓶頸,並且可以在集群中發布寫操作(不會只寫入到一個shard)。
對於一個shard創建一個shard key
一個shard一個shard key可以讓mongos直接從一個shard上返回結果,但是shard key必須最為主要的過濾條件。使用高隨機的shard key 很難再指定的shard 上操作。
使用組合shard key
當存在在collection中的字段都不是最優的key,那么選擇多個字段作為組合shard key會更加的理想。
基數(cardinality 選擇性類似)
基數決定了這個系統中可以分為幾個chunks。
1.如果用state作為shard key,因為state少會造成集群不均衡有以下影響:
a.不能分離chunks因為這個chunk都是同一個shard key,遷移這些chunk變得無比困難。很難讓集群均衡。
b.如果固定了chunks的個數,那么就不能超過這個個數了。
2.zipcode字段作為shard key:
這個字段選擇度高但是也有可能照成un-splittable的情況,有些用戶很多,有些卻很少。
3.考慮手機號碼作為shard key:
手機號碼是高基數(和選擇度一樣)的,所有的用戶都有自己的手機號碼並且是唯一的。
高基數可以保證數據均衡,但是不能保證查詢隔離和合適的寫擴展。
9.3.1.3使用Hash Shard Key
Hash Shard Key是使用Hash索引的字段
Shard Collection
使用shard key來shard collection,
sh.shardCollection( "records.active", { a:"hashed" } )
使用a的hash值作為shard key。
指定初始化Chunks
如果使用hash shard key來shard空的collection,mongodb會自動創建和遷移空的chunks。每個shard有2個chunks。可以使用shardCollection的numInitialChunks來控制初始化collection的chunk個數。
Mongodb hash索引會截斷float小數點后面的值,再計算。
9.3.1.4 Shard集群的認證
使用—keyfile來控制集群內組件的互通,keyfile都一樣,內容可以隨意
過程
1.創建key file保存認證信息
2.通過以下方式啟用驗證:
a.在配置文件中:keyFile = /srv/mongodb/keyfile
b.在命令行上配置—keyfile
3.添加第一個管理員,創建一個管理員
9.3.1.5添加Shard到集群
當shard增加到集群的時候要保證,集群有足夠的能力來遷移數據。
添加一個shard 到集群
1.使用mongo連接到mongos
mongo --host mongos0.example.net --port 27017
2.使用sh.addShard增加shard
添加一個復制集,添加一個成員就可以了,以前的版本要添加所有的成員。
sh.addShard("rs1/mongodb0.example.net:27017")
添加一個單實例shard
sh.addShard("mongodb0.example.net:27017")
9.3.1.6為生產環境部署3個Config服務
這個過程是把只有單個config的測試環境轉變為3個config服務的生產環境。
1.關閉所有mongodb進程
2.復制dbpath下的所有文件到其他config服務
rsync -az /data/configdb mongo-config1.example.net:/data/configdb
rsync -az /data/configdb mongo-config2.example.net:/data/configdb
3.啟動所有3個config server
mongod --configsvr
4.重啟所有的mongod和mongos
9.3.1.7把復制集轉化為shard集群
概述
這個過程是把3成員的復制集轉為2個shard的集群,每個shard是3成員的復制集。
1.創建選擇一個3成員的復制集,插入一些數據到collection
2.啟動config數據庫,並且創建一個單shard的集群
3.創建第二個復制集
4.把第二個復制集添加到這個集群上
5.在需要shard的collection進行shard。
過程
使用測試數據部署復制集:使用下面的順序來部署復制集
1.為復制集創建文件夾。
mkdir -p /data/example/firstset1 /data/example/firstset2 /data/example/firstset3
2.啟動3個mongod實例
mongod --dbpath /data/example/firstset1 --port 10001 --replSet firstset --oplogSize 700 --rest
mongod --dbpath /data/example/firstset2 --port 10002 --replSet firstset --oplogSize 700 --rest
mongod --dbpath /data/example/firstset3 --port 10003 --replSet firstset --oplogSize 700 --rest
3.使用mongo打開一個shell
4.通過以下命令來初始化復制集
db.runCommand({"replSetInitiate":
{"_id":"firstset", "members": [{"_id":1, "host":"localhost:10001"},
{"_id":2, "host":"localhost:10002"},
{"_id":3, "host":"localhost:10003"}
]}})
{
"info":"Config now saved locally. Should come online in about a minute.",
"ok":1
}
5.使用一下代碼插入測試數據
use test
switched to db test
people = ["Marc", "Bill", "George", "Eliot", "Matt", "Trey", "Tracy", "Greg", "Steve", "Kristina", "Katie", "Jeff"];
for(var i=0; i<1000000; i++){
name = people[Math.floor(Math.random()*people.length)];
user_id = i;
boolean= [true, false][Math.floor(Math.random()*2)];
added_at =newDate();
number =Math.floor(Math.random()*10001);
db.test_collection.save({"name":name, "user_id":user_id, "boolean":boolean, "added_at":added_at, "number":number });
}
部署Shard基礎設施:這個過程創建了3個config數據庫
1.為config服務創建文件夾
mkdir -p /data/example/config1 /data/example/config2 /data/example/config3
2.啟動config數據庫
mongod --configsvr --dbpath /data/example/config1 --port 20001
mongod --configsvr --dbpath /data/example/config2 --port 20002
mongod --configsvr --dbpath /data/example/config3 --port 20003
3.啟動mongos實例
mongos --configdb localhost:20001,localhost:20002,localhost:20003 --port 27017 --chunkSize 1
--chunkSize表示chunk的大小這里是1MB
4.添加第一個shard
a.連接到mongos
mongo localhost:27017/admin
b.增加使用addShard增加sahrd
db.runCommand( { addShard :"firstset/localhost:10001,localhost:10002,localhost:10003" } )
c.返回一下信息說明添加成功
{ "shardAdded":"firstset", "ok":1 }
部署第二個復制集:下面的過程部署第二個復制集
1.創建復制集文件夾
- /data/example/secondset1
- /data/example/secondset2
- /data/example/secondset3
2.啟動3個mongod實例
mongod --dbpath /data/example/secondset1 --port 10004 --replSet secondset --oplogSize 700 --rest
mongod --dbpath /data/example/secondset2 --port 10005 --replSet secondset --oplogSize 700 --rest
mongod --dbpath /data/example/secondset3 --port 10006 --replSet secondset --oplogSize 700 --rest
3.連接到mongos
mongo localhost:10004/admin
4.初始化第二個復制集
db.runCommand({"replSetInitiate":
{"_id":"secondset",
"members": [{"_id":1, "host":"localhost:10004"},
{"_id":2, "host":"localhost:10005"},
{"_id":3, "host":"localhost:10006"}
]}})
{
"info":"Config now saved locally. Should come online in about a minute.",
"ok":1
}
5.把復制集增加到集群
use admin
db.runCommand( { addShard :"secondset/localhost:10004,localhost:10005,localhost:10006" } )
是不是只寫一個成員就可以了
6.驗證shard成員,使用listShards命令
db.runCommand({listShards:1})
{
"shards": [
{
"_id":"firstset",
"host":"firstset/localhost:10001,localhost:10003,localhost:10002"
},
{
"_id":"secondset",
"host":"secondset/localhost:10004,localhost:10006,localhost:10005"
}
],
"ok":1
}
啟動Shard:mongodb必須要再數據庫和collection級別啟動shard
在數據庫級別啟動Shard:使用enableSharding命令啟動數據庫
db.runCommand( { enableSharding :"test" } )
{ "ok":1 }
在索引上創建Shard Key:Mongodb使用shard key來發發布數據到shard,一旦決定之后你不能修改shard key。好的shard key:
1.可以均衡的分發數據
2.經常被同時訪問的文檔會被放在連續的chunk下
3.在活動的shard中有效的分發
Shard key一般的選擇是復合索引,hash或者主鍵。Shard key的選擇還是和數據,應用程序結構和使用方式有關。
Shard Collection:
use admin
db.runCommand( { shardCollection :"test.test_collection", key : {"number":1} })
{ "collectionsharded":"test.test_collection", "ok":1 }
之后均衡器會均衡chunk,可以在test下的db.stats()或者db.printShardIngStatus()查看是否在同步。
9.3.1.8把shard集群轉化為復制集
把一個有復制集的集群轉為復制集
這個例子是,集群只有一個shard復制集,用以下方法把shard轉為復制集
1.配置應用程序連接到primary成員
2.如果啟動的時候有shardsrv選項,那么刪除這個選項。
把集群轉為復制集
1.創建一個新的復制集,保證這個復制集的性能能夠達到要求。
2.停止所有的寫入。通過重新配置應用程序或者停止所有mongos
3.使用mongodump和mongorestore備份恢復數據到新的復制集
4.配置應用程序連接到新的復制集
9.3.2 Shard集群維護教程
查看集群配置,使用相同的名字遷移config服務,使用不同的名字遷移config服務,替換ocnfig服務,遷移shard到不同的硬件,備份元數據,配置均衡器,管理均衡器,刪除shard。
9.3.2.1查看集群配置
查看shard可用的數據庫
通過在config數據庫對databases collection的查詢
use config
db.databases.find( { "partitioned":true } )
查看所有的shard
使用listShards命令查看
use admin
db.runCommand( { listShards :1 } )
查看集群的細節
使用db.printShardingStatus,sh.status查看集群的細節
Sh.status返回一下:
Sharding version顯示shard元數據版本
Shards顯示了shard的列表
Database顯示了集群中的所有數據庫
Chunks信息顯示了每個shard有幾個chunk。
9.3.2.2使用相同的hostname遷移config
把config遷移到同名的系統。如果要遷移所有的config那么每個都執行一下過程:
1.關閉這個config server,會影響config數據,只讀
2.修改dns指向新的系統
3.復制老的config的數據到新的系統上
rsync -az /data/configdb/ mongodb.config2.example.net:/data/configdb
4.然后以config服務啟動mongod
mongod --configsvr
當第三個config啟動集群的元數據有可以被寫入(可以遷移chunk和split chunk)
9.3.2.3在不同HostName之前遷移config
這個過程遷移config服務到一個新的不同名的服務器,這樣會修改config的主機名,還會導致下線時間和集群內的所有進程重啟。
如果要遷移所有config,依次按一下過程執行:
1.關閉均衡器
2.關閉config服務,會導致config數據只讀
3.復制老的config的數據到新的系統上
rsync -az /data/configdb/ mongodb.config2.example.net:/data/configdb
4.然后以config服務啟動mongod
mongod --configsvr
5.關閉所有mongodb進程,包括:shard,mongod或者復制集,提供config服務的mongod,mongos實例。
6.重啟所有提供shard服務器的mongod
7.更新mongos的configdb設置
8.重啟mongos
9.重啟均衡器
9.3.2.4替換一個Config服務
這個過程是替換不可用的config服務。這個過程假設替換過程中,hostname不就被修改。
1.關閉均衡器(是否可以不關閉,是為了讓元數據不可寫,但是如果config已經出現不可用,沒有必要。)
2.提供一個新的系統和之前的設備有一樣的hostname
3.關閉其中一個config服務,復制數據到新的設備上
rsync -az /data/configdb/ mongodb.config2.example.net:/data/configdb
4.啟動之前是好的config服務
5.啟動新的config服務
mongod --configsvr
6.啟動均衡器
9.3.2.5遷移集群到不同的硬件環境
這個過程是啟動shard集群到一個新的環境下。沒有宕機時間,遷移狀況下可讀寫。
如果集群下有單節點shard那么先把這個mongod轉變為復制集可以讓你的集群一直在可用狀態下。
關閉均衡器
關閉均衡器來停止chunk遷移,在整個過程中不要有元數據寫入,若均衡器已經啟動了遷移,那么先等待均衡器遷移完,在執行這個過程。sh.stopBalancer()來關閉均衡器。然后可以使用sh.getBalancerStatue()來查看均衡器狀態。
遷移config服務
從mongos的configdb配置的字符串,從最后一個開始遷移。不要重命名config服務。
1.關閉config服務,元數據變成只讀
2.修改DNS指向新的設備
3.復制數據到新的設備上
rsync -az /data/configdb/ mongodb.config2.example.net:/data/configdb
4.然后以config服務啟動mongod
mongod --configsvr
重啟mongos實例
如果configdb修改是遷移的一部分,那么就需要在修改configdb之前關閉mongos。避免出現configdb字符串不一樣的沖突。
如果configdb保持不變,只要順序的遷移就可以了:
1.關閉mongos實例,使用shutdown命令,如果configdb字符串要修改,要關閉所有mongos實例。
2.如果config服務hostname修改,更新所有mongos上configdb字符串。
3.重啟mongos實例確保已經更新了configdb。
遷移Shard
使用一下過程遷移shard
遷移一個復制集:為了遷移整個集群,就需要遷移shard,要遷移shard,就要遷移secondary。
如果是2個vote的集群那么要加一個仲裁,遷移結束后可以把仲裁刪掉。
遷移復制集的一個成員:
1.關閉mongod,確保感覺關閉,可以使用shutdown命令
2.啟動數據文件夾到新的設備上
3.在新的設備上重啟mongod
4.連接到集群的當前primary
5.如果hostname被修改,使用rs.reconfig()重新配置。
cfg=rs.conf()
cfg.members[2].host="pocatello.example.net:27017"
rs.reconfig(cfg)
6.確認復制集配置,rs.config()
7.等待成員恢復,使用rs.status()查看狀態
遷移Shard復制集中的Primary:遷移過程中,當執行選舉的時候才會讓復制集不可用,不能執行讀寫。但是選舉的過程很快。
1.stepDown primary讓secondary上來當primary
2.查看復制集進程,看看是否成功變成secondary,變成secondary之后在執行遷移secondary的步驟。
遷移單實例Shard:單實例shard遷移的時候,先轉化為復制集然后再遷移。
重新啟動均衡器
連接到mongos實例,然后執行sh.setBalancerState(true)
9.3.2.6備份集群元數據
1.關閉均衡器
2.關閉其中一個config
3.備份整個數據文件夾
4.重新啟動config
5.啟動均衡器
9.3.2.7配置均衡器進程行為
均衡器運行在其中一個mongos上,保證chunks在集群中的均衡分布。默認的均衡器的配置已經足夠使用了。管理員也有可以根據應用來重新配置均衡器。
配置均衡器調度窗口
均衡器調度窗口,是均衡器只能在這個窗口時間內調度。
配置默認chunk大小
默認chunk的大小為64MB,當然可以修改chunk的大小,通過修改config數據庫中的settings collection。也可以在mongos啟動是,指定參數--chunkSize來指定chunk的大小。
修改指定Shard的最大存儲大小
最大存儲大小,指定后,如果mapped大小超過了最大存儲大小,均衡器就不會再移入數據這個mapped大小是值所有被映射的數據庫文件的大小。maxsize被保存在config數據庫中的shards collection下。
默認maxsize可以無限大,撐滿所有可用空間為止。這個maxsize可以在添加shard的時候指定也可以手動修改:
1.通過addShard指定,單位MB
db.runCommand( { addshard : "example.net:34008", maxSize : 125 } )
2.修改已存在shard的maxsize
use config
db.shards.update( { _id : "shard0000" }, { $set : { maxSize : 250 } } )
為chunk遷移修改復制集行為
在2.2.1之后_secondaryThrottle變成moveChunk命令的一個參數。_secondaryThrottle可以讓均衡器在遷移是等待secondary完成。
2.4之后,_secondaryThrottle默認啟動。
當啟動之后,secondary會要求寫注意為{w:2}來保證修改至少被寫入一個secondary。
可以通過以下命令來設置_secondaryThrottle:
useconfig
db.settings.update(
{"_id":"balancer"},
{$set:{"_secondaryThrottle":true}},
{upsert:true}
)
9.3.2.8管理均衡器
檢查均衡器狀態
使用sh.getBalancerState()查看均衡器狀態
檢查均衡器鎖(查看均衡器是否活動)
為了查看均衡器進程是否活動可以使用以下過程:
1.連接到mongos
2.切換到config
3.查看config中的locks collection查看均衡器是否活動
db.locks.find({_id:"balancer"}).pretty()
輸出:
{ "_id":"balancer",
"process":"mongos0.example.net:1292810611:1804289383",
"state":2,
"ts":ObjectId("4d0f872630c42d1978be8a2e"),
"when":"Mon Dec 20 2010 11:41:10 GMT-0500 (EST)",
"who":"mongos0.example.net:1292810611:1804289383:Balancer:846930886",
"why":"doing balance round"}
1.從process可以看出均衡器進程運行在mongos0.example.net下
2.state狀態為2 說明在活動中,早期的版本為1
均衡器調度窗口
當數據增長很緩慢,並且遷移會影響性能的時候,可以使用調度窗口來限制均衡器的調度。
1.連接到mongos
2.切換到config數據庫
3.使用一下命令來修改調度器窗口
db.settings.update({_id:"balancer"},{$set:{activeWindow:{start:"<start-time>",stop:"<stop-time>"}}},true)
如:
db.settings.update({_id:"balancer"},{$set:{activeWindow:{start:"23:00",stop:"6:00"}}},true)
刪除均衡器調度窗口
使用一下命令來刪除調度窗口
useconfig
db.settings.update({_id:"balancer"},{$unset:{activeWindow:true}})
關閉均衡器
默認均衡器只有在需要均衡數據的時候運行。也可以使用一下過程關閉均衡器:
1.連接到mongos
2.使用命令sh.setBalancerState(false)來關閉均衡器
3.通過sh.getBalancerState()來獲取均衡器狀態
可以用以下代碼驗證關閉后不再有遷移:
useconfig
while(sh.isBalancerRunning()){
print("waiting...");
sleep(1000);
}
驅動下,關閉均衡器只能修改settings collection
db.settings.update({_id:"balancer"},{$set:{stopped:true}},true)
啟動均衡器
1.使用mongo連接到mongos
2.使用以下任意一種語句來啟用:
sh.setBalancerState(true)
db.settings.update({_id:"balancer"},{$set:{stopped:false}},true)
在備份的時候關閉均衡器
mongodb在遷移時備份會導致數據不一致,所以要在備份時關閉均衡器。可選方法:
1.設置均衡器調度窗口,在窗口外備份
2.手動關閉均衡器
如果關閉均衡器的時候有遷移在處理,那么會先等待遷移完成,然后清理掉之后要遷移的。
要在備份前確認是否有均衡器在運行。
!sh.getBalancerState()&&!sh.isBalancerRunning()
9.3.2.9刪除Shard集群中的Shard
為了刪除shard,必須確認shard中的數據已經被遷移出去了。不要使用一下過程來遷移整個shard集群到一個新的環境。
確保均衡器是啟動的
使用sh.getBalancerState()來查看均衡器是否啟動。
確定要刪除shard的hostname
1.使用listShards查看shard
db.adminCommand( { listShards:1 } )
2.使用sh.status()或者db.printShardingStatus()查看shard
從shard中刪除chunk
使用removeShard命令來抽取shard中的chunk到其他shard。
useadmin
db.runCommand({removeShard:"mongodb0"})
輸出
{
"msg":"draining started successfully",
"state":"started",
"shard":"mongodb0",
"ok":1
}
檢查遷移狀態
查看遷移狀態可以使用一下命令
useadmin
db.runCommand({removeShard:"mongodb0"})
輸出
{
"msg":"draining ongoing",
"state":"ongoing",
"remaining":{
"chunks":42,
"dbs":1
},
"ok":1
}
其中remaining文檔顯示了剩下要遷移的chunks,dbs表示這個數據庫中primary的數據庫個數。
移動非shard數據
在shard環境下,只有primary數據庫有非shard的collection
1.使用一下命令查看,刪除的shard是不是primary
輸出中,databases字段列了所有數據庫和它的primary,以下結果說明products數據庫的primary在mongodb0上:
{ "_id":"products", "partitioned":true, "primary":"mongodb0"}
2.使用movePrimary命令移動primary
db.runCommand({movePrimary:"products",to:"mongodb1"})
這個命令只有當全部處理完之后才會返回
完成遷移
為了清理所有的元數據和完成遷移,再次使用removeShard命令
useadmin
db.runCommand({removeShard:"mongodb0"})
當完成的時候會返回以下結果:
{
"msg":"removeshard completed successfully",
"state":"completed",
"shard":"mongodb0",
"ok":1
}
只有完成到這步的時候,你才能完全的關閉這個shard的進程。
9.3.3 Shard集群的數據管理
創建chunk,split chunk,遷移chunk,修改chunk大小,Tag意向Sharding,管理Shard Tag
,強制唯一鍵,Shard GridFS數據存儲。
9.3.3.1創建Chunks
很多情況下shard集群會自動創建和發布chunk,不需要用戶參與,但是有些場景下,Mongodb不能創建豬狗的chunk或者發布數據不夠快,如:
1.shard一個以前只存儲在primary的collection
2.想要提取大量數據到集群中,這種提取會導致不均衡,特別是單向增長或者單向遞減的數據,會插入到單個chunk中。
這些操作時資源密集型有以下幾個理由:
1.chunk遷移是把chunk里面的數據從這個shard復制到另外一個shard
2.mongodb一次只遷移一個chunk
3.mongodb只會在插入操作后才split
只能在空的collection上split,如果在已經有數據的collection會自動split,如果手動split,會導致無法估計chunk個數和大小,會影響數據均衡的行為。
例,可以使用一下代碼來創建chunk
for ( var x=97; x<97+26; x++ ){
for( var y=97; y<97+26; y+=6 ) {
var prefix = String.fromCharCode(x) + String.fromCharCode(y);
db.runCommand( { split : "myapp.users" , middle : { email : prefix } } );
}
}
9.3.3.2 Split Chunk
如果chunk大小草果chunk size,mongodb會自動的split這個chunk。但是還是會有手動split chunk出現:
1.有大量的數據但是chunk比較少
2.如果想要導入大量的數據,這些數據起初是在單個chunk或shard 上的。
當mongos認為遷移對未來插入性能有好處,均衡器可能會遷移split的chunk到一個新的shard。均衡器不會中斷手動的split,也不會中斷自動的split。
使用sh.status確定當前chunks
使用split命令來split chunk,要不是middle,要不是find也可以使用sh.splitFind(),sh.splitAt()。
在有某個值的chunk進行平均的split
sh.splitFind( "records.people", { "zipcode":"63109" } )
對指定位置進行split,新的chunk的邊界從指定的值開始。
sh.splitAt( "records.people", { "zipcode":"63109" } )
9.3.3.3遷移Chunk
一般chunk遷移都是自動的,但是以下情況可以考慮自動遷移:
1.split空的collection,然后分發他們
2.如果在均衡窗口內無法遷移chunk
使用moveChunk命令來遷移chunk。
db.adminCommand( { moveChunk : "myapp.users",
find : {username : "smith"},
to : "mongodb-shard3.example.net" } )
把包含shard key為 smith的chunk啟動到mongodb-shard3
可以使用如下命令遷移:
var shServer = [ "sh0.example.net", "sh1.example.net", "sh2.example.net", "sh3.example.net", "sh4.example.net" ];
for ( var x=97; x<97+26; x++ ){
for( var y=97; y<97+26; y+=6 ) {
var prefix =String.fromCharCode(x) +String.fromCharCode(y);
db.adminCommand({moveChunk :"myapp.users", find : {email : prefix}, to : shServer[(y-97)/6]})
}
}
2.2版本后,會有個_secondaryThrottle參數當為true時,mongodb會保證對chunk的遷移分發到secondary。
當出現以下錯誤時是有太多游標打開了這個chunk,要不等待游標關閉,要不干掉游標。
The collection's metadata lock is already taken.
9.3.3.4修改Chunk的大小
如果你注意到,自動遷移超過了硬件的處理能力,就可能想要減少chunk大小。修改chunk大小過程:
1.連接到mongos
2.切換到config數據庫
3.使用save命令來保存chunk大小
db.settings.save( { _id:"chunksize", value: <sizeInMB> } )
修改chunk有一下幾個限制:
1.自動split會在插入或者修改的時候發生
2.如果設置了低的chunk,那么需要時間來split所有的chunk
3.split是不能undone的
4.如果增加了chunk的大小,那么只有通過insert,和update讓chunk增長到新的大小。
9.3.3.5 Tag意向Sharding
Mongodb支持通過tag標記一組shard key來指明一個shard或者一組shard。可以通過tag做到以下事情:
1.把數據庫隔離到指定的shard
2.保證相關的數據所在的shard 的位置
行為和操作
tag標記了一組shard key。均衡器通過chunk的shard key遷移到tag的高值比chunk低值大的shard下。
如果collection中的chunks不均衡,均衡器遷移,均衡器遷移tag區間內的chunk到,關聯到這個tag的shard中。
配置tag,關聯到一個或者多個shard之后,集群可能會花一些時間均衡shard中的數據,取決於當前的數據分布。
跨多個tag的chunk
一個chunk包含的shard key可能跨多個tag,為了解決這個問題,均衡器可能會把chunk遷移到比chunk的shard key小的tag上。
如:有2個tag
1.shard key 為100-200的tag NYC
2.shard key為200-300的tag SFO
當有一個chunk要遷移shard key為150-220,那么會被遷移到NYC,因為150到200比到300近。
9.3.3.6管理Shard Tag
標記Shard
使用sh.addShardTag()把tag和指定的shard關聯,一個shard可以有多個shard,多個shard也可以有一個tag。
sh.addShardTag("shard0000", "NYC")
sh.addShardTag("shard0001", "NYC")
sh.addShardTag("shard0002", "SFO")
sh.addShardTag("shard0002", "NRT")
使用sh.renoveShardTag來刪除關聯
sh.removeShardTag("shard0002", "NRT")
標記Shard Key
使用sh.addTagRange()把shard key分配給tag。
1.分配2個數組給NYC
2.分配一個數組給SFO
sh.addTagRange("records.users", { zipcode: "10001" }, { zipcode: "10281" }, "NYC")
sh.addTagRange("records.users", { zipcode: "11201" }, { zipcode: "11240" }, "NYC")
sh.addTagRange("records.users", { zipcode: "94102" }, { zipcode: "94135" }, "SFO")
刪除Tag上的Shard key
use config
db.tags.remove({ _id: { ns: "records.users", min: { zipcode: "10001" }}, tag: "NYC" })
查看存在的shard tag
通過sh.status查看被管理到shard的tag,shard的tag被存在config數據庫shards collection下,通過查詢shards collection查看tag:
use config
db.shards.find({ tags: "NYC" })
tag的shard key組被保存在config的tags collection下,sh.status()也顯示了tag的shard key
use config
db.tags.find({ tags: "NYC" })
9.3.3.7 shard集群強制唯一鍵
唯一約束是在collection中每個文檔有一個唯一的值。對於shard集群來說,唯一約束不能約束整個shard collection,只能約束本地的collection。
如果要保證整個collection的唯一性,有2個選擇:
1.使用shard key 可以的唯一性
2.為shard collection創建非shard的collection來約束唯一性。
Shard key唯一性約束
過程:可以在shardCollection中指定unique保證唯一性
db.runCommand( { shardCollection : "test.users" , key : { email : 1 } , unique : true } );
限制:
1.只能限制一個字段的唯一性
2.可以使用組合的shard key,只能對組合進行唯一約束
很多情況下,最好的shard key是組合的可以允許讀隔離和寫擴展。
其他字段的唯一約束
若不能創建到shard key,或者需要在多個字段強制唯一性,你就必須創建一個代理collection,這個collection包含了到原始文旦的引用和要唯一的字段。
過程:如果要在email字段保持唯一性
{
"_id" : ObjectId("...")
"email" ": "..."
}
_id反應了原始文檔,email是要唯一的字段
如果collection要shard
db.runCommand( { shardCollection : "records.proxy" ,
key : { email : 1 } ,
unique : true } );
如果不需要shard
db.proxy.ensureIndex( { "email" : 1 }, { unique : true } )
在插入數據時的操作:
db = db.getSiblingDB('records');
var primary_id = ObjectId();
db.proxy.insert({
"_id" : primary_id
"email" : "example@example.net"
})
// if: the above operation returns successfully,
// then continue:
db.information.insert({
"_id" : primary_id
"email": "example@example.net"
// additional information...
})
考慮:
1.你的應用程序必須要能夠抓取插入報錯
2.如果代理collection要shard,只能在要保持唯一性的字段作為shard key唯一
3.在多於1個字段唯一性,你必須為每個字段創建代理collection,如果要再一個collection上創建多個唯一性,那么就不能shard
9.3.3.8 Shard GridFS數據存儲
當要shard一個GridFS存儲,考慮一下幾點:(不知道為啥要對file collection和 chunk collection進行shard)
File collection
Chunks Collection
9.3.4 Shard集群Troubleshoot
9.3.4.1 Config數據庫錯誤字符串
Mongos的configdb都要使用同一個字符串,不然會有一下錯誤:
could not initialize sharding on connection
mongos specified a different config database string
9.3.4.2 因為老的config數據導致游標錯誤
如果mongos沒有及時更新元數據的cache:
could not initialize cursor across all shards because : stale config detected
9.3.4.3在移動config服務避免下線
使用DNS名稱來減少下線時間,名稱一改所有成員都要重啟。
9.4 Shard Reference
http://docs.mongodb.org/manual/reference/sharding/
也可以看
http://docs.mongodb.org/manual/reference/