深入理解MongoDB

文章目录

  • 一、副本集详解
    • 1、基本概念
      • (1)副本集的作用
      • (2)oplog简介
    • 2、副本集的基本架构
    • 3、副本集选主过程
      • (1)三个存储数据的集群
      • (2)arbiter节点
      • (3)Bully算法
      • (4)MongoDB的选主过程
      • (5)为什么副本集数量最好为奇数?
      • (6)心跳和同步机制
  • 二、MongoDB的路由、分片技术
    • 1、MongoDB的Sharding架构
    • 2、分片的优势
      • (1)chunk分裂及迁移
      • (2)chunkSize对分裂及迁移的影响
    • 3、数据区分
      • (1)基于范围的分片
      • (2)基于哈希的分片
  • 三、MongoDB的索引原理
    • 1、索引类型
  • 四、mongorocks引擎原理解析
    • 1、数据存储及索引
    • 2、存储管理
  • 五、MongoDB重要机制总结
    • 1、MongoDB数据文件内部结构
    • 2、MongoDB数据同步
    • 3、分片机制
    • 4、服务器角色
  • 六、MongoDB数据一致性问题
    • 1、WriteConcern
    • 2、ReadConcern
    • 3、MongoDB如何保证数据一致性?

一、副本集详解

1、基本概念

  一组副本集就是一组mongod实例掌管同一个数据集,实例可以在不同的机器上面。实例中包含一个主导,接受客户端所有的写入操作,其他都是副本实例,从主服务器上获得数据并保持同步。

  主服务器很重要,包含了所有的改变操作(写)的日志。但是副本服务器集群包含有所有的主服务器数据,因此当主服务器挂掉了,就会在副本服务器上重新选取一个成为主服务器。每个副本集还有一个仲裁者,仲裁者不存储数据,只是负责通过心跳包来确认集群中集合的数量,并在主服务器选举的时候作为仲裁决定结果

  常见MongoDB集群是一个主节点和两个副本节点。主节点负责处理客户端请求,其余的副本节点节点,负责复制主节点上的数据。主节点记录在其上的所有操作oplog,副本节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致

(1)副本集的作用

  MongoDB的Replica Set即副本集方式主要有两个目的,一个是数据冗余做故障恢复使用,当发生硬件故障或者其它原因造成的宕机时,可以使用副本进行恢复。

  另一个是做读写分离,读的请求分流到副本上,减轻主(Primary)服务器的读压力。

深入理解MongoDB_第1张图片

(2)oplog简介

  oplog是local库下的一个固定集合,Secondary就是通过查看Primary 的oplog这个集合来进行复制的。每个节点都有oplog,记录这从主节点复制过来的信息,这样每个成员都可以作为同步源给其他节点。

  副本集中数据同步的详细过程:Primary节点写入数据,Secondary通过读取Primary的oplog得到复制信息,开始复制数据并且将复制信息写入到自己的oplog。如果某个操作失败(只有当同步源的数据损坏或者数据与主节点不一致时才可能发生),则备份节点停止从当前数据源复制数据。如果某个备份节点由于某些原因挂掉了,当重新启动后,就会自动从oplog的最后一个操作开始同步,同步完成后,将信息写入自己的oplog,由于复制操作是先复制数据,复制完成后再写入oplog,有可能相同的操作会同步两份,不过MongoDB在设计之初就考虑到这个问题,将oplog的同一个操作执行多次,与执行一次的效果是一样的(幂等性)。

2、副本集的基本架构

  具有三个存储数据的成员的复制集有:一个主节点;两个副本节点组成,主节点宕机时,这两个副本节点都可以被选为主节点。
深入理解MongoDB_第2张图片
  当主节点宕机后,两个副本节点都会进行竞选,其中一个变为主节点,当主节点恢复后,作为副本节点加入当前的副本集群即可。

3、副本集选主过程

(1)三个存储数据的集群

