深入学习MongoDB中的集群分片技术

随着数据和用户数量的急剧增加,单个 MongoDB 实例可能不足以高效地处理所有数据和请求。分片通过将数据分散到多个服务器上,提高了数据库的存储容量、查询响应速度和并发处理能力。

那么接下来我们将通过这篇文章来学习一下 MongoDB 的集群分片。

什么是分片

分片(Sharding)是一种数据库架构技术,用于处理大规模数据集和高并发请求。它涉及将一个大的数据库分解成多个较小、更易管理和更快速响应的部分。这些较小的部分称为"分片"。每个分片包含数据库的一个子集,并且可以分布在多个服务器或位置上,从而分散负载和存储需求。分片通常用于大型、高流量的应用程序,如社交网络、电子商务网站和大型企业应用。

分片的主要目标和好处包括以下几个方面:

  1. 性能提升:通过分布数据和负载,分片有助于提高查询响应时间和处理更多的并发请求。

  2. 可扩展性:随着数据量和用户基础的增长,分片使得数据库可以通过添加更多的分片或服务器来水平扩展。

  3. 负载均衡:分片可以将数据和流量分布到多个服务器上,有助于均衡负载,减轻单个服务器的压力。

  4. 数据局部性:某些情况下,可以根据地理位置或其他因素将相关数据存储在特定的分片中,从而提高访问速度和效率。

MongoDB 使用分片来支持非常大的数据集和高吞吐量操作的部署。

分片的目的

当数据库应用面临高数据量和高吞吐量的挑战时,单机解决方案往往会遇到性能瓶颈。大量的查询请求可以迅速耗尽单台服务器的 CPU 资源,而大数据量则对存储能力构成压力。随着内存的耗尽,系统不得不依赖更慢的磁盘 IO,导致性能进一步下降。为了解决这些问题,通常有两种扩展策略:垂直扩展和水平扩展。

垂直扩展

垂直扩展,也称为"规模上升",是通过增加单台服务器的硬件资源来提升其性能和容量的方法。这包括增加更多的 CPU 核心、更大的内存、更快的硬盘或更大的存储空间。垂直扩展的优点在于它相对简单,不需要改变现有的应用架构或数据库结构。然而,垂直扩展也有其局限性:

成本问题:高性能的服务器通常成本昂贵,而且价格随着性能的提升而超线性增长。 物理限制:硬件升级总有极限,你不能无限制地增加 CPU 和内存。 单点故障:所有数据和服务集中在一台机器上,若这台机器出现问题,整个系统将受到影响。

水平扩展

水平扩展,或称为"规模外扩展",指的是将数据和负载分布在多台服务器上。在数据库管理中,这通常通过分片来实现。分片涉及将数据划分成多个部分,每部分存储在不同的服务器上。这种方法的优势在于:

可扩展性:通过添加更多的服务器,可以不断扩大数据库的容量和处理能力,应对不断增长的用户和数据量。 灵活性:可以根据需要动态地添加或移除服务器,使得系统更加灵活。 成本效率:使用多台标准的、低成本服务器通常比使用单台高性能服务器更经济。 提高可用性:分布式系统减少了单点故障的风险,通过冗余可以提高整体的系统可用性。 然而,水平扩展也有其挑战,包括增加的系统复杂性、数据一致性的维护、以及可能的网络延迟问题。此外,分片需要仔细的规划,包括如何划分数据以及如何在不同分片之间平衡负载。

总的来说,垂直扩展和水平扩展各有优劣,适用于不同的场景。在一些情况下,结合使用这两种策略可能是最佳解决方案。随着云计算和分布式技术的发展,水平扩展逐渐成为处理大规模数据和高并发应用的首选方式。

分片设计思想

分片设计思想是数据库和应用架构中的一种策略,旨在通过将数据分散存储在多个服务器上来提高系统的可扩展性、性能和可用性。这种方法特别适用于大规模的、高吞吐量的应用场景。分片设计思想的核心在于如何有效地将数据分布在不同的节点上,同时保持数据的可访问性和一致性。

