MongoDB4.2 分片掃盲說明

說明:

      在掃盲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:分片元數據更改記錄的存儲集合。如來自變更大塊拆分的記錄:

    {
     "_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字段中的塊:

    {
       "_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集合信息

    {
       "_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:記錄分片群集中的活動組件:

    { "_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(),如:

    { "_id" : "chunksize", "value" : 64 }
    { "_id" : "balancer", "mode" : "full", "stopped" : false }
    { "_id" : "autosplit", "enabled" : true }

    View Code

  • shards:記錄集合的每個分片信息。

    --單機
    { "_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:包含分片標籤設置:

    {
        "_id" : { "ns" : "records.users", "min" : { "zipcode" : "10001" } },
        "ns" : "records.users",
        "min" : { "zipcode" : "10001" },
        "max" : { "zipcode" : "10281" },
        "tag" : "NYC"    --標籤
    }

    View Code

  • version:版本信息:
  • { "_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:為每次分片集合更改元數據存儲一個文檔。如:

    {
     "_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:為集群中的每個塊存儲一個文檔,如:

    {
       "_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:為集群中的每個分片集合存儲一個文檔,如:

    {
       "_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集合中來獲取鎖,如:

    {
       "_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實例存儲一個文檔,如:

    {
       "_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:包含分片配置,也可以設置。如:更改塊大小,更改平衡器狀態

    { "_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:表示集群中的每個分片,如:

    -- 單節點分片
    { "_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:保存當前的元數據版本號,如:

    { "_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 的分片方法:

Name Description
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"

   }
)

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

-- 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)

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)

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)

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"
            }
        ]
    }

    驗證:

    -- 用戶管理賬號登陸
    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 分片管理