Mongodb中Sharding集羣

隨着mongodb數據量的增多,可能會達到單個節點的存儲能力限制,以及application較大的訪問量也會導致單個節點無法承擔,所以此時需要構建集羣環境,並通過sharding方案將整個數據集拆分成多個更小的chunk,並分佈在集羣中多個mongod節點上,最終達到存儲和負載能力擴容、壓力分流的作用。在sharding架構中,每個負責存儲一部分數據的mongod節點稱爲shard(分片),shard上分佈的數據塊稱爲chunk,collections可以根據“shard key”(稱爲分片鍵)將數據集拆分爲多個chunks,並相對均衡的分佈在多個shards上。

    1)sharding模式將應用的數據訪問操作分散到多個shard上,每個shard只承擔一部分請求;比如read操作只需要訪問持有數據的shard節點即可。

    2)sharding模式也減少每個shard節點的數據存儲量。

 

 

    上圖爲sharded cluster的拓撲結構,它包含三個組件:shards、config servers和query routers:

    1)Shards:存儲節點,爲了提高可用性和數據一致性,通常每個shard是一個“replica set”結構。

    2)Query routers:查詢路由節點,即mongos節點,mongos接收客戶端請求,並負責將operations根據路由規則轉發給合適的shard或者shards,然後再將result返回給客戶端,它起到了一個路由、轉發的作用,類似於proxy層。sharded集羣可以有多個mongos節點,可以均衡客戶端請求。對於Sharded集羣,客戶端(包括shell)訪問數據需要通過mongos,如果直接與shard相連,只能看到一些零碎的數據表。

    3)Config servers:存儲集羣的metadata數據,數據中包含shards節點列表、chunk與數據集的映射關係、lock信息等等;它爲集羣的樞紐部分,mongos使用這些信息將請求路由到特定的shards上,對於production環境,一個集羣必須有3個Condig servers。

    對於測試環境,shard節點可以爲單個mongod,和一個Config server。

 

    數據的分區根據“shard key”,對於每個需要sharding的collection,都需要指定“shard key”(分片鍵);分片鍵必須是索引字段或者爲組合索引的左前綴;mongodb根據分片鍵將數據分成多個chunks,並將它們均勻分佈在多個shards節點上。目前,mongodb支持兩種分區算法:區間分區(Range)和哈希(Hash)分區。

    1)Range分區:首先shard key必須是數字類型,整個區間的上下邊界分別爲“正無窮大”、“負無窮大”,每個chunk覆蓋一段子區間,即整體而言,任何shard key均會被某個特定的chunk所覆蓋。區間均爲作閉右開。每個區間均不會有重疊覆蓋,且互相臨近。當然chunk並不是預先創建的,而是隨着chunk數據的增大而不斷split。(參見下文)

    2)Hash分區:計算shard key的hash值(64位數字),並以此作爲Range來分區,基本方式同1);Hash值具有很強的散列能力,通常不同的shard key具有不同的hash值(衝突是有限的),這種分區方式可以將document更加隨機的分散在不同的chunks上。

     

    Range分區更好的支持range查詢,根據指定的shard key進行range查詢,router可以很簡單的判斷出那些chunks覆蓋此range,並將請求轉發給特定的幾個shards。不過當shard key是單調遞增時,range分區會導致數據分佈不均,因爲在一定時間內,所有write請求(讀取最新數據的read請求)將會映射到一個shard上,即少數shards在某段時間內承載了系統的大部分請求。

    Hash分區正好相反,即使是單調遞增的shard key,它們的Hash值也有較大不同,因此這些數據將會比較隨機的分散在多個chunks上,但是這引入了range查詢的問題,臨近的shard key可能分佈在不同的chunks上甚至是shards上,這意味着range查詢需要訪問所有的shards,特別是在有sort、limit等操作時。

 

    數據的增刪操作以及集羣中增減shards節點,都可能導致數據的分佈不均,不過mongos提供了balancer機制,它可以對chunks進行split(分裂)和遷移,最終動態平衡數據分佈。

    Splitting:一個後臺進程用於避免chunk增長的過大,當chunk尺寸超過指定的chunk size時(默認爲64M,可以命令修改),mongodb將會把此chunk分成等同的2個;inserts和updates操作均可以觸發split,分離時mongodb不會遷移任何數據,也不會對shard產生影響(split之後shard將會修改config server中的metadata,IO通訊方式同“chunk遷移”,參見下文)。

    Balancing:一個後臺線程用於管理chunks遷移,balancer可以運行在任何一個(多個)mongos上;當集羣中collection數據分佈不均時,balancer將把一部分chunks從chunks量最大的shard上遷移到持有量最小的shards上,直到平衡爲止;在chunk遷移時,源shard將會把此chunk數據全部發送給目標shard,在此期間,源shard仍負責接收客戶端的請求(read、write);最終,在config servers上變更chunks的位置信息。如果遷移過程中,發生異常,balancer將會終止此chunk的遷移,chunk也將繼續保留在原來的shard上;當遷移成功後,mongodb將會刪除原來shard上的chunk文件。

    集羣環境可以動態調整,比如數據量增大到一定程度,可以向集羣中增加shard節點;如果數據量緊縮,也可以移除shard;這些過程均會觸發chunks的動態平衡。

 