深入理解MongoDB_第3张图片
  MongoDB副本集故障转移功能得益于它的选举机制。选举机制采用了Bully算法,可以很方便从分布式节点中选出主节点。一个分布式集群架构中一般都有一个所谓的主节点,可以有很多用途,比如缓存机器节点元数据,作为集群的访问入口等等。

(2)arbiter节点

深入理解MongoDB_第4张图片

  由于arbiter节点没有复制数据,因此这个架构中仅提供一个完整的数据副本。arbiter节点只需要更少的资源,代价是更有限的冗余和容错。当主节点宕机时,将会选择副本节点成为主节点,主节点修复后,将其加入到现有的副本集群中即可。

深入理解MongoDB_第5张图片

(3)Bully算法

  Bully算法是一个分布式系统中动态选择master节点的算法,进程号最大的非失效的节点将被选为master。Bully算法是一种协调者(主节点)竞选算法,主要思想是集群的每个成员都可以声明它是主节点并通知其他节点,别的节点可以选择接受这个声称或是拒绝并进入主节点竞争。被其他所有节点接受的节点才能成为主节点。节点按照一些属性来判断谁应该胜出。这个属性可以是一个静态ID,也可以是更新的度量像最近一次事务ID(最新的节点会胜出)。

(4)MongoDB的选主过程

  1. 得到每个节点的最后操作时间戳。每个MongoDB都有oplog机制会记录本机的操作,方便和主服务器进行对比数据是否同步还可以用于错误恢复。

  2. 如果集群中大部分节点宕机了保留活着的节点都为secondary状态并停止(主节点降备),停止选举。

  3. 如果集群中选举出来的主节点或者所有从节点最后一次同步时间看起来很旧了,停止选举等待人来操作。

  4. 如果上面都没有问题就选择最后操作时间戳最新(保证数据是最新的)的服务器节点作为主节点

【选主的触发条件】:

  • 初始化一个副本集时

  • 副本集和主节点断开连接,可能是网络问题。

  • 主节点挂掉

(5)为什么副本集数量最好为奇数?

  官方推荐副本集的成员数量为奇数,最多12个副本集节点,最多7个节点参与选举。最多12个副本集节点是因为没必要一份数据复制那么多份,备份太多反而增加了网络负载和拖慢了集群性能;而最多7个节点参与选举是因为内部选举机制节点数量太多就会导致1分钟内还选不出主节点,凡事只要适当就好。这个“12”、“7”数字还好,通过他们官方经过性能测试定义出来可以理解。

深入理解MongoDB_第6张图片

  假设四个节点被分成两个IDC,每个IDC各两台机器,如下图。但这样就出现了个问题,如果两个IDC网络断掉,这在广域网上很容易出现的问题,在上面选举中提到只要主节点和集群中大部分节点断开链接就会开始一轮新的选举操作,不过MongoDB副本集两边都只有两个节点,但是选举要求参与的节点数量必须大于一半,这样所有集群节点都没办法参与选举,只会处于只读状态。但是如果是奇数节点就不会出现这个问题,假设3个节点,只要有2个节点活着就可以选举,5个中的3个,7个中的4个。

(6)心跳和同步机制

心跳】:

  综上所述,整个集群需要保持一定的通信才能知道哪些节点活着哪些节点挂掉。MongoDB节点会向副本集中的其他节点每两秒就会发送一次pings包,如果其他节点在10秒钟之内没有返回就标示为不能访问。每个节点内部都会维护一个状态映射表,表明当前每个节点是什么角色、日志时间戳等关键信息。如果是主节点,除了维护映射表外还需要检查自己能否和集群中内大部分节点通讯,如果不能则把自己降级为secondary只读节点。

同步】:

  副本集同步分为初始化同步和keep复制。初始化同步指全量从主节点同步数据,如果主节点数据量比较大同步时间会比较长。而keep复制指初始化同步过后,节点之间的实时同步一般是增量同步

二、MongoDB的路由、分片技术

1、MongoDB的Sharding架构

深入理解MongoDB_第7张图片
  从图中可以看到有四个组件:mongos、config server、shard、replica set。

