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 分片管理