一、Sharded集羣構成

    1、Shards:上文已知,shards即爲存儲實際數據的mongodb節點,每個shard持有多個chunks。但是有些collection是不需要sharding的,即此collection的數據保存一個shard節點上,也不會被split,我們稱這個節點爲primary shard;一個database中,所有的非sharding類型的collections均會保存在同一個primary shard上;但不同的databases它們的primary shard可能不同。如果你希望修改某個database的primay shard,可以使用“movePrimary”指令。

    在production環境中,每個shard通常是replica set結構,爲了避免數據丟失或者不一致的情況。如果一個shard的replica set中所有的members都失效,這意味着此shard的數據將不可用,但是其他shard仍然可以繼續提供讀寫服務;不過application的查詢需要能夠應對這種問題,如果你希望某個shard失效後仍然可以查詢部分數據,數據缺失是可以接收的,那麼可以在read操作中指定“partial”選項:

Java代碼  收藏代碼
  1. MongoCursor<Document> cursor = collection.find(Filters.eq("name""zhangsan")).batchSize(32)  
  2.                 .limit(32)  
  3.                 .partial(true)  

 

    2、Config servers:配置服務器,Cluster的樞紐部分,用於保存metadata數據;production環境中,需要三個(exactly)config servers,所有的config servers都有效時才能將metadata保存成功;這三個config servers並非replica set結構,它們獨立部署。(參見下文)不過對於測試環境,可以只有一個config server;如果只有一個config server,那麼它將成爲集羣的單點。如果config servers失效,那麼整個集羣也將無法訪問,如果metadata數據丟失,那麼整個集羣將無法使用。

    Config servers將metadata保存在config數據庫中(稍後介紹),mongos實例將會從config server中獲取metadata並在本地緩存,並用於路由reads、writes請求mongodb只會在“chunk遷移之後”、“chunk split之後”纔會修改metadata數據。當需要修改metadata時,協調者(mongos)將會把變更指令發送給三個config servers並獲得它們的響應結果,如果結果不同,則意味着產生了數據不一致的情況,則可能需要人工干預;此時,balancer也將不會執行chunk遷移,mongos也不會執行chunks分裂。

    當mongos啓動時將會從config servers獲取metadata信息,運行時的某些錯誤也會導致mongos重新獲取metadata。此外,config server中還保存了一些locks,我們稍後介紹。

 

    只要有一個config server失效,那麼集羣的metadata都將處於只讀狀態,可以對shards進行數據讀寫,但是chunks分離和遷移將不能進行,知道三個config servers全部有效爲止。如果三個config servers都失效,那麼意味着集羣將不能讀取metadata數據,如果此時重啓mongos,那麼它將不能獲取metadata數據,將無法提供router,直到config servers有效。此外,metadata的數據量非常小,所以這不會對Config servers或者mongos帶來存儲上的壓力。Config server的負載非常小,它對硬件配置要求很低,只需要較少的內存和存儲空間即可。

    prodution環境中,需要三個config servers;如果僅僅爲了測試可以只需要一個。

 

    備註:mongodb 3.2+版本終於調整了Config servers的部署模式(這裏總有問不完的“爲什麼需要三個Config Servers”),放棄了必須使用三個Config Servers的要求;Config Servers可以採用“Replica set”架構模式,且必須使用WiredTiger存儲引擎。這種調整,可以有效的提升config servers的數據一致性,可能利用replica set架構的優點,Config Servers的個數可以擴展到50個節點。不過replica set中,不能有“arbiters”、“delayed”類型的members,且它們的“buildIndexes”必須設定爲true。

 

    3、mongos:routers,本身不保存任何用戶數據,負責轉發客戶端的讀寫請求、對shard的結果進行收集歸併、運行balancer進程、跟蹤split等;通常我們在每個application節點上部署一個mongos,因爲mongos佔用內存極少,幾乎不佔用磁盤,它只需要消耗一定的內存、CPU用於處理數據即可,此外這樣部署application與mongos通信距離最短、效率較高。你可能想在applications與mongos之間搭建提個proxy或者負載均衡器,這種方式是很難實施的,而且可能會帶來很多的問題,proxy需要能夠對mongodb的數據protocol進行編解碼。對於read、write操作,客戶端通常會隨機選擇一個mongos,這在一定程度上提供了簡單的負載均衡;不過對於有Cursor的read操作,在Cursor遍歷期間,請求只會發送給一個mongos,因爲只有那個mongos持有Curosr信息。

 

    如果query中指定了sort,mongos將$orderby參數傳遞到選定的shards上(根據shard key選定chunks和shards),有此database的primary shard負責接收和merge每個shard排序後的結果,並將結果通過mongos返回給客戶端。如果query指定了limit(),那麼mongos將limit傳給指定的shards,每個shards通過limit限定數據返回的條數,最終mongos收到的數據條數可能大於limit,所以mongos需要再次應用limit,然後纔將結果返回給客戶端。如果客戶端使用了skip(),mongos不會將skip參數傳遞給shards,因爲這這對結果篩選沒有幫助,mongos將收到shards尚未skip的數據,然後skip並組裝數據返回給客戶端,主要原因是,每個shard時刻都會有新的數據插入,所以mongos無法提前計算該從何處skip。如果skip和limit同時使用,這稍微簡單一些,mongos將limit + skip的和,作爲limit傳遞給shards,然後和skip一樣,再次在本地執行skip,用於提高查詢的性能。這也要求women,需要skip操作時,儘可能指定limit以提高效率,而且在sharding環境中,使用sort通常是一個比較高耗的操作(儘管shard key索引是有序的)。

 

    對於沒有指定shard key的查詢、update、remove以及“聚合”方法,mongos都會將操作廣播給所有的shards。對於以上非sharded collection,它們的數據會被保存在primary shard上,儘管application可以直接鏈接此shard獲取數據,但是爲了集羣數據訪問的協調性,我們建議仍使用mongos作爲router。

 