mongos】:

  数据库集群请求的入口,所有的请求都通过mongos进行协调,不需要在应用程序添加一个路由选择器,mongos自己就是一个请求分发中心,它负责把对应的数据请求转发到对应的shard服务器上。在生产环境通常有多个mongos作为请求的入口,防止其中一个挂掉所有的MongoDB请求都没有办法操作。

config server】:

  顾名思义为配置服务器,存储所有数据库元信息(路由、分片)的配置。mongos本身没有物理存储分片服务器和数据路由信息,只是缓存在内存里,配置服务器则实际存储这些数据。mongos第一次启动或者关掉重启就会从 config server 加载配置信息,以后如果配置服务器信息变化会通知到所有的 mongos 更新自己的状态,这样 mongos 就能继续准确路由。在生产环境通常有多个 config server 配置服务器,因为它存储了分片路由的元数据,这个可不能丢失!就算挂掉其中一台,只要还有存货,MongoDB集群就不会挂掉。

shard】:

  这就是分片,如下图:
深入理解MongoDB_第8张图片
  一台机器的一个数据表 Collection1 存储了 1T 数据,压力太大了!在分给4个机器后,每个机器都是256G,则分摊了集中在一台机器的压力。实际运行的数据库还有硬盘的读写、网络的IO、CPU和内存的瓶颈。在MongoDB集群只要设置好了分片规则,通过mongos操作数据库就能自动把对应的数据操作请求转发到对应的分片机器上。在生产环境中分片的片键需要设置好,这个影响到了怎么把数据均匀分到多个分片机器上,不要出现其中一台机器分了1T,其他机器没有分到的情况,这样还不如不分片!

2、分片的优势

  • 对集群进行抽象,让集群“不可见”:mongos就是掌握统一路口的路由器,其会将客户端发来的请求准确无误的路由到集群中的一个或者一组服务器上,同时会把接收到的响应拼装起来发回到客户端。

  • 保证集群总是可读写:MongoDB通过多种途径来确保集群的可用性和可靠性。将MongoDB的分片和复制功能结合使用,在确保数据分片到多台服务器的同时,也确保了每分数据都有相应的备份,这样就可以确保有服务器换掉时,其他的从库可以立即接替坏掉的部分继续工作

  • 使集群易于扩展:当系统需要更多的空间和资源的时候,MongoDB使我们可以按需方便的扩充系统容量。

3、集群中的数据分布

  在一个shard server内部,MongoDB还是会把数据分为chunks,每个chunk代表这个shard server内部一部分数据。chunk的产生,会有以下两个用途:

Split】:

  当一个chunk的大小超过配置中的chunk size(默认是64M)时,MongoDB的后台进程会把这个chunk切分成更小的chunk,从而避免chunk过大的情况

Balance】:

  在MongoDB中,balancer是一个后台进程,负责chunk的迁移,从而均衡各个shard server的负载,系统初始1个chunk,chunk size默认值64M,生产库上选择适合业务的chunk size是最好的。ongoDB会自动拆分和迁移chunks。

(1)chunk分裂及迁移

  随着数据的增长,其中的数据大小超过了配置的chunk size,默认是64M,则这个chunk就会分裂成两个。数据的增长会让chunk分裂得越来越多。
深入理解MongoDB_第9张图片
  这时候,各个shard上的chunk数量就会不平衡。这时候,mongos中的一个组件balancer就会执行自动平衡。把chunk从chunk数量最多的shard节点挪动到数量最少的节点。
深入理解MongoDB_第10张图片

(2)chunkSize对分裂及迁移的影响

  MongoDB 默认的 chunkSize 为64MB,如无特殊需求,建议保持默认值;chunkSize 会直接影响到 chunk 分裂、迁移的行为。

  chunkSize 越小,chunk 分裂及迁移越多,数据分布越均衡;反之,chunkSize 越大,chunk 分裂及迁移会更少,但可能导致数据分布不均。

  chunkSize太小,容易出现 jumbo chunk(即shardKey 的某个取值出现频率很高,这些文档只能放到一个chunk里,无法再分裂)而无法迁移;chunkSize越大,则可能出现chunk内文档数太多(chunk 内文档数不能超过250000)而无法迁移。

  chunk自动分裂只会在数据写入时触发,所以如果将chunkSize 改小,系统需要一定的时间来将chunk分裂到指定的大小。

  chunk只会分裂,不会合并,所以即使将chunkSize改大,现有的chunk数量不会减少,但chunk大小会随着写入不断增长,直到达到目标大小。