分片的基本原理是数据分区,即将数据分成多个部分,每个部分存储在不同的服务器(或节点)上。数据可以根据不同的分区策略进行划分,如:

  1. 范围分区:根据数据的范围(如日期、ID 范围等)将数据分到不同的分片上。
  2. 哈希分区:使用哈希函数对某个关键字(如用户 ID)进行哈希计算,根据计算结果分配到不同的分片上。
  3. 列表分区:根据预定义的列表将数据映射到特定的分片上。

分片的一个主要目的是提高系统的可扩展性。通过分片,系统可以通过简单地增加更多的服务器来水平扩展,从而支持更多的数据和更高的并发请求。

由于数据被分散存储,多个查询可以进行并行地在不同的服务器上执行,减少了单个服务器的负载,加快了查询相应时间。而且可以提高系统的可用性和容错能力。即使某个分片发生故障,其他分片仍然可以继续提供服务。

例如,如果数据库 1tb 的数据集,并有 4 个分片,然后每个分片可能仅持有 256 GB 的数据。如果有 40 个分片,那么每个切分可能只有 25GB 的数据。

什么是 chunk

使用 MongoDB sharding 后,数据会议 chunk 为单位(默认 64mb) 根据 shardKey 分散到后端一个或多个 shard 上。

Chunk 是根据分片键将数据集分割成的更小、可管理的片段。每个 chunk 包含分片键值范围内的所有数据。通过这种方式将数据在各个分片间移动和平衡的基本单元。MongoDB 使用 chunks 来分散数据负载,保证集群中数据的均匀分布。

Chunk 分裂(Splitting)

随着数据不断增长,某些 chunk 可能会变得过大。如果不加以控制,过大的 chunk 可能会导致数据分布不均、查询性能下降以及单个分片上的负载过重。

它的分裂过程如下步骤所示:

  1. 触发条件:当一个 chunk 的大小达到预设的阈值(默认是 64MB),MongoDB 会自动触发分裂过程。
  2. 分裂过程:MongoDB 选择一个点,这个点基于分片键将当前 chunk 分割成两个更小的 chunk。选择的点尽可能使得两个新 chunk 的大小接近。
  3. 分裂结果:分裂完成后,原来的大 chunk 被两个更小的 chunk 替代。这两个新 chunk 仍然覆盖原来 chunk 的整个键值范围,但每个 chunk 包含的数据量更少。

Chunk 迁移(Migration)

数据分布随时间变化,某些分片可能会存储比其他分片更多的数据。为了保持集群的负载均衡和性能,MongoDB 需要将数据从一个分片迁移到另一个分片。

它的迁移过程如下步骤所示:

  1. 触发条件:MongoDB 的 Balancer 进程会定期检查分片间的数据分布。当它发现数据分布不均时,会自动触发迁移过程。
  2. 迁移过程:Balancer 选择一个分片作为迁移的源,另一个分片作为目的地。然后,它确定哪些 chunk 需要从源分片移动到目的地分片,并逐个执行迁移。
  3. 数据一致性:在迁移过程中,MongoDB 使用锁和版本控制机制确保数据一致性。客户端的读写操作可以在迁移过程中继续进行,不会感知到背后的迁移活动。

随着数据的增长,其中的数据大小超过了配置的 chunk size,默认是 64MB,则这个 chunk 就会分裂成两个。数据的增长会让 chunk 分裂得越来越多。

这时候,各个 shard 上的 chunk 数量就会不平衡。这时候,mongos 中的一个组件 balancer 就会执行自动平衡。把 chunk 从 chunk 数量最多的 shard 节点挪动到数量最少的节点。

Balancer

在 MongoDB 分片集群中,Balancer 是一个后台进程,负责监控和维护分片之间的数据分布平衡。Balancer 的主要目的是确保集群中每个分片的数据量和请求负载大致相等,以此来优化整个集群的性能和效率。

