哈希分片(Hashed Sharding)
哈希分片使用单个字段哈希索引或者复合哈希索引(4.4中的新功能)作为分片键来跨分片集群对数据进行分区。
在单个字段哈希索引上分片(Sharding on a Single Field Hashed Index)
哈希分片以减少 *目标操作和广播操作(Targeted Operations vs. Broadcast Operation)*为代价,在分片集群中提供更均匀的数据分布。哈希操作后,具有'关闭'(close)分片键值的文档不太可能位于同一块或者分片上------mongos更有可能执行广播操作(broadcast operation)来完成给定的范围查询。mongos可以将具有相等匹配的查询定位到单个分片。

注:哈希索引计算单个字段的哈希值作为索引值;该值用作你的分片键 。自V4.0版本,mongo shell提供 convertShardKeyToHashed() 方法。此方法使用与哈希索引相同的哈希函数,可用于查看某个键的哈希值。
在复合哈希索引上分片(Sharding on a Compound Hashed Index)
MongoDB 4.4 添加了对使用单个哈希字段创建复合索引的支持。为了创建复合哈希索引,在创建索引时将 哈希(hashed)指定为任何单个索引键的值。
复合哈希索引计算复合索引中单个字段的哈希值;这个值与索引中的其他字段一起作为分片键。
复合哈希索引支持区域分片([zone sharding](zone shading))等功能,其中一个前缀(即第一个)非哈希字段或多个字段支持区域范围,而哈希字段支持分片数据的更均匀分布。复合哈希索引还吃之带有哈休前缀的分片键,用于解决与单调递增字段相关的数据分布问题。
MongoDB在使用哈希索引解析查询时自动计算哈希,应用程序不需要计算哈希值。
注意
MongoDB哈希索引在哈希之前将浮点数截断为64位整数。例如,哈希索引会为包含值2.3 2.2 和2.9的字段储存相同的值。为防止冲突,请勿对无法可靠转换为64位整数(然后再转换为浮点数)的浮点数使用散列索引。MongoDB哈希索引不支持大于 2^53次方的浮点值。
哈希分片的分片键(Hashed Sharding Shard Key)
你选择作为哈希分片键的字段应该有良好的基数,或大量不同的值。哈希键很适合具有单调更改的字段(如ObjectId值或者时间戳)的分片键。一个很好的例子是 _id字段,假设字段只包含ObjectId的值
哈希分片 VS. 范围分片(Hashed Sharding vs Ranged Sharding)
范围分片的概念可以查看 MongoDB 分片集.md --> 分片策略。
给定使用单调递增值X作为分片键的集合,使用范围分片会导致传入插入(incoming inserts)的分布类似于以下内容:

由于X的值始终在增加,因此上限为maxKey的块接收到大多数传入。这将插入操作限制为包含此块单个分片,从而减少或消除了分片集群分布式写入的优势。
通过在x上使用哈希索引,插入的分布类似于:

由于数据现在分布的更均匀,因此插入可以有效地分布在整个集群中。
对集合分片(Shard the Collection)
使用 sh.shardCollection() 方法,指定集合完整的命名空间和用作分片键的目标哈希索引。
shell
sh.shardCollection( "database.collection", { <field> : "hashed" } )
为了在复合哈希索引上对集合分片,指定集合完整的命名空间和用作分片键的目标复合哈希索引。
shell
sh.shardCollection(
"database.collection",
{ "fieldA" : 1, "fieldB" : 1, "fieldC" : "hashed" }
)
重要
- 从V4.4版本开始,你可以通过向现有键添加一个或多个后缀字段来优化集合的分片键。在更早的版本中,一旦你对集合进行了分片,分片键的选择是不可变的。你不能为集合选择一个不同的分片键;
- 自V4.2版本,你可以更新文档的分片键值,除非分片键字段是不可变的
_id字段。在v4.2版本之前,一个文档的分片键值是不可变的。
分片填充集合(Shard a Populated Collection)
如果使用哈希分片键对填充索引进行分片:
- 分片操作创建初始块以覆盖分片键值的整个范围。创建的块数取决于配置的块大小。
- 在初始块创建之后,平衡器根据需要在分片之间迁移这些初始块,并管理未来的块分布。
分片空集合(Shard an Empty Collection)
自V4.0.3,如果已经为集合定义了区域和区域范围(zones and zones range),则分片集合操作可以为空的或不存在的集合执行初始块创建和分发。块的初始创建和分发允许更快地设置分区分片。在初始分发后,平衡器照常管理块分配。
在单字段哈希分片键上分片空集合(Sharding Empty Collection on Single Field Hashed Shard Key)
- 没有为空的或不存在的集合指定区域和区域范围:
- 分片操作创建空的块以覆盖整个分片键值的范围并执行初始化块分配。默认情况下,该操作在每个分片创建2个块并跨集群迁移。你可以使用
numInitialChunks选项来指定不同数量的初始块。这种块的初始创建和分配允许更快地设置分片(sharding)。 - 在初始分配之后,平衡器管理接下来的块分配。
- 分片操作创建空的块以覆盖整个分片键值的范围并执行初始化块分配。默认情况下,该操作在每个分片创建2个块并跨集群迁移。你可以使用
- 已经为空的或不存在的集合指定了区域和区域范围(该功能在4.0.3版本之后才可用):
- 分片操作为定义的区域范围创建空快以及任何其他块以覆盖分片键值的整个范围,并根据区域范围执行从初始块分布。这种块的初始创建和分布允许更快地设置分区分片(zoned sharding)。
- 在初始分配之后,平衡器管理接下来的块分配。
使用哈希字段前缀在复合哈希分片键上分片空集合(Sharding Empty Collection on Compound Hashed Shard Key with Hashed Field Prefix)
如果复合哈希分片键有哈希字段作为前缀(即,哈希字段是分片键的第一个字段):
- 如果没有为空的或不存在的集合指定区域和区域范围:
- 分片操作创建空的块以覆盖整个分片键值的范围并执行初始化块分发。所有非哈希字段的值在每个分割点都是
minKey,默认情况下,该操作为每个分片创建两个块并在集群内迁移。你可以使用numInitialChunks选项来指定不同数目的初始块。块的初始创建和分发允许更快的设置分片。 - 在初始分发之后,平衡器管理接下来的块分发。
- 分片操作创建空的块以覆盖整个分片键值的范围并执行初始化块分发。所有非哈希字段的值在每个分割点都是
- 为空的或不存在的集合指定从minKey到maxKey的但各区域,并将
presplitHashedZones指定给sh.shardCollection():- 分片操作为定义的区域范围创建空块以及任何其他块以覆盖分片键值的整个范围,并根据区域范围执行初始块分布。块的初始创建和分布允许更快的设置分区分片;
- 在初始分发后,平衡器管理后面的块分发。
在具有非哈希前缀的复合哈希分片键上分片空集合(Sharding Empty Collection on Compound Hashed Shard Key with Non-Hashed Prefix)
如果复合哈希分片键有一个或多个非哈希字段作为前缀(即,哈希字段并不是符合哈希分片键的第一个字段):
-
若没有为空的或不存在的集合指定区域和区域范围,且
preSplitHashedZones选项为false或者省略,则MongoDB在分片集合时不执行任何初始块创建和分发; -
若没有为空的或不存在的集合以及
preSplitHashedZones指定区域和区域范围,sh.shardCollection() / shardCollection()返回错误; -
若为空的或不存在的集合制定了区域和区域范围,且将
preSplitHashedZones选项指定给sh.shardCollection():- 分片操作为定义的区域范围创建空块以及其他任何块以覆盖分片键值得范围;
- 分片操作进一步细分每个范围得初始块,以便为区域中得每个分片分配相同数量的块;
- 块的初始化创建和分发允许更快地设置分区分片。在初始化分发之后,平衡器管理接下来的块分发。
每个区域的定义范围必须满足特定的要求。关于这些要求的描述和一个完整示例,参考:Pre-Define Zones and Zone Ranges for an Empty or Non-Existing Collection