🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] ### 概述 在 MongoDB 中,有两种数据冗余方式,一种 是 Master-Slave 模式(主从复制),一种是 Replica Sets 模式(副本集)。 ``` Mongodb一共有三种集群搭建的方式: Replica Set(副本集)、 Sharding(切片) Master-Slaver(主从)【目前已不推荐使用了!!!】 其中,Sharding集群也是三种集群中最复杂的。 副本集比起主从可以实现故障转移!!非常实用! MongoDB目前已不推荐使用主从模式,取而代之的是副本集模式。副本集其实一种互为主从的关系,可理解为主主。 副本集指将数据复制,多份保存,不同服务器保存同一份数据,在出现故障时自动切换。对应的是数据冗余、备份、镜像、读写分离、高可用性等关键词; 而分片则指为处理大量数据,将数据分开存储,不同服务器保存不同的数据,它们的数据总和即为整个数据集。追求的是高性能。 在生产环境中,通常是这两种技术结合使用,分片+副本集。 ``` ### MongoDB主从 ***** #### 主从配置 主从复制是[MongoDB](http://lib.csdn.net/base/mongodb)最常用的复制方式,也是一个简单的[数据库](http://lib.csdn.net/base/mysql)同步备份的集群技术,这种方式很灵活.**可用于备份,故障恢复,读扩展**等.  ` ` 最基本的设置方式就是建立一个主节点和一个或多个从节点,每个从节点要知道主节点的地址。采用双机备份后主节点挂掉了后从节点可以接替主机继续服务。所以这种模式**比单节点的高可用性要好很多**。  配置主从复制的注意点: ``` 1)在数据库集群中要明确的知道谁是主服务器,主服务器只有一台. 2)从服务器要知道自己的数据源也就是对应的主服务是谁. 3)--master用来确定主服务器,--slave 和 --source 来控制从服务器 ``` ` ` master-node.conf ``` port=27017 bind_ip = 182.48.115.238 dbpath=/usr/local/mongodb/data logpath=/usr/local/mongodb/log/mongo.log logappend=true journal = true fork = true master = true //确定自己是主服务器 ``` slave-node.conf ``` port=27017 dbpath=/usr/local/mongodb/data logpath=/usr/local/mongodb/log/mongo.log logappend=true journal = true fork = true bind_ip = 182.48.115.236 //确定主数据库端口 source = 182.48.115.238:27017 //确定主数据库端口 slave = true //确定自己是从服务器 ``` #### 主从复制的原理 ``` 在主从结构中,主节点的操作记录成为oplog(operation log)。oplog存储在一个系统数据库local的集合oplog.$main中,这个集合的每个文档都代表主节点上执行的一个操作。 从服务器会定期从主服务器中获取oplog记录,然后在本机上执行!对于存储oplog的集合,MongoDB采用的是固定集合,也就是说随着操作过多,新的操作会覆盖旧的操作! 主从复制的其他设置项 --only 从节点指定复制某个数据库,默认是复制全部数据库 --slavedelay 从节点设置主数据库同步数据的延迟(单位是秒) --fastsync 从节点以主数据库的节点快照为节点启动从数据库 --autoresync 从节点如果不同步则从新同步数据库(即选择当通过热添加了一台从服务器之后,从服务器选择是否更新主服务器之间的数据) --oplogSize 主节点设置oplog的大小(主节点操作记录存储到local的oplog中) ``` >在slave-node从节点的local数据库中,存在一个集合sources。这个集合就保存了这个服务器的主服务器是谁 ### MongoDB副本集(Replica Sets)  ***** mongodb 不推荐主从复制,推荐建立副本集(Replica Set)来保证1个服务挂了,可以有其他服务顶上,程序正常运行,几个服务的数据都是一样的,后台自动同步。主从复制其实就是一个单副本的应用,没有很好的扩展性和容错性。 ` ` Replica Set 使用的是 n 个 mongod 节点,构建具备自动的容错功能(auto-failover),自动恢复的(auto-recovery)的高可用方案。使用 Replica Set 来实现读写分离。通过在连接时指定或者在主库指定 slaveOk,由Secondary 来分担读的压力,Primary 只承担写操作。对于 Replica Set 中的 secondary 节点默认是不可读的。 ` ` #### 副本集的结构及原理 副本集包括三种节点:主节点、从节点、仲裁节点: * **主节点负责处理客户端请求,读、写数据, 记录在其上所有操作的 oplog**; * **从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作**,从而保证从节点的数据与主节点一致。默认情况下,从节点不支持外部读取,但可以设置; 副本集的机制在于主节点出现故障的时候,余下的节点会选举出一个新的主节点,从而保证系统可以正常运行。 * **仲裁节点不复制数据,仅参与投票**。由于它没有访问的压力,比较空闲,因此不容易出故障。由于副本集出现故障的时候,存活的节点必须大于副本集节点总数的一半, 否则无法选举主节点,或者主节点会自动降级为从节点,整个副本集变为只读。因此,增加一个不容易出故障的仲裁节点,可以增加有效选票,降低整个副本集不可用的 风险。仲裁节点可多于一个。也就是说只参与投票,不接收复制的数据,也不能成为活跃节点。 >官方推荐MongoDB副本节点最少为3台, 建议副本集成员为奇数,最多12个副本节点,最多7个节点参与选举。限制副本节点的数量,主要是因为一个集群中过多的副本节点,增加了复制的成本,反而拖累了集群 的整体性能。 太多的副本节点参与选举,也会增加选举的时间。而官方建议奇数的节点,是为了避免脑裂 的发生。 ` ` ##### 如何选举主库 MongoDB 的副本集不同于以往的主从模式:自动故障转移 ` ` **影响选举的因素和条件** 以下因素都会决定一个从库能否成为主库。 * **心跳**:副本集成员每 2 秒发送一次心跳(pings),如果心跳在 10 秒内没有返回,则其它成员将其标记为不可访问。每个节点内部都会维护一个状态映射表,表明当前每个节点是什么角色、日志时间戳等关键信息。如果是主节点,除了维护映射表 外还需要检查自己能否和集群中内大部分节点通讯,如果不能则把自己降级为secondary只读节点。 * **优先级**:其它成员更愿投票给 priority 值高的。 * Priority 为 0 的不能成为 primary 也不会寻求选举 * 只要当前主节点具有最高优先级值,或者没有具有较高优先级的辅助节点在该集合中的最新 oplog(操作日志)条目的 10 秒内,副本集就不会保持选举。 * 如果优先级较高的成员在当前主节点的最新 oplog 条目的 10 秒内赶上,则该集合保持选举,以便为优先级较高的节点提供成为主节点的机会。 * **Optime**:时间戳比较老的不能成为 primary。 * **Connections**:如果大多数副本集不可访问或不可用,则所有剩余成员变为只读。何为大多数?例如 3 个副本集中 2 个为大多数,5 个副本集中有 3 个为大多数。选举需要时间,在选举的过程中,副本集由于没有 primary,不能接受写入,所有剩余成员都变为只读。 ` ` ##### 选举过程 整个选举过程其实非常快,对用户来说几乎是透明的。以下步骤为具体选举步骤。 1. 得到每个服务器节点的最后操作时间戳。每个 mongodb 都有 oplog 机制会记录本机的操作,方便和主服务器进行对比数据是否同步还可以用于错误恢复。 2. 如果集群中大部分服务器 down 机了,保留活着的节点都为 secondary 状态并停止,不选举了。 3. 如果集群中选举出来的主节点或者所有从节点最后一次同步时间看起来很旧了,停止选举等待人来操作。 4. 如果上面都没有问题就选择最后操作时间戳最新(保证数据是最新的)的服务器节点作为主节点。 ` ` ##### 选举触发条件 正常情况下不会触发选举,如有以下条件之一则会出触发选举。 1. 初始化一个副本集时。 2. 副本集和主节点断开连接,可能是网络问题。 3. 主节点挂掉。 ` ` ##### 如何降级为 Standalone 模式 有些场景例如核心业务从 MongoDB 下架等,则可以从副本集模式降级为 Standalone 模式。 1. 注释掉`replicaSet`参数。 2. 删除数据库目录下的`local.*`文件(注意删除操作必须在 MongoDB 停止服务的情况下进行)。 ***注意**:当切换回 Standalone 模式下时所做的任何事情都是没有 oplog 的,这意味着当再次回到 Replica Set 模式中时会丢失部分数据(直接使用 SECONDARY 未经过删除`local.*`文件的原配置启动),此时MongoDB 会报错,此处是个坑。所以这个时候得重新搭建副本集。* ` ` ##### 将 Standalone 模式转化为副本集模式 项目前期对于 MongoDB 的要求没有那么高,随着业务的增长,发现项目对 MongoDB 的可靠性要求越来越高,此时可以将 MongoDB 切换为副本集模式。 1. 关闭 mongodb 实例。 2. 配置 replSet 参数以及生成配置 keyFile 。 3. 连接到 mongodb。 4. 初始化,增加成员。 ` ` ##### 数据同步 ``` 副本集同步分为初始化同步和keep复制。初始化同步指全量从主节点同步数据,如果主节点数据量比较大同步时间会比较长。而keep复制指初始化同步过后,节点 之间的实时同步一般是增量同步。初始化同步不只是在第一次才会被处罚,有以下两种情况会触发: 1)secondary第一次加入,这个是肯定的。 2)secondary落后的数据量超过了oplog的大小,这样也会被全量复制。 ``` ` ` 相对于传统主从模式的优势: ``` 传统的主从模式,需要手工指定集群中的 Master。如果 Master 发生故障,一般都是人工介入,指定新的 Master。 这个过程对于应用一般不是透明的,往往伴随着应用重 新修改配置文件,重启应用服务器等。 而 MongoDB 副本集,集群中的任何节点都可能成为 Master 节点。一旦 Master 节点故障,则会在其余节点中选举出一个新的 Master 节点。 并引导剩余节点连接到新的 Master 节点。这个过程对于应用是透明的。 ``` >一个副本集即为服务于同一数据集的多个 MongoDB 实例,其中一个为主节点,其余的都为从节点。主节 点上能够完成读写操作,从节点仅能用于读操作。主节点需要记录所有改变数据库状态的操作,这些记录 保存在 oplog 中,这个[文件存储](https://cloud.tencent.com/product/cfs?from=10680)在 local 数据库,各个从节点通过此 oplog 来复制数据并应用于本地,保持 本地的数据与主节点的一致。**oplog 具有幂等性,即无论执行几次其结果一致**,这个**比 mysql 的二进制日 志更好用**。 ` ` #### 副本集的搭建 ***** * OS: CentOS 7 64 位。 * Software: 采用的`mongodb-linux-x86_64-4.0.10`二进制包。 * 准备三台虚拟机:mg1(primary)->10.0.4.23,mg2(secondary)->10.0.4.64,mg3(secondary)->10.0.4.33。 ` ` ##### 配置副本集 建立三个成员的副本集,首先你得建立三个数据库的存放目录: ``` mkdir -p ./sd01 ./sd02 ./sd03 ``` 然后我们启动三个副本集成员,其中`replSet`参数后面跟的是副本集的名称,将需要有关联的成员的副本集名称要一致: ``` mongod --dbpath ./s01 --port 27017 --replSet s0 mongod --dbpath ./s02 --port 27018 --replSet s0 mongod --dbpath ./s03 --port 27019 --replSet s0 ``` 创建仲裁者也同样非常简单,建立一个空的数据目录,然后和其他副本集成员一样设置同样的副本集名称启动,最后通过`rs.add`方法的第二个参数设置为`true`: ``` mongod --dbpath ./replDb/s5 --port 27020 --replSet s0 mongod --port 27017 --host localhost > rs.add('localhost:27020', true); ``` 然后通过Mongo Shell进入到端口为27017的成员中,并配置副本集: ``` mongod --port 27017 --host localhost ``` 进入到Mongo Shell后通过`rs.initiate`方法来配置副本集: ``` > var conf = { _id: 'r0', version: 1, members: [ { _id: 0, host: 'localhost:27017' }, { _id: 1, host: 'localhost:27018' }, { _id: 2, host: 'localhost:27019' }, { _id: 3, host: 'localhost:27020' } ] }; > rs.initiate(conf); ``` `initiate`接受一个对象,对象`_id`为副本集名称,必须和启动副本集设置的一致才能添加进来(本例为`rs0`)。`version`为版本号,每当我们修改副本集配置的时候这个版本号都会递加1,而`members`则为副本集成员,我们可以在这里一次性添加完,也可以只添加一个后面再通过`add`方法添加,比如下面这样: ``` > rs.add('localhost:27018'); > rs.add('localhost:27019'); ``` ` ` ##### 删除副本集 删除副本集通过`rs.remove`方法来删除,它接受一个`<localhost>:<port>`这样的字符串。下面是删除一个副本集成员: ``` > rs.remove('localhost:27019'); ``` 方法二: ``` cfg = rs.conf() cfg.members[0].host = "你的IP 或者域名" rs.reconfig(cfg) ``` ` ` ##### 查询副本集配置 查看副本集配置是通过`rs.conf`方法查询,返回包含所有副本集的配置内容. ``` > rs.conf(); ``` ` ` ##### 查询主节点 每当使用`rs.add`方法添加成员的时候可能会影响选举来选择主节点是谁,查看主节点通过`rs.isMaster()`方法来查看。 ``` > rs.isMaster(); ``` 返回的内容中有几个可以了解 ``` { "hosts" : [ //副本集成员 "localhost:27017", "localhost:27018", "localhost:27019" ], "setName" : "rs0", //副本集名称 "setVersion" : 3, //副本集配置版本 "ismaster" : true, //是否是主节点 "primary" : "localhost:27017", //主节点成员的主机地址 "me" : "localhost:27017", //当前所在主机 } ``` ` ` ##### 读取副节点 副节点默认是无法读取的,我们可以通过`rs.setSlaveOk()`方法来设置Slave属性为true。下面是设置端口为27018的副节点可以进行读取 ``` mongod --port 27018 --host localhost > rs.setSlaveOk(); ``` ` ` ##### 隐藏成员 隐藏成员通过设置成员的`hidden`属性为`true`并且优先值`priority`为0则可以隐藏此成员,隐藏成员不能当主节点也不能当其他成员的复制源 ``` > rs.add({ > _id: 1, > _host: 'localhost:27017', > hidden: true, > priority: 0 > }) ``` ` ` ##### 延迟复制 通过设置成员的`slaveDelay`的值并且优先值`priority`为0,来让当前成员滞后多少秒后才开始复制数据。 ``` > rs.add({ > _id: 1, > _host: 'localhost:27017', > slaveDelay: 120, > priority: 0 > }) ``` ` ` ##### 索引管理 通过成员的`buildIndexes`的值并且优先值`priority`为0,来设置是否在备份机器上建立相同的索引,一般这个选项只用于纯粹备份的服务器。 下面的代码设置添加的副本集成员不创建索引。 ``` > rs.add({ > _id: 1, > _host: 'localhost:27017', > buildIndexes: false > priority: 0 > }) ``` ` `