二、shard key(分片鍵)

    shark key可以決定collection數據在集羣的分佈,shard key必須爲索引字段或者爲組合索引的左前綴。documents插入成功後,任何update操作都不能修改shard key,否則會拋出異常。我們不能將

“multikey index”作爲shard key。  

 

    Hashed類型分片鍵,只能對單個Filed建立hashed索引;所以選擇分片鍵需要非常慎重,最好它具有較好的“維度”(cardinality,基數),即此字段的重複值較少;單調遞增的字段值作爲Hashed分片鍵是一個不錯的選擇,比如ObjectId或者timestamp。如果對一個空的collection使用Hashed分片鍵,默認情況下mongodb自動在每個shard節點上創建2個空的chunks,不過我們可以在shardCollection指令中指定“numInitialChunks”參數來限定初始化chunks的個數。

 

    在選擇shard key時,需要考慮到應用的需求,讀寫比、以及讀取數據的方式。如果cluster有較大的write請求,極少的read或者update,那麼shard key就需要注意write壓力的分流,儘可能讓write操作分散在多個shards上,比如採用Hashed分區、使用ObjectId(單調遞增)作爲shard key。如果read量很大,只有較少的write,此時需要考慮read的方式,如果通常爲range查詢(比如timestamp > 某個時間),那麼就需要使用Range分區 + 單調遞增的shard key方式(timestamp),如果通常查詢的匹配方式通常爲“相等”比較,那麼採用Hash分區可以獲得更好的性能。

 

    就是高效的查詢方式就是mongos只需要將請求轉發到單個shard上,相反,如果查詢中沒有指定shard key,mongos將會把請求轉發到所有的shards,並且等待它們都返回結果,這種“scatter/gather”方式會導致操作時間很長,通常在“聚合方式”中才會出現。如果查詢時指定的完整的shard key字段(可能爲組合鍵),那麼mongos只會將請求路由到一個shard上;如果查詢指定了shard key字段的最左前綴,那麼mongos將可能將請求路由到少數多個shards,而且覆蓋shard key的字段數量越多,參與查詢的shard個數將越少,這個原理和索引的特性非常類似;比如shard key爲{"zipcode":1,"name":1,"age":1},那麼查詢條件爲{"zipcode":"10010","name":1}將比只使用"zipcode"查詢獲得的性能更高,參與查詢的shard更少。

 

    通常我們應該使用組合字段作爲shard key,除非能夠斷定某單個字段值是“唯一的、不重複的”纔會使用單個字段作爲shard key,最終組合字段必須能夠提高cardinality(降低重複值),這樣對chunk分裂有很大的幫助。

 

