分片
在Mongodb里面存在另一种集群,就是分片技术,可以满足MongoDB数据量大量增长的需求。 当MongoDB存储海量的数据时,一台机器可能不足以存储数据,也可能不足以提供可接受的读写吞吐量。这时,我们就可以通过在多台机器上分割数据,使得数据库系统能存储和处理更多的数据。
分片作用
- 通过分片进行水平扩展,解决海量数据的存储问题
- 解决高并发写入或读取数据的问题
存储、读写模型对比
-
单节点:具备MongoDB基本的功能和服务能力,生产环境极少使用 -
副本集:一组具有相同数据的mongod节点服务的集合,一主节点多个备份节点; -
分片集群:把集合的数据分散到多个分片集群,每个分片集群存储一部分数据;
分片的原理
基本的分片集群架构如下图:
分片集群由Shard、Mongos和Config server 3个组件构成:
- Mongos是Sharded cluster的访问入口,把操作请求路由到正确的shard;
- Config server存储了整个 分片群集的配置信息,其中包括 chunk信息;
- Shard存储实际的数据,以chunk为单位存数据;
数据管理
MongoDB将文档分组为块(Chunk),每个块由给定的片键特定范围内的文档组成,一个块只能存在一个分片上,一个分片包含多个块,注意具有相同片键的文档一定保存在相同的块中。Config server用一个比较小的文档就能维护块跟分片的映射关系。 分片支持单个集合的数据分散在多个分片上。目前主要有两种数据分片的策略:
范围分片(Range based sharding)
根据字段的范围不同将一个集合的数据存储在不同的chunk。 优势是需要在一定范围内的查询,因为文档按照片键的区间存储在分片; 缺点是如果shardkey有明显递增(或者递减)趋势,则新插入的文档多会分布到同一个chunk,无法满足高并发的写操作;
hash分片(Hash based sharding)
根据指定的shard key计算hash值,根据hash值的范围不同将文档分布到不同的chunk。 优点Hash分片与范围分片互补,能将文档随机的分散到各个chunk,充分的扩展写能力,弥补了范围分片的不足; 缺点不能高效的服务范围查询,所有的范围查询要分发到后端所有的Shard才能找出满足条件的文档
均衡器
均衡器负载数据的迁移,它会周期性以分片中块(chunk)的数量为标准检查分片间是否需要进行数据迁移。由mongos扮演均衡器的角色,每隔一定时间,mongos就会尝试变身为均衡器。
块(Chunk)
chunk是指一个集合数据中的子集,每个chunk都是基于片键的范围取值,区间是左闭右开;新分片的集合起初只有一个Chunk;
Chunk的拆分
在MongoDB中,mongos会记录在每个Chunk中插入了多少数据,当达到某个阀值,就会触发检查对Chunk进行拆分;
- 块拆分只需要改变块的元数据即可,无需对数据进行移动;
- 具有相同片键的文档必须保存在同一个Chunk;
- mongos通过维护写入计数器判定是否达到拆分阀值,mongos进程重启计数器会重置为0;
Chunk的迁移
均衡器会定期检测各个shard上的Chunk数量分布,当检测到各个shard上的chunk数量存在分布不均匀的情况时;也支持用户主动发起迁移请求;chunk迁移操作是chunk级别的并发,且存在大量密集的写入和删除操作,在业务高峰期对性能造成不小的影响; 如何规避chunk自动迁移的影响:
- 指定只在特定时间点可以进行chunk迁移(或者关闭balancer),避开业务高峰期
- 设置合理的分片建,保证chunk分布均匀,减少chunk迁移操作
- 预分片,减少chunk的迁移
- 如果集合数据分布有较强的倾向性,不一定总能保证chunk的均衡,划分zones也能避免chunk频繁迁移,因为不满足zones约束条件的chunk不会迁移到该zones下的shard
分片键选择
根据均衡器工作原理可了解到即使chunk数量均衡,文档数量可能并不均衡;因此选择片键关乎到每个分片的压力。
影响片键效率的因素
- 片键的取值基数
- 片键的取值分布尽可能均匀
- 避免单调递增或递减的片键
- 定向性好,对主要查询要具有定向能力
- 尽可能符合更多的业务场景
- 片键的类型,单调递增/减选用范围分片会写入集中在单一分片,造成性能瓶颈;
片键注意项
- 文档一旦插入,片键不可变,如需修改需要删掉文档后修改片键重新拆入;
- 片键必须有索引
- 片键的大小限制512字节
- 不能像已经分片的集合插入无片键的文档
片键与索引
- 空集合:如果 shardKey 上的索引不存在,分片操作将自动创建该索引
- 非空集合:必须首先创建 shardKey 字段的索引才能继续分片操作
- 复合索引,其中片键是索引的前缀
- 唯一索引特性
- 不能在哈希索引上指定唯一约束;
- 只能创建
_id 或者片键上的唯一索引或创建以 shardKey 为前缀的唯一复合索引; _id 字段如果不是 shardKey 或 shardKey 的前缀,则_id 索引唯一性仅限于当前分片;- 对于要分片的集合,如果该集合具有其他唯一索引,则无法分片该集合;
- 对于已分片的集合,不能在其他字段上创建唯一索引;
分片相关的指令
sh.getBalancerState() :查看是否开启 Balancer,返回true表示启动;sh.isBalancerRunning() :balancer是否正在运行,返回true表示正在迁移数据db.collection.getShardDistribution() :观察数据在所有分片上的分布情况及是否均衡
|