1、MongoDB主从复制架构原理和缺陷
master-slave架构中master节点负责数据的读写,slave没有写入权限只负责读取数据。
在主从结构中,主节点的操作记录成为oplog(operation log)。oplog存储在系统数据库local的oplog.$main集合中,这个集合的每个文档都代表主节点上执行的一个操作。从服务器会定期从主服务器中获取oplog记录,然后在本机上执行!对于存储oplog的集合,MongoDB采用的是固定集合,也就是说随着操作过多,新的操作会覆盖旧的操作!
主从结构没有自动故障转移功能,需要指定master和slave端,不推荐在生产中使用。
mongodb4.0后不再支持主从复制!
[main] Master/slave replication is no longer supported
2、复制集replica sets
(1)什么是复制集
复制集是由一组拥有相同数据集的mongod实例做组成的集群。
复制集是一个集群,它是2台及2台以上的服务器组成,以及复制集成员包括Primary主节点,secondary从节点和投票节点。
复制集提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性,保证数据的安全性。
(2)为什么要使用复制集
- 高可用:
- 防止设备(服务器、网络)故障。
- 提供自动failover 功能。
- 技术来保证高可用
- 灾难恢复
- 当发生故障时,可以从其他节点恢复 用于备份。
- 功能隔离
- 我们可以在备节点上执行读操作,减少主节点的压力
- 比如:用于分析、报表,数据挖掘,系统任务等。
(3)复制集集群架构原理
一个复制集中Primary节点上能够完成读写操作,Secondary节点仅能用于读操作。Primary节点需要记录所有改变数据库状态的操作,这些记录保存在 oplog 中,这个文件存储在 local 数据库,各个Secondary节点通过此 oplog 来复制数据并应用于本地,保持本地的数据与主节点的一致。oplog 具有幂等性,即无论执行几次其结果一致,这个比 mysql 的二进制日志更好用。
oplog的组成结构
{
"ts" : Timestamp(1446011584, 2),
"h" : NumberLong("1687359108795812092"),
"v" : 2,
"op" : "i",
"ns" : "test.nosql",
"o" : { "_id" : ObjectId("563062c0b085733f34ab4129"), "name" : "mongodb", "score" : "10"}
}
ts:操作时间,当前timestamp + 计数器,计数器每秒都被重置
h:操作的全局唯一标识
v:oplog版本信息
op:操作类型
i:插入操作u:更新操作
d:删除操作
c:执行命令(如createDatabase,dropDatabase)
n:空操作,特殊用途
ns:操作针对的集合
o:操作内容
o2:更新查询条件,仅update操作包含该字段
复制集数据同步分为初始化同步和keep复制同步。初始化同步指全量从主节点同步数据,如果Primary节点数据量比较大同步时间会比较长。而keep复制指初始化同步过后,节点之间的实时同步一般是增量同步。
初始化同步有以下两种情况会触发:
- Secondary第一次加入。
- Secondary落后的数据量超过了oplog的大小,这样也会被全量复制。
MongoDB的Primary节点选举基于心跳触发。一个复制集N个节点中的任意两个节点维持心跳,每个节点维护其他N-1个节点的状态。
心跳检测:
整个集群需要保持一定的通信才能知道哪些节点活着哪些节点挂掉。mongodb节点会向副本集中的其他节点每2秒就会发送一次pings包,如果其他节点在10秒钟之内没有返回就标示为不能访问。每个节点内部都会维护一个状态映射表,表明当前每个节点是什么角色、日志时间戳等关键信息。如果主节点发现自己无法与大部分节点通讯则把自己降级为secondary只读节点。
主节点选举触发的时机:
- 第一次初始化一个复制集
- Secondary节点权重比Primary节点高时,发起替换选举
- Secondary节点发现集群中没有Primary时,发起选举
- Primary节点不能访问到大部分(Majority)成员时主动降级
当触发选举时,Secondary节点尝试将自身选举为Primary。
主节点选举是一个二阶段过程+多数派协议。
第一阶段:
检测自身是否有被选举的资格 如果符合资格会向其它节点发起本节点是否有选举资格的FreshnessCheck,进行同僚仲裁
第二阶段:
发起者向集群中存活节点发送Elect(选举)请求,仲裁者收到请求的节点会执行一系列合法性检查,如果检查通过,则仲裁者(一个复制集中最多50个节点 其中只有7个具有投票权)给发起者投一票。
- pv0通过30秒选举锁防止一次选举中两次投票。
- pv1使用了terms(一个单调递增的选举计数器)来防止在一次选举中投两次票的情况。
多数派协议:
发起者如果获得超过半数的投票,则选举通过,自身成为Primary节点。获得低于半数选票的原因,除了常见的网络问题外,相同优先级的节点同时通过第一阶段的同僚仲裁并进入第二阶段也是一个原因。因此,当选票不足时,会sleep[0,1]秒内的随机时间,之后再次尝试选举。
3、复制集搭建
(1) 主节点配置 mongo_37017.conf
# 主节点配置
dbpath=/data/mongo/data/server1
bind_ip=0.0.0.0
port=37017
fork=true
logpath=/data/mongo/logs/server1.log
replSet=lagouCluster
(2)从节点1配置 mongo_37018.conf
dbpath=/data/mongo/data/server2
bind_ip=0.0.0.0
port=37018
fork=true
logpath=/data/mongo/logs/server2.log
replSet=lagouCluster
(3)从节点2配置 mongo_37019.conf
dbpath=/data/mongo/data/server3
bind_ip=0.0.0.0
port=37019
fork=true
logpath=/data/mongo/logs/server3.log
replSet=lagouCluster
(4)初始化节点配置
启动三个节点,然后进入任意一个节点运行如下命令:(打开mongo终端)
var cfg ={"_id":"lagouCluster",
"protocolVersion" : 1,
"members":[
{"_id":1,"host":"192.168.211.133:37017","priority":10},
{"_id":2,"host":"192.168.211.133:37018"}
]
}
rs.initiate(cfg)
rs.status()
(5)节点的动态增删
# 增加节点
rs.add("192.168.211.133:37019")
# 删除slave 节点
rs.remove("192.168.211.133:37019")
(6)复制集操作演示
进入主节点 ----- 插入数据 ------ 进入从节点验证
注意:默认节点下从节点不能读取数据。调用 rs.slaveOk() 解决为了保证高可用,在集群当中如果主节点挂掉后,会自动在从节点中选举一个重新做为主节点。
rs.status():查看节点状态的详细信息
节点说明:
- PRIMARY 节点: 可以查询和新增数据
- SECONDARY 节点:只能查询不能新增 基于priority 权重可以被选为主节点
- ARBITER 节点: 不能查询数据和新增数据 ,不能变成主节点
4、复制集成员的配置参数
参数字段 | 类型说明 | 取值 | 说明 |
_id | 整数 | _id:0 | 复制集中的标识 |
host | 字符串 | host:"主机:端口" | 节点主机名 |
arbiterOnly | 布尔值 | arbiterOnly:true | 是否为仲裁(裁判)节点 |
priority(权重) | 整数 | priority=0|1 | 默认1,是否有资格变成主节点,取值范围0-1000,0永远不 会变成主节点 |
hidden | 布尔值 | hidden=true|false,0|1 | 隐藏,权重必须为0,才可以设置 |
votes | 整数 | votes= 0|1 | 投票,是否为投票节点,0 不投票,1投票 |
slaveDelay | 整数 | slaveDelay=3600 | 从库的延迟多少秒 |
buildIndexes | 布尔值 | buildIndexes=true|false,0|1 | 主库的索引,从库也创建,_id索引无效 |
举例:
var cfg ={"_id":"lagouCluster",
"protocolVersion" : 1,
"members":[
{"_id":1,"host":"192.168.211.133:37017","priority":10},
{"_id":2,"host":"192.168.211.133:37018","priority":0},
{"_id":3,"host":"192.168.211.133:37019","priority":5},
{"_id":4,"host":"192.168.211.133:37020","arbiterOnly":true} // 方式一:指定裁判节点
]
};
// 重新装载配置,并重新生成集群节点。
rs.reconfig(cfg)
//重新查看集群状态
rs.status()
5、有仲裁节点复制集搭建
除了上面那种在初始化节点时指定好仲裁节点,也可以初始化后进行仲裁节点的指定。
注入节点执行:(方式二)
rs.addArb("IP:端口");
示例:
rs.addArb("192.168.211.133:37020")