三、sharding機制

    1、balancing:如果一個shard上chunks比其他shard更多,即不平衡狀態,那麼mongos將會自動對chunks遷移以達到平衡,balancing的過程不會影響用戶的數據操作。集羣中任何mongos實例都可以啓動balancing線程,默認balancer是開啓狀態;Config 數據庫(Config servers中)中有個lock表,當balancer活躍時,相應的mongos將會嘗試通過修改document方式獲取“lock”,如果獲取“lock”成功,則此mongos負責balancing工作。大家需要注意,mongos實例的本地系統時間會對lock機制帶來影響,需要所有的mongos(包括集羣中的所有shards、config servers)的時間保持一致(ntpd指令)。

    balancer將chunks從持有chunks最多的shard上遷移到持久chunks最少的shard,每次遷移一個,直到集羣相對平衡(最多與最少之間相差不超過2個)。chunks遷移可能會消耗磁盤空間,那些已經遷移出去的chunks不會立即刪除,而是歸檔到一個特定的目錄下,“歸檔”(archive)特性默認是開啓的;此外遷移還會消耗一定的網絡帶寬,或許會影響到性能,影響用戶操作的IO吞吐量。建議每次遷移一個chunk,且只有當“最多”與“最少”的差值達到threshold時纔開始balancer;或者指定一個時間區間,balancer只會在此時間段內纔會遷移chunks。

    threshold:最小化balancing對集羣的影響,只有當shards上“最多”與“最少”chunks個數差值達到閥值時,纔會重新平衡chunks分佈。threshold的值目前沒有辦法修改,當chunks總數< 20時,此值爲2,總數 >= 80時,此值爲8,其他爲4。一旦balancing工作啓動,只有當chunks分佈均衡後纔會停止,即“最多”與“最少”的差值不大於2。

    默認情況下,mongodb會儘可能的耗盡可用磁盤空間,所以我們需要關注mongodb對磁盤的消耗量;不過當向集羣中添加shard節點時,可以指定當前shard允許使用的最大磁盤空間(max size),當shard的磁盤消耗量達到最大值後,balancer將不會向其再遷移chunks,但這不影響此shard上繼續接受write操作。(參見下文addShard指令)

 

    2、chunks遷移過程:

  • balancer向source shard發送“moveChunk”指令。(參見下文moveChunk)
  • source shard開始move指定chunk;在遷移期間,用戶操作仍然會被route到source shard,它仍負責此chunk上的read、write操作。
  • destination shard沒有source所需要的indexes的話,此時構建相應的索引。
  • destination shard開始請求chunk中的documents,並在本地保存。
  • 在此期間,可能此chunk上已經有數據變更了;那麼當chunk數據發送完畢後,destination shard將會同步這些變更數據。
  • 當同步結束後,destination shard將會與Config servers建立鏈接,並在medata中更新此chunk的位置信息。此期間source會阻塞客戶端的write操作。(此後原chunk做下線操作)
  • 此後的read、write請求將會被route到新的shard上;對於舊的chunk,如果其上沒有打開的cursor,則source shard將會刪除它。(默認是移動到歸檔目錄下,位於dbpath下的“moveChunk”目錄)

    最後一步主要是source shard等待cursor關閉並刪除chunk,稱爲“刪除階段”,不過balancer可以不需要等待它結束即可開始下一個chunk的遷移,在一定程度上提高了遷移的效率,可以讓chunks數據儘快遷移完畢,集羣儘快達到均衡。有時候,“刪除階段”可能需要等到很長時間,那麼我們可以指定“_waitForDelete”參數表示等待“刪除階段”的最長時間,超時後balancer將放棄等待轉而開始遷移下一個chunk。

    如果被遷移的chunk尺寸已經超過了設定值或者其持有的documents個數超過最大值(參見此文),它將不能被遷移,需要等待被split後才能遷移。

    _secondaryThrottle(節流,閥門):通常每個shard是一個replica set結構,對於chunk遷移,其實就是destination批量讀取source中的documents並寫入到replica set的過程(primary),這時就涉及到“write concern”問題,即write寫入到多少個secondaries之後才返回。sharding環境中“_secondaryThrottle”參數就是用於控制此特性,默認爲true,表示至少同步給一個secondary,語義等同於write concern中的{w:2},只有chunks中所有的documents同步到至少一個secondary後纔會繼續遷移下一個chunk;可以將此值設置爲false,即關閉“閥門”,默認效果等同於{w : 1},不過此時我們還可以額外的指定“write concern”參數表示documents需要同步到多個secondaries。(運維方式參見下文)

 

    3、spit:默認每個chunk的大小爲64M,我們可以調節此值;較小的chunk可以將使數據分佈的更加均衡,便於遷移,但是帶來的問題就是split更加頻繁,也增加了mongos路由的開支(每個chunk持有的數據量小,每個query意味着需要訪問的chunk個數較多);較大的chunk不便於遷移,但是split次數較少,metadata信息較少,mongos路由簡單,不過如果chunk過大會導致數據分佈不均。不過個人認爲64M還是太小了,建議增加到256M。

    因爲spit操作只會有insert或者update觸發。

 

    4、shark key indexes:前文已經瞭解到,將一個collection開啓sharding時需要指定shard key,不過在此之前,需要創建一個以shard key字段開頭的索引。比如shard key爲{"zipcode" : 1,"username" : 1},那麼需要創建所以{"zipcode" : 1,"username" : 1}或者{"zipcode" : 1,"username" : 1,"others":1...}。

 

