- 文档
- 教程
- 数据库
【免费下载链接】db-tutorial
📚 后端程序员应该掌握的主流数据库知识
导读:本文是 db-tutorial 项目 MongoDB 专题的核心篇章之一,围绕 MongoDB 分片 展开。当单节点无法承载海量数据或吞吐量瓶颈时,MongoDB 通过分片(Sharding)机制提供水平扩展能力。读完本文,你将掌握分片集群的三大组件(shard、mongos、config servers)及其职责、分片 Key 的设计原则与变更规则、Hash 分片与范围分片两种策略的取舍,以及如何将应用接入分片集群进行读写。本文内容以 MongoDB 4.4 版本特性为基础(与本仓库 MongoDB 运维 文档所采用的 4.4.0 版本一致),并辅以仓库内 Spring Boot Data MongoDB 工程的配置与源码进行印证。
一、为什么需要分片:从垂直扩展到水平扩展
当 MongoDB 需要存储海量数据时,单个节点往往面临两重压力:
- 容量压力:单节点磁盘不足以存储全量数据;
- 吞吐压力:单节点的 CPU、内存、IO 无法提供令人满意的读写吞吐量。
面对这种情况,有两种扩展思路:
- 垂直扩展(Scale Up):升级单机硬件(更强的 CPU、更大的内存与磁盘)。这种方案成本高、存在物理上限,且扩容时需要停机维护。
- 水平扩展(Scale Out):将数据分散到多台服务器上,让集群整体承载数据与请求。MongoDB 的分片机制正是为此设计的水平扩展方案。
分片集群特点
MongoDB 分片集群具有以下核心特点:
- 对应用完全透明:应用无需感知数据分布在哪个分片,读写请求都经由统一入口转发;
- 数据自动均衡:均衡器(Balancer)在后台运行,自动在各分片间迁移数据块(chunk),维持数据分布均匀;
- 动态扩容:可以随时向集群添加新的分片节点,无需停止服务;
- 提供三种分片方式:MongoDB 支持 Hash 分片、范围分片,以及基于区域(Zone)的分片数据局部性控制(本文后续展开)。
从本仓库 MongoDB 应用指南 的对比表可以看出,MongoDB 的扩展性定位是"垂直扩展 + 水平扩展"双轨支持,数据容量"没有理论上限"——这正是分片机制的价值所在。
二、分片集群组件
一个完整的 MongoDB 分片集群包含三类组件:
1. shard(分片)
- 每个分片包含分片数据的子集;
- 每个分片都可以部署为副本集(Replica Set),从而在分片内部获得高可用能力——这与本仓库 MongoDB 复制 文档描述的主从同步、故障转移机制一脉相承:主节点负责写操作并记录 oplog,从节点异步同步数据,主节点失联时自动选举新主节点。分片集群的生产实践通常要求每个 shard 本身就是一个三节点副本集。
2. mongos(查询路由器)
- mongos 充当查询路由器(Query Router),在客户端应用程序和分片集群之间提供统一接口;
- 从 MongoDB 4.4 开始,mongos 可以支持hedged reads(对冲读),同时向多个副本集成员发送读请求并返回最先到达的结果,以最大程度地减少延迟;
- 客户端只与 mongos 通信,绝不直接连接单个分片进行读写。
3. config servers(配置服务器)
- 提供集群元数据存储和分片数据分布的映射;
- 分片 Key 与 chunk 的分布信息、集群中每个分片的状态都保存在 config 服务器上;
- 生产环境中 config servers 通常也以副本集形式部署,避免单点故障导致整个集群无法路由。
三者协作的工作流程是:客户端 → mongos 路由 → 根据 config servers 中的元数据映射 → 定位到具体 shard 完成读写。
三、分片集群的分布规则
理解分片集群的数据分布,需要把握以下几个关键规则:
collection 是分片的基本单位
MongoDB 以 collection 为单位,将数据分布在集群中的各个分片上。分片时针对具体 collection 执行,而非对整个数据库整体打散。集群最多允许1024 个分片。
分片之间数据不重复
MongoDB 分片集群的分片之间数据不重复(区别于复制集的数据冗余),只有当所有分片都正常时,集群才能完整工作——某个分片不可用会导致其承载的那部分数据无法访问。这正是每个 shard 需要以副本集方式部署的原因:副本集保证单个 shard 内部的高可用,从而间接保障整个集群的完整性。
分片与未分片的 collection 并存
一个数据库可以同时包含分片和未分片的 collection:
- 分片 collection:分布在集群各节点上,数据被切成 chunk 分散到多个 shard;
- 未分片 collection:存储在主节点(primary shard)上;
- 每个数据库都有其自己的主节点——这里的"主节点"是指该数据库未分片集合的默认存储分片,可通过
movePrimary命令调整。
四、路由节点 mongos 详解
要连接 MongoDB 分片集群(无论读写分片还是未分片的 collection),必须连接到 mongos 路由器。客户端不应该连接到单个分片节点进行读写操作。
连接 mongos 的方式与连接 mongod 完全相同,例如通过mongoshell 或 MongoDB 驱动程序连接其监听端口即可。
路由节点 mongos 的核心作用:
- 提供集群的单一入口:客户端只维护一个连接地址;
- 转发应用端请求:接收应用发来的读写与查询指令;
- 选择合适数据节点进行读写:根据 config servers 的元数据,将请求路由到承载目标数据 chunk 的 shard;
- 合并多个数据节点的返回:对跨分片查询(如范围查询跨越多个 chunk)结果进行合并、排序后统一返回给客户端。
因为 mongos 是集群的单一入口,承担所有请求转发,一般建议 mongos 节点至少部署 2 个,既提供入口高可用,也分担路由压力。
五、分片 Key 设计
MongoDB 使用分片 Key在各个分片之间分发 collection 的 document。分片 Key 由 document 中的一个或多个字段组成,它是整个分片集群设计的"灵魂"。
分片 Key 字段的缺失与更新规则
分片 Key 字段在不同版本下的约束不同,本文以 4.4 版本为基准:
| 能力 | MongoDB 4.4 及以后 | 更早版本 |
|---|---|---|
| 分片 Key 字段缺失 | 允许缺失,跨分片分布文档时视为空值;但路由查询时不会按空值处理 | 4.2 及更早版本要求分片 collection 的每个 document 都必须存在分片 Key 字段 |
| 分片后优化分片 Key | 可通过在现有 Key 上添加一个或多个后缀字段来优化 | 4.2 及更低版本无法在分片后更改分片 Key 的选择 |
| 更新 document 的分片 Key 值 | 4.2 起允许更新,除非分片 Key 字段是不可变的_id字段 | 4.0 及更低版本中分片 Key 字段值不可变 |
提示:以上版本差异是选择分片 Key 时必须考虑的前置条件。若应用可能需要在分片后调整 Key,应规划 4.4+ 版本并保留后缀字段的扩展余地。
分片 Key 索引要求
要对已填充的 collection进行分片,该 collection 必须具有以分片 Key 开头的索引(分片 Key 可以是该复合索引的前缀)。分片一个空 collection时,如果该 collection 还没有针对指定分片 Key 的适当索引,MongoDB 会自动创建支持索引。
这与本仓库 MongoDB 索引 文档中"索引是特殊的数据结构,存储在一个易于遍历读取的数据集合中"的描述相呼应——分片路由依赖索引快速定位 chunk 边界。
分片 Key 策略:影响均衡与性能
分片 Key 的选择会影响分片集群的性能、效率和可伸缩性,分片 Key 及其后备索引的选择也会影响集群可以使用的分片策略。
MongoDB 将分片数据划分为chunk(数据块),每个 chunk 都有基于分片 Key 的上下限。为了在整个集群中的所有分片上实现 chunk 的均匀分布,均衡器(Balancer)在后台运行,并在各分片间迁移 chunk。分片 Key 设计不当会导致某些 chunk 数据量过大(数据倾斜),均衡器频繁迁移数据,反而拖累集群性能。
六、分片策略:Hash 分片与范围分片
MongoDB 支持两种分片策略:Hash 分片和范围分片。
1. Hash 分片
Hash 分片策略的处理流程:
- 先计算分片 Key 字段值的哈希值;
- 然后根据分片键值(哈希结果)为每个 chunk 分配一个范围。
优点:尽管分片 Key 的范围可能是"接近"的,但它们的哈希值不太可能落在同一个 chunk 上。基于 Hash 的数据分发有助于实现更均匀的数据分布,尤其适合分片 Key单调递增/递减的数据集(如自增 ID、时间戳、订单号)——这类 Key 若用范围分片会全部写入同一个分片,而 Hash 分片会将相邻 Key 打散到不同分片。
代价:Hash 分片意味着对分片 Key 做范围查询时,不太可能针对单个分片,往往需要把查询广播到多个分片执行,产生更多集群范围内的广播操作,范围查询性能下降。
注意:使用哈希索引解析查询时,MongoDB 会自动计算哈希值,应用程序不需要自行计算哈希。
2. 范围分片
范围分片的处理流程:
- 根据分片 Key 值将数据划分为多个范围;
- 根据分片 Key 值为每个 chunk 分配一个范围。
优点:值比较近似的一系列分片 Key 更有可能驻留在同一个 chunk 上,因此范围查询可以精准命中单个(或少数)分片,避免广播,范围查询效率高。
缺点:范围分片的效率完全取决于选择的分片 Key。分片 Key 考虑不周全会导致数据分布不均(例如按时间戳范围分片时,新数据永远写入最新的分片),这会削弱分片的某些优势,甚至导致性能瓶颈。
两种策略如何选
| 对比维度 | Hash 分片 | 范围分片 |
|---|---|---|
| 数据分布均匀度 | 高(尤其适合单调 Key) | 依赖分片 Key 的取值分布 |
| 单点范围查询 | 差(易触发广播) | 好(精准路由到少数分片) |
| 典型场景 | 自增 ID、时间戳等单调字段,且以单点读写为主 | 业务上强依赖 Key 范围查询(如按用户 ID 区间、地域分区) |
实践中常见做法是:业务以等值查询为主、Key 单调增长时选 Hash 分片;业务强依赖范围扫描时选范围分片,但要精心设计 Key 的取值分布。
七、分片集群中的区域(Zone)
区域(Zone)机制用于提高跨多个数据中心的分片集群的数据局部性(Data Locality)。
核心规则:
- 在分片集群中,可以基于分片 Key 创建分片数据的区域;
- 可以将每个区域与集群中的一个或多个分片关联;一个分片可以与任意数量的区域关联;
- 在平衡的集群中,MongoDB仅将区域覆盖的 chunk 迁移到与该区域关联的分片——即通过区域控制"哪些数据放在哪些分片(机房)";
- 每个区域覆盖一个或多个分片 Key 值范围,区域覆盖的每个范围始终包括其上下边界(闭区间);
- 在定义区域覆盖的新范围时,必须使用分片 Key 中包含的字段;如果使用复合分片 Key,则范围必须包含分片 Key 的前缀。
实践建议:选择分片 Key 时,就应考虑将来可能使用的区域。例如按用户所在地区作为区域划分维度时,分片 Key 应包含地区字段或其前缀,否则后续无法按区域精准放置数据。
八、实操:将应用接入分片集群
1. 应用只连 mongos
分片集群部署完成后,应用(或mongoshell)的连接地址是mongos 的地址与端口,而不是某个 mongod 分片节点。典型连接串形如:
mongodb://mongos-host1:27017,mongos-host2:27017/admin客户端 SDK 会像连接普通 mongod 一样连接 mongos;路由、合并、均衡等逻辑全部由 mongos 与集群内部完成,应用侧无感知——这正是"对应用完全透明"的体现。
2. 分片管理命令
分片相关的管理操作通过连接 mongos 后在mongoshell 中执行(以下为 MongoDB 4.4 标准管理命令用法,与本仓库运维文档采用的 4.4.0 版本对应,具体命令参数以官方文档为准):
// 启用数据库分片 sh.enableSharding("test") // 对 collection 执行分片:指定分片 Key // 范围分片 sh.shardCollection("test.order", { customerId: 1 }) // Hash 分片 sh.shardCollection("test.order", { _id: "hashed" }) // 查看分片状态 sh.status()- 对已填充 collection 分片前,需先创建以分片 Key 开头的索引;
"hashed"表示对该字段建立哈希索引并采用 Hash 分片策略。
3. 仓库中 Spring Boot 应用的连接配置
本仓库的 MongoDB 工程位于 codes/javadb/mongodb,基于 Spring Boot 2.6.3 与spring-boot-starter-data-mongodb(见 pom.xml)。其默认连接配置在 application.properties 中:
spring.data.mongodb.host = localhost spring.data.mongodb.port = 27017 spring.data.mongodb.database = test #spring.data.mongodb.username = #sprin.data.mongodb.password =当目标环境是分片集群时,连接配置应指向 mongos(如多 mongos 可用逗号分隔的地址),并可进一步启用鉴权字段:
spring.data.mongodb.host = mongos1,mongos2 spring.data.mongodb.port = 27017 spring.data.mongodb.database = test spring.data.mongodb.username = root spring.data.mongodb.password = root应用启动后,Spring Data MongoDB 通过MongoTemplate与仓库(Repository)抽象完成数据访问。仓库工程中的实体类(如 aggregation/Order.java)以@Document标注映射 collection,仓储接口(如 OrderRepository.java)除继承CrudRepository外,还支持通过@Aggregation注解声明聚合管道,对应的集成测试见 OrderRepositoryIntegrationTests.java。
在分片集群场景下,这些实体与仓库代码无需任何改动——MongoDB 分片的"透明性"保证了应用层代码与单机、复制集环境完全一致;差异仅在部署层(分片集群的 mongod 以--shardsvr启动、config server 以--configsvr启动、mongos 以--configdb指定配置服务器集合),这与复制集是"部署形态"而非"应用形态"的差别同理,可参考 MongoDB 复制 中"副本集是一组维护相同数据集的 mongod 进程"的表述。
九、总结与延伸阅读
MongoDB 分片是一个"部署层面"的横向扩展方案,理解它的关键在于三个层面:
- 组件层面:shard(数据子集 + 副本集高可用)、mongos(统一路由入口,至少 2 个)、config servers(元数据与分布映射);
- 数据层面:以 collection 为分片单位、最多 1024 个分片、chunk 由均衡器自动迁移、分片 Key 决定数据分布与策略选择;
- 设计层面:分片 Key 的选择(单调性、范围查询需求、未来区域规划)直接决定 Hash/范围分片的效果,是分片成败的核心变量。
结合本仓库继续深入学习:
- MongoDB 分片(本文)
- MongoDB 复制:分片集群中每个 shard 的高可用基础
- MongoDB 索引:分片 Key 的后备索引要求
- MongoDB 应用指南:数据库、集合、文档等基础概念
- MongoDB 运维:mongod 启动、备份恢复等运维操作
- MongoDB 专题 README:完整教程导航
- 文档
- 教程
- 数据库
【免费下载链接】db-tutorial
📚 后端程序员应该掌握的主流数据库知识
相关推荐
MongoDB水平扩展:分片集群架构与数据分布策略
MongoDB水平扩展:分片集群架构与数据分布策略 MongoDB分片集群是一个高度可扩展的分布式数据库架构,通过将数据水平分割到多个服务器节点来实现大规模数据
数据库文档数据库后端magnetW数据库分片策略:范围与哈希分片
magnetW数据库分片策略:范围与哈希分片 数据库分片(Database Sharding)是解决大规模数据存储与查询性能问题的关键技术。在magnetW项目
桌面应用网页爬虫CANN/asc-devkit:设置GM到L1内层循环步长
asc_set_gm2l1_loop1_stride 产品支持情况 |产品 | 是否支持 | | |: :| | Ascend 950PR/Ascend 950
人工智能深度学习算子库CANNAscend
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考