3、数据区分

  MongoDB中数据的分片是以集合为基本单位的,集合中的数据通过片键(Shard key)被分成多部分。其实片键就是在集合中选一个键,用该键的值作为数据拆分的依据。注意:分片键是不可变的而且必须有索引。

(1)基于范围的分片

  Sharded Cluster支持将单个集合的数据分散存储在多shard上,用户可以指定根据集合内文档的某个字段即shard key来进行范围分片(range sharding)。

  对于基于范围的分片,MongoDB按照片键的范围把数据分成不同部分。假设有一个数字的片键:想象一个从负无穷到正无穷的直线,每一个片键的值都在直线上画了一个点。MongoDB把这条直线划分为更短的不重叠的片段,并称之为数据块,每个数据块包含了片键在一定范围内的数据。在使用片键做范围划分的系统中,拥有“相近”片键的文档很可能存储在同一个数据块中,因此也会存储在同一个分片中。
深入理解MongoDB_第11张图片

(2)基于哈希的分片

  分片过程中利用哈希索引作为分片的单个键,且哈希分片的片键只能使用一个字段,而基于哈希片键最大的好处就是保证数据在各个节点分布基本均匀
深入理解MongoDB_第12张图片

  对于基于哈希的分片,MongoDB计算一个字段的哈希值,并用这个哈希值来创建数据块。在使用基于哈希分片的系统中,拥有“相近”片键的文档很可能不会存储在同一个数据块中,因此数据的分离性更好一些。

  Hash分片与范围分片互补,能将文档随机的分散到各个chunk,充分的扩展写能力,弥补了范围分片的不足,但不能高效的服务范围查询,所有的范围查询要分发到后端所有的Shard才能找出满足条件的文档。

三、MongoDB的索引原理

  索引就是将文档按照某个(或某些)字段顺序组织起来,以便能根据该字段高效地查询。MongoDB默认会为插入的文档生成_id字段(如果应用本身没有指定该字段),_id是文档唯一的标识,为了保证能根据文档id快速查询文档,MongoDB默认会为集合创建_id字段的索引。

1、索引类型

深入理解MongoDB_第13张图片
深入理解MongoDB_第14张图片
深入理解MongoDB_第15张图片

四、mongorocks引擎原理解析

1、数据存储及索引

  mongorocks是基于开源KV数据库RocksDB实现的一个MongoDB存储引擎,借助RocksDB的优秀特性,mongorocks能很好的支持高并发随机写入、读取的应用场景

  插入新文档时,MongoDB会调用底层KV引擎存储文档内容,并生成一个 RecordId 作为文档的位置信息标识,通过 RecordId 就能在底层KV引擎读取到文档的内容。如果插入的集合包含索引(MongoDB的集合默认会有_id索引),针对每项索引,还会往底层KV引擎插入一个新的key-value,key是索引的字段内容,value为插入文档时生成的 RecordId,这样就能快速根据索引找到文档的位置信息。
深入理解MongoDB_第16张图片
深入理解MongoDB_第17张图片

2、存储管理

在这里插入图片描述
深入理解MongoDB_第18张图片
深入理解MongoDB_第19张图片
深入理解MongoDB_第20张图片

五、MongoDB重要机制总结

1、MongoDB数据文件内部结构

深入理解MongoDB_第21张图片

  • MongoDB在数据存储上按命名空间来划分,一个Collection是一个命名空间,一个索引也是一个命名空间。

  • 同一个命名空间的数据被分成很多个Extent,Extent之间使用双向链表连接

  • 在每一个Extent中,保存了具体每一行的数据,这些数据也是通过双向链表来连接的

  • 每一行数据存储空间不仅包括数据占用空间,还可能包含一部分附加空间,这使得在数据Update变大后可以不移动位置。

  • 索引以BTree结构实现