四、部署

    我們本機構建一個sharding測試環境,節點部署列表如下:

    1)shard:2個,端口分別爲27018、28018,單節點。(提示,線上環境,至少2個shards,且每個shard都是replica set結構)

    2)config server:1個,端口爲27019。(提示,線上環境,必須三個config servers)

    3)mongos:一個,端口爲27017。(提示,線上環境,隨application節點部署,通常有多個)

    需要注意,我們確保所有同類型的節點的配置一樣(除端口、文件路徑外),以免出現問題。如下配置是基於“測試環境”的,如果爲production,需要將“smallFiles”設置爲true。

 

    1、Config server部署 

Java代碼  收藏代碼
  1. systemLog:  
  2.     quiet: false  
  3.     path: /data/configdb/logs/mongod.log  
  4.     logAppend: false  
  5.     destination: file  
  6. processManagement:  
  7.     fork: true  
  8.     pidFilePath: /data/configdb/mongod.pid  
  9. net:  
  10.     bindIp: 127.0.0.1  
  11.     port: 27019  
  12.     maxIncomingConnections: 65536  
  13.     wireObjectCheck: true  
  14.     ipv6: false   
  15. storage:  
  16.     dbPath: /data/configdb/db  
  17.     indexBuildRetry: true  
  18.     journal:  
  19.         enabled: true  
  20.     directoryPerDB: false  
  21.     engine: mmapv1  
  22.     syncPeriodSecs: 60   
  23.     mmapv1:  
  24.         quota:  
  25.             enforced: false  
  26.             maxFilesPerDB: 8  
  27.         smallFiles: true      
  28.         journal:  
  29.             commitIntervalMs: 100  
  30. operationProfiling:  
  31.     slowOpThresholdMs: 100  
  32.     mode: off  
  33. sharding:  
  34.     clusterRole: configsvr  
 

    大家需要清楚,config server上需要保存數據,比如“config”數據庫,所以需要配置engine的參數;此外比較重要的就是sharding部分,指定clusterRole爲“configsvr”。

