MongoDB分片集
分片是一种在多台机器上面分布数据的方法。MongoDB用分片集来支持具有非常大的数据集和高吞吐量操作的部署。
具有高数据集或高吞吐量应用程序的数据库系统可能会挑战单个服务器的容量。例如,高查询率会耗尽服务器CPU的容量。大于系统RAM的工作集大小会加重磁盘驱动器的I/O容量。
解决系统增长的方法有两种:垂直扩展和水平扩展。
垂直扩展涉及增加单个服务器的容量,例如使用更大的CPU,添加更多的RAM或增加存储空间量。可用技术的限制可能会限制单个机器对于给定的工作负载足够强大。此外,基于云的提供商具有基于可用硬件配置的硬上限。因此,垂直缩放有一个实际最大值。
水平扩展涉及划分系统数据集并在多个服务器上加载,添加额外的服务器可以根据需要增加容量。虽然单台机器的整体速度或者容量可能不高,但每台机器处理整体工作负载的一个子集,可能比单台高速大容量服务器提供更好的效率。扩展部署的容量只需根据需要添加额外的服务器,这可以比单机的高端硬件更低的成本。代价是基础设施和部署维护的复杂性增加。
MongoDB通过分片来支持水平扩展。
分片集
一个MongoDB分片集由以下部分组成:
- 分片(shard):每一个分片包含分片数据的一个子集。每个分片都可以部署为副本集。
- mongos :mongos充当查询路由(路由进程),在客户端应用程序和分片集群之间提供接口。从MongoDB4.4开始,mongos可以支持对冲读取以最小化延迟。
- 配置服务器(config servers):配置服务器存储集群的元数据和配置设置。

MongoDB在集合层级对数据分片,将集合数据分布在集群中的各个分片上。
分片键(shard keys)
MongoDB通过分片键将集合内的文档分布在不同的分片上。分片键由文档中的一个或者多个字段组成:
- 自V4.4,分片集合中的文档可能会缺少分片键字段。在跨分片分布文档时,缺少的分片键字段被视为具有空值(null),但在路由查询时则不会。
- 在V4.2及之前版本中,分片集合中的每个文档都必须存在分片键字段。
在对集合分片时选择分片键:
- 自V5.0,可以通过修改集合的分片键来对集合重新分片;
- 自V4.4,可以通过对现有分片键添加一个或多个后缀字段来优化分片键;
- 在V4.2及之前的版本中,在分片发生后不能改变分片键的选择。
文档的分片键决定了文档在分片间的分布:
- 自V4.2,你可以更新文档的分片键值,除非文档的分片键字段是不可变的
_id字段; - 在V4.0及之前的版本,文档的分片键值是不可变的。
分片键索引(shard key index)
要对填充的(populated)集合进行分片,该集合必须具有以分片键开头的索引。当对一个空集合进行分片时,如果集合还没有指定分片键的适当索引,MongoDB将创建支持索引。
分片键策略(shard key strategy)
分片键的选择影响分片集群的性能、效率和可扩展性。具有最佳硬件和基础配置的集群可能因为分片键的选择而遭受瓶颈。分片键及其后备索引(basking index)的选择也会影响集群可以使用的分片策略。
块(Chunks)
MongoDB将分片数据分成块。每个块都有一个基于分片键的包含下限和互斥上限。
平衡器和均匀的块分布(Balancer & even chunk distribution)
为了在集群的所有分片上实现块的均匀分布,平衡器在后台运行以跨平台迁移块。
分片的优势
读写
MongoDB在分片集群中的分片之间分配读写工作负载,允许每个分片处理集群操作的子集。通过添加更多分片,读写工作负载都可以在集群中水平扩展。
对于包含分片键或复合分片键前缀的查询,mongos可以将查询定位到特定分片或分片集。这些有针对性的操作通常比广播到集群中的每个分片更有效。
自V4.4,mongos可以支持对冲读取以最小化延迟。
存储容量
分片(sharding)将数据分布在集中中的分片(shards)上,允许每个分片包含总集群数据的一个子集。随着数据集的增长,额外的分片增加了集群的存储容量。
高可用性(high availability)
将配置服务器和分片部署为副本集提供了更高的可用性。
即使一个或多个分片副本集完全不可用,分片集群也可以继续执行部分读写。这意味着,当处于不可用状态的分片上的数据不能访问时,针对可用分片的读或写操作仍然可以成功。
分片前的注意事项
分片集群基础设施要求和复杂性需要仔细规划、执行和维护。
一旦一个集合被分片,MongoDB无法提供一个有效的方法使被分片的集合恢复至未分片的状态。
虽然您可以稍后重新分片您的集合,但请务必仔细考虑您的分片键以避免可伸缩性和性能的问题。
如果查询不包括分片键或者复合分片键的前缀,mongos会执行广播操作(broadcast operation),查询集群中的所有分片。这些分散/收集查询可能是长时间运行的操作。
分片和非分片集合
数据库可以混合有分片和非分片集合。分片集合被分区并分布在集群中的分片上。未分片的集合存储在主分片上。每个数据库都有自己的主分片。

