分片(sharding)是指将数据库拆分,将其分散在不同的机器上的过程。将数据分散到不同的机器上,不需要功能强大的服务器就可以存储更多的数据和处理更大的负载。
分片是每个分片都拥有整个数据集的一个子集,且相互是不同的数据,多个分片的数据合起来构成整个数据集。
分片的基本思想:
将集合切成小块,这些块分散到若干片里,每个片只负责总数据的一部分。
mongoDB 的分片,是将collection 的数据进行分割,然后将不同的部分分别存储到不同的机器上,当 collection 所占空间过大时,我们需要增加一台新的机器,分片会自动将 collection 的数据分发到新的机器上。然后通过一个名为 mongos 的路由进程进行操作,mongos 知道数据和片的对应关系(通过配置服务器)。
使用场景:
1)机器的磁盘不够用了,使用分片解决磁盘空间的问题。
2)单个mongod已经不能满足写数据的性能要求,通过分片让写压力分散到各个分片上面,使用分片服务器自身的资源。
3)想把大量数据放到内存里提高性能,通过分片使用分片服务器自身的资源。
要构建一个MongoDB Sharding Cluster(分片集群),需要三种角色:
1)分片服务器(Shard Server)
mongod 实例,用于存储实际的数据块,实际生产环境中一个 shard server 角色可由几台机器组个一个 relica set 承担,防止主机单点故障这是一个独立普通的mongod进程,保存数据信息。可以是一个副本集也可以是单独的一台服务器。
2)配置服务器(Config Server)
mongod 实例,存储了整个 Cluster Metadata,其中包括 chunk 信息。这是一个独立的mongod进程,保存集群和分片的元数据,即各分片包含了哪些数据的信息。最先开始建立,启用日志功能。像启动普通的 mongod 一样启动 配置服务器,指定configsvr 选项。不需要太多的空间和资源,配置服务器的 1KB 空间相当于真是数据的 200MB。保存的只是数据的分布表。
3)路由服务器(Route Server)
mongos实例,前端路由,客户端由此接入,且让整个集群看上去像单一数据库,前端应用起到一个路由的功能,供程序连接。本身不保存数据,在启动时从配置服务器加载集群信息,开启 mongos 进程需要知道配置服务器的地址,指定configdb选项。
片键的意义
片键必须是一个索引 ,通过sh.shardCollection 加会自动创建索引。一个自增的片键对写入和数据均匀分布就不是很好, 因为自增的片键总会在一个分片上写入,后续达到某个阀值可能会写到别的分片。但是按照片键查询会非常高效。随机片键对数据的均匀分布效果很好。注意尽量避免在多个分片上进行查询。在所有分片上查询,mongos 会对结果进行归并排序
为何需要水平分片
1)减少单机请求数,将单机负载,提高总负载
2)减少单机的存储空间,提高总存空间
分片组件组成:mongos、config server、shard、replica set。
1)mongos,
数据库集群请求的入口,所有的请求都通过mongos进行协调,不需要在应用程序添加一个路由选择器,mongos自己就是一个请求分发中心,它负责把对应的数据请求请求转发到对应的shard服务器上。在生产环境通常有多mongos作为请求的入口,防止其中一个挂掉所有的mongodb请求都没有办法操作。
2)配置服务器(Config servers)
存储所有数据库元信息(路由、分片)的配置。mongos本身没有物理存储分片服务器和数据路由信息,只是缓存在内存里,配置服务器则实际存储这些数据。
mongos 第一次启动或者关掉重启就会从 config server 加载配置信息,以后如果配置服务器信息变化会通知到所有的 mongos 更新自己的状态,这样mongos 就能继续准确路由。
在生产环境通常有多个 config server 配置服务器,因为它存储了分片路由的元数据,防止数据丢失!
MongoDB是在collection级别实现的水平分片。
3)数据分片(Shards)
分片(sharding)是指将数据库拆分,将其分散在不同的机器上的过程。将数据分散到不同的机器上,不需要功能强大的服务器就可以存储更多的数据和处理更大的负载。
基本思想就是将集合切成小块,这些块分散到若干片里,每个片只负责总数据的一部分,最后通过一个均衡器来对各个分片进行均衡(数据迁移)。
分片(Shards)可以是一个单独的mongod实例,也可以是一个副本集。在高可用性的分片架构还需要对于每一个分片构建replica set副本集保证分片的可靠性。生产环境通常是2个副本+1个仲裁。
4)查询路由(Query Routers)
数据库集群请求的入口,所有的请求都通过 mongos 进行协调,不需要在应用程序添加一个路由选择器,mongos 自己就是一个请求分发中心,它负责把对应的数据请求转发到对应的 shard 服务器上。mongos不要配置为rs,因为它不存储数据。
在生产环境通常有多个 mongos 作为请求的入口,防止其中一个挂掉所有的 mongodb 请求都没有办法操作。
5)replica set(副本集)
其实就是shard的备份,防止shard挂掉之后数据丢失。复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性, 并可以保证数据的安全性。
6)仲裁者(Arbiter)
是复制集中的一个MongoDB实例,它并不保存数据。仲裁节点使用最小的资源并且不要求硬件设备,不能将Arbiter部署在同一个数据集节点中,可以部署在其他应用服务器或者监视服务器中,也可部署在单独的虚拟机中。为了确保复制集中有奇数的投票成员(包括primary),需要添加仲裁节点做为投票,否则primary不能运行时不会自动切换primary。
总结:应用请求mongos来操作mongodb的增删改查,配置服务器存储数据库元信息,并且和mongos做同步,数据最终存入在shard(分片)上,为了防止数据丢失同步在副本集中存储了一份,仲裁在数据存储到分片的时候决定存储到哪个节点。
人脸:
代表客户端。
mongos:
mongos就是一个路由服务器,它会根据管理员设置的"片键"将数据分摊到自己管理的mongod集群,数据和片的对应关系以及相应的配置信息保存在"config服务器"上。
客户端只需要对mongos进行操作就行了,至于如何进行分片,不需要客户端参与,由mongos和config来完成。
mongod:
一个普通的数据库实例或者副本集
分片集群由以下3个服务组成:
Shards Server: 每个shard由一个或多个mongod进程组成,用于存储数据
Config Server: 用于存储集群的Metadata信息,包括每个Shard的信息和chunks信息
Route Server: 用于提供路由服务,由Client连接,使整个Cluster看起来像单个DB服务器
见分支
MongoDB将文档分组成为块,每个块由给定片键特定范围内的文档组成,一个块只存在于一个分片上,所以MongoDB用一个较小的表就能维护块和分片的映射关系。
块与块间的数据不能重复,因此不能使用数组来作为片键,因为MongoDB会为数组创建多个索引条目,从而导致同一数据在多个块中出现。
注意:块是个逻辑概念,而非物理存储实现
查看块信息
块信息保存在config.chunks集合中,sh.status()里面也带有分块的信息。
如果单个分片键可能重复的话,可以创建复合分片键,方式跟创建复合索引一样。
拆分块
mongos会记录每个块中插入了多少数据,如果一个块的数据大小超出了块的大小,或者达到某个阀值(拆分点), MongoDB会自动的拆分这个块。
拆分的时候,配置服务器会创建新的块文档,同时修改旧的块范围。进行拆分的时候,所有的配置服务器都必须可以到达,否则不会进行拆分,此时就会造成"拆分风暴",也就是mongos不断发起拆分请求,却无法拆分的情况。唯一的解决办法就是保证配置服务器的健康和稳定。
均衡器会周期性的检查分片间是否存在不均衡,如果存在,则开始块的迁移。不均衡的表现是:一个分片明显比其他分片拥有更多的块。
1:均衡器可以是任意一个mongos
2:每隔几秒钟,mongos就会尝试成为均衡器,如果没有其他可用的均衡器,mongos就会对整个集群加锁,以防止配置服务器对集群进行修改,然后做一次均衡操作
3:均衡不会影响mongos的正常路由操作,因此对客户端没有任何影响
4:可以查看config.locks集合,看看哪一个mongos是均衡器,语句如下;
db.locks.findOne({"_id":"balancer"});
(1)_id为balancer的文档就是均衡器
(2)字段who表示当前或者曾经作为均衡器的mongos
(3)字段state表示均衡器是否在运行,0非活动,2正在均衡,1正在尝试得到锁,一般不会看到状态1
默认情况下,MongoDB会在分片间均匀的分配数据,但是,如果服务器配置严重失衡,比如某些机器配置非常高,而其他机器只是普通机器,那么就应该使用maxSize选项,来指定分片能增长到的最大存储容量,单位是MB。
例如:db.runCommand({"addShard":"myrepl/ip:port","maxSize":1000});
注意maxSize更像是一个建议而非规定,MongoDB不会从maxSize处截断一个分片,也不会阻止分片增加数据,而是会停止将数据移动到该分片,当然也可能会移走一部分数据。因此可以理解这个maxSize是一个建议或者提示。
1:如果在从机上查询数据,就必须要可接受过时数据
2:使用集群时,不能把整个集合看成一个"即时快照"了,这个问题很严重,比如:
(1)在一个分片集合上,对数据进行计数,很有可能得到的是比实际文档多的数据,因为有可能数据正在移动复制
(2)唯一索引也无法强制保证了
(3)更新操作也面临问题,无法确保在多个分片间只发生一次,因此要更新单个文档,一定要在条件中使用片键,否则会出现问题
当需要进行迁移的时候,将持有过多数据的分片上的块分割,使得分割出来的区间刚好满足迁移需要,然后再进行迁移。
比如:如果shard 1中存有[a, f]区间的数据,数据量为500G,此时需要从shard 1上面迁移100G到shard 4,以保证数据的均匀分布。经统计,shard 1中的[a, d]段的数据为400G,[d, f]段数据为100G,因此将shard 1中的[d, f]段的数据直接迁移到shard 4上面。
同理,需要从shard 2中迁移100G的数据到shard 3中。这种迁移方式的数据迁移量是理论上的最小值。