什么是分片集?
副本集(ReplicaSet) 用于解决读请求扩展、高可用等问题。但随着业务场景的进一步增长,可能会出现以下问题:
- 存储容量超出单机磁盘容量;
- 活跃数据集超出单机内存容量,很多读请求需要从磁盘读取;
- 写入量超出单机 IOPS 上限
MongoDB 分片集群(Sharded Cluster)是对数据进行水平扩展的一种方式,使用分片集群来支持大数据集和高吞吐量的业务场景。
垂直扩容(Scale Up)& 水平扩容(Scale Out):
- 垂直扩容:用更好的服务器,提高 CPU 处理核数、内存数、带宽等;
- 水平扩容:将任务分配到多台计算机上
分片集基本架构
- Mongos
- 分片集群的访问入口;
- 对请求进行路由、分发、合并;
- 部署多个 Mongos 来保证高可用
- ConfigServer
- 存储元信息和集群配置;
- 部署为副本集来保证高可用
- Shard
- 存储用户数据,不同 Shard 保存不同用户数据;
- 部署为副本集来保证高可用
有了一个分片集群以后,Drivers 需要通过连接 Mongos 来达到和整个集群交互的目的,而 Mongos 则会根据客户端的请求来向后端不同的 Shard 进行请求的发起。
默认情况下,每个 database 的集合都是未分片的,存储在一个固定的 shard 上,称为 primary shard。当创建一个 database 时,系统会根据各个 shard 的存储数据量,选择一个数据量最小的 shard 作为新 database 的 primary shard。
eg:sh.enableSharding("records")
用于启用 records 数据库的分片功能; sh.shardCollection("records.people", { zipcode: 1 } )
对 records.people 集合基于 zipcode 的范围进行分片。
分片键(Shard Key)
- 范围分片
根据 Shard Key 的值进行数据分片。- 优点:很好的满足范围查询的需求;
- 缺点:分片键单调写入,无法扩充写能力
如上图所示,是一个基于 x 的范围分片,数据被分为了 4 部分,切割点分别是 x: -75,x: 25,x: 175 值,相近的数据是相邻的。在这种情况下,可以很好的满足范围查询的需求;但是如果是基于分片键的单调写入,由于所有的写入数据都会被最后一个 Chunk 来承载,所以就无法很好的扩充写能力。
- 哈希分片
根据 Shard Key 计算哈希值,基于哈希值进行数据分片。- 优点:分片单调写入,充分的扩展写能力;
- 缺点:不能高效的进行范围查询
如上图所示,数据经过哈希计算后被打散到不同的 Chunk 上,对于分片键单调写入的场景,可以充分的扩展写能力,但是却不能高效的进行范围查询。
选择合理的分片键
- Cardinality(基数):越大越好。例如若以性别作为分片键,则数据最多被拆分成 2 份;若以月份作为分片键,则数据可以被拆分成 12 份;
- Frequency(频率,文档中出现某个值的频率):越低越好。例如若以所在城市作为分片键统计全国人口分布情况,则一线城市所在的 Chunk 存储大量数据;
- ·Monotonically Changing(单调变化):使用哈希分片记录日志集合,使用日志生成时间作为分片键。例如若使用范围分片,数据写入只会在最后一个 Shard 上完成
分片键的约束
Shard Key 必须是一个索引。非空集合须在 ShardCollection 前创建索引(用于将一个集合分片的命令);空集合 ShardCollection 自动创建索引。
- 4.4 版本之前:
- Shard Key 大小不能超过 512 Bytes;
- 仅支持单字段的哈希分片键,例如 { x: “hashed” };
- Document 中必须包含 Shard Key;
- Shard Key 包含的 Field 不可以被修改
- 4.4 版本之后:
- Shard Key 大小不作限制;
- 支持复合哈希分片键,例如 { x: 1, y: “hashed” };
- Document 中可以不包含 Shard Key,插入时被当做 Null 处理;
- 为 Shard Key 添加后缀 refineCollectionShardKey 命令,可以修改 Shard Key 包含的 Field
- 4.2 版本之前,Shard Key 对应的值不可以修改;
- 4.2 版本之后,如果 Shard Key 为非 _ID 字段,那么可以修改 Shard Key 对应的值
Mongos 请求转发策略
- 特定目标的操作(Targeted Operations)
根据分片键计算出目标 Shard(s),然后发起请求并返回结果。包含分片键的查询、更新、删除、插入操作。
如上图所示,以 a 为 Shard Key 时,如果请求当中携带了 a 字段,那么 Mongos 就可以识别出来它的目标 Shard,如果是 Shard B,就可以直接跟 Shard B 进行交互,获取结果并返回给客户端。
- 广播的操作(Broadcast Operations)
将请求发送给所有的 Shard,合并查询结果并返回给客户端。不包含分片键的查询操作、_ID 字段的更新、删除操作。
Chunk & Balancer
MongoDB 基于 Shard Key 将 Collection 拆分成多个数据子集,每个子集称为一个 Chunk,Chunk 是分割存储在分片集群上的数据集的最小单元。
ShardedCollection 的数据按照 Shard Key 划分为 MinKey ~ MaxKey 区间;每个 Chunk 有自己负责的一个区间(左闭右开);存储 ShardedCollection 的 Shard 上有该 Collection 的一个或多个 Chunk。
Chunk 分裂(Chunk Splits)
随着数据的写入,当 Chunk 增长到指定大小(默认为 64 MB)时,MongoDB 会对 Chunk 进行分裂。
- 手动触发
sh.splitAt(namespace, query)
,指定 Chunk 分裂点;sh.splitFind(namespace, query)
,从中间分裂目标 Chunk;- 调整 ChunkSize。调小 ChunkSize 可以让 Chunk 更均衡的分布,但是 Chunk 迁移次数会增加;调大 ChunkSize 会减少 Chunk 迁移次数,但会导致 Chunk 分布不均
- 自动触发
只有插入和更新操作才会触发自动 Chunk Split。当 Chunk Size 被调小时,不会立即发生 Chunk Split
注意:一个最小的 Chunk 可以只包含一个唯一的 Shard Key,这样的 Chunk 不可以再进行分裂,称为 JumboChunk。
Chunk 迁移(Chunk Migration)
保证数据负载均衡,MongoDB 支持 Chunk 在 Shard 之间进行迁移。
- 自动触发
当 Chunk 在 Shard 之间分布不均时,Balancer 进程会自动触发; - 手动触发
sh.moveChunk(namespace, query, destination)
Chunk 迁移的影响:
- 影响 Shard 使用磁盘的大小;
- 增加网络带宽及系统负载,会对系统性能造成影响
Chunk 迁移的约束:
- 每个 Shard 在同一时间内只能有一个 Chunk 进行迁移;
- 不会迁移 Chunk 中文档数量是平均 Chunk 文档数 1.3 倍的 Chunk(4.4 版本中提供了选项支持)
Balancer 进程:
- Balancer 是 MongoDB 的一个后台进程,用以保证集合的 Chunk 在各个 Shard 上是均衡的;
- Balancer 运行在 ConfigServer 的 Primary 节点。 默认为开启状态;
- 当分片集群中发生 Chunk 不均衡时,Balancer 将触发 Chunk 从 Chunk 数量最多的 Shard 向 Chunk 数量最少的 Shard 上进行迁移
参考资料
- 玩转 MongoDB 从入门到实战