通过确保数据均匀分布,Balancer 有助于优化查询效率和提升整体性能,使得 MongoDB 集群可以平滑地扩展。当新分片加入集群时,Balancer 会自动将数据迁移到新分片,帮助分散负载。并且通过均衡分片上的数据量,Balancer 有助于防止单个分片因过载而成为性能瓶颈或故障点。

chunkSize 对分裂及迁移的影响

MongoDB 默认的 chunkSize 为 64MB,如无特殊需求,建议保持默认值;chunkSize 会直接影响到 chunk 分裂、迁移的行为。

chunkSize 越小,chunk 分裂及迁移越多,数据分布越均衡;反之,chunkSize 越大,chunk 分裂及迁移会更少,但可能导致数据分布不均。

chunkSize 太小,容易出现 jumbo chunk(即 shardKey 的某个取值出现频率很高,这些文档只能放到一个 chunk 里,无法再分裂)而无法迁移;chunkSize 越大,则可能出现 chunk 内文档数太多(chunk 内文档数不能超过 250000 )而无法迁移。

chunk 自动分裂只会在数据写入时触发,所以如果将 chunkSize 改小,系统需要一定的时间来将 chunk 分裂到指定的大小。

chunk 只会分裂,不会合并,所以即使将 chunkSize 改大,现有的 chunk 数量不会减少,但 chunk 大小会随着写入不断增长,直到达到目标大小。

分片集群架构

MongoDB 分片集群由以下组件组成:

  1. 分片:每个分片包含分片数据的子集。每个分片都可以部署为副本集。
  2. mongos:mongos 充当查询路由器,提供客户端应用程序和分片集群之间的接口。从 MongoDB 4.4 开始,mongos 可以支持对冲读取以最大限度地减少延迟。
  3. 配置服务器:配置服务器存储集群的元数据和配置设置。

在 MongoDB 的集群分片架构中,配置服务器(config servers)扮演了重要的角色,但它们的作用并不是直接存储业务数据。

配置服务器主要负责存储整个分片集群的元数据,这包括集群的配置信息和分片数据的分布情况。

元数据是关于数据的数据,对于 MongoDB 分片集群而言,这些元数据包括:

  1. 分片的位置:元数据指明哪些数据存储在哪个分片上。
  2. 集群配置:包括分片的配置,如每个分片的地址、副本集成员等。
  3. 路由信息:帮助查询路由到正确的分片。

分片是存储实际数据的地方。每个分片包含数据的一个子集。分片集群可以有多个分片,每个分片通常是一个独立的数据库服务器或者服务器集群(副本集),这些数据都被分散存储在这些分片上。

路由器(mongos)充当客户端与分片集群之间的中介。它负责接收客户端发来的请求,并基于配置服务器提供的元数据信息,将这些请求智能地定向到适当的分片。这个过程确保了所有查询和写入操作都被精确地发送到持有相关数据的分片上。在这个架构中,客户端无需直接与分片进行通信,而是通过这个高效的路由器与整个集群交互。

配置服务器存储着整个集群运行所必需的关键信息,它们对于集群的正常运行非常重要。如果配置服务器不可用,可能会影响到整个集群的操作,因此通常会有多个配置服务器以保证高可用性和数据的安全。

如下图所示,描述了分片集群内组件的交互:

分片键

分片键是集合内的一个或多个字段,它决定了文档在哪个分片上存储。MongoDB 根据分片键的值将数据分割成不同的范围,每个范围(也称为分区)被指定给一个特定的分片。选择合适的分片键对于优化查询性能、保证数据分布的均衡以及提高写入的吞吐量等方面都至关重要。

