MongoDB 8.0——分片 分片1、分片基础1.1、分片介绍1.1.1、垂直扩展1.1.2、水平扩展1.2、分片键介绍1.3、分片的优点1.3.1、读取/写入优点1.3.2、存储容量优点1.3.3、高可用性优点2、分片集群2.1、分片集群的组成2.2、生产配置2.3、开发配置3、分片键的应用3.1、分片键索引3.2、将集合分片3.3、选择分片键3.4、更改分片键3.5、对集合重新分片3.5.1、重新分片之前需要满足的要求3.5.2、重新分片的操作步骤1、分片基础1.1、分片介绍MongoDB数据库分片就是将数据分成块再将块存储到不同的服务器上其实是一种分布式存储数据的方法。如果数据库系统存在大型数据集或高吞吐量应用程序的情形可能会对单个服务器的容量构成挑战。例如较高的查询速率可能会耗尽服务器的CPU容量。大于系统RAM的工作集大小会对磁盘驱动器的I/O容量造成压力。因此有两种方法可以解决系统扩展的问题分别是垂直扩展(Vertical Scaling)和水平扩展(HorizontalScaling)。1.1.1、垂直扩展涉及增大单个服务器的容量例如使用更强大的CPU、添加更多RAM或增加存储空间量。可用技术所存在的限制可能会导致单个机器对于给定工作负载来说不够强大。因此垂直扩展存在实际的最大值。1.1.2、水平扩展涉及将系统数据集和负载划分到多个服务器以及按需增加服务器以提高容量。虽然单个机器的总体速度或容量可能不高但每个机器均可处理总体工作负载的一部分因此可能会比单个高速、高容量服务器提供更高的效率。扩展部署的容量只需按需添加额外的服务器而且这可能会比添加单个机器的高端硬件整体成本更低但这种做法的代价在于会增大部署的基础设施与维护的复杂性。对于MongoDB数据库而言支持通过分片进行水平扩展。1.2、分片键介绍MongoDB数据库使用分片键在分片之间分发集合的文档分片键由文档中的一个或多个字段组成分片集合中的文档可能缺少分片键字段。当跨分片分发文档时缺少的分片键字段时将被视为null值但在路由查询时则不会。MongoDB在对集合进行分片时会选择分片键。从MongoDB 5.0版本开始就可以通过更改集合的分片键对集合重新分片。此外还可以通过向现有分片键添加后缀字段或添加新字段来优化分片键。文档的分片键值决定了其在各分片中的分布。我们可以更新文档的分片键值除非分片键字段是不可变的_id字段。分片键索引用于对已填充的集合进行分片该集合必须具有以分片键开头的索引。对空集合进行分片时如果该集合还没有指定分片键的适当索引MongoDB数据库会创建支持索引。分片键策略对于分片键的选择会影响分片集群的性能、效率和可扩展性。具有最佳硬件和基础架构的集群可能会因为选择分片键而遇到瓶颈分片键及其后备索引的选择也会影响集群可以使用的分片策略。MongoDB数据库将数据分片为数据段每个数据段都有一个基于分片键、包含下限且不包含上限的范围。为了实现数据在集群中所有分片上的均匀分布负载均衡器会在后台运行以便在各分片之间迁移范围。1.3、分片的优点MongoDB数据库分片具有如下几项优点。1.3.1、读取/写入优点MongoDB在分片集群中的分片之间分配读写工作负载支持每个分片处理集群操作的子集。通过添加更多的分片读写工作负载都可以在集群中横向扩展。对于包含分片键或复合分片键前缀的查询mongos可将查询定向到特定分片或一组分片。这些有针对性的操作通常比向集群中的每个分片进行广播更为有效。1.3.2、存储容量优点分片将数据分布在集群中的分片上从而允许每个分片包含整个集群数据的子集。随着数据集的增长更多的分片会增加集群的存储容量。1.3.3、高可用性优点按副本集来部署配置服务器和分片可提高可用性。即使一个或多个分片副本集变为完全不可用分片集群仍可继续执行部分读取和写入操作。换言之即便无法访问不可用分片上的数据针对可用分片的读取或写入仍可成功完成。2、分片集群2.1、分片集群的组成MongoDB分片集群由以下组件构成。分片每个分片都包含分片数据的一个子集。每个分片都必须作为一个副本集进行部署。mongosmongos充当查询路由器在客户端应用程序和分片集群之间提供接口。配置服务器配置服务器会存储集群的元数据和配置设置。配置服务器必须以副本集(CSRS)的形式部署。关于分片集群内各组件之间的交互原理具体描述如图所示。MongoDB数据库在集合级别对数据进行分片从而将集合数据分布到集群中的分片上。2.2、生产配置在MongoDB分片集群中确保数据冗余和系统高可用性是生产环境的关键要求。对于生产分片集群的部署可考虑以下事项将配置服务器部署为3节点副本集。将每个分片部署为3成员副本集。部署一个或多个mongos路由器。对于副本集分布而言如果有可能可以考虑将每个副本集的一个成员部署在适合作为灾难恢复位置的站点中。注意将副本集成员分布在两个数据中心比分布在一个数据中心更有优势。当成员分布在两个数据中心时如果其中一个数据中心发生故障数据仍可供读取而如果所有成员都位于单个数据中心则无法实现此功能。如果具有少数成员的数据中心出现故障副本集仍然可以支持写入操作和读取操作。但是如果具有大多数成员的数据中心出现故障副本集将变为只读。如果有可能可将成员分布在至少3个数据中心。对于配置服务器副本集(CSRS)最佳实践是分布在3个数据中心也可根据成员数量来增加数据中心数量​。如果使用第3个数据中心的成本过高一种可行的分布方法是在两个数据中心均匀分配数据承载成员并将剩余成员存储在云中。对于分片数量而言分片集群需要至少两个分片来分发分片数据。如果计划日后启用分片但在部署时又不需要启用那么单个分片的分片集群可能会很有用。基于mongos的数量和分布mongos路由器在部署多个mongos实例时支持高可用性和可扩展性。如果代理或负载均衡器位于应用程序和mongos路由器之间则必须为其配置客户端关联性。客户端关联性允许来自一个客户端的每个连接到达同一个mongos。要实现分片级别的高可用性可以在已运行mongos实例的同一硬件上添加mongos实例在应用程序级嵌入mongos路由器。mongos路由器会与配置服务器频繁通信。随着路由器数量的增加性能可能会下降。如果性能下降可减少路由器的数量。另外最多只能部署30个mongos路由器。在生产配置中常用的分片集群架构如图所示。2.3、开发配置MongoDB数据库分片集群为了进行测试和开发可以部署具有最少数量组件的分片集群。一般来讲这些非生产集群具有以下组件一个mongos实例。单个分片副本集。副本集配置服务器。下图展示了仅用于开发目的的分片集群架构。3、分片键的应用3.1、分片键索引MongoDB分片键可以是单个索引字段也可以是复合索引涵盖的多个字段复合索引决定了集合文档在集群分片中的分布。MongoDB将分片键值或哈希分片键值的取值区间划分为不重叠的分片键值或哈希分片键值范围。每个分片键值范围都与一个数据块相关联MongoDB会尝试在集群中的各个分片之间均匀分配这些数据块。分片键与数据段分发的有效性有直接关系。MongoDB中的所有分片集合都必须具有支持分片键的索引。索引可以是分片键上的索引也可以是复合索引其中分片键是索引的前缀。如果集合为空则sh.shardCollection()会在分片键上创建索引如果此类索引尚不存在​。如果集合不为空则必须先创建索引然后才能使用sh.shardCollection()。如果索引是唯一支持分片键的非隐藏索引则无法将其删除或隐藏。MongoDB可以对分片键值范围内的分片键索引执行唯一性约束。通过在分片键上使用唯一索引MongoDB将确保整个键的组合的唯一性而不是分片键的各个组成部分。对于分片键值范围内的分片集合只有以下索引是唯一的分片键上的索引。一个复合索引其中分片键是前缀。默认_id索引。只有当_id字段也是分片键时分片集群才会在整个集群中对_id字段执行唯一性约束。如果_id字段不是分片键或者只是分片键的前缀则唯一性约束只适用于存储文档的分片。这意味着两个或多个文档可以具有相同的_id值前提是其出现在不同的分片上。如果_id字段不是分片键MongoDB希望应用程序确保所有分片上_id值的唯一性。如果是唯一索引约束则意味着以下几点对于即将被分片的集合如果该集合还有其他的唯一索引则无法对其进行分片。对于已分片的集合无法在其他字段上创建唯一索引。唯一索引为缺少索引字段的文档存储空值即缺少的索引字段将被视为null索引键值的另一个实例。要确保分片键值的唯一性需要将unique参数设置为true值传递给sh.shardCollection()方法详细说明如下如果集合为空sh.shardCollection()会在分片键上创建唯一索引条件是这种索引尚不存在​。如果集合不为空则必须先创建索引然后才能使用sh.shardCollection()。尽管可以有以分片键为前缀的唯一复合索引但如果使用unique参数则集合必须在分片键上有唯一索引。另外不能在哈希索引上指定唯一约束。3.2、将集合分片MongoDB必须指定要进行分片的集合的完整命名空间和分片键才能对其进行分片。我们可以使用mongosh方法sh.shardCollection()对集合进行分片具体代码如下sh.shardCollection(namespace,key)第1个参数namespace指定设计人员想要对其进行分片的集合的完整命名空间(database.collection)。第2个参数key指定文档{shard key field1: 1|hashed, ...}其中1表示基于范围的分片hashed表示哈希分片。关于分片键字段与字段值具体描述如下。缺少的分片键字段分片集合中的文档可能缺少分片键字段。缺失的分片键与值为null的分片键属于同一范围。更改文档的分区密钥值可以更新文档的分片键值除非分片键字段是不可变的_id字段。更改集合的分片键从MongoDB 5.0版本开始可以通过更改文档的分片键对集合重新分片。设计人员可以通过向现有分片键添加后缀字段来优化分片键。3.3、选择分片键MongoDB分片键的选择会影响数据段的创建和在可用分片间的分配。数据的分布会影响分片集群内操作的效率和性能。理想的分片键可以让MongoDB在整个集群中均匀地分发文档同时也有利于实施常见的查询模式。在选择分片键时需要考虑以下几点分片键的关联基数。分片键值出现的频率。潜在的分片键是否单调递增。分片查询模式。分片键限制。如果定期更改文档的分片键值以使该值位于不同分片拥有的分片键范围则可能会影响集群性能因为在分片之间迁移文档会涉及额外的资源。分片键的关联基数决定了负载均衡器可以创建的最大数据段数。尽可能选择关联基数高的分片键。关联基数低的分片键会降低集群中水平扩展的有效性。在任何给定时间每个唯一的分片键值只能存在于一个数据段上。现在考虑一个数据集其包含带有continent字段的用户数据。如果选择在continent上进行分片则分片键的关联基数将为7。关联基数为7意味着分片集群内的数据段不能超过7个每个数据段存储一个唯一的分片键值。这也会将集群中的有效分片数限制为7即添加7个以上的分片不会带来任何好处。下图展示了使用字段X作为分片键的分片集群。如果X的关联基数较低则插入操作的分布情况会是图中展示的这样。如果数据模型要求在关联基数较低的键上进行分片可考虑使用字段的复合索引来增加关联基数。关联基数低的分片键本身并不保证数据在分片集群中均匀分布。分片键的频率以及单调变化的分片键值的深度也有助于数据的分布。分片键频率表示给定分片键值在数据中出现的频率。如果多数文档仅包含可能的分片键值的一部分则存储具有这些值的文档的数据段可能会成为集群内的瓶颈。此外随着这些数据段的增长它们可能会成为不可分割的数据段因为它们无法被进一步分割。这会降低集群内横向扩展的有效性。下图演示了使用字段X作为分片键的分片集群。如果X的子集值出现频率较高则插入操作的分布情况会是图中展示的这样字段X的子集值(12)会集中在一个分片上。如果数据模型要求在具有高频值的键上进行分片可以考虑使用唯一或低频率值的复合索引。低频率的分片键本身并不保证数据在分片集群中均匀分布。分片键的关联基数以及单调变化的分片键值的潜力也有助于数据的分布。关于单调变化的分片键内容针对单调增加或减少的值的分片键更有可能将插入操作分布到集群内的单个数据段中。之所以发生这种情况是因为每个集群都有一个捕获范围上限为maxKey的数据段maxKey始终是所有值中最高的值。同样还有一个捕获范围下限为minKey的数据段minKey始终是所有值中最低的值。如果分片键值总是增加的则所有新的插入操作都会路由到以maxKey为上限的数据段。如果分片键值总是减少的则所有新的插入操作都会路由到以minKey为下限的数据段。包含该上下限数据段的分片会成为写入操作的瓶颈。为优化数据分布包含全局maxKey或minKey的数据段不会位于同一分片。在分割数据段时具有maxKey或minKey数据段的新数据段会位于不同的分片。下图演示了使用字段X作为分片键的分片集群。如果X的值单调递增则插入操作的分布情况可能会是图中展示的这样字段X的子集值在大于或等于20上单调递增会集中在一个分片上。如果分片键值单调递减则所有插入操作都将改为路由到Chunk A。如果数据模型需要对单调变化的键进行分片可以考虑使用哈希分片。对于不产生单调变化的分片键本身并不保证数据在分片集群中均匀分布。分片键的关联基数和频率也有助于数据的分布。关于分片查询模式的使用理想的分片键可将数据均匀地分布在分片集群中同时也有利于实施常见的查询模式。在选择分片键时可考虑最常使用的查询模式以及给定分片键能否涵盖这些模式。在分片集群中如果查询包含分片键则mongos仅会将这些查询路由到包含相关数据的分片。如果查询不包含分片键则这些查询会被广播到所有分片以进行评估。这些类型的查询称为“分散-聚合”查询。每个请求涉及多个分片的查询效率较低并且向集群添加更多分片时无法实现线性扩展。这不适用于针对大量数据进行操作的聚合查询。在这些情况下​“分散-聚合”查询可能是一种有用的方法因为它允许查询在所有分片上并行运行。3.4、更改分片键在MongoDB数据库中理想的分片键可以让MongoDB在整个集群中均匀地分发文档同时也有利于实施常见的查询模式。另外不理想的分片键会导致数据分布不均匀并产生以下问题巨型数据块。负载分布不均。查询性能随时间推移下降。为了解决上述这些问题MongoDB支持变更分片键从MongoDB 5.0版本开始可以通过更改集合的分片键对集合重新分片。可以通过向现有分片键添加后缀字段来优化分片键。在对数据集进行重新分片时数据分布修复最为有效。如果想改进数据分布并且集群满足重新分片的条件则应该将集合重新分片而不是优化分片键。如果集群不符合重新分片的条件则应优化分片键。优化集合的分片键可以实现更细粒度的数据分布并且可以解决现有键由于关联基数不足而导致巨型数据块的问题。注意不要修改任何当前分片键字段的范围或哈希类型这会导致数据不一致。例如请勿将分片密钥从{customer_id: 1}修改为{customer_id: “hashed”,order_id: 1}。从MongoDB 5.0版本开始可以通过为集合提供新的分片键来对集合重新分片。如果要优化集合的分片键可以使用refineCollectionShardKey命令。refineCollectionShardKey会向现有键添加一个或多个后缀字段以创建新的分片键。例如在一个test数据库中有一个orders集合其分片键为{customer_id: 1}可以使用refineCollectionShardKey命令将分片键更改为新的分片键{customer_id: 1, order_id: 1}具体代码如下3.5、对集合重新分片在MongoDB数据库中理想的分片键可以让MongoDB在整个集群中均匀地分发文档同时也有利于实施常见的查询模式。而如果由于数据分配不平均不太理想的分片键可能会导致性能或扩展问题。从MongoDB 5.0版本开始可以通过更改集合的分片键来更改集群中的数据分配情况。从MongoDB 7.2版本开始可以使用相同的分片键对集合进行重新分片从而允许在不更改分片键的情况下重新分发数据以包含新分片或不同区域。3.5.1、重新分片之前需要满足的要求在对集合进行重新分片之前请确保满足以下要求应用程序允许受影响的集合块进行两秒钟的写入。在写入受阻期间应用程序的延迟会增加。如果工作负载无法满足此要求可考虑优化分片键。数据库符合以下资源要求确保每个分片上的可用存储空间至少是重新分片的集合大小及其总索引的集合大小的两倍再除以分片数量。storage_req(collection_storage_sizeindex_size)*2/shard_count例如假设一个集合包含2TB数据并具有分布在4个分片上的400GB索引。要对此集合执行重新分片操作每个分片都需要1.2TB的可用存储空间。1.2TBstorage(2TBcollection0.4TBindex)*2/4shards为了满足存储要求可能需要在重新分片操作期间升级到下一个存储层。操作完成后可以缩减规模。确保I/O容量低于50%。确保CPU负载低于80%。必须重写应用程序的查询才能同时使用当前分片键和新的分片键。如果应用程序可以容忍停机则可以执行以下步骤来避免重写应用程序的查询从而同时使用当前分片键和新的分片键停止应用程序。重写应用程序以使用新的分片键。等待重新分片完成。要监控重新分片进程可以使用$currentOp管道阶段。部署重写的应用程序。如果没有正在运行的索引被创建使用db.currentOp()检查是否有任何正在运行的索引被创建具体代码如下在结果文档中如果inprog字段值为空数组则表示没有正在运行的索引被创建具体代码如下注意重新分片是一个密集型写入操作会显著提高操作日志的生成速率。为了避免操作日志无限增长建议设置固定的操作日志大小并适当增加操作日志的大小以最大程度减少一个或多个从节点因操作日志溢出而过时的可能性。在集合重新分片操作中分片可以是发送分片也可以是接收分片。发送分片是指当前存储分片集合数据段的分片而接收分片是指根据分片键和区域存储分片集合新数据段的分片。分片可以同时作为发送分片和接收分片。除非使用区域划分否则发送分片和接收分片是相同的。3.5.2、重新分片的操作步骤配置服务器的主节点始终担任重新分片协调器的角色并负责启动重新分片操作的每个阶段。重新分片的具体步骤如下。步骤1开始重新分片操作。连接到mongos时发出reshardCollection命令指定要重新分片的集合和新分片键具体代码如下MongoDB将阻止写入的最大秒数设置为两秒并且重新开始分片操作。步骤2监控重新分片操作。要监控重新分片操作可以使用$currentOp管道阶段具体代码如下注意想要查看更新的值需要连续运行前面的管道。步骤3完成重新分片操作。在整个重新分片进程中完成重新分片操作的预计时间(remainingOperationTimeEstimatedSecs)会减少。当估计时间低于两秒时MongoDB会阻止写入并完成重新分片操作。在完成重新分片操作的估计时间低于两秒之前重新分片操作默认不会阻止写入。在写入受阻期间应用程序的延迟会增加。重新分片进程完成后重新分片命令会返回ok: 1具体代码如下如果要查看重新分片操作是否成功完成需要检查sh.status()方法的输出具体代码如下sh.status()sh.status()方法的输出包含小部分数据库。如果重新分片成功完成则输出会列出集合的新分片键值具体代码如下如果重新分片的集合使用Atlas Search则在重新分片操作完成后搜索索引变得不可用。重新分片操作完成后需要手动重新生成索引。