2、MongoDB数据同步

深入理解MongoDB_第22张图片

  • 红色箭头表示写操作可以写到Primary上,然后异步同步到多个Secondary上。

  • 蓝色箭头表示读操作可以从Primary或Secondary任意一个中读取。

  • 各个Primary与Secondary之间一直保持心跳同步检测,用于判断Replica Sets的状态

3、分片机制

深入理解MongoDB_第23张图片

  • MongoDB的分片是指定一个分片key来进行,数据按范围分成不同的chunk,每个chunk的大小有限制。

  • 有多个分片节点保存这些chunk,每个节点保存一部分的chunk。

  • 每一个分片节点都是一个Replica Sets,这样保证数据的安全性。

  • 当一个chunk超过其限制的最大体积时,会分裂成两个小的chunk

  • 当chunk在分片节点中分布不均衡时,会引发chunk迁移操作

4、服务器角色

深入理解MongoDB_第24张图片

  • 客户端访问路由节点mongos来进行数据读写

  • config服务器保存了两个映射关系,一个是key值的区间对应哪一个chunk的映射关系,另一个是chunk存在哪一个分片节点的映射关系

  • 路由节点通过config服务器获取数据信息,通过这些信息,找到真正存放数据的分片节点进行对应操作

  • 路由节点还会在写操作时判断当前chunk是否超出限定大小。如果超出,就分列成两个chunk。

  • 对于按分片key进行的查询和update操作来说,路由节点会查到具体的chunk然后再进行相关的工作。

  • 对于不按分片key进行的查询和update操作来说,mongos会对所有下属节点发送请求然后再对返回结果进行合并。

六、MongoDB数据一致性问题

1、WriteConcern

深入理解MongoDB_第25张图片
  mongodb的写入策略有多种方式,写入策略是指当客户端发起写入请求后,数据库什么时候给应答,mongodb有三种处理策略:客户端发出去的时候,服务器收到请求的时候,服务器写入磁盘的时候。

  Replica Acknowledged:这个方式和Acknowledged是一样的意思,适用于Replica sets模式。Acknowledged模式下只有一台机器收到了请求就返回了,对于复制集模式有多台机器的情况,可以要求有多台机器收到写入请求后再响应客户端。这种更安全,但是导致了客户端耗时增加,所以要结合自己的场景设置合适的策略。

  writeConcern: { w: “majority” },majority表示多数节点写入成功后才响应客户端也可以替换成具体的数子,比如w:2表示至少写入2个节点才返回。wtimeout表示超时时间,还有一个参数可以设置true,false表示是否是写入日志才返回。
深入理解MongoDB_第26张图片

2、ReadConcern

  readConcern 决定到某个读取数据时,能读到什么样的数据。

  Local:能读取任意数据,这个是默认设置

  Majority:只能读取到成功写入到大多数节点的数据

3、MongoDB如何保证数据一致性?

  WriteConcern 的语义是说,「写大多数副本成功后才向客户端确认」,所以它只能保证「server 端告诉你写成功了,那么它一定成功的写到大多数节点了」;而当 WriteConcern: majority 失败时,它不能保证一定就失败了,具体几个节点成功是不确定的(比如错误是网络超时,你没法确定 server 端最终的状态)。

  WriteConcern 跟一致性是2个不同的问题,数据的一致性在 MongoDB 里是由 RAFT 最终来保证的。

  如果读写都在主节点上,当主节点挂掉以后通过bully算法重新选主,在这个过程中整个系统暂时不可用;选主过程中,副本节点通过日志复制保证和主节点的数据一致性,当新的主节点选出来以后,整个系统对外是保证了数据一致性的,但是牺牲了一些可用性

参考:http://www.lanceyan.com/tech/mongodb/mongodb_cluster_1.html
https://www.iteye.com/news/24836

你可能感兴趣的:(#,分布式系统与高可用架构,#,MongoDB)