分片键可以是单个索引字段,也可以是复合索引覆盖的多个字段,复合索引确定集合文档在集群分片中的分布:

  1. 如果 userId 被选定为分片键,MongoDB 将依据文档中的 userId 值来确定其应归属于哪个分片。在此情形下,文档在各分片之间的分布直接受该字段值的影响。通过精心挑选此类键,可以确保数据在分片间均匀分布,从而避免某个分片过于繁忙而形成热点。
  2. 分片键可以由集合中的多个字段构成。举例来说,若选择 {userId: 1, orderId: 1} 作为分片键,MongoDB 将综合考虑 userId 和 orderId 的值来确定每个文档应存放的分片。这种复合分片键的方法提供了对文档在各分片中分布的更细致控制,有助于实现数据在多维度上的均匀分布,减少数据热点,并在查询条件与分片键匹配时,可能还会提升查询效率。

需要特别注意的是,集合一旦定义了分片键,就无法更改它,并且这个键必须关联一个索引。MongoDB 依赖这个索引来迅速识别和将查询定向到正确的分片。同时,分片键的总大小有一个上限,即 512 字节。这些约束确保了 MongoDB 能够高效地管理和路由集群中的数据。

范围分片

范围分片的工作原理首先涉及选择集合中的一个或多个字段作为分片键,这通常是可以定义数据范围的属性,例如时间戳或用户 ID。接着,系统会根据分片键的值将数据分割成多个连续的范围,每个范围定义了一个键值区间。比如,用户 ID 从 1000 到 1999 可能是一个范围,2000 到 2999 是另一个范围。最后,每个数据范围被分配给集群中的一个分片,确保拥有特定分片键值的所有文档都存储在对应的分片上。通过这种方式,范围分片策略使得相关数据在物理上相邻,从而优化了查询性能,并允许数据在多个分片间均匀分布。

虽然范围分片在多种情况下都非常有用,但在实施时也面临着一些挑战。首先是热点问题,如果大量的写入操作集中在特定范围内,那么对应的分片可能会遇到性能瓶颈。例如,使用时间戳作为分片键时,最新的数据可能都会集中在同一个分片上。其次,数据倾斜也是一个常见的问题,如果分片键的值分布不均匀,可能导致某些分片存储大量数据而其他分片相对较少,造成负载和存储不平衡。最后,随着数据量增长和分布变化,维护分片的范围和数量可能变得复杂,可能需要进行数据迁移和重新平衡,这是一个涉及多方面考量的过程。因此,虽然范围分片提供了许多优势,但也需要仔细规划和持续维护以确保系统的高效和稳定运行。

范围分片特别适合于那些数据具有自然顺序和范围查询需求的应用场景。例如:

  1. 时间序列数据:对于日志、事件或其他时间序列数据,使用时间戳作为分片键可以有效地将数据分布在不同的分片上。

  2. 顺序 ID 分布:对于有自然顺序的 ID(如用户 ID 或订单 ID),范围分片可以提供有效的数据组织和查询性能。

  3. 范围密集型查询:任何经常需要范围查询的应用都可能从范围分片中受益,因为它可以减少需要扫描的分片数量。

范围分片是一个强大的策略,特别适合于那些数据有明显范围和顺序的应用。它可以提高查询效率,优化数据存储,但也需要仔细地规划和维护以避免数据倾斜和热点问题。

哈希分片

哈希分片是 MongoDB 中的一种数据分布策略,它通过计算分片键的哈希值并根据这个哈希结果将数据均匀分配到不同分片上,使用一致性哈希函数确保数据即使在分片数量变化时也能保持均衡分布。

在哈希分片中,MongoDB 首先选择一个或多个字段作为分片键,然后计算插入或更新的文档的分片键的哈希值,根据这个哈希值将文档均匀地分配到集群的不同分片上,确保了数据的均匀分布和有效的查询路由。

对于基于哈希的分片,MongoDB 计算一个字段的哈希值,并用这个哈希值来创建数据块。在使用基于哈希分片的系统中,拥有"相近"片键的文档很可能不会存储在同一个数据块中,因此数据的分离性更好一些。

