五、MongoDB的高可用,可伸缩架构

本文主要讨论这几个问题:

  1. 副本集
  2. 分片集群

在第一节文章里,我们提到了机器故障导致系统不可用的问题,以及集中式系统在计算能力和存储能力方面的瓶颈,而通过提升机器硬件性能的纵向扩展方式能力有限,无法满足日益增长的需求,系统需要通过分布式的方式获得横向扩展的能力。这些刚好对应了系统对高可用,以及可伸缩架构的需求。
而MongoDB最吸引人的地方之一,就是它原生支持这两方面的需求,其中高可用对应的就是副本集的部署方式,可伸缩则对应的是分片集群的部署方式。注意,这两种部署方式并不是互相排斥的,比如通过将分片集群的每个分片部署为副本集,则整个集群就兼顾了高可用和高可伸缩的特性。
下面我们就对副本集和分片集群做一个概述,了解它们的作用和基本原理。关于副本集和分片集群在开发和管理上详细的内容则留待后面章节再叙述。

1. 副本集

简单的说,副本集就是通过部署多个mongod实例构成的一个集群,这个集群中的mongod之间有主从关系。

  • 数据复制冗余:
    其中一个mongod实例做为主节点(primary节点),只有这个主节点才能接收写操作,主节点接收到的写操作不仅会保存到其数据文件中,而且还会将写操作记录到一个称为oplog(类似mysql中的binlog)的特殊定容集合中。
    除主节点外的其他节点都是从节点(secondary节点,从节点根据具体的配置和职责,还可以进行细分,为了不增加理解的复杂度,这里暂时不详细描述),从节点不接收写操作,它不断跟踪并同步主节点的oplog,并且在自己的节点上重放这些同步的oplog操作,从而保证拥有和主节点一样的数据副本。
    主、从节点都可以接收读请求。如果读和写都在主节点上,那么这样能避免因为主从数据同步的延迟,而造成读写不一致的情况。如果选择从节点进行读取数据,主节点只负责接收写操作,那么这样就构成了所谓的读写分离结构,通过从节点分担读压力,避免读写都在主上对资源的竞争,能获得更好的读写负载,缺点就是可能会出现读写不一致的情况。
    五、MongoDB的高可用,可伸缩架构_第1张图片
  • 通过选举机制实现自动故障转移:
    上面描述了数据如何在主从节点间复制,保证了数据冗余。但是显然还缺少一种机制,来保证不会因为主节点挂掉以后,我们的MongoDB就无法保证正常的读写服务。而这种机制,就是我们接下来要说的副本集内部的选举机制。在副本集中,节点之间通过长连接维持心跳信息,当我们的主节点由于某种情况down掉以后,从节点检测不到主节点的心跳信息,于是发起选举,而选举的目的,就是重新推选出一个从节点成为新的主节点,以继续正常维持我们的MongoDB数据读写服务。
    五、MongoDB的高可用,可伸缩架构_第2张图片

五、MongoDB的高可用,可伸缩架构_第3张图片

2. 分片集群

无论是单节点MongoDB服务,还是上面副本集的部署方式,我们的所有数据都是保存在一台服务器上的(数据并没有分割放置在多个不同的机器上,副本集的每个节点也都保存的是完整的一份数据)。这样,当数据越来越多,同时进行数据访问的用户越来越多的时候,单个服务器存储能力,以及处理并发读写的能力将成为瓶颈(虽然副本集可以通过读写分离,通过多个从节点分担读压力,但是当数据集越来越大,单台服务器的缓存也无法hold住所有读请求需要的热数据)。
这个时候,我们很容易想到的一种解决方案就是,能不能将数据分成多份,保存到多个不同的服务器,每个服务器就只需要负责其中一部分的存储和读写请求,从而解决了单台服务器无法应对的问题。
这种方案,其实就是我们MongoDB中的分片集群部署模式,在我们的分片集群部署中,每个分片负责保存一部分数据,对于每个分片,我们仍然可以采用上面介绍的副本集的方式,通过复制冗余的方式,保证这部分数据的高可用性。

  • 数据分片路由:
    在分片集群中,首先要解决的问题肯定是,如何对数据进行切分。我们的分片是以集合为单位的,那么MongoDB是如何实现对分片集合进行数据分割的呢?首先,我们要指定集合中的哪个字段(或哪些字段)作为我们的片键。这个字段或多个字段组成的片键构成一个值空间,对数据切分实际上就变为了对这个值域进行划分。根据片键的类型(范围片键,哈希片键等),MongoDB确定了每个分片保存的那部分数据片键的范围。当用户插入一个文档时,MongoDB会根据其中的片键字段信息,判断这个文档应该存放到哪个分片,并将这个写请求路由到对应分片上去。当用户修改或查询一个文档时,如果查询条件中带有片键信息,MongoDB会将这个信息直接路由给对应分片;如果查询条件中没有片键字段,MongoDB则将请求广播给所有分片进行处理。

  • 数据分块,迁移均衡
    通过片键,我们解决了数据分布和路由的问题。但是,我们无法保证数据是完全均匀分布的,也无法保证用户写入数据时是完全均衡的,因此在实际情况中,我们的某些分片可能数据多,某些分片数据少,或者说某些分片增长速度快,某些分片增长速度慢。为了避免因此造成某些节点数据不断增长而无法存储的情况,MongoDB将数据按照一定大小分块(chunk,分块是通过分裂的方式实现的,当数据块到达指定大小时,MongoDB会尝试将这个数据块分裂成两个),当分片间数据块的数量差异达到某个阙值时,MongoDB就会进行数据块迁移,将数据块从多的分片迁移到少的分片上去,同时更新相应的片键分布信息,这样就达到了数据在分片间迁移均衡的目的。

我们结合分片部署的结构,再简要描述下分片集群的原理:在分片集群中,主要有三种角色:负责路由和数据均衡调度的mongos节点,负责保存片键路由等元数据信息的config节点,以及保存数据的shard节点(每个分片可以是单个mongod,也可以是副本集)。首先,用户的请求会发送给mongos节点,mongos节点会从config节点拉取片键相关的路由信息进行同步、缓存,根据这些信息,mongos判断应该将请求发送到哪个(些)分片进行处理,分片处理完成后,再将结果返回给mongos。当分片间数据块不均衡程度超过设定阙值时,mongos就会协调进行数据块在分片间进行迁移,迁移完成后,将新的分片片键映射信息告诉config节点进行更新。
五、MongoDB的高可用,可伸缩架构_第4张图片

你可能感兴趣的:(MongoDB)