连接到分片集群
必须连接到mongos路由器才能与分片集群中的任何集合进行交互,这包括分片集合和非分片集合。客户端不应该连接到单个分片以执行读写操作。

可以通过像使用mongoshell或者MongoDB驱动连接到mongod的方式来连接到mongos。
分片策略
MongoDB支持两种分片策略,用于跨分片集群分布数据。
哈希分片(hashed shard)
散列分片涉及计算分片键字段值的散列。然后根据散列的分片键值为每个块分配一个范围。
MongoDB在使用散列索引解析查询时会自动计算散列值,应用程序不需要计算哈希。

虽然一系列分片键可能"接近",但它们的散列值不太可能在同一个块上。基于散列值的数据分布有助于更均匀的数据分布,尤其是在分片键单调变化的数据集中。
但是,散列分布意味着对分片键的基于范围的查询不太可能针对单个分片,从而导致更多集群范围的广播操作。
范围分片(ranged sharding)
范围分片涉及到根据分片键值将数据划分范围,然后根据分片键值为每一个块分配一个范围。

值"接近"的一系列分片键更有可能驻留在同一个块上,这允许有针对性的操作,因为mongos可以将操作仅路由到包含所需数据的分片。
范围分片的效率取决于选择的分片键。考虑不周到的分片键会导致数据分布不均匀,这可能会抵消分片的一些好处或导致性能瓶颈。
分片集群中的区域
区域可以帮助改善跨多个数据中心的分片集群的数据局部性。
在分片集群中,你可以根据分片键创建分片数据区域。你可以将每个区域与集群中的一个或多个分片相关联。一个分片可以与任意数量的区域相关联。在平衡集群中,MongoDB仅将区域覆盖的块迁移到与该区域关联的分片。
每个区域涵盖一个或多个分片键范围,区域覆盖的范围始终包括其下边界,但不包括其上边界。

在定义要覆盖的区域的新范围时,您必须使用分片键中包含的字段。如果使用复合分片键,范围必须包含分片键的前缀。
选择分片键时应考虑未来可能使用的区域。
分片中的排序规则(Collations in Sharding)
使用 shardCollection 命令和 collation : {locale : "simple"} 选项对具有默认排序规则的集合进行分片。成功的分片需要:
- 集合必须有一个索引,其前缀是分片键;
- 索引必须具有排序规则
{locale : "simple"}
使用排序规则创建新集合时,请确保在对集合进行分片之前满足这些条件。
对分片集合的查询仍然使用为集合配置的默认排序规则。要使用分片键索引的 {locale : "simple"} ,请在查询的排序规则文档中指定 {locale : "simple"}.
变更流(Change Stream)
自V3.6,变更流对副本集和分片集均可用。变更流允许应用程序访问实时数据更改,而不会拖尾oplog的复杂性和风险。应用程序可以使用变更流来订阅一个或多个集合上的所有数据更改。
事务(Transactions)
自V4.2,随着分布式事务的引入,分片集群上可以使用多文档事务。
直到一个事务被提交,在事务中进行数据更改才可见。
但是,当一个事务写入多个分片时,并非所有外部读取操作都需要等待已提交事务的结果在各个分片中可见。例如,如果事务已提交并且写入 1 在分片 A 上可见,但写入 2 在分片 B 上尚不可见,则读取关注"本地"的外部读取可以读取写入 1 的结果而不会看到写入 2。