哈希分片特别适合于那些键值分布不均或没有明显排序和范围模式的场景。例如,在用户 ID 或随机生成的键值等高基数且分布均匀的分片键上表现良好。然而,对于需要频繁进行范围查询的应用,可能需要考虑其他分片策略。

Hash 分片与范围分片互补,能将文档随机的分散到各个 chunk,充分的扩展写能力,弥补了范围分片的不足,但不能高效的服务范围查询,所有的范围查询要分发到后端所有的 Shard 才能找出满足条件的文档。

片键选择原则

MongoDB 中的分片键(Shard Key)是定义数据如何在分片集群中分布的关键因素。选择合适的分片键至关重要,因为它直接影响到集群的性能、数据分布的均匀性以及应用的可扩展性。

分片键在 MongoDB 中扮演至关重要的角色,它不仅决定数据在分片间的分布从而影响读写效率,还能通过实现数据的均匀分布帮助负载均衡,同时,与查询模式对齐的分片键还能优化查询性能并减少跨分片的查询需求。

选择合适的分片键对于确保 MongoDB 分片集群的性能和可扩展性至关重要。以下是一些关于分片键选择的建议:

  1. 高基数:选择一个具有大量唯一值的字段作为分片键。高基数的分片键有助于数据均匀分布,避免某些分片过于繁忙而成为热点。

  2. 写入分散性:选择一个能够均匀分散写入操作的键。如果大量写入集中在某个特定范围,可能会产生写入热点,影响性能。

  3. 查询效率:选择经常用于查询条件的字段作为分片键,这样可以提高查询效率。尽量保证查询操作能够在单个分片上完成,减少跨分片的查询。

  4. 避免递增键:递增键(如时间戳或序列号)可能导致新插入的数据总是分配到同一分片,从而产生热点。考虑使用散列分片键或更随机的键值。

  5. 复合分片键:如果单个字段不足以均匀分布数据或满足查询需求,可以考虑使用多个字段的组合作为复合分片键。

  6. 避免频繁修改的键:分片键一旦选择,将无法更改。选择稳定的字段作为分片键,避免使用可能频繁修改的字段。

选择分片键是一个复杂但至关重要的决策,需要基于对应用的数据特征、查询模式和未来增长的深入理解。正确的分片键选择可以显著提高 MongoDB 集群的性能、可扩展性和效率。

参考资料

总结

MongoDB 的分片集群是一种高效的策略,用于通过在多个服务器上水平分布数据来提高大型数据库的可扩展性和性能。它涉及将数据分为多个 chunk,并根据分片键将它们分配到不同的分片上,从而实现负载均衡和系统的水平扩展。虽然分片集群极大地增强了处理大量数据的能力,但它也需要精心的设计和管理,特别是在选择分片键和进行持续监控方面,以确保数据均匀分布和系统高效运行。

相关推荐
喵叔哟29 分钟前
重构代码之取消临时字段
java·前端·重构
还是大剑师兰特1 小时前
D3的竞品有哪些,D3的优势,D3和echarts的对比
前端·javascript·echarts
王解1 小时前
【深度解析】CSS工程化全攻略(1)
前端·css
一只小白菜~1 小时前
web浏览器环境下使用window.open()打开PDF文件不是预览,而是下载文件?
前端·javascript·pdf·windowopen预览pdf
方才coding1 小时前
1小时构建Vue3知识体系之vue的生命周期函数
前端·javascript·vue.js
man20171 小时前
【2024最新】基于springboot+vue的闲一品交易平台lw+ppt
vue.js·spring boot·后端
阿征学IT1 小时前
vue过滤器初步使用
前端·javascript·vue.js
王哲晓1 小时前
第四十五章 Vue之Vuex模块化创建(module)
前端·javascript·vue.js
丶21361 小时前
【WEB】深入理解 CORS(跨域资源共享):原理、配置与常见问题
前端·架构·web
发现你走远了1 小时前
『VUE』25. 组件事件与v-model(详细图文注释)
前端·javascript·vue.js