Java代碼  收藏代碼
  1. >./mongod -f configsvr.conf  
 

    2、shard節點部署:本例中有2個shard節點,配置文件除了port和dbpath不同之外,其他配置一樣,如下爲shard_0.conf示例:

Java代碼  收藏代碼
  1. systemLog:  
  2.     quiet: false  
  3.     path: /data/shard_0/logs/mongod.log  
  4.     logAppend: false  
  5.     destination: file  
  6. processManagement:  
  7.     fork: true  
  8.     pidFilePath: /data/shard_0/mongod.pid  
  9. net:  
  10.     bindIp: 127.0.0.1  
  11.     port: 27018  
  12.     maxIncomingConnections: 65536  
  13.     wireObjectCheck: true  
  14.     ipv6: false   
  15. storage:  
  16.     dbPath: /data/shard_0/db  
  17.     indexBuildRetry: true  
  18.     journal:  
  19.         enabled: true  
  20.     directoryPerDB: false  
  21.     engine: mmapv1  
  22.     syncPeriodSecs: 60   
  23.     mmapv1:  
  24.         quota:  
  25.             enforced: false  
  26.             maxFilesPerDB: 8  
  27.         smallFiles: true      
  28.         journal:  
  29.             commitIntervalMs: 100  
  30. operationProfiling:  
  31.     slowOpThresholdMs: 100  
  32.     mode: off  
  33. sharding:  
  34.     clusterRole: shardsvr  
  35.     archiveMovedChunks: false  

    配置與config server差不多,需要注意的是clusterRole需要爲“shardsvr”;此外我們設定了“archiveMovedChunks”爲false表示在chunks遷移完成之後直接刪除,否則將chunks移動到“moveChunk”目錄下。

Java代碼  收藏代碼
  1. >./mongod -f shard_0.conf  
  2. >./mongod -f shard_1.conf  
 

    3、mongos部署

Java代碼  收藏代碼
  1. systemLog:  
  2.     quiet: false  
  3.     path: /data/mongos/logs/mongod.log  
  4.     logAppend: false  
  5.     destination: file  
  6. processManagement:  
  7.     fork: true  
  8.     pidFilePath: /data/mongos/mongod.pid  
  9. net:  
  10.     bindIp: 127.0.0.1  
  11.     port: 27017  
  12.     maxIncomingConnections: 65536  
  13.     wireObjectCheck: true  
  14.     ipv6: false   
  15. sharding:  
  16.     configDB: 127.0.0.1:27019  
  17.     chunkSize: 64  
 

    mongos不需要存儲任何數據,所以它不需要配置storage有關的參數,最重要的參數爲configDB,指定congfig servers的地址列表,如果爲多個則已“,”分割。啓動mongos進程:

Java代碼  收藏代碼
  1. >./mongos -f mongos.conf  
 

    4、addShard:上文的配置文件可知,mongos配置了config servers的地址,那麼mongos與config servers可以建立通訊;但是我們尚沒有看到shard節點如何參與到集羣的。在sharding集羣中,提供了addShard指令,我們可以在運行時動態添加shard節點。注意以後幾乎所有的用戶操作,均需要通過mongos,我們通過mongo shell鏈接到mongos並執行如下操作,將shard_0和shard_1添加到sharding集羣中:

Java代碼  收藏代碼
  1. > ./mongo -host 127.0.0.1 -port 27017  
  2. mongos> sh.addShard("127.0.0.1:27018");  
  3. mongos> sh.addShard("127.0.0.1:28018");  
  4. mongos> sh.status();  

 

    我們將兩個“孤立”的shard通過addShard方法添加到sharding集羣,addShard方法接收host地址,如果shard爲replica set結構,那麼需要通過addShard方法將所有的members添加到集羣(複製集架構模式參):

Java代碼  收藏代碼
  1. mongos> sh.addShard("rs0/127.0.0.1:27018");  
  2. mongos> sh.addShard("rs0/127.0.0.1:28018");  

    我們可以通過sh.status()方法查看sharding集羣的狀態,其中包括shards列表信息。

    如果你想限制shard的磁盤使用量,則使用addShard指令來指定maxSize(單位MB)

