概念
在了解了这篇文章之后,可以进行该篇文章的说明和测试。MongoDB 副本集(Replica Set)是有自动故障恢复功能的主从集群,有一个Primary节点和一个或多个Secondary节点组成。类似于MySQL的MMM架构。更多关于副本集的介绍请见官网。也可以在google、baidu上查阅。
副本集中数据同步过程:Primary节点写入数据,Secondary通过读取Primary的oplog得到复制信息,开始复制数据并且将复制信息写入到自己的oplog。如果某个操作失败,则备份节点停止从当前数据源复制数据。如果某个备份节点由于某些原因挂掉了,当重新启动后,就会自动从oplog的最后一个操作开始同步,同步完成后,将信息写入自己的oplog,由于复制操作是先复制数据,复制完成后再写入oplog,有可能相同的操作会同步两份,不过MongoDB在设计之初就考虑到这个问题,将oplog的同一个操作执行多次,与执行一次的效果是一样的。简单的说就是:
当Primary节点完成数据操作后,Secondary会做出一系列的动作保证数据的同步:
1:检查自己local库的oplog.rs集合找出最近的时间戳。
2:检查Primary节点local库oplog.rs集合,找出大于此时间戳的记录。
3:将找到的记录插入到自己的oplog.rs集合中,并执行这些操作。
副本集的同步和主从同步一样,都是异步同步的过程,不同的是副本集有个自动故障转移的功能。其原理是:slave端从primary端获取日志,然后在自己身上完全顺序的执行日志所记录的各种操作(该日志是不记录查询操作的),这个日志就是local数据 库中的oplog.rs表,默认在64位机器上这个表是比较大的,占磁盘大小的5%,oplog.rs的大小可以在启动参数中设 定:—oplogSize 1000,单位是M。
注意:在副本集的环境中,要是所有的Secondary都宕机了,只剩下Primary。最后Primary会变成Secondary,不能提供服务。
部署复制集
概述
由三个节点组成的 复制集 为网络故障或是其他的系统故障提供了足够的冗余。该复制集也有足够的分布式读操作的能力。复制集应该保持奇数个节点,这也就保证了 选举 可以正常的进行。参见 复制集概览
以获得更多有关复制集设计的信息。
我们通常现从一个会成为复制集成员的 mongod
实例开始来配置复制集。然后为复制集新增实例。
要求
在生产环境的部署中,我们应该尽可能将复制集中得节点置于不同的机器上。当使用虚拟机的时候,我们应该将 mongod
实例置于拥有冗余电源和冗余网络的机器上。
在我们部署复制集之前,我们必须在 复制集 的每个机器上安装MongoDB实例。如果我们还没安装MongoDB,请参考 安装指南 。
部署复制集的注意事项
架构
在生产环境中,我们应该将每个节点部署在独立的机器上,并使用标准的MongoDB端口 27017
。使用 bind_ip
参数来限制访问MongoDB的应用程序的地址。
准备服务器
10.200.6.30 |
连通性
确保各个节点之间可以正常通讯,且各个客户端都处于安全的可信的网络环境中。可以考虑以下事项:
- 建立虚拟的专用网络。确保各个节点之间的流量是在本地网络范围内路由的。(Establish a virtual private network. Ensure that your network topology routes all traffic between members within a single site over the local area network.)
- Configure access control to prevent connections from unknown clients to the replica set.
- 配置网络设置和防火墙规则来对将MongoDB的端口仅开放给应用程序,来让应用程序发的进出数据包可以与MongoDB正常交流。
最后请确保复制集各节点可以互相通过DNS或是主机名解析。我们需要配置DNS域名或是设置 /etc/hosts
文件来配置。
10.200.6.30 sh_01 |
详细步骤
安装MongoDB
以下为安装脚本,下载到本地以后bash install_mongodb.3.2.16.sh
curl -O 'https://raw.githubusercontent.com/BoobooWei/booboo_mongodb/master/scripts/install_mongodb.3.2.16.sh' |
修改配置文件
只需要开启:replSet 参数即可。格式为:
replication: |
启动服务
每一个节点都启动
mongodb.server start |
启动后会提示:
2018-08-16T17:57:46.700+0800 I REPL [initandlisten] Did not find local voted for document at startup. |
说明需要进行初始化操作,初始化操作只能执行一次。
初始化副本集
登入任意一台机器的MongoDB执行:因为是全新的副本集所以可以任意进入一台执行;要是有一台有数据,则需要在有数据上执行;要多台有数据则不能初始化。
rs.initiate() |
- “_id”: 副本集的名称
- “members”: 副本集的服务器列表
- “_id”: 服务器的唯一ID
- “host”: 服务器主机
- “priority”: 是优先级,默认为1,优先级0为被动节点,不能成为活跃节点。优先级不位0则按照有大到小选出活跃节点。
- “arbiterOnly”: 仲裁节点,只参与投票,不接收数据,也不能成为活跃节点。
添加副本集节点
添加节点的命令如下:
rs.add() |
执行操作
booboo:PRIMARY> rs.add('sh_02:27017') |
6:日志
查看252上的日志:
2018-08-16T18:16:48.547+0800 I REPL [ReplicationExecutor] New replica set config in use: { _id: "booboo", version: 3, protocolVersion: 1, members: [ { _id: 0, host: "sh_01:27017", arbiterOnly: false, buildIndexes: true, hidden: false, priority: 1.0, tags: {}, slaveDelay: 0, votes: 1 }, { _id: 1, host: "sh_02:27017", arbiterOnly: false, buildIndexes: true, hidden: false, priority: 1.0, tags: {}, slaveDelay: 0, votes: 1 }, { _id: 2, host: "am_01:27017", arbiterOnly: false, buildIndexes: true, hidden: false, priority: 1.0, tags: {}, slaveDelay: 0, votes: 1 } ], settings: { chainingAllowed: true, heartbeatIntervalMillis: 2000, heartbeatTimeoutSecs: 10, electionTimeoutMillis: 10000, getLastErrorModes: {}, getLastErrorDefaults: { w: 1, wtimeout: 0 }, replicaSetId: ObjectId('5b754da04c30f36bbab72366') } } |
至此,整个副本集已经搭建成功了。
查看副本集状态
booboo:PRIMARY> rs.status() |
删副本集节点
删除sh_02节点
rs.remove() |
操作明细
booboo:PRIMARY> rs.remove('sh_02:27017') |
sh_02的节点已经被移除。
再将sh_02加回来rs.add('sh_02:27017')
操作Secondary
默认情况下,Secondary是不提供服务的,即不能读和写。会提示:
error: { “$err” : “not master and slaveOk=false”, “code” : 13435 }
在特殊情况下需要读的话则需要:
rs.slaveOk() ,只对当前连接有效。
booboo:SECONDARY> db.t1.find().limit(1) |
测试副本集功能
当前副本集状态如下:
rs.config() |
测试副本集数据复制功能
在Primary上插入数据:
booboo:PRIMARY> use test |
在Secondary上查看是否已经同步:
booboo:SECONDARY> db.superman.find().limit(1) |
数据已经同步。
测试优先级更改功能
将sh_01的优先级改为0.5
booboo:PRIMARY> cfg = rs.config() |
更改优先级后
rs.config() |
测试副本集故障转移功能
关闭Primary节点,查看其他2个节点的情况:
[root@sh_01 ~]# mongodb.server stop |
看到sh_02已经从 SECONDARY 变成了 PRIMARY。具体的信息可以通过日志文件得知。继续操作:
booboo:SECONDARY> use admin |
查看副本集状态
booboo:SECONDARY> use admin |
重新启动sh_01,由于其优先级别最高,所以会重新选举primary为sh_01
[root@sh_01 ~]# mongodb.server start |
注意:
所有的Secondary都宕机、或则副本集中只剩下一个节点,则该节点只能为Secondary节点,也就意味着整个集群智能进行读操作而不能进行写操作,当其他的恢复时,之前的primary节点仍然是primary节点。
当某个节点宕机后重新启动该节点会有一段的时间(时间长短视集群的数据量和宕机时间而定)导致整个集群中所有节点都成为secondary而无法进行写操作(如果应用程序没有设置相应的ReadReference也可能不能进行读取操作)。
官方推荐的最小的副本集也应该具备一个primary节点和两个secondary节点。两个节点的副本集不具备真正的故障转移能力。
副本集应用
修改rs.config()中的相应value即可达到目的:
副本集应用 | 说明 | 方法 |
---|---|---|
修改复制集节点的优先级 | 修改复制集节点在选举中的优先级 | cfg.members[0].priority = 0.5 |
禁止从节点升职为主节点 | 防止从节点在选举中升职为主节点 | cfg.members[1].priority = 0 |
配置一个隐藏节点 | 将从节点设置为应用程序不可见来用其提供特殊需求,如备份等 | cfg.members[0].priority = 0;cfg.members[0].hidden = true |
配置一个延时复制节点 | 将从节点设置为延时复制节点,来提高数据安全性 | cfg.members[0].priority = 0;cfg.members[0].hidden = true;cfg.members[0].slaveDelay = 3600 |
配置一个不参与投票的节点 | 配置一个拥有数据但是不可进行投票的从节点 | cfg.members[3].votes = 0 |
将从节点转换为投票节点 | 将一个从节点变为投票节点 |
修改复制集节点的优先级
修改复制集节点的优先级 |
禁止从节点升职为主节点
禁止从节点升职为主节点 |
配置一个隐藏节点
配置一个隐藏节点 |
配置一个延时复制节点
配置一个延时复制节点 |
配置一个不参与投票的节点
配置一个不参与投票的节点 |
将从节点转换为投票节点
将从节点转换为投票节点 |
读写分离
MongoDB副本集对读写分离的支持是通过Read Preferences特性进行支持的,这个特性非常复杂和灵活。
应用程序驱动通过read reference来设定如何对副本集进行读取操作,默认的,客户端驱动所有的读操作都是直接访问primary节点的,从而保证了数据的严格一致性。
支持五种的read preference模式:官网说明
复制集读选项模式 | 详细说明 |
---|---|
primary |
默认模式,所有的读操作都在复制集的 主节点 进行的。 |
primaryPreferred |
在大多数情况时,读操作在 主节点 上进行,但是如果主节点不可用了,读操作就会转移到 从节点 上执行。 |
secondary |
All operations read from the secondary members of the replica set. |
secondaryPreferred |
在大多数情况下,读操作都是在 从节点 上进行的,但是当 从节点 不可用了,读操作会转移到 主节点 上进行。 |
nearest |
读操作会在 复制集 中网络延时最小的节点上进行,与节点类型无关。 |
python连接mongo副本集
脚本:
#coding:utf-8 |
脚本执行打印出的内容:
[root@sh_01 ~]# python m1.py |
模拟主节点故障
总结
搭建副本集
No. | 步骤 | 操作 |
---|---|---|
1 | 修改配置文件 | replSetName: booboo |
2 | 启动服务 | mongodb.server start |
3 | 初始化副本集 | rs.initiate() |
4 | 确认初始化配置 | rs.conf() |
5 | 添加副本集节点 | rs.add() |
6 | 检查副本集状态 | rs.status() |
副本集的方法
名称 | 描述 |
---|---|
rs.add() |
将成员添加到副本集。 |
rs.addArb() |
将仲裁器添加到副本集。 |
rs.conf() |
返回副本集配置文档。 |
rs.freeze() |
防止现任成员在一段时间内选举为主要成员。 |
rs.help() |
返回副本集函数的基本帮助文本。 |
rs.initiate() |
初始化新的副本集。 |
rs.printReplicationInfo() |
从主数据库的角度打印副本集状态的报告。 |
rs.printSlaveReplicationInfo() |
从辅助节点的角度打印副本集状态的报告。 |
rs.reconfig() |
通过应用新的副本集配置对象重新配置副本集。 |
rs.remove() |
从副本集中删除成员。 |
rs.slaveOk() |
设置当前连接的slaveOk 属性。已过时。使用readPref() 和Mongo.setReadPref() 来设置读取首选项。 |
rs.status() |
返回包含有关副本集状态的信息的文档。 |
rs.stepDown() |
导致当前的初选成为强制选举的次要。 |
rs.syncFrom() |
设置此副本集成员将同步的成员,覆盖默认同步目标选择逻辑。 |