MongoDB4.2 分片掃盲說明

jyzhou發表於2021-03-16

說明:

      在掃盲MongoDB相關的一些知識的時候,順手做下筆記。本文將說明分片相關的內容。在比較早之前已經對這些有過說明,可以看MongoDB 分片的原理、搭建、應用分片(sharding)是指將資料庫拆分,將其分散在不同的機器上的過程。將資料分散到不同的機器上,不需要功能強大的伺服器就可以儲存更多的資料和處理更大的負載。其基本思想就是將集合切成塊,把這些塊分散到若干分片裡,並且可以自動切分大塊。每個分片只負責總資料的一部分,最後通過一個均衡器來對各個分片進行均衡(資料遷移)。通過一個名為mongos的路由程式進行操作,mongos知道資料和片的對應關係(通過配置伺服器)。即分片就是一個水平擴容的措施,自帶了代理路由的方法。使用分片的時機:

1,機器的磁碟不夠用了。使用分片解決磁碟空間的問題。
2,單個mongod已經不能滿足寫資料的效能要求。通過分片讓寫壓力分散到各個分片上面。
3,想把大量資料放到記憶體裡提高效能。即通過分片使用分片伺服器自身的資源。

從MongoDB 4.2開始,隨著分散式事務的引入,分片群集上可以使用多文件事務。

知識點:

一、元件(components

MongoDB 分片有3個不同的元件組成:

①:shard: 包含資料的分片節點(MongoDB例項),從MongoDB3.6起,必須將分片作為副本集進行部署,以提供冗餘和高可用性。
②:mongos:查詢路由器,在客戶端應用程式和分片群集之間提供介面,必須用mongos路由器才能與分片群集中的任何集合進行互動。 這包括分片和未分片的集合。
③:config servers:配置伺服器,儲存叢集的後設資料和配置。 從MongoDB 3.4開始,配置伺服器必須部署為副本集(CSRS)。

shard

MongoDB是在集合級別對資料進行分片,將資料分佈在叢集中的各個分片上。資料庫可以包含分片和未分片集合,分片資料在叢集中的分片上分佈,未分片的資料儲存在主分片上, 每個資料庫都有其自己的主分片(包含該資料庫的所有未分片集合)。主分片根據mongos通過在資料量最少( mongos將listDatabase命令返回的totalSize欄位用作選擇條件)的分片來選擇主分片。通過movePrimary來進行主分片的遷移。使用舊副本集的分片部署新的分片群集時,所有現有資料庫將繼續駐留在其原始副本集上,隨後建立的資料庫可以駐留在叢集中的其他分片上。

Config Servers

配置伺服器儲存分片叢集的後設資料,後設資料反映分片叢集中所有資料和元件的狀態以及每個分片上的塊列表和定義塊的範圍。mongos例項會快取此資料,並使用其將讀取和寫入操作路由到正確的分片。 當叢集的後設資料發生更改(拆分塊或新增分片)時會更新快取。 MongoDB還使用配置伺服器來管理分散式鎖。每個分片叢集必須具有自己的配置伺服器。 不要將相同的配置伺服器用於不同的分片群集。

從3.4開始不再支援使用映象mongod例項作為配置伺服器(SCCC),必須將分片作為副本集(CSRS:Replica Set Config Servers)進行部署。要將配置伺服器從SCCC轉換為CSRS,請參閱MongoDB 3.4手冊《將配置伺服器升級到副本集》。配置伺服器的副本整合員必須要求:不是仲裁、沒有延遲、有buildIndexes。

讀寫配置伺服器時,MongoDB使用“多數”的寫入關注(Read Concern 中的"majority")和“多數”的“讀取關注”(Read Concern 中的"majority")級別。

可用性:如果配置伺服器副本和主伺服器失聯,並且無法選擇主伺服器,則叢集的後設資料將變為只讀。分片叢集的讀取和寫入資料不受影響,但不會發生任何塊遷移或塊拆分。如果所有配置伺服器都不可用,則群集可能無法執行。

配置服務中的集合:

  • changelog:分片後設資料更改記錄的儲存集合。如來自變更大塊拆分的記錄:
    MongoDB4.2 分片掃盲說明
    {
     "_id" : "<hostname>-<timestamp>-<increment>",  --標識
     "server" : "<hostname><:port>",        -- 主機名:埠
     "clientAddr" : "127.0.0.1:63381",         -- 客戶端的地址
     "time" : ISODate("2012-12-11T14:09:21.039Z"), --更改時間
     "what" : "split",    --更改型別,dropCollection、dropCollection.start、dropDatabase、dropDatabase.start、moveChunk.start、moveChunk.commit、split、multi-split
     "ns" : "<database>.<collection>",  --更改的名稱空間
     "details" : {         --更改詳細資訊的文件
        "before" : {
           "min" : {
              "<database>" : { $minKey : 1 }
           },
           "max" : {
              "<database>" : { $maxKey : 1 }
           },
           "lastmod" : Timestamp(1000, 0),
           "lastmodEpoch" : ObjectId("000000000000000000000000")
        },
        "left" : {
           "min" : {
              "<database>" : { $minKey : 1 }
           },
           "max" : {
              "<database>" : "<value>"
           },
           "lastmod" : Timestamp(1000, 1),
           "lastmodEpoch" : ObjectId(<...>)
        },
        "right" : {
           "min" : {
              "<database>" : "<value>"
           },
           "max" : {
              "<database>" : { $maxKey : 1 }
           },
           "lastmod" : Timestamp(1000, 2),
           "lastmodEpoch" : ObjectId("<...>")
        }
     }
    }     
    View Code
  • chunks:為叢集中的每一個塊儲存一個文件。儲存分片鍵的值的範圍,這些分片鍵描述了min和max欄位中的塊:
    MongoDB4.2 分片掃盲說明
    {
       "_id" : "mydb.foo-a_\"cat\"",    --塊名
       "lastmod" : Timestamp(2, 1), 
       "lastmodEpoch" : ObjectId("5d8aa9fbe7a2f52c300e8e6f"),
       "ns" : "mydb.foo",     --集合名
       "min" : {               --塊中的最小值
             "animal" : "cat"
       },
       "max" : {              --塊中的最大值
             "animal" : "dog"
       },
       "shard" : "shard0004",   --塊所在的分片
       "history" : [ { "validAfter" : Timestamp(1569368571, 27), "shard" : "shard0004" } ]
    }
    View Code
  • collections:記錄叢集中的每個分片集合資訊,如:records.pets集合資訊
    MongoDB4.2 分片掃盲說明
    {
       "_id" : "records.pets",   --集合名
       "lastmod" : ISODate("1970-01-16T15:00:58.107Z"),   --最後修改時間
       "dropped" : false,    --是否刪除
       "key" : {                 -- 索引名
             "a" : 1
       },
       "unique" : false,      --唯一性
       "lastmodEpoch" : ObjectId("5078407bd58b175c5c225fdc"),
       "uuid" :  UUID("f8669e52-5c1b-4ea2-bbdc-a00189b341da")
    }
    View Code
  • databases:記錄叢集中的每個資料庫資訊,包括是否分片,主分片等,sh.status().Database
    { "_id" : "test", "primary" : "shardA", "partitioned" : true, "version" : { "uuid" : UUID("516a5f79-5eb9-4844-8ee9-b8e9de91b760"), "lastMod" : 1 } }
    { "_id" : "hr", "primary" : "shardA", "partitioned" : false, "version" : { "uuid" : UUID("8e39d61d-6259-4c33-a5ed-bcd2ae317b6f"), "lastMod" : 1 } }
    { "_id" : "reporting", "primary" : "shardB", "partitioned" : false, "version" : { "uuid" : UUID("07c63242-51b3-460c-865f-a67b3372d792"), "lastMod" : 1 } }
  • lockpings:記錄分片群集中的活動元件:
    MongoDB4.2 分片掃盲說明
    { "_id" : "example.com:30000:1350047994:16807", "ping" : ISODate("2012-10-12T18:32:54.892Z") }
    View Code
  • locks:儲存分散式鎖,配置伺服器副本集的主通過將文件插入到locks集合中來獲取鎖,從3.4版開始,狀態欄位將始終具有值2,以防止任何舊的mongos例項執行平衡操作。 
    {
       "_id" : "balancer",    
       "state" : 2,
       "ts" : ObjectId("5be0bc6cb20effa83b15baa8"),
       "who" : "ConfigServer:Balancer",
       "process" : "ConfigServer",
       "when" : ISODate("2018-11-05T21:56:13.096Z"),
       "why" : "CSRS Balancer"
    }

    從3.6版開始,平衡器不再具有lock功能。

  • mongos:記錄叢集關聯的每個mongos例項資訊,每30秒將ping傳送到叢集的所有成員,以便可以驗證mongos是否處於活動狀態
    {
       "_id" : "example.com:27017",     --地址標識
       "advisoryHostFQDNs" : [
          "example.com"
       ],
       "mongoVersion" : "4.2.0",
       "ping" : ISODate("2019-09-25T19:26:52.360Z"),  --上一次ping時間
       "up" : NumberLong(50),   --執行時間
       "waiting" : true
    }
  • settings:包含分片配置:塊大小、平衡器狀態。從MongoDB 4.2開始: 自動切分(Autosplit):要啟用或禁用自動拆分標誌,請使用相應的sh.enableAutoSplit()sh.disableAutoSplit(),如:
    MongoDB4.2 分片掃盲說明
    { "_id" : "chunksize", "value" : 64 }
    { "_id" : "balancer", "mode" : "full", "stopped" : false }
    { "_id" : "autosplit", "enabled" : true }
    View Code
  • shards:記錄集合的每個分片資訊。
    MongoDB4.2 分片掃盲說明
    --單機
    { "_id" : "shard0000", "host" : "localhost:30000", "state" : 1 }
    
    --副本集
    { "_id" : "shard0001", "host" : "shard0001/localhost:27018,localhost:27019,localhost:27020", "state" : 1 }
    
    -- 分片已分配了zone,則此文件將具有一個標籤欄位,其中包含分配給它的區域的陣列
    { "_id" : "shard0002", "host" : "localhost:30002", "state" : 1, "tags": [ "NYC" ] }
    View Code
  • tags:包含分片標籤設定:
    MongoDB4.2 分片掃盲說明
    {
        "_id" : { "ns" : "records.users", "min" : { "zipcode" : "10001" } },
        "ns" : "records.users",
        "min" : { "zipcode" : "10001" },
        "max" : { "zipcode" : "10281" },
        "tag" : "NYC"    --標籤
    }
    View Code
  • version:版本資訊:
  • MongoDB4.2 分片掃盲說明
    { "_id" : 1, "minCompatibleVersion" : 5, "currentVersion" : 6, "clusterId" : ObjectId("5d8bc01a690d8abbd2014ddd") }
    View Code   
  • system.sessions:儲存會話記錄。在mongod或mongos例項上建立會話時,存在於該例項的記憶體中。例項將定期將其快取的會話同步到system.sessions集合。從4.2.7版本開始,MongoDB自動將system.sessions集合拆分為至少1024個塊,並將這些塊均勻地分佈在叢集中的各個分片上。
  • config.transactions:記錄副本集、分片叢集的事務的記錄

mongos 

mongos通過快取配置伺服器的後設資料來跟蹤分片上的資料,將操作從應用程式和客戶端路由到mongod例項。大致的流程:接收 -> 通過配置伺服器確定目標分片 -> 各個目標分片處理  -> mongos合併。如何處理查詢修飾符:

  • 排序(Sorting):如果查詢的結果未排序,則mongos例項將開啟一個結果遊標,該遊標將對分片上的所有遊標進行“輪循”
  • 限制(Limits):如果查詢使用limit遊標方法限制了結果集的大小,則mongos例項將該限制傳遞給分片,然後將該限制重新應用於結果,再將結果返回給客戶端。
  • 跳過(Skips):如果查詢使用skip遊標方法指定了要跳過的記錄數,則mongos無法將跳過傳遞給分片,而是從分片中檢索未跳過的結果,並組裝完整結果時跳過適當數量的文件。

當與limit結合使用時,mongos會將限制加上skip的值傳遞給分片,以提高這些操作的效率。客戶端連線到mongos時,db.isMaster將返回一個包含msg欄位的文件,其中包含字串isdbgrid。mongos的後設資料操作命令可以見手冊

二、分片鍵(shard key

MongoDB使用片鍵在各分片之間進行分發,分片鍵由集合中的非陣列結構的一個或多個欄位組成,不能超過512個位元組。對集合進行分片時需要先選擇一個分片鍵,如果是一個非空集合,分片鍵必須是索引,對於空集合,會自動對分片鍵加上索引。不能在分片後修改分片鍵,分片鍵的選擇會影響叢集的效能和效率。如果查詢不包含分片鍵或複合分片鍵的字首,則mongos將執行廣播操作,查詢分片群集中的所有分片,所以分片鍵的選擇非常重要。即分片鍵確定集合文件在分片中的分佈,分片鍵是集合文件中存在的索引欄位或索引複合欄位。如果刪除了分片鍵的最後一個有效索引,請在分片鍵上重新建立索引來進行恢復。

注意:從MongoDB 4.2開始,可以更新文件的分片鍵值,在MongoDB 4.2之前,文件的分片鍵欄位值是不可變的。更新分片鍵值需要在mongos上執行,並且條件包含完整的分片鍵。

唯一索引(Unique Indexes

不能在雜湊索引上指定唯一約束,對於分片集合,分片鍵上的索引、複合索引(分片鍵是字首)、預設的_id索引可以有唯一屬性。唯一索引約束:

  • 對於要分片的集合,分片集合具有其他唯一索引(非分片鍵),則無法分片該集合。
  • 對於已分片的集合,不能在其他欄位上建立唯一索引。

MongoDB不支援跨分片的唯一索引,除非唯一索引包含完整的分片鍵作為索引字首,MongoDB將在整個鍵而不是單個欄位上強制唯一性。理想的分片鍵允許MongoDB在整個​​叢集中平均分配文件,需要考慮分片鍵的基數頻率單調性

①:分片鍵的基數:分片鍵的基數決定了平衡器可以建立的最大塊數,選擇具有高基數的分片鍵並不能保證在分片群集中均勻分佈資料。如果需要在低基數的鍵上分片,使用和具有較高相對基數的欄位組成複合索引。

②:分片鍵的頻率:表示給定值在資料中出現的頻率。具有低頻的分片鍵並不能保證整個分片群集中的資料均勻分佈。如果訪問頻率很高,則會出現熱點塊,導致該節點成為叢集中的瓶頸。

③:分片鍵的單調性:單調遞增或遞減的分片鍵更有可能將插入內容分佈到群集中的單個分片上。如最大(maxKey)或則最小(minKey),如果資料模型需要對單調更改的鍵進行分片,請考慮使用雜湊分片。

雜湊分片(Hashed Sharding)& 分片策略
使用雜湊索引在分片群集中對資料進行分割槽,雜湊索引計算單個欄位的雜湊值作為索引值,作為分片鍵,提供了更均勻的資料分佈。選擇作為雜湊分片鍵的欄位應具有良好的基數或大量不同的值。 雜湊鍵非常適合具有欄位像ObjectId值或時間戳那樣單調的分片鍵。基於該策略分發有助於資料更均勻,但基於範圍的查詢不太可能針對單個分片,從而導致更多的分片操作。

建立雜湊索引: 

db.orders.createIndex( { _id: "hashed" } )

建立雜湊分片:

sh.shardCollection( "database.collection", { <field> : "hashed" } )

MongoDB雜湊索引不支援大於253的浮點值,從版本4.0開始,mongo shell提供了convertShardKeyToHashed來檢視片鍵鍵的雜湊值。

範圍分片(Ranged Sharding

基於範圍的分片是預設的分片方法,該分片將資料按照分片鍵值分為連續範圍。 對範圍讀取比較有效,但如果分片鍵選擇不佳,讀取和寫入效能均可能降低:比如某範圍的資料比其他的多出很多。對於大範圍、低頻率、非單調性的片鍵效率比較高。基於該策略分發附近的資料在同一塊物理空間上的可能性更高。

建立範圍分片:

sh.shardCollection( "database.collection", { <shard key> } )

針對分片鍵的單調性,範圍分片和雜湊分片的區別:

  • 範圍分片會導致限制插入的分佈:插入只在一個塊的單個分片,減少或消除了分片叢集分散式寫入的優勢。
  • 雜湊分片在資料分佈更加均勻,可以在整個群集中高效地分佈插入內容。

分片集合有二種情況:對已有集合和空集合的分片。

  • 已有集合分片

    分片操作將建立初始塊,以覆蓋分片鍵值的整個範圍。在初始塊建立之後,平衡器會在分片上適當地遷移這些初始塊,並管理日後的塊分配。

  • 空集合分片

    分片操作將建立一個空塊,以覆蓋分片鍵值的整個範圍。在建立初始塊之後,平衡器將在塊之間適當地遷移初始塊,並管理後續的塊分配。

區域(Zones

基於分片鍵建立分片區域,將每個區域與叢集中的一個或多個分片關聯,分片可以與任意數量的區域關聯。在平衡任務中,MongoDB僅將區域覆蓋的塊遷移到與該區域關聯的分片。在定義要覆蓋的區域的新範圍時,必須使用分片鍵中包含的欄位。 如果使用複合分片鍵,則範圍必須包含分片鍵的字首。常見部署模式:

  • 隔離特定的資料子集。
  • 根據分片硬體/效能將資料路由到分片。
  • 最相關的資料留在地理上最靠近應用程式伺服器的分片上。

定義範圍:MongoDB提供了相關的方法sh.updateZoneKeyRangesh.addTagRange)和sh.addShardToZonesh.addShardTag) 。從MongoDB 4.0.2開始,可以在未分片的集合或不存在的集合上執行,刪除集合將刪除其關聯的區域/標籤範圍。

給區域新增分片:連線mongos例項,可使用sh.addShardToZonesh.addShardTag)新增,將區域與特定分片相關聯。也可以使用sh.removeShardFromZonesh.removeShardTag)刪除,將刪除分片中的區域。單個分片可以具有多個區域,並且多個分片也可以具有相同的區域。如:將區域NYC新增到兩個分片,並將區域SFO和NRT新增到第三個分片:

sh.addShardToZone("shard0000", "NYC")
sh.addShardToZone("shard0001", "NYC")
sh.addShardToZone("shard0002", "SFO")
sh.addShardToZone("shard0002", "NRT")

將從分片中刪除NRT區域:

sh.removeShardFromZone(“ shard0002”,“ NRT”)

區域定義好之後,需要定義每個區域的分片範圍:連線到mongos例項,使用sh.updateZoneKeyRangesh.addTagRange)方法。 任何給定的分片鍵範圍只能分配一個區域, 範圍不能重疊。如:

新增區域範圍:

sh.updateZoneKeyRange("records.users", { zipcode: "10001" }, { zipcode: "10281" }, "ABC")   --分片鍵zipcode範圍 10001 到 10281 分配給區域ABC
sh.updateZoneKeyRange("records.users", { zipcode: "11201" }, { zipcode: "11240" }, "ABC")
sh.updateZoneKeyRange("records.users", { zipcode: "94102" }, { zipcode: "94135" }, "CBA")

刪除區域範圍:sh.removeRangeFromZone(sh.removeTagRange)

sh.removeRangeFromZone("records.user", {zipcode: "10001"}, {zipcode: "10281"})

檢視存在區域:使用sh.status或則檢視集合:

use config
db.shards.find({ tags: "NYC" })  --返回帶有NYC區域的所有分片

檢視區域範圍:使用sh.status或則檢視集合:

use config
db.tags.find({ tag: "NYC" })    --返回與NYC區域關聯的任何範圍

基於位置的片鍵的流程:可用於將特定範圍的塊出現在特定的分片中

① 為減少對效能的影響,可以在集合上禁用平衡器,以確保在配置新區域時不進行任何遷移。

sh.disableBalancing("db.collection")

使用sh.isBalancerRunning檢查平衡器是否關閉。

分片新增到適當的區域

sh.addShardTag(<shard name>, "NA")  --分片新增到NA區域
sh.addShardTag(<shard name>, "EU")  --分片新增到EU區域

 定義每個區域的範圍,使用sh.addTagRange方法將其與NA區域相關聯:

sh.addTagRange(
  "chat.messages",   --集合名稱
  { "country" : "US", "userid" : MinKey },   --下限
  { "country" : "US", "userid" : MaxKey },  --上限
  "NA"  --區域名稱
)

啟用平衡器:

sh.enableBalancing("db.collection")

使用sh.isBalancerRunning檢查平衡器是否關閉。

如果需要更新某個區域的名字和範圍,則需要先刪除區域,再重新新增,刪除:

sh.removeTagRange(
  "chat.messages",
  { "country" : "UK", "userid" : MinKey },
  { "country" : "UK", "userid" : MaxKey }
  "EU"
)

最後通過sh.status檢視這個分片的資訊。從MongoDB 4.0.3開始,對空集合或不存在的集合進行分片之前設定區域和區域範圍可以更快地設定區域分片。關於基於區域位置的分片可以用於很多場景,比如寫資料到一個效能好的分片(ssd),再通過平衡器來進行自動平衡到其他分片;使用者按照分片鍵寫到離自己近的資料中心等等,關於更多的區域例子可以看官方文件 

三、塊(Chunks

MongoDB通過分片鍵把集合分成多有上下限的塊,根據設定的大小可以進行塊自動拆分,最後按照各個分片的塊多少進行平衡(遷移)。mongos根據分片鍵值寫入塊,當超出配置的塊大小時,會進行拆分,插入和更新都可以觸發塊拆分。

分片操作會建立初始塊,並覆蓋分片鍵值的整個範圍,建立的塊數取決於配置的塊大小,平衡器會在分片上適當的遷移這些初始塊,並管理日後的塊分配。

預設塊大小為64 MB,塊的大小會影響遷移的每個塊的最大文件數,小塊資料導致更均勻的資料分佈,但代價是遷移頻率更高;大塊導致更少的遷移,但以不均勻資料分佈為代價。

拆分:資料塊增長到超過指定的資料塊大小或者如果該資料塊中的文件數超過了每塊要遷移的最大文件數,插入和更新可能會觸發拆分。 即超過預設塊大小(預設塊大小)64 MB的塊,該塊觸發將塊拆分為兩個塊(拆分是後設資料更改)。

遷移:拆分導致各個分片資料不均衡,使用平衡器進行均衡遷移各個節點的塊。遷移有2種方式:

  • 手動:手動執行遷移,見手冊
  • 自動:當分片集合的塊在分片中分佈不均時,平衡器程式會自動遷移分塊。見手冊

平衡器是管理塊遷移的後臺程式,如果最大和最小分片之間的塊數差異超過遷移閾值,則平衡器將開始在整個群集中遷移塊,以確保資料的均勻分佈。

大塊(Jumbo Chunks):塊超出指定的塊大小,但無法進行拆分。最常見的情況是大塊表示單個分片鍵值。由於塊無法拆分,因此它將繼續增長,超出塊大小,成為巨型塊。

moveChunk目錄:sharding.archiveMovedChunks 預設情況下禁用,除MongoDB 2.6 和3.0版本。表示源分片會將文件中遷移的塊中的文件存在以storage.dbPath中的moveChunk目錄下的集合名稱空間命名的目錄中。

操作:

建立塊:大多數情況下,分片群集將自動建立/拆分和分發塊,而無需使用者干預。

分割塊:如果塊超過上限大小,則MongoDB會在插入後拆分塊。 但是遇到不可分的大塊則需要手動拆分塊: sh.splitFind() 和 sh.splitAt()

合併塊mergeChunks命令允許將同一分片上的連續塊合併為一個塊。

修改塊:預設塊大小為64 MB,適用於大多數部署。 如果自動遷移的I/O超出了硬體的處理能力,則可能需要減小塊的大小。 對於自動拆分和遷移,較小的塊大小會導致更快速,更頻繁的遷移。 塊大小的允許範圍在1M到1024M之間。

修改塊的限制:

  • 自動拆分僅在插入或更新時發生。
  • 如果減小塊大小,則所有塊可能都需要花費一些時間才能拆分為新的大小。
  • 塊拆分無法撤消。
  • 如果增加塊大小,則現有塊僅通過插入或更新來增長,直到達到新大小為止。
  • 塊大小的允許範圍在1M到1024M之間。

四、平衡(Balancer

平衡器是後臺程式,用於監視每個分片上的塊數。 當給定分片上的塊數達到特定的遷移閾值時,將自動在分片之間遷移塊,並使每個分片上的塊數均相等。過程對於使用者和應用程式是完全透明的,可能在進行過程中可能會對效能產生一些影響。預設情況下,平衡器程式始終處於啟用狀態。注意的是平衡器一次只能遷移一個塊。

從MongoDB 3.4開始,平衡器在配置伺服器副本集(CSRS)的主資料庫上執行:

在3.4中,當啟用平衡器程式時,配置伺服器副本集的主資料庫通過修改Config資料庫的locks集合中的_id:“ balancer”文件來獲取“balancer lock”。 
從3.6開始,平衡器不再具有“lock”功能。

塊遷移可能會影響磁碟空間,因為預設情況下,源分片會自動存檔遷移的文件。並帶來一些頻寬和負載方面的開銷,這兩者都會影響資料庫效能。

MongoDB 3.4開始,可以執行並行塊遷移,遵守分片每次最多隻能參與一個遷移的限制,對於具有n個分片的叢集,最多可以執行n/2(向下舍入)的並行塊遷移。

新增節點刪除節點會造成不平衡,因為新分片沒有任何塊或則刪除的分片需要把塊重新分配到整個叢集中。 當MongoDB立即開始將資料遷移到新的分片或遷移刪除分片上的資料時,群集平衡可能需要一些時間才能達到平衡。

塊遷移程式過程:

1:balancer程式將moveChunk命令傳送到源分片。
2:源分片使用moveChunk命令開始移動,在遷移過程中,對塊的操作將路由到源分片。源分片還是負責該塊的操作。
3:目標分片將構建源中所需結構。
4:目標分片開始請求源塊中的文件,並開始接收資料的副本
5:在接收到塊中的最終文件之後,目標分片將啟動同步過程,以確保具有對遷移過程中發生的已遷移文件的更改。
6:完全同步後,源分片將連線到配置資料庫,並使用塊的新位置來更新叢集後設資料。
7:在源分片完成後設資料的更新之後,並且一旦塊上沒有開啟的遊標,源分片就會刪除其文件副本。

遷移閾值

為了最大程度地減少遷移對叢集的影響,平衡器僅在分片集合的塊分配達到某些閾值之後才開始平衡。 閾值適用於集合中具有最多塊的分片與該集合中具有最少塊的分片之間的塊數差異。 平衡器具有以下閾值:

Number of Chunks    Migration Threshold
Fewer than 20        2
20-79             4
80 and greater         8

第一個值為總塊數,第二個位分片節點之間塊的差值

注意:如果塊中的文件數大於配置的塊大小除以平均文件大小的結果的1.3倍,則無法移動塊。 db.collection.stats包含avgObjSize欄位,該欄位表示集合中的平均文件大小。

平衡器管理在版本3.4中進行了更改:平衡器程式已從mongos例項移至配置伺服器副本集的主成員。

① 檢查平衡器狀態sh.getBalancerState() ,sh.status()

檢查平衡器是否執行:登入mongos執行 sh.isBalancerRunning()

修改塊大小:分片群集的預設塊大小為64 MB,更改預設塊大小會影響在遷移和自動拆分過程中正在處理的塊。

1:連線mongos
2use config
3:db.settings.save( { _id:"chunksize", value: <sizeInMB> } )

④:設定平衡視窗時間

1:連線mongos
2use config
3:確保平衡器啟用:
sh.startBalancer() -- 從MongoDB 4.2開始,sh.startBalancer()還為分片群集啟用自動拆分。
4:修改平衡器的視窗:
db.settings.update(
   { _id: "balancer" },
   { $set: { activeWindow : { start : "<start-time>", stop : "<stop-time>" } } },
   { upsert: true }
)
--使用指定兩位數的小時和分鐘值(即HH:MM)的時間值替換<start-time>和<end-time>,這些值指定了平衡視窗的開始和結束邊界。

--對於HH值,請使用00到23之間的小時值。
--對於MM值,請使用00-59之間的分鐘值。
注意:時間為當前伺服器所在的時區

注意:設定平衡器視窗時,請勿使用sh.startBalancer方法。

取消平衡視窗時間

1:登入mongos
2use config
3:執行 db.settings.update({ _id : "balancer" }, { $unset : { activeWindow : true } })

⑥ 禁用平衡器:連線mongos執行 sh.stopBalancer(); 如果正在進行遷移,則系統將在停止之前完成正在進行的遷移。從MongoDB 4.2開始,sh.stopBalancer還會禁用分片群集的自動拆分。使用sh.getBalancerState()驗證是否禁用。要從驅動程式禁用平衡器,請對admin資料庫使用balancerStop命令:db.adminCommand( { balancerStop: 1 } )。備份期間禁用平衡,不然在塊遷移期間備份導致資料不一致。

啟用平衡器:連線mongos執行sh.startBalancer();要從驅動程式啟用平衡器,請對admin資料庫使用balancerStart命令:db.adminCommand( { balancerStart: 1 } )。從MongoDB 4.2開始,sh.startBalancer還為分片群集啟用自動拆分。

禁用\啟用集合的平衡器:sh.disableBalancing("db.collection")、sh.enableBalancing("db.collection"),驗證是否禁用或則啟用:

db.getSiblingDB("config").collections.findOne({_id : "students.grades"}).noBalance;
返回:
空:表示集合名稱空間不正確。
true:則禁用平衡。
false:則當前已啟用平衡,但過去已對該集合禁用平衡。平衡器的平衡將在下一次平衡器執行時開始。
未返回:當前已啟用平衡,並且此集合以前從未禁用過平衡。平衡器的平衡將在下一次平衡器執行時開始。

⑨ 更改塊遷移的複製行為

_secondaryThrottle確定何時對塊中的下一個文件進行遷移

use config
db.settings.update(
   { "_id" : "balancer" },
   { $set : { "_secondaryThrottle" : { "w": "majority" }  } },
   { upsert : true }
)

返回:

ture:遷移繼續進行塊中的下一個文件之前,在塊遷移期間,每個文件移動都必須至少從一個輔助節點接收到確認,這等效於{w:2}。

false: 遷移過程將不等待複製到輔助節點,而是繼續下一個文件。

Wait for Delete 影響平衡器如何從分片遷移多個塊。預設情況下,平衡器在開始下一個塊遷移之前不會等待正在進行的遷移的刪除階段完成。要使刪除階段阻止下一次塊遷移的開始,可以將_waitForDelete設定為true。

use config
db.settings.update(
   { "_id" : "balancer", "_waitForDelete": true },
   { $unset : { "_waitForDelete" : "" } }
)

⑩ 更改給定分片的最大儲存大小:預設情況下,分片對儲存大小沒有任何限制。但可以在分片群集中為給定分片設定最大儲存。選擇目標分片時,平衡器將忽略遷移超過配置的最大儲存大小的分片。建立maxSize欄位,併為其分配一個整數值。 maxSize欄位表示分片的最大儲存大小(以M為單位):

設定一個最大大小為一個1024 MB的分片:

config = db.getSiblingDB(“ config”)
config.shards.updateOne({“ _id”:“ <shard>”},{$ set:{“ maxSize”:1024}})

該值包括分片上所有資料檔案(包括本地和管理資料庫)的對映大小。也可以在新增分片時設定maxSize:

config = db.getSiblingDB("config")
config.runCommand( { addshard : "example.net:34008", maxSize : 125 } )

五、Config DB說明

訪問配置資料庫並檢視支援分片操作的集合列表,需要連線到mongos例項。config資料庫主要供內部使用,不要修改該資料庫。在配置資料庫中使用以下集合來支援分片:

  • config.changelog:為每次分片集合更改後設資料儲存一個文件。如:
    MongoDB4.2 分片掃盲說明
    {
     "_id" : "<hostname>-<timestamp>-<increment>",  --操作ID標識
     "server" : "<hostname><:port>",    --存放資料的伺服器的主機名
     "clientAddr" : "127.0.0.1:63381",     --客戶端的地址
     "time" : ISODate("2012-12-11T14:09:21.039Z"),  --更改發生時間的ISODate時間戳
     "what" : "split",   --記錄的更改型別
     "ns" : "<database>.<collection>",  --更改的名稱空間
     "details" : {   --有關更改的詳細資訊
        "before" : {
           "min" : {
              "<database>" : { $minKey : 1 }
           },
           "max" : {
              "<database>" : { $maxKey : 1 }
           },
           "lastmod" : Timestamp(1000, 0),
           "lastmodEpoch" : ObjectId("000000000000000000000000")
        },
        "left" : {
           "min" : {
              "<database>" : { $minKey : 1 }
           },
           "max" : {
              "<database>" : "<value>"
           },
           "lastmod" : Timestamp(1000, 1),
           "lastmodEpoch" : ObjectId(<...>)
        },
        "right" : {
           "min" : {
              "<database>" : "<value>"
           },
           "max" : {
              "<database>" : { $maxKey : 1 }
           },
           "lastmod" : Timestamp(1000, 2),
           "lastmodEpoch" : ObjectId("<...>")
        }
     }
    }
    View Code
  • config.chunks:為叢集中的每個塊儲存一個文件,如:
    MongoDB4.2 分片掃盲說明
    {
       "_id" : "mydb.foo-a_\"cat\"",  --塊的id,有資料庫.集合-索引名_塊中最小值
       "lastmod" : Timestamp(2, 1),
       "lastmodEpoch" : ObjectId("5d8aa9fbe7a2f52c300e8e6f"),
       "ns" : "mydb.foo",   --集合名稱空間
       "min" : {                 --塊的最小值(包含)
             "animal" : "cat"
       },
       "max" : {                --塊的最大值
             "animal" : "dog"
       },
       "shard" : "shard0004",    --所屬分片
       "history" : [ { "validAfter" : Timestamp(1569368571, 27), "shard" : "shard0004" } ]
    }            
    View Code
  • config.collections:為叢集中的每個分片集合儲存一個文件,如:
    MongoDB4.2 分片掃盲說明
    {
       "_id" : "records.pets",   --集合名
       "lastmod" : ISODate("1970-01-16T15:00:58.107Z"),
       "dropped" : false,      
       "key" : {          --索引
             "a" : 1
       },
       "unique" : false,   --是否唯一索引
       "lastmodEpoch" : ObjectId("5078407bd58b175c5c225fdc"),
       "uuid" :  UUID("f8669e52-5c1b-4ea2-bbdc-a00189b341da")
    }
    View Code
  • config.databases為叢集中的每個資料庫儲存一個文件,也可以通過sh.status()中的Database來獲得。如:
    { "_id" : "test", "primary" : "shardA", "partitioned" : true, "version" : { "uuid" : UUID("516a5f79-5eb9-4844-8ee9-b8e9de91b760"), "lastMod" : 1 } }
    { "_id" : "hr", "primary" : "shardA", "partitioned" : false, "version" : { "uuid" : UUID("8e39d61d-6259-4c33-a5ed-bcd2ae317b6f"), "lastMod" : 1 } }
    { "_id" : "reporting", "primary" : "shardB", "partitioned" : false, "version" : { "uuid" : UUID("07c63242-51b3-460c-865f-a67b3372d792"), "lastMod" : 1 } }

    _id:庫名;
    primary:主分片位置
    partitioned:是否分片
    version:版本

  • config.lockpings:跟蹤分片群集中的活動組,如:ping
    { "_id" : "example.com:30000:1350047994:16807", "ping" : ISODate("2012-10-12T18:32:54.892Z") }
  • config.locks:儲存分散式鎖。 配置伺服器副本集的主通過將文件插入到locks集合中來獲取鎖,如:
    MongoDB4.2 分片掃盲說明
    {
       "_id" : "test.myShardedCollection",   
       "state" : 2,   --防止任何舊式mongos例項執行平衡操作
       "process" : "ConfigServer",
       "ts" : ObjectId("5be0b9ede46e4f441a60d891"),
       "when" : ISODate("2018-11-05T21:52:00.846Z"),  --指定配置伺服器成員成為主伺服器的時間
       "who" : "ConfigServer:Balancer",
       "why" : "Migrating chunk(s) in collection test.myShardedCollection"
    }
    View Code

    從3.6版開始,平衡器不再具有“lock”功能。 如果從3.4升級到3.6,則可以選擇刪除任何剩餘的“ _id”:“ balancer”文件。

  • config.mongos:為與叢集關聯的每個mongos例項儲存一個文件,如:
    MongoDB4.2 分片掃盲說明
    {
       "_id" : "example.com:27017",
       "advisoryHostFQDNs" : [
          "example.com"
       ],
       "mongoVersion" : "4.2.0",
       "ping" : ISODate("2019-09-25T19:26:52.360Z"),  --顯示上次ping的時間
       "up" : NumberLong(50),    --截至上次ping的mongos的正常執行時間
       "waiting" : true
    }
    View Code

    mongos例項每30秒將ping傳送到群集的所有成員,以便群集可以驗證mongos是否處於活動狀態。

  • config.settings:包含分片配置,也可以設定。如:更改塊大小,更改平衡器狀態
    MongoDB4.2 分片掃盲說明
    { "_id" : "chunksize", "value" : 64 }    --塊大小
    { "_id" : "balancer", "mode" : "full", "stopped" : false }  --平衡器設定
    { "_id" : "autosplit", "enabled" : true }   --是否自動切大塊
    View Code

    從MongoDB 4.2開始:

    balancerStart 為分片群集啟用自動拆分。
    balancerStop 為禁用分片群集的自動拆分。
    請使用相應的 sh.enableAutoSplit()方法或sh.disableAutoSplit()方法。

  • config.shards:表示叢集中的每個分片,如:
    MongoDB4.2 分片掃盲說明
    -- 單節點分片
    { "_id" : "shard0000", "host" : "localhost:30000", "state" : 1 }
    
    
    --副本集分片
    { "_id" : "shard0001", "host" : "shard0001/localhost:27018,localhost:27019,localhost:27020", "state" : 1 }
    
    
    --分配了區域分片
    { "_id" : "shard0002", "host" : "localhost:30002", "state" : 1, "tags": [ "NYC" ] }
    View Code
  • config.tags:包含叢集中每個區域範圍的文件,如:
    {
        "_id" : { "ns" : "records.users", "min" : { "zipcode" : "10001" } },
        "ns" : "records.users",
        "min" : { "zipcode" : "10001" },
        "max" : { "zipcode" : "10281" },
        "tag" : "NYC"
    }
  • config.version:儲存當前的後設資料版本號,如:
    MongoDB4.2 分片掃盲說明
    { "_id" : 1, "minCompatibleVersion" : 5, "currentVersion" : 6, "clusterId" : ObjectId("5d8bc01a690d8abbd2014ddd") }
    
    --或執行db.getCollection("version").find()
    View Code
  • config.system.sessions:所有成員使用的會話記錄,在mongod或mongos例項上建立會話時,該會話的記錄最初僅存在於該例項的記憶體中,例項將定期將其快取的會話同步到system.sessions集合,預設會對system.sessions集合進行分片。從4.2.7版本開始,MongoDB自動將system.sessions集合拆分為至少1024個塊,並將這些塊均勻地分佈在叢集中的各個分片上。
  • config.transactions:儲存用於支援副本集和分片群集的可重試寫入和事務的記錄。

六、命令方法說明

mongo Shell 的分片方法:

NameDescription
sh.addShard() 給叢集新增一個節點。
sh.addShardTag() 別名為sh.addShardToZone(),給分片節點定義區域標籤,將分片與區域關聯。 
sh.addShardToZone() 同上
sh.addTagRange() 別名為sh.updateZoneKeyRange(),給分片標籤新增鍵範圍,將一系列分片鍵與區域標籤關聯
sh.disableBalancing() 禁用平衡器;可以在單個集合上禁止,不影響其他集合。
sh.enableBalancing() 開啟平衡器
sh.disableAutoSplit() 禁用分片叢集的自動拆分.
sh.enableAutoSplit() 開啟分片群集的自動拆分
sh.enableSharding() 指定一個庫開啟分片
sh.getBalancerHost() 自MongoDB 3.4起不推薦使用,獲取平衡器主機
sh.getBalancerState() 是否啟用均衡器
sh.removeTagRange() 別名sh.removeRangeFromZone(),移除分片鍵指定標籤區域的範圍
sh.removeRangeFromZone() 同上
sh.help() 返回幫助文件
sh.isBalancerRunning() 返回平衡器程式當前是否正在遷移塊
sh.moveChunk() 遷移分片群集中的塊
sh.removeShardTag() 別名為sh.removeShardFromZone(),刪除叢集的標籤區域
sh.removeShardFromZone() 同上
sh.setBalancerState() 啟用或禁用在分片之間遷移塊的平衡器
sh.shardCollection() 為一個集合開啟分片
sh.splitAt() 切塊,使用分片鍵的特定值作為分割點,將現有的塊分為兩個塊。
sh.splitFind() 將包含與查詢匹配的文件的現有塊分為兩個大致相等的塊
sh.startBalancer() 啟用平衡器並等待平衡開始
sh.status() 報告叢集狀態,同 db.printShardingStatus().
sh.stopBalancer() 禁用平衡器,並等待任何進行中的平衡完成
sh.waitForBalancer() 等待平衡器狀態更改
sh.waitForBalancerOff() 等待直到平衡器停止執行
sh.waitForPingChange() 等待分片群集中的一個mongos的ping狀態更改
sh.updateZoneKeyRange() 將一系列分片鍵與區域標籤關聯,同sh.addTagRange()
convertShardKeyToHashed() 返回一個雜湊值

 

1. sh.addShard(<url>):將分片副本集或單節點新增到分片叢集。 在mongos例項上執行:

sh.addShard("<replica_set>/<hostname><:port>,<hostname1><:port1>")
sh.addShard("<hostname><:port>")
db.runCommand( { addShard: "repl0/mongodb3.example.net:27327"} )

mongos對addShard命令及sh.addShard()使用"majority" 。將分片新增到分片叢集時,會影響所有現有分片集合的叢集分片之間的塊平衡,而塊遷移也會影響到叢集效能,所以要麼禁用平衡器新增節點,要麼就在低峰期新增節點。是addShard命令的包裝,包含了其他引數,如:maxSize。

2. sh.addShardTag(shard,tag):同sh.addShardToZone(shard, zone)將分片與標籤或區域相關聯。MongoDB使用標籤將屬於標記範圍內的塊定向到特定的分片。在mongos例項上執行:

sh.addShardTag("shard0000", "NYC")
sh.addShardTag("shard0001", "LAX")
sh.addShardTag("shard0002", "NRT")

db.adminCommand( { addShardToZone : "shard0000" , zone : "JFK" } )

一個分片可以有多個標籤,區域。覆蓋的塊將分配給與該表現或區域關聯的分片。

3. sh.addShardToZone(shard, zone):同上,3.4版中的新功能:將分片與區域相關聯。區域覆蓋的塊將分配給與該區域關聯的分片。可以將一個區域與多個分片關聯,而一個分片可以與多個區域關聯,在mongos例項上執行:

sh.addShardToZone("shard0000", "JFK")
sh.addShardToZone("shard0001", "LAX")
sh.addShardToZone("shard0002", "NRT")

-- 分片和多個區域關聯
sh.addShardToZone("shard0000", "LGA")

db.adminCommand( { addShardToZone : "shard0000" , zone : "JFK" } )

4.  sh.addTagRange(namespace, minimum, maximum, tag):同sh.updateZoneKeyRange(namespace, minimum, maximum, zone),將一定範圍的分片鍵值附加到分片標籤上。

在mongos例項上執行:

sh.addTagRange( "exampledb.collection",  -- 集合名
                { state: "NY", zip: MinKey },     -- 片鍵最小範圍
                { state: "NY", zip: MaxKey },    -- 片鍵最大範圍
                "NY"                                        -- 標籤或區域
              )        

5. sh.updateZoneKeyRange(namespace, minimum, maximum, zone),同上。3.4版中的新功能:將分片鍵值範圍與區域關聯。範圍不能重疊,即一個區域可以具有與其關聯的多個資料範圍,但是一個範圍最多可以與一個區域關聯。注意:將範圍與區域關聯後,平衡器必須首先執行才能將區域覆蓋的範圍內的任何塊遷移到該區域內的分片。給定分片叢集的已配置區域,在平衡完成之前,某些塊可能駐留在錯誤的分片上。在mongos例項上執行:

-- 以下操作將在alpha區域上建立一個片鍵a範圍下限為1且上限為10的範圍:
sh.updateZoneKeyRange(
   "exampledb.collection",
   { a : 1 },
   { a : 10 },
   "alpha"
)


-- 通過將null傳遞給zone欄位來刪除先前建立的範圍:
sh.updateZoneKeyRange(
   "exampledb.collection",
   { a : 1 },
   { a : 10 },
   null
)

admin = db.getSiblingDB("admin")
admin.runCommand(
   {
      updateZoneKeyRange : "exampledb.collection",
      min : { a : 1, b : 1 },
      max : { a : 10, b : 10 },
      zone : "alpha"

   }
)

注意:如果更新片鍵的範圍,需要完全匹配才能操作。如使用該方法操作叢集:

MongoDB4.2 分片掃盲說明
-- 1:啟動分片庫
sh.enableSharding("exampledb");

-- 2:啟動分片集合
sh.shardCollection("exampledb.contacts",  { zip: 1 } );

-- 3:使用sh.addShardToZone建立區域
sh.addShardToZone("shardA", "DC1")
sh.addShardToZone("shardB", "DC2")

-- 4:使用sh.updateZoneKeyRange建立範圍
sh.updateZoneKeyRange(
   "exampledb.contacts",
   { zip: 10001 },
   { zip: 10090 },
   "DC1"
);

sh.updateZoneKeyRange(
   "exampledb.contacts",
   { zip: 90001 },
   { zip: 96054 },
   "DC2"
);

-- 5:檢視叢集狀態
sh.status()


---
--- Sharding Status ---
  sharding version: {
     "_id" : 1,
     "minCompatibleVersion" : 5,
     "currentVersion" : 6,
     "clusterId" : ObjectId("5b80c06d35a961fd0ae1986d")
  }
  shards:
        {  "_id" : "shardA",  "host" : "shardA/mongodb0.example.net:27018,mongodb1.example.net:27018,mongodb2.example.net:27018",  "state" : 1,  "tags" : [ "DC1" ] }
        {  "_id" : "shardB",  "host" : "shardB/mongodb3.example.net:27018,mongodb4.example.net:27018,mongodb5.example.net:27018",  "state" : 1,  "tags" : [ "DC2" ] }
  active mongoses:
        "4.2.0" : 2
  autosplit:
        Currently enabled: yes
  balancer:
        Currently enabled:  yes
        Currently running:  no
        Failed balancer rounds in last 5 attempts:  0

        Migration Results for the last 24 hours:

                No recent migrations

  databases:
        {  "_id" : "config",  "primary" : "config",  "partitioned" : true }
        {  "_id" : "exampledb",  "primary" : "shardA",  "partitioned" : true,  "version" : {  "uuid" : UUID("6c351bcf-acd2-4fd9-82d8-9f6bd7321558"),  "lastMod" : 1 } }
                exampledb.contacts
                        shard key: { "zip" : 1 }
                        unique: false
                        balancing: true

                        chunks:

                                shardA   3

                                shardB   2

                        { "zip" : { "$minKey" : 1 } } -->> { "zip" : 10001 } on : shardA Timestamp(1, 0)

                        { "zip" : 10001 } -->> { "zip" : 10090 } on : shardA Timestamp(1, 1)

                        { "zip" : 10090 } -->> { "zip" : 90001 } on : shardB Timestamp(1, 2)

                        { "zip" : 90001 } -->> { "zip" : 96054 } on : shardB Timestamp(1, 3)

                        { "zip" : 96054 } -->> { "zip" : { "$maxKey" : 1 } } on : shardA Timestamp(1, 4)

                         tag: DC1  { "zip" : 10001 } -->> { "zip" : 10090 }

                         tag: DC2  { "zip" : 90001 } -->> { "zip" : 96054 }

---
View Code

6. sh.disableBalancing(namespace):禁用指定分片集合的平衡器,這不會影響同一叢集中其他分片集合的塊平衡。在mongos例項上執行:

sh.disableBalancing("xxx.aaa")

7. sh.enableBalancing(namespace):為分片集合啟用平衡器,在mongos例項上執行:

sh.enableBalancing("xxx.aaa")

8. sh.disableAutoSplit()禁用config.settings集合中的autosplit標誌。 為分片群集啟用自動拆分後,MongoDB會根據分塊代表的分片鍵值自動分塊,以防止分塊增長太大。預設情況下會啟用自動拆分。在mongos例項上執行。

9. sh.enableAutoSplit():啟用分片群集的自動拆分,預設情況下會啟用自動拆分。在config.settings集合中啟用autosplit標誌。在mongos例項上執行。從MongoDB 4.2起 sh.startBalancer() 也為分片群集啟用自動拆分。

10. sh.enableSharding(database, primaryShard):在指定的資料庫上啟用分片。在mongos例項上執行:

sh.enableSharding(
   <database>,
   <primary shard>  --可選。主分片,包含未分片的集合。從MongoDB 4.2.2(和4.0.14)開始可用
)

mongos對enableSharding命令使用Write Concern "majority" 。

11. sh.getBalancerHost():從3.4版開始不推薦使用,從3.4開始平衡器在CSRS的主資料庫上執行。使用名為“ ConfigServer”的程式ID持有“balancer”鎖,不會釋放。

12. sh.getBalancerState():檢查是否啟用平衡器,true:啟動;false:未啟動

13. sh.removeTagRange(namespace, minimum, maximum, tag):同sh.removeRangeFromZone(),刪除範圍的分片鍵值,在mongos例項上執行:

sh.removeTagRange( "exampledb.collection",
                { state: "NY", zip: MinKey },
                { state: "NY", zip: MaxKey },
                "NY"
              )

注意:片鍵範圍需要完全匹配才能操作。

14. sh.removeRangeFromZone(namespace, minimum, maximum):同上,3.4版中的新功能:刪除分片鍵值範圍和區域之間的關聯。在mongos例項上執行:

sh.removeRangeFromZone( "exampledb.collection",
                { a : 1 },
                { a : 10 }
              )

--複合片鍵
sh.removeRangeFromZone( "exampledb.collection",
                { a : 1, b : 1 },
                { a : 10, b : 10 }
              )

注意:片鍵範圍需要完全匹配才能操作。

15. sh.isBalancerRunning():平衡器是否在執行。正在遷移塊:true;未執行:false。在mongos例項上執行。

16. sh.moveChunk(namespace, query, destination):將包含查詢指定的文件的塊移動到目標分片,在平衡器自動遷移下,避免直接呼叫sh.moveChunk。在mongos例項上執行:

-- 查詢zipcode為53187的文件的塊,將該塊移動到名為shard0019的分片
sh.moveChunk("records.people", { zipcode: "53187" }, "shard0019")

17. sh.removeShardTag(shard, tag):同sh.removeShardFromZone(),刪除標籤和分片之間的關聯。在mongos例項上執行。

18. sh.removeShardFromZone(shard, zone):同上,刪除標籤和分片之間的關聯。在mongos例項上執行。如果指定的分片是與該區域關聯的最後一個分片,則必須確保沒有與該區域關聯的剩餘範圍。 在執行sh.removeShardFromZone之前,使用updateZoneKeyRange刪除與該區域關聯的所有現有範圍。

19. sh.setBalancerState(state):啟用或禁用平衡器。 從MongoDB 4.2開始,該方法還將在啟用平衡器時啟用自動拆分,並在禁用平衡器時禁用自動拆分。在mongos例項上執行:

sh.setBalancerState(true): 啟用平衡器
sh.setBalancerState(false):禁用平衡器

20. sh.shardCollection(namespace, key, unique, options):使用鍵作為分片鍵對集合進行分片

namespace:-- 要分片的集合的名稱空間,格式為<database>。<collection>。

key-- 分片欄位:1:範圍分片;hashed:雜湊分片
-- 除非集合為空,否則索引必須在shardCollection命令之前存在。如果集合為空,不存在分片鍵的索引,MongoDB會在分片集合之前建立索引。

unique--true,唯一約束,雜湊分片鍵不支援唯一約束。預設為false。如果指定選項文件,則必須是唯一的。

options: --可選
    --numInitialChunks :指定在使用hashed的分片鍵對空集合進行分片時最初要建立的塊數。如果定義了區域標籤,則該引數無效。
    --collation:排序規則

注意:從MongoDB 4.2開始,可以更新文件的分片鍵值(除非分片鍵欄位是不可變的_id欄位),之前版本不能更新,在mongos例項上執行:

-- 為集合records.people使用zipcode的分片鍵
sh.shardCollection("records.people", { zipcode: 1 } )

-- last_name欄位上的雜湊分片鍵,5個初始塊,以及簡單的排序。
sh.shardCollection(
  "phonebook.contacts",
  { last_name: "hashed" },
  false,
  {
    numInitialChunks: 5,
    collation: { locale: "simple" }
  }
)

注意:mongos使用"majority"write concern 

21. sh.splitAt(namespace, query):根據查詢拆分塊。將原始塊分成兩個塊。一個資料塊以該範圍以原始下限(包括下限)開始,並以指定的分片鍵值(不包括此值)結束;另一個塊以指定的分片鍵值(含)為下限,結束於原始上限(不包含)。在mongos例項上執行:

-- 在分片x為70的塊進行拆分
sh.splitAt( "test.foo", { x: 70 } )

22. sh.splitFind(namespace, query):根據查詢找出塊,並在資料塊的中間點拆分兩個大致相等的塊。 在mongos例項上執行:

-- 在中間點拆分包含分片鍵值x的塊:
sh.splitFind( "test.foo", { x: 70 } )

23. sh.startBalancer(timeout, interval):啟動平衡器。 從MongoDB 4.2開始,還為分片群集啟用自動拆分。在mongos例項上執行

sh.startBalancer()

db.adminCommand( { balancerStart: 1 } )

24. sh.status():在mongos例項上執行時,列印叢集詳細的資訊。

25. sh.stopBalancer(timeout, interval):禁用平衡器,從MongoDB 4.2開始,還會禁用分片群集的自動拆分。如果正在進行平衡,則操作將等待平衡完成。在mongos例項上執行。 

sh.stopBalancer()

db.adminCommand( { balancerStop: 1 } )

26. sh.waitForBalancer(wait, timeout, interval):等待平衡器狀態改變(等待直到平衡停止並變為非活動狀態),在mongos例項上執行。

27. sh.waitForBalancerOff(timeout, interval):等待直到平衡器沒有執行,在mongos例項上執行。

28. sh.waitForPingChange(activePings, timeout, interval):等待活動狀態之一的ping狀態更改,並且僅在指定的ping更改狀態時返回。在mongos例項上執行。

29. convertShardKeyToHashed(<Object>):獲取欄位的hash值,和雜湊索引使用相同的雜湊函式。可以獲得片鍵為雜湊索引的雜湊值:

use test

db.orders.createIndex( { _id: "hashed" } )

sh.shardCollection( "test.orders", { _id : "hashed" } )



-- 文件資訊:
{
  _id: ObjectId("5b2be413c06d924ab26ff9ca"),
  "item" : "Chocolates",
  "qty" : 25
}


-- 獲取該文件的分片鍵的雜湊值:
convertShardKeyToHashed( ObjectId("5b2be413c06d924ab26ff9ca") )

以上所有的命令都是在分片(sh)下執行的操作,如果用分片命令:在admin庫下執行,db.runCommand()或則db.adminCommand()的各種方法可以看手冊。對於sh的命令其還有其他

的一些方法,比較常用的有:

1. flushRouterConfig:清除快取的路由表。使用此命令強制重新整理路由表快取,在大多數情況下是自動發生的。 只需要在movePrimary執行或手動清除巨型(jumbo)塊標誌之後執行:

-- 重新整理指定集合的快取
db.adminCommand({ flushRouterConfig: "<db.collection>" } )

-- 重新整理指定資料庫及其集合的快取
db.adminCommand({ flushRouterConfig: "<db>" } )

-- 不帶引數或傳入1時,重新整理所有資料庫及其集合的快取:
db.adminCommand("flushRouterConfig")
db.adminCommand( { flushRouterConfig: 1 } )

注意:從MongoDB 4.0.6開始,可以在mongos和mongod裡允許,之前只能在mongos裡執行。

2. clearJumboFlag:清除大塊標記。在mongos例項上執行:

db.adminCommand( {
   clearJumboFlag: "<database>.<collection>",
   bounds : <array>
} )


db.adminCommand( {
   clearJumboFlag: "test.jumbo",
   bounds: [{ "x" : 1 }, { "x" : 2 }]
} )

3. cleanupOrphaned:從分片中刪除其分片鍵值落入不屬於該分片的單個或單個連續範圍內的孤立文件,在mongod上執行:

db.adminCommand( {
   "cleanupOrphaned": "test.info",
   "startingFromKey": { x: 2 },
   "secondaryThrottle": true
} )

4. listShards:返回分片資訊,在mongos上執行:

db.adminCommand({ listShards: 1 })

5. movePrimary:重新分配主分片,主分片儲存所有未分片的集合。 首先更改群集後設資料中的主分片,然後將所有未分片的集合遷移到指定的分片,在mongos上執行:

db.adminCommand( { movePrimary : "test", to : "shard0001" } )

執行movePrimary時,不要對該資料庫中任何未分片的集合執行任何讀或寫操作。 在遷移期間針對這些集合發出的讀取或寫入操作可能會導致意外行為,包括遷移操作。考慮安排一個維護時段,在此期間應用程式停止對叢集的所有讀取和寫入,還要考慮重建索引的開銷。如果目標分片包含有衝突的集合名稱空間,movePrimary將失敗。

6. mergeChunks:將分片上的連續塊範圍合併為單個塊。 從mongos例項上執行:

db.adminCommand( { mergeChunks : <namespace> ,
                 bounds : [ { x: <minValue>, y: <minValue> },
                            { x: <maxValue>, y: <maxValue> } ] } )

7. removeShard:從分片群集中刪除一個分片,執行removeShard時,MongoDB通過平衡器將分片的塊移動到叢集中的其他分片來。 一旦完成,MongoDB就會從叢集中刪除分片,

一次只能刪除一個分片,如果現有的removeShard操作正在進行中,則removeShard返回錯誤。如果要刪除的分片也是主分片,則在從該分片遷移所有資料之後,必須手動將資料庫移至新的分片。從mongos例項上執行:

db.adminCommand( { removeShard : "bristol01" } )

8. split:將叢集中的一個塊分成兩個塊。 mongos例項會自動拆分和管理塊,但是在特殊情況下,需要split命令手動建立拆分。必須在admin庫下執行。 

db.adminCommand( { split: <database>.<collection>,
                   <find|middle|bounds> } )


-- split:完整名稱空間
-- find: 分片鍵上的查詢語句,匹配包含指定文件的塊。
-- bounds:要分割的塊的邊界,陣列格式。 適用於使用雜湊分片鍵的集合中的塊。 必須由兩個文件組成,這些文件指定了塊的上下分鍵值。 這些值必須與現有塊的最小值和最大值匹配
-- middle:建立兩個塊的分割點的文件

注意:當與find或bounds選項一起使用時,split命令沿中位數拆分塊。 該命令不能使用find或bounds選項拆分空塊,因為空塊沒有中間值。要在空塊中拆分,請在split命令中使用Middle選項,或者在splitAt命令中使用。

連線到mongos例項,並在admin庫下進行:

db.adminCommand( { split: <database>.<collection>,
                   find: <document> } )

db.adminCommand( { split: <database>.<collection>,
                   middle: <document> } )

db.adminCommand( { split: <database>.<collection>,
                   bounds: [ <lower>, <upper> ] } )

要為使用雜湊分片鍵的集合建立拆分,請使用bounds引數,不要使用Middle引數。split按範圍而不是大小建立兩個相等的塊,並且不使用所選點作為新塊的邊界。如:

① 平均拆分:匹配_id為99的塊,將其拆分為兩個大小相等的塊:

db.adminCommand( { split : "test.people", find : { _id : 99 } } )

② 定義任意分割點:匹配_id為99的塊,將其拆分為兩個塊,並將匹配的文件作為拆分塊之一的下限。對集合中的資料進行預拆分時,通常使用此形式

db.adminCommand( { split : "test.people", middle : { _id : 99 } } )

③ 使用雜湊分片鍵的值分割塊:使用一個包含兩個單欄位文件的陣列來表示用於拆分塊的雜湊分片鍵的最小值和最大值

db.adminCommand( { split: "test.people",
                  bounds : [ { userid: NumberLong("-5838464104018346494") },
                             { userid: NumberLong("-5557153028469814163") }
             ] } )

在拆分的時候遇到平衡器拆分,可能會報: "The collection's metadata lock is already taken."。此訊息表明拆分失敗,沒有副作用,重試split命令。

9. splitChunk:拆分塊,使用 sh.splitFind() 和 sh.splitAt()

10. shardingState:一個管理命令,用於報告mongodb是否為分片群集的成員。 

更多的命令,參考官方文件說明。

七、部署說明

上面已經介紹了分片叢集中的各個元件,現在來說明建一個新的分片叢集。

部署環境:

版本:
-- 4.2
成員:
-- 2個mongos例項
-- 3個配置伺服器(CSRS)
-- 3個分片()

1. 部署配置伺服器從3.4開始不再支援使用映象mongod例項作為配置伺服器(SCCC),必須將分片作為副本集(CSRS:Replica Set Config Servers)進行部署。配置伺服器的副本整合員必須要求:不是仲裁、沒有延遲、有buildIndexes。

① 新增配置檔案:3個例項(15317、15318、15319)

MongoDB4.2 分片掃盲說明
systemLog:
   verbosity: 0
   quiet: false
   traceAllExceptions: false
   path: "/usr/local/mongo-config_1/logs/mongodb.log"
   logAppend: true
   logRotate: rename
   destination: file

processManagement:
   fork: true
   pidFilePath: "/usr/local/mongo-config_1/mongodb.pid"

net:
   port: 15317
   bindIp: 0.0.0.0
   maxIncomingConnections: 65536
   wireObjectCheck: true
   ipv6: false
   unixDomainSocket:
      enabled: true
      pathPrefix: /tmp
      filePermissions: 0700

#security:
#   keyFile: "/usr/local/mongo-config_1/test-keyfile"
#   authorization: enabled

storage:
   dbPath: "/usr/local/mongo-config_1/data"
#   indexBuildRetry: true
#   repairPath: <string>
   journal:
      enabled: true
      commitIntervalMs: 100 
   directoryPerDB: true
   syncPeriodSecs: 60
   engine: wiredTiger
   wiredTiger:
      engineConfig:
         cacheSizeGB: 1 
         journalCompressor: snappy
         directoryForIndexes: false
      collectionConfig:
         blockCompressor: snappy
      indexConfig:
         prefixCompression: true

operationProfiling:
   # 指定應分析哪些操作,預設off:分析器已關閉,並且不收集任何資料;slowOp:收集比slowms的時間長的資料;all:收集所有操作的資料
   mode: slowOp
   slowOpThresholdMs: 100 
   slowOpSampleRate: 1

replication:
   oplogSizeMB: 100
   replSetName: test-configs
   enableMajorityReadConcern: true

sharding:
   clusterRole: configsvr
   archiveMovedChunks: false
View Code

最重要的引數就是: clusterRole

sharding:
   clusterRole: configsvr

② 配置檔案啟動:3個節點

/usr/local/mongodb/bin/mongod -f /usr/local/mongo-config_1/mongo.conf 

/usr/local/mongodb/bin/mongod -f /usr/local/mongo-config_2/mongo.conf 

/usr/local/mongodb/bin/mongod -f /usr/local/mongo-config_3/mongo.conf 

③ 初始化配置副本集:關於副本集的知識可以看上一篇介紹

在任意一臺mongo上執行:

rs.initiate(
  {
    _id: "test-configs", -- 和配置檔案裡的replSetName保持一致
    configsvr: true,     -- 配置伺服器
    members: [           -- 成員
      { _id : 0, host : "192.168.163.134:15317" },
      { _id : 1, host : "192.168.163.134:15318" },
      { _id : 2, host : "192.168.163.134:15319" }
    ]
  }
)

關於副本集的配置引數說明,如成員的各種屬性、setttings等,可以看文件或則上篇文章說明。到此,配置伺服器部署完成。

2. 部署分片伺服器:2個分片,2個分片為副本集模式,副本集名為:shard1、shard2

① 新增配置檔案:(分片1的副本集:27017、27018、27019;分片2的副本集:37017、37018、37019)

MongoDB4.2 分片掃盲說明
systemLog:
   verbosity: 0
   quiet: false
   traceAllExceptions: false
   path: "/usr/local/mongodb_1/logs/mongodb.log"
   logAppend: true
   logRotate: rename
   destination: file
   timeStampFormat: iso8601-local

processManagement:
   fork: true
   pidFilePath: "/usr/local/mongodb_1/mongodb.pid"

net:
   port: 27017
   bindIp: 0.0.0.0
   maxIncomingConnections: 65536
   wireObjectCheck: true
   unixDomainSocket:
      enabled: true
      pathPrefix: /tmp
      filePermissions: 0700

#security:
#   keyFile: "/usr/local/mongodb_1/xx_keyfile"
#   authorization: disabled
#   authorization: enabled

storage:
   dbPath: "/usr/local/mongodb_1/data"
   journal:
      enabled: true
      commitIntervalMs: 500
   directoryPerDB: true
   syncPeriodSecs: 60
   engine: wiredTiger
   wiredTiger:
      engineConfig:
         cacheSizeGB: 1
         journalCompressor: snappy
         directoryForIndexes: false
         maxCacheOverflowFileSizeGB: 0
      collectionConfig:
         blockCompressor: snappy
      indexConfig:
         prefixCompression: true

operationProfiling:
   # 指定應分析哪些操作,預設off:分析器已關閉,並且不收集任何資料;slowOp:收集比slowms的時間長的資料;all:收集所有操作的資料
   mode: slowOp
   # 慢操作時間閾值(以毫秒為單位),版本4.0中進行了更改:slowOpThresholdMs設定可用於mongod和mongos
   slowOpThresholdMs: 100
   # 分析記錄慢速操作
   slowOpSampleRate: 1.0
   # 過濾器,記錄耗時超過2秒的查詢操作
   #filter: '{ op: "query", millis: { $gt: 2000 } }'

replication:
   oplogSizeMB: 100
   replSetName: shard1
   enableMajorityReadConcern: false

sharding:
   clusterRole: shardsvr
   archiveMovedChunks: false
View Code

最重要的引數就是: clusterRole

sharding:
   clusterRole: shardsvr

注意配置多個分片的時候注意每個分片的replSetName不能衝突。

② 配置檔案啟動:3個分片節點(6個例項)

/usr/local/mongodb/bin/mongod -f /usr/local/mongodb_1/mongo.conf 

/usr/local/mongodb/bin/mongod -f /usr/local/mongodb_2/mongo.conf 

/usr/local/mongodb/bin/mongod -f /usr/local/mongodb_3/mongo.conf 

/usr/local/mongodb/bin/mongod -f /usr/local/mongodb_11/mongo.conf 

/usr/local/mongodb/bin/mongod -f /usr/local/mongodb_12/mongo.conf 

/usr/local/mongodb/bin/mongod -f /usr/local/mongodb_13/mongo.conf 

③ 初始化配置副本集:關於副本集的知識可以看上一篇介紹

在任意一臺mongo上執行:

rs.initiate(
  {
    _id: "shard1",
    members: [
      { _id : 0, host : "192.168.163.134:27017" },
      { _id : 1, host : "192.168.163.134:27018" },
      { _id : 2, host : "192.168.163.134:27019" }
    ]
  }
)


rs.initiate(
  {
    _id: "shard2",
    members: [
      { _id : 0, host : "192.168.163.134:37017" },
      { _id : 1, host : "192.168.163.134:37018" },
      { _id : 2, host : "192.168.163.134:37019" }
    ]
  }
)

關於副本集的配置引數說明,如成員的各種屬性、setttings等,可以看文件或則上篇文章說明。到此,分片伺服器部署完成。

3. 部署路由伺服器通過快取配置伺服器的後設資料來跟蹤分片上的資料

① 新增配置檔案:2個例項(3306、3307)

MongoDB4.2 分片掃盲說明
systemLog:
   verbosity: 0
   quiet: false
   traceAllExceptions: false
   path: "/usr/local/mongos_1/logs/mongodb.log"
   logAppend: true
   logRotate: rename
   destination: file

processManagement:
   fork: true
   pidFilePath: "/usr/local/mongos_1/mongodb.pid"

net:
   port: 3306
   bindIp: 0.0.0.0
   maxIncomingConnections: 65536
   wireObjectCheck: true
   ipv6: false
   unixDomainSocket:
      enabled: true
      pathPrefix: /tmp
      filePermissions: 0700

#security:
#   keyFile: "/usr/local/mongos_1/test-keyfile"

replication:
   localPingThresholdMs: 15

sharding:
   configDB: test-configs/192.168.163.134:15317,192.168.163.134:15318,192.168.163.134:15319
View Code 

最重要的引數就是: configDB,後面的值為配置伺服器。

sharding:
   configDB: test-configs/192.168.163.134:15317,192.168.163.134:15318,192.168.163.134:15319

注意配置多個mongos路由的時候只需要改變其埠和日誌的儲存路徑即可。

② 配置檔案啟動:2個路由節點

/usr/local/mongodb/bin/mongos -f /usr/local/mongos_1/mongos.conf 

/usr/local/mongodb/bin/mongos -f /usr/local/mongos_2/mongos.conf 

4. 新增分片:可以在新增分片之前先關閉平衡器(sh.sh.stopBalancer())

方法一:
sh.addShard("shard1/192.168.163.134:27017,192.168.163.134:27018,192.168.163.134:27019")

方法二:可以指定當前分片的最大儲存大小
db.runCommand( { addShard: "shard2/192.168.163.134:37017,192.168.163.134:37018,192.168.163.134:37019",maxSize:100})

新增後的資訊如下:

mongos> sh.status()
--- Sharding Status --- 
  sharding version: {
      "_id" : 1,
      "minCompatibleVersion" : 5,
      "currentVersion" : 6,
      "clusterId" : ObjectId("604b246ac6dab8f7a4728d9a")
  }
  shards:
        {  "_id" : "shard1",  "host" : "shard1/192.168.163.134:27017,192.168.163.134:27018,192.168.163.134:27019",  "state" : 1 }
        {  "_id" : "shard2",  "host" : "shard2/192.168.163.134:37017,192.168.163.134:37018,192.168.163.134:37019",  "maxSize" : NumberLong(100),  "state" : 1 }
  active mongoses:
        "4.2.8" : 2
  autosplit:
        Currently enabled: no
  balancer:
        Currently enabled:  no
        Currently running:  no
        Failed balancer rounds in last 5 attempts:  0
        Migration Results for the last 24 hours: 
                No recent migrations
  databases:
        {  "_id" : "config",  "primary" : "config",  "partitioned" : true }
                config.system.sessions
                        shard key: { "_id" : 1 }
                        unique: false
                        balancing: true
                        chunks:
                                shard1    1
                        { "_id" : { "$minKey" : 1 } } -->> { "_id" : { "$maxKey" : 1 } } on : shard1 Timestamp(1, 0) 

可以看到,MongoDB預設已經對system.sessions集合進行了分片,預設分為1024個塊,並將這些塊均勻地分佈在叢集中的各個分片上。因為關閉了平衡器,只顯示1個塊,沒有自動拆分成1024個塊。

啟動分片:

① 資料庫分片:對資料庫啟用分片並不會重新分發資料,但可以在該資料庫中分片集合

sh.enableSharding('test')

為資料庫啟用分片後,MongoDB會為該資料庫分配一個主分片,其中MongoDB將所有資料儲存在該資料庫中。

② 集合分片:如果集合已經有資料,則必須在分片之前建立一個支援分片鍵的索引。 如果集合為空,則會將建立索引。

sh.shardCollection('test.test',{'name':1})

也可以選擇雜湊索引。分片結束之後,叢集資訊:

mongos> sh.status()
--- Sharding Status --- 
  sharding version: {
      "_id" : 1,
      "minCompatibleVersion" : 5,
      "currentVersion" : 6,
      "clusterId" : ObjectId("604b246ac6dab8f7a4728d9a")
  }
  shards:
        {  "_id" : "shard1",  "host" : "shard1/192.168.163.134:27017,192.168.163.134:27018,192.168.163.134:27019",  "state" : 1 }
        {  "_id" : "shard2",  "host" : "shard2/192.168.163.134:37017,192.168.163.134:37018,192.168.163.134:37019",  "maxSize" : NumberLong(100),  "state" : 1 }
  active mongoses:
        "4.2.8" : 2
  autosplit:
        Currently enabled: yes
  balancer:
        Currently enabled:  yes
        Currently running:  no
        Failed balancer rounds in last 5 attempts:  0
        Migration Results for the last 24 hours: 
                512 : Success
  databases:
        {  "_id" : "config",  "primary" : "config",  "partitioned" : true }
                config.system.sessions
                        shard key: { "_id" : 1 }
                        unique: false
                        balancing: true
                        chunks:
                                shard1    512
                                shard2    512
                        too many chunks to print, use verbose if you want to force print
        {  "_id" : "test",  "primary" : "shard2",  "partitioned" : true,  "version" : {  "uuid" : UUID("571a4b0b-9d08-46d3-92a5-1b3a6ca0ccb6"),  "lastMod" : 1 } }
                test.test
                        shard key: { "name" : 1 }
                        unique: false
                        balancing: true
                        chunks:
                                shard2    1
                        { "name" : { "$minKey" : 1 } } -->> { "name" : { "$maxKey" : 1 } } on : shard2 Timestamp(1, 0) 

以上資訊說明:test資料庫已經分片,其下的test集合根據name片鍵也分片成功,主分片在shard2的分片中,目前還有一個塊(沒有資料)。到此,資料庫分片的部署已經完成。

完成部署:以上分片部署是在不需要認證的情況下搭建的,要是有認證該如何部署呢?其實好上一篇介紹副本集裡說的一樣,通過Keyfile x.509證照的方式進行成員間的認證。關於成員間的認證可以看:內部/成員身份驗證

現在說明通過Keyfile金鑰的方式進行認證:

① 金鑰:在部署各個元件之前,先生成金鑰檔案(Keyfile):

openssl rand -base64 756 > <path-to-keyfile>
chmod 400 <path-to-keyfile>

② 共享金鑰:複製金鑰檔案到各個元件上。並且停掉平衡器: 

sh.stopBalancer()

③ 新增引數:security.keyFile,設定各個副本集元件:引數keyFile會自動開啟authorization引數

注意:在使用認證引數開啟例項後,只能通過localhost介面將mongo shell連線mongod例項,localhost介面僅在未建立使用者的情況下可用,建立第一個使用者後,localhost介面關閉。

已有服務上新增認證,需要先關閉各個元件(mongos -> config server -> shard server):

db.getSiblingDB("admin").shutdownServer()

db.shutdownServer()

 操作

  1. 設定配置伺服器keyFile
    在上面的配置檔案中新增引數:
    security:
       keyFile: "/usr/local/mongodb/cc-keyfile"

    確定之後重新啟動每個成員。

  2. 設定分片伺服器keyFile
    security:
       keyFile: "/usr/local/mongodb/cc-keyfile"

    確定之後重新啟動每個成員,另外還可以給分片叢集的主新增使用者,該使用者只能訪問分片,不能用來mongos訪問。並且必須通過localhost介面訪問分片的主上建立使用者,建立第一個使用者後,Localhost介面關閉,需要使用者密碼才能登陸,所以建立的第一個使用者必須具有建立其他使用者(例如,具有userAdminAnyDatabase的使用者)的特權。這樣可以確保可以在Localhost介面關閉後建立其他使用者。
    建立使用者(可選,可以在mongos上建立使用者訪問整個叢集):

    root@test4:/usr/local# mongo localhost:27017/admin
    MongoDB shell version v4.2.8
    connecting to: mongodb://localhost:27017/admin?compressors=disabled&gssapiServiceName=mongodb
    Implicit session: session { "id" : UUID("399e6b11-e0a5-428e-94d1-68b8c19315c3") }
    MongoDB server version: 4.2.8
    
    shard1:PRIMARY> db.createUser(
    ...   {
    ...     user: "dba",
    ...     pwd: passwordPrompt(),
    ...     roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
    ...   }
    ... )
    Enter password: 
    Successfully added user: {
        "user" : "dba",
        "roles" : [
            {
                "role" : "userAdminAnyDatabase",
                "db" : "admin"
            }
        ]
    }

    在各個分片主上執行完畢之後,可以驗證下賬號是否可以登陸。關於建立使用者的說明,可以看MongoDB 4.2 使用者管理

  3. 設定路由伺服器(mongos)keyFile
    security:
       keyFile: "/usr/local/mongodb/cc-keyfile"

    確定之後重新啟動每個成員,在啟動mongos連線配置伺服器之前,需要先設定配置伺服器為副本集模式,否則開啟mongos失敗。

  4. 本地(localhost)連線任意mongos建立使用者管理員(其他mongos會同步):
    root@test4:/usr/local# mongo localhost:3306/admin
    MongoDB shell version v4.2.8
    connecting to: mongodb://localhost:3306/admin?compressors=disabled&gssapiServiceName=mongodb
    Implicit session: session { "id" : UUID("f1861f49-d46d-4031-afa5-2ea6f0896e89") }
    MongoDB server version: 4.2.8
    mongos> db.createUser(
    ...   {
    ...     user: "ccc",
    ...     pwd: passwordPrompt(),
    ...     roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
    ...   }
    ... )
    Enter password: 
    Successfully added user: {
        "user" : "ccc",
        "roles" : [
            {
                "role" : "userAdminAnyDatabase",
                "db" : "admin"
            }
        ]
    }

    注意:該使用者能在mongos路由伺服器和config 配置伺服器上上登入,不能在分片上登入。mongos上登入可以訪問整個叢集資料。並且必須通過localhost介面訪問分片的主上建立使用者,建立第一個使用者後,Localhost介面關閉,需要使用者密碼才能登陸,所以建立的第一個使用者必須具有建立其他使用者(例如,具有userAdminAnyDatabase的使用者)的特權。這樣可以確保可以在Localhost介面關閉後建立其他使用者。

  5. 建立叢集管理的管理使用者
    root@test4:~# mongo localhost:3306/admin
    MongoDB shell version v4.2.8
    connecting to: mongodb://localhost:3306/admin?compressors=disabled&gssapiServiceName=mongodb
    Implicit session: session { "id" : UUID("2b51738b-83c8-47c8-af9e-103542339f5f") }
    MongoDB server version: 4.2.8
    mongos> show dbs;
    mongos> db.auth('ccc','ccc')
    1
    mongos> db.getSiblingDB("admin").createUser(
    ...   {
    ...     "user" : "admin",
    ...     "pwd" : passwordPrompt(),
    ...     roles: [ { "role" : "clusterAdmin", "db" : "admin" } ]
    ...   }
    ... )
    Enter password: 
    Successfully added user: {
        "user" : "admin",
        "roles" : [
            {
                "role" : "clusterAdmin",
                "db" : "admin"
            }
        ]
    }

    驗證:

    MongoDB4.2 分片掃盲說明
    -- 使用者管理賬號登陸
    root@test4:~# mongo 192.168.163.134:3306/admin -uccc -pccc
    MongoDB shell version v4.2.8
    connecting to: mongodb://192.168.163.134:3306/admin?compressors=disabled&gssapiServiceName=mongodb
    Implicit session: session { "id" : UUID("1c65ecfb-0b23-4628-be30-1d00cb6da1ca") }
    MongoDB server version: 4.2.8
    -- 使用者管理賬號沒有許可權
    mongos> sh.status()
    2021-03-13T13:43:20.882+0000 E  QUERY    [js] uncaught exception: Error: error: {
        "ok" : 0,
        "errmsg" : "not authorized on config to execute command { find: \"version\", filter: {}, limit: 1.0, singleBatch: true, lsid: { id: UUID(\"1c65ecfb-0b23-4628-be30-1d00cb6da1ca\") }, $clusterTime: { clusterTime: Timestamp(1615642988, 1), signature: { hash: BinData(0, F232D02C5A62C8138455E2D7DBF9074FA3295B08), keyId: 6938679688579514385 } }, $db: \"config\" }",
        "code" : 13,
        "codeName" : "Unauthorized",
        "operationTime" : Timestamp(1615642998, 1),
        "$clusterTime" : {
            "clusterTime" : Timestamp(1615642998, 1),
            "signature" : {
                "hash" : BinData(0,"0A6gHIjHVTNz7QEotl3GYJlLl+s="),
                "keyId" : NumberLong("6938679688579514385")
            }
        }
    } :
    _getErrorWithCode@src/mongo/shell/utils.js:25:13
    DBCommandCursor@src/mongo/shell/query.js:696:15
    DBQuery.prototype._exec@src/mongo/shell/query.js:111:28
    DBQuery.prototype.hasNext@src/mongo/shell/query.js:282:5
    DBCollection.prototype.findOne@src/mongo/shell/collection.js:255:10
    printShardingStatus@src/mongo/shell/utils_sh.js:551:19
    sh.status@src/mongo/shell/utils_sh.js:98:5
    @(shell):1:1
    -- 切換叢集管理賬號
    mongos> db.auth('admin','admin')
    1
    -- 有許可權
    mongos> sh.status()
    --- Sharding Status --- 
      sharding version: {
          "_id" : 1,
          "minCompatibleVersion" : 5,
          "currentVersion" : 6,
          "clusterId" : ObjectId("604b246ac6dab8f7a4728d9a")
      }
      shards:
            {  "_id" : "shard1",  "host" : "shard1/192.168.163.134:27017,192.168.163.134:27018,192.168.163.134:27019",  "state" : 1 }
            {  "_id" : "shard2",  "host" : "shard2/192.168.163.134:37017,192.168.163.134:37018,192.168.163.134:37019",  "maxSize" : NumberLong(100),  "state" : 1 }
      active mongoses:
            "4.2.8" : 2
      autosplit:
            Currently enabled: yes
      balancer:
            Currently enabled:  yes
            Currently running:  no
            Failed balancer rounds in last 5 attempts:  5
            Last reported error:  Could not find host matching read preference { mode: "primary" } for set shard2
            Time of Reported error:  Sat Mar 13 2021 11:19:24 GMT+0000 (UTC)
            Migration Results for the last 24 hours: 
                    No recent migrations
      databases:
            {  "_id" : "config",  "primary" : "config",  "partitioned" : true }
                    config.system.sessions
                            shard key: { "_id" : 1 }
                            unique: false
                            balancing: true
                            chunks:
                                    shard1    512
                                    shard2    512
                            too many chunks to print, use verbose if you want to force print
            {  "_id" : "test",  "primary" : "shard2",  "partitioned" : true,  "version" : {  "uuid" : UUID("571a4b0b-9d08-46d3-92a5-1b3a6ca0ccb6"),  "lastMod" : 1 } }
                    test.test
                            shard key: { "name" : 1 }
                            unique: false
                            balancing: true
                            chunks:
                                    shard2    1
                            { "name" : { "$minKey" : 1 } } -->> { "name" : { "$maxKey" : 1 } } on : shard2 Timestamp(1, 0) 
    
    mongos> 
    bye
    -- 叢集管理賬號登陸
    root@test4:~# mongo 192.168.163.134:3306/admin -uadmin -padmin
    MongoDB shell version v4.2.8
    connecting to: mongodb://192.168.163.134:3306/admin?compressors=disabled&gssapiServiceName=mongodb
    Implicit session: session { "id" : UUID("72862349-2fa0-4347-a29c-134633963a7e") }
    MongoDB server version: 4.2.8
    Server has startup warnings: 
    2021-03-13T13:21:22.136+0000 I  CONTROL  [main] ** WARNING: You are running this process as the root user, which is not recommended.
    2021-03-13T13:21:22.136+0000 I  CONTROL  [main] 
    mongos> sh.status()
    --- Sharding Status --- 
      sharding version: {
          "_id" : 1,
          "minCompatibleVersion" : 5,
          "currentVersion" : 6,
          "clusterId" : ObjectId("604b246ac6dab8f7a4728d9a")
      }
      shards:
            {  "_id" : "shard1",  "host" : "shard1/192.168.163.134:27017,192.168.163.134:27018,192.168.163.134:27019",  "state" : 1 }
            {  "_id" : "shard2",  "host" : "shard2/192.168.163.134:37017,192.168.163.134:37018,192.168.163.134:37019",  "maxSize" : NumberLong(100),  "state" : 1 }
      active mongoses:
            "4.2.8" : 2
      autosplit:
            Currently enabled: yes
      balancer:
            Currently enabled:  yes
            Currently running:  no
            Failed balancer rounds in last 5 attempts:  5
            Last reported error:  Could not find host matching read preference { mode: "primary" } for set shard2
            Time of Reported error:  Sat Mar 13 2021 11:19:24 GMT+0000 (UTC)
            Migration Results for the last 24 hours: 
                    No recent migrations
      databases:
            {  "_id" : "config",  "primary" : "config",  "partitioned" : true }
                    config.system.sessions
                            shard key: { "_id" : 1 }
                            unique: false
                            balancing: true
                            chunks:
                                    shard1    512
                                    shard2    512
                            too many chunks to print, use verbose if you want to force print
            {  "_id" : "test",  "primary" : "shard2",  "partitioned" : true,  "version" : {  "uuid" : UUID("571a4b0b-9d08-46d3-92a5-1b3a6ca0ccb6"),  "lastMod" : 1 } }
                    test.test
                            shard key: { "name" : 1 }
                            unique: false
                            balancing: true
                            chunks:
                                    shard2    1
                            { "name" : { "$minKey" : 1 } } -->> { "name" : { "$maxKey" : 1 } } on : shard2 Timestamp(1, 0) 
    View Code

    注意:路由伺服器(mongos)上建立的賬號會同步到配置伺服器(config server)上,配置伺服器不需要單獨建立賬號密碼了,但不會同步到分片(shard server)上。分片伺服器的賬號在初始化叢集的時候建立。為了方便賬號管理,建議分片伺服器上建立的賬號密碼最好和在路由伺服器上建立的賬號密碼一致,並且管理員的賬號的role設定為root。關於賬號管理的可以看MongoDB 4.2 使用者管理

  6. 最後開啟平衡器
    sh.startBalancer()

  在新服務上新增認證

   如果是新服務,則只需要在配置檔案裡新增好keyFile引數啟動並授權相應的賬號即可。順序為:

  1. 開啟配置伺服器,並設定為副本集。
  2. 開啟分片伺服器,並設定為副本集,建立使用者密碼。
  3. 開啟路由伺服器,建立使用者密碼(會同步到配置伺服器)。  

八、總結

本文介紹了分片的相關元件、部署和常用的操作方法。由於篇幅的原因,關於一些場景的操作,後面會新起一篇文章進行說明。

參考文件:

Sharding

MongoDB的相關操作函式

MongoDB 分片的原理、搭建、應用

MongoDB 分片管理

 

相關文章