Java代碼  收藏代碼
  1. >use admin;  
  2. >db.runCommand(addShard:"127.0.0.1:27017",maxSize:1024,name:"shard0000")  
  3. ###如果對已經指定maxSize的shard可以通過如下方式update  
  4. >use config;  
  5. >db.shards.update({_id:"shard0000"},{$set:{maxSize:125}})  

 

    5、開啓sharding

    sharding集羣已經構建完成,接下來需要存儲數據;但是首先需要將Database開啓sharding,否則數據仍然無法在集羣中分佈,即數據庫、collection默認爲non-sharding。對於non-sharding的database或者collection均會保存在primary shard上(概念參見上文),直到開啓sharding纔會在集羣中分佈。

Java代碼  收藏代碼
  1. mongos> use test  
  2. switched to db test  
  3. mongos> sh.enableSharding("test");  
  4. "ok" : 1 }  

 

    此後我們可以對collection開啓sharding,在此之前需要先指定shard key和建立“shard key索引”,我們根據application對數據訪問的模式,來設定shard key,比如我們有一個address表用戶的地址,這個表通常使用zipcode來查詢數據():

Java代碼  收藏代碼
  1. mongos> use test  
  2. switched to db test  
  3. mongos> db.address.createIndex({"zipcode":1,"name":1});  
  4. mongos> sh.shardCollection("test.address",{"zipcode":1,"name":1});  
  5. "collectionsharded" : "test.address""ok" : 1 }  

 

    那麼address表將使用“zipcode”作爲第一維shard key,採用range分區模式,如果某個chunk中的數據到達“max chunk size”時將會根據zipcode分裂成2個chunk;如果某個chunk中所有的documents的zipcode都一樣時,則會使用“name”作爲第二維shard key,仍採用range分區模式(name可以爲字符串,根據其索引排序分裂),將此chunk分裂成2個,我們可以通過sh.status()查看每個chunk的分裂區間:

Java代碼  收藏代碼
  1. "zipcode" : { "$minKey" : 1 }, "name" : { "$minKey" : 1 } } -->> { "zipcode" : 100010"name" : "AACKszLd" } on : shard0001 Timestamp(30)   
  2. "zipcode" : 100010"name" : "AACKszLd" } -->> { "zipcode" : 100010"name" : "ELGCzqPb" } on : shard0001 Timestamp(40)   
  3. "zipcode" : 100010"name" : "ELGCzqPb" } -->> { "zipcode" : 100010"name" : "IVKDDqod" } on : shard0000 Timestamp(41)   
  4. "zipcode" : 100010"name" : "IVKDDqod" } -->> { "zipcode" : 100010"name" : "JhAwtBbT" } on : shard0000 Timestamp(313)   
  5. ...           
  6. "zipcode" : 100010"name" : "zzzqUAyi" } -->> { "zipcode" : { "$maxKey" : 1 }, "name" : { "$maxKey" : 1 } } on : shard0001 Timestamp(37)   

 

    我們可以看出“name”在chunk中是按照字典順序排序的。我們使用“組合shard key”,在一定程度上可以提高選擇性的維度和chunk的可分裂性,如果你是在找不到組合key那麼可以將_id作爲補充字段:

Java代碼  收藏代碼
  1. sh.shardCollection("test.record",{"state":1,"_id":1})  

 

    比如有一個orders表,用於保存用戶的訂單,這個表通常根據用戶id查詢,那麼我們可以對“userid”字段建立hash索引,以及建立hash分區的shard key:

Java代碼  收藏代碼
  1. mongos> db.orders.createIndex({"userid":"hashed"});  
  2. mongos> sh.shardCollection("test.orders",{"userid":"hashed"});  

  

    前文已經提到,如果使用hash分區的話,那麼shard key的重複值一定要儘可能的少,否則這些相同值的document將會保存在同一個shard上,而導致shard無法分裂,從而失去sharding的意義。“_id”字段可以在hash分區時非常有效,可以考慮選用。

 

