一、MongoDB分片介绍
1.1 什么是MongoDB分片
对于数据库来讲,在大数据量和高吞吐量的场景下,会对单节点造成较大压力,大查询会将单机CPU耗尽,大数据量会对单机的存储压力较大,最终会耗尽系统的内存而将压力转移到磁盘IO上。MongoDB采用分片技术来将大型集合分割到不同服务器上,也就是水平扩展。
分片技术为应对高吞吐量与大数据量提供了方法,减少了每个分片需要存储的数据量和需要处理的请求数,因此,通过分片集群可以提高MongoDB的存储容量和吞吐量。
1.2 分片技术的优势
1、透明化
MongoDB自带mongos路由进程。通过mongos将客户端发来的请求准确无误的路由到集群中的一个或者一组服务器上,同时会把接收到的响应聚合起来发回到客户端。
2.高可用
MongoDB通过将副本集和分片集群结合使用,在确保数据实现分片的同时,也确保了各分片数据都有相应的备份,这样就可以确保当主服务器宕机时,其他的从库可以立即替换,继续工作。
3.易扩展
当系统需要更多的空间和资源的时候,MongoDB使我们可以按需方便的扩充系统容量。
组件 | 说明 | |
---|---|---|
Mongos | 提供对外应用访问,所有操作均通过mongos执行。一般有多个mongos节点。数据迁移和数据自动平衡。 | |
Config Server | 存储集群所有节点、分片数据路由信息。默认需要配置3个Config Server节点。 | |
Mongod | 存储应用数据记录。一般有多个Mongod节点,达到数据分片目的。 |
Mongos本身并不持久化数据,分片集群将所有的元数据都会存储到Config Server,而数据会分散存储到各个分片。Mongos启动后,会从Config Server加载元数据,开始提供服务,将用户的请求正确路由到对应的分片。当数据写入时,MongoDB Cluster根据分片键设计写入数据。当外部语句发起数据查询时,MongoDB根据数据分布自动路由至指定节点返回数据。
二、集群数据分布
2.1 Chunk是什么
在一个分片节点内部,MongoDB会把数据分为chunks,每个chunk代表这个分片节点内部一部分数据。chunk会有以下两个用途:
- 分裂:当一个chunk的大小超过配置中的chunk size时,MongoDB的后台进程会把这个chunk切分成更小的chunk,从而避免chunk过大的情况。
- 迁移:在MongoDB中,balancer是一个后台进程,负责chunk的迁移,从而均衡各个分片节点的负载,系统初始1个chunk,chunk size默认值64M,生产库上选择适合业务的chunk size是最好的。
MongoDB会自动拆分和迁移chunks
2.2 分片集群的数据分布
- 使用chunk来存储数据
- 集群搭建完成之后,默认开启一个chunk,大小是64M
- 存储需求超过64M,chunk会进行分裂,如果单位时间存储需求很大,设置更大的chunk
- chunk会被自动均衡迁移
2.3 chunk size的选择
适合业务的chunk size是最好的
chunk的分裂和迁移非常消耗IO资源;当插入和更新时会导致chunk分裂,读数据不会分裂。
- 小chunk size:数据均衡是迁移速度快,数据分布更均匀。数据分裂频繁,路由节点消耗更多资源。
- 大chunk size:数据分裂少,数据块移动集中消耗IO资源,通常100-200M。
2.4 chunk分裂及迁移
随着数据的增长,其中的数据大小超过了配置的chunk size,则这个chunk就会分裂成两个。数据的增长会让chunk分裂得越来越多。这时候,各个分片上的chunk数量就会不平衡。这时候,mongos中的一个组件balancer 就会执行自动平衡。把chunk从chunk数量最多的shard节点挪动到数量最少的节点。
chunk Size越小,chunk分裂及迁移越多,数据分布越均衡;反之,chunk Size越大,chunk分裂及迁移会更少,但可能导致数据分布不均。chunk Size太小,容易出现 jumbo chunk(即shardKey 的某个取值出现频率很高,这些文档只能放到一个 chunk 里,无法再分裂)而无法迁移;chunk Size 越大,则可能出现 chunk 内文档数太多(chunk内文档数不能超过250000)而无法迁移。
chunk自动分裂只会在数据写入和更新时触发,所以如果将chunk Size改小,系统需要一定的时间来将chunk分裂到指定的大小。chunk只会分裂,不会合并,所以即使将chunk Size改大,现有的chunk数量不会减少,但chunk大小会随着写入不断增长,直到达到目标大小。
2.5 数据区分
MongoDB中数据的分片是以集合为基本单位的,集合中的数据通过分片键被分成多部分。分片键是作为数据拆分的依据,所以一个好的分片键至关重要。分片键必须有索引,通过sh.shardCollection会自动创建索引(前提是此集合不存在的情况下)。
一个自增的分片键对写入和数据均匀分布就不是很好,因为自增的分片键总会在一个分片上写入,后续达到某个阀值可能会写到别的分片。但是按照分片键查询会非常高效。随机分片键对数据的均匀分布效果很好,注意尽量避免在多个分片上进行查询。在所有分片上查询,mongos会对结果进行归并排序。
对集合进行分片时,需要选择一个分片键,分片键是每条记录都必须包含的,且建立了索引的单个字段或复合字段,MongoDB按照分片键将数据划分到不同的数据块中,并将数据块均衡地分布到所有分片中。
为了按照片键划分数据块,MongoDB使用基于范围的分片方式或者基于哈希的分片方式:
注意:
- 分片键是不可变。
- 分片键必须有索引。
- 分片键大小限制512bytes。
- 分片键用于路由查询。
- MongoDB不接受已进行collection级分片的collection上插入无分片键的文档(也不支持空值插入)
2.5.1 以范围为基础的分片
MongoDB将单个集合的数据分散存储在多shard上,用户可以指定根据集合内文档的某个字段即分片键来进行范围分片。对于基于范围的分片,MongoDB按照片键的范围把数据分成不同部分。在使用分片键做范围划分的系统中,相近的分片键文档很可能存储在同一个数据块中,因此也会存储在同一个分片中。
2.5.2 基于哈希的分片
分片过程中利用哈希索引作为分片的单个键,且哈希分片的片键只能使用一个字段,而基于哈希片键最大的好处就是保证数据在各个节点分布基本均匀。
对于基于哈希的分片,MongoDB计算一个字段的哈希值,并用这个哈希值来创建数据块。在使用基于哈希分片的系统中,拥有”相近”片键的文档很可能不会存储在同一个数据块中,因此数据的分离性更好一些。
哈希分片与范围分片互补,能将文档随机的分散到各个chunk,充分的扩展写能力,弥补了范围分片的不足,但不能高效的服务范围查询,所有的范围查询要分发到后端所有的Shard才能找出满足条件的文档。
2.6 分片键选择建议
1、递增的sharding key
- 数据文件挪动小
2、随机的sharding key
- 数据分布均匀,insert的写IO均匀分布在多个片上
3、混合型key
- 大方向随机递增,小范围随机分布
为了防止出现大量的chunk均衡迁移,可能造成的IO压力。我们需要设置合理分片使用策略(分片算法(range、hash))
注意:
分片键是不可变、分片键必须有索引、分片键大小限制512bytes、分片键用于路由查询。
MongoDB不接受已进行collection级分片的collection上插入无分片键的文档(也不支持空值插入)
三、总结
MongoDB分片集群类似于MySQL中间件集群,解决了大数据量高吞吐的业务场景。