五、運維

    1、查看集羣信息

    上文中我們提到sh.status()方法,此方法可以查看集羣中有關“database是否開啓sharding”、“primary shard的位置”、“collection的chunks列表”等詳細信息,因爲版面問題,暫不贅言,請參考“db.status()”

    此外,我們在“config”數據庫中也可以看到很多系統自建的collections:

Java代碼  收藏代碼
  1. mongos> use config  
  2. switched to db config  
  3. mongos> show collections;  
  4. actionlog  
  5. changelog  
  6. chunks  
  7. collections  
  8. databases  
  9. lockpings  
  10. locks  
  11. mongos  
  12. settings  
  13. shards  
  14. system.indexes  
  15. tags  
  16. version  

 

    比如我要查看集羣中chunks的列表,那麼可以直接從“chunks”這個collection查詢即可。“collections”表存儲了每個collection的配置信息,“databases”表可以查看是否開啓了sharding,“setttings”表中查看cluster的整體配置信息等等。

 

    2、Balancer配置

    balancer運行在mongos實例上,控制chunks的分佈和遷移,全局只有一個balancer處於active狀態,我們可以通過“sh.getBalancerState()”或者“sh.status()”查看balancer是否開啓,可以通過“sh.getBalancerHost()”查看balancer運行在哪個mongos上。

    1)可以通過sh.setBalancerState(false)來關閉balancer功能。當然可以通過設定爲true開啓balancer。

    2)尅通過sh.startBlancer()或者sh.stopBalancer()來開關閉balancer。同上。

    3)可以通過db.locks.find({_id:"balancer"})查看balancer持有鎖的情況。

    4)可以通過修改settting表中的配置來指定balancer的運行時間區間:

Java代碼  收藏代碼
  1. db.settings.update(  
  2.    { _id: "balancer" },  
  3.    { $set: { activeWindow : { start: "23:00", stop: "6:00" } } },  
  4.    { upsert: true }  
  5. )  

 

    其中start和stop格式爲“HH:mm”,不需要指定日期。修改activeWindow配置時需要確保balancer的state爲true。

 

    3、_secondaryThrottle與waitForDelete

    這兩個參數都與chunk遷移有關,其中_secondaryThrottle表示是對secondary進行節流,默認爲true,其語義等同write concern中的{w:2},即當chunk遷移時(documents複製)destination shard中至少有一個secondary接收成功,balancer纔會繼續進行下一個chunk;不過開發者可以關閉此參數(同{w:1}),同時與write concern一起使用:

Java代碼  收藏代碼
  1. use config  
  2. db.settings.update(  
  3.    { "_id" : "balancer" },  
  4.    { $set : { "_secondaryThrottle" : false ,  
  5.               "writeConcern": { "w""majority" } } },  
  6.    { upsert : true }  
  7. )  

 

    _waitForDelete表示balancer是否等待source shard刪除已經遷移成功的chunk後才繼續處理下一個chunk,默認爲false,即不等待。

Java代碼  收藏代碼
  1. use config  
  2. db.settings.update(  
  3.    { "_id" : "balancer" },  
  4.    { $set : { "_waitForDelete" : true } },  
  5.    { upsert : true }  
  6. )  

 

    4、split

    chunk的分裂通常由mongos於shard配合自動完成,不過有些情況下我們可能希望手動split:

Java代碼  收藏代碼
  1. sh.splitFind("test.address",{"zipcode":"63109"})  
  2. sh.splitAt("test.address",{"zipcode":"63019"})  

    spiltFind語義爲:查找符合條件的第一個document所在的chunk,並將此chunk分裂成相同大小的2份。splitAt語義爲:查找符合條件的第一個document所在的chunk,並以其爲界限分成2個chunk,有可能這兩個chunk大小不等。

 

    5、修改chunk size配置

    chunkSize默認爲64,需要在mongos配置文件中指定,我們也通過指令修改:

Java代碼  收藏代碼
  1. use config  
  2. db.settings.save({_id:"chunksize",value:<sizeInMB>})  

 

 

其他:

1)config數據庫詳解:https://docs.mongodb.org/manual/reference/config-database/,從中我們可以瞭解到sharding集羣的通訊方式。

2)sharding指令和shell方法參考:https://docs.mongodb.org/manual/reference/sharding/

3)http://www.slideshare.net/deysigmarra/mongo-db-shardingguide

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章