类似Redis的主从复制和哨兵模式,但略有不同。项目地址参考码云。
含义:
与Redis主从复制类似,用户对MongoDB主节点进行写操作,副节点通过异步的形式对主节点的数据**(集合、文档、索引、数据库)**进行复制完成数据同步。由于复制操作为异步,因此副节点不保证与主节点的数据完全同步。<br />主节点负责处理所有写入数据的请求,副节点和主节点(默认)处理所有的读取请求,副节点从主节点(或符合条件的副节点)处复制数据<br />默认情况下,每个节点每隔两秒钟发送一次心跳请求,当请求超过10秒时则认为节点处于不健康状态,可能已经掉线,**一个复制集最多支持50个节点**<br /> ![捕获.PNG](https://cdn.nlark.com/yuque/0/2021/png/21405095/1626585048658-8532b374-3056-43d4-8bd2-f3486c350827.png#clientId=u2d9a97e5-0c70-4&from=ui&height=410&id=u4642a383&margin=%5Bobject%20Object%5D&name=%E6%8D%95%E8%8E%B7.PNG&originHeight=636&originWidth=1001&originalType=binary&ratio=1&size=86173&status=done&style=stroke&taskId=u37455371-aadd-4c9c-bfcc-ef4fbd9471f&width=645)<br />**复制集图解**
选举详解:
普通模式:
在复制集当中的每个节点都有“选举计数器”,当每个节点参与新的选举后,都会将计数器值进行加一。当主节点出现故障时,MongoDB将会从副节点中选出一个“候选节点”,“候选节点”会对自己的计数器进行加一操作,然后将计数器的值包含在“投票请求”中对其他节点(包含疑似掉线的原主节点)进行发送,如果其他节点收到“投票请求”时发现计数器的值比自身的要大,将对自身计数器值进行更新并返回投票结果(同意或反对)。当投票的同意结果超过半数,候选节点将升级为主节点,如果同意结果没有超过半数,将重新选出一个“候选节点”进行选举。
当整个复制集中只剩下一个MongoDB可以正常工作的时候,选举将永远无法生效,因此创建MongDB复制集的时候至少需要三个节点(一般为奇数),但最多只有7个投票节点
触发选举的规则:主节点和副节点的心跳请求超时、复制集初始化、新节点加入复制集
投票结果的判断规则:候选节点数据的同步性与自身数据同步性相比是否更高,高返回同意,低返回反对
投票机(仲裁者):
该选举模式适合数据不需要所有的副节点都进行复制,或复制集中的MongoDB数量为偶数
投票机本身不包含任何数据(不能成为主节点),只能为其他节点进行投同意票,可避免出现偶数的MongoDB副本集在投票中无法出现半数同意的结果
在MongoDB4.0+版本后被废弃
复制过程详解:
当一个节点作为全新成员加入到复制集当中,或对一个节点再次进行初始化,这种情况被称为**“初始同步”**。当“初始同步”开始后,会将新节点内的数据全部清空再进行数据同步。在进行数据同步的过程中,主节点接受到新数据并不会传给新节点进行复制,而是通过“同步写库记录”进行数据同步<br />“同步写库记录”类似Redis的AOF操作,主节点记录操作记录,副节点一次性拷贝所有的记录保存到一个集合中(默认为 local数据库的 oplog.rs 集合),然后再根据集合内的数据操作记录更新数据<br />**注意点:**
1. **写库记录的数据可以被重复使用**
2. **根据记录更新数据的过程为多线程分批次操作**
3. **写库记录的大小和文档大小不一定成正比**
投票机创建案例:
搭建一个一主一副的副本集,配合投票机模拟主机宕机的选举操作。配置文件详解参考《MongoDB安装》文档。
创建主节点:
1、建立存放数据和日志的目录
mkdir -p /mongodb/replica_sets/myrs_27017/log \ &
mkdir -p /mongodb/replica_sets/myrs_27017/data/db
2、创建配置文件
vim /mongodb/replica_sets/myrs_27017/mongod.conf
systemLog:
destination: file
path: "/mongodb/replica_sets/myrs_27017/log/mongod.log"
logAppend: true
storage:
dbPath: "/mongodb/replica_sets/myrs_27017/data/db"
journal:
enabled: true
processManagement:
fork: true
pidFilePath: "/mongodb/replica_sets/myrs_27017/log/mongod.pid"
net:
bindIp: 0.0.0.0
port: 27017
replication:
replSetName: myrs
3、启动节点服务
/usr/local/mongo/bin/mongod -f /mongodb/replica_sets/myrs_27017/mongod.conf
主节点启动完成
创建副节点:
1、建立存放数据和日志的目录
mkdir -p /mongodb/replica_sets/myrs_27018/log \ &
mkdir -p /mongodb/replica_sets/myrs_27018/data/db
2、创建配置文件
vim /mongodb/replica_sets/myrs_27018/mongod.conf
systemLog:
destination: file
path: "/mongodb/replica_sets/myrs_27018/log/mongod.log"
logAppend: true
storage:
dbPath: "/mongodb/replica_sets/myrs_27018/data/db"
journal:
enabled: true
processManagement:
fork: true
pidFilePath: "/mongodb/replica_sets/myrs_27018/log/mongod.pid"
net:
bindIp: 0.0.0.0
port: 27018
replication:
replSetName: myrs
3、启动节点服务
/usr/local/mongo/bin/mongod -f /mongodb/replica_sets/myrs_27018/mongod.conf
副节点启动完成
创建投票机:
1、建立存放数据和日志的目录
mkdir -p /mongodb/replica_sets/myrs_27019/log \ &
mkdir -p /mongodb/replica_sets/myrs_27019/data/db
2、创建配置文件
vim /mongodb/replica_sets/myrs_27019/mongod.conf
systemLog:
destination: file
path: "/mongodb/replica_sets/myrs_27019/log/mongod.log"
logAppend: true
storage:
dbPath: "/mongodb/replica_sets/myrs_27019/data/db"
journal:
enabled: true
processManagement:
fork: true
pidFilePath: "/mongodb/replica_sets/myrs_27019/log/mongod.pid"
net:
bindIp: 0.0.0.0
port: 27019
replication:
replSetName: myrs
3、启动节点服务
/usr/local/mongo/bin/mongod -f /mongodb/replica_sets/myrs_27019/mongod.conf
投票机启动完成
初始化副本集:
当指定副本集名称时,大多数命令已无法生效,需要先登录主节点并进行初始化
/usr/local/mongo/bin/mongo --port 27017 登录主节点
rs.initiate() 初始化
出现PRIMARY时完成主节点初始化
查看副本集配置:
使用 rs.config() 查看默认的副本集配置,priority代表优先级,优先级越大在投票中月可能获得同意,除投票机以外的节点默认为1
默认的配置
查看副本集状态:
使用 rs.status() 查看初始化后的副本集状态,”set” 为副本集的名字,”myState” 为1说明状态常,”members” 为副本集成员数组,此时只有一个: “name” : “localhost.localdomain:27017” ,该成员的角色是 “stateStr” : “PRIMARY”, 该节点是健康的: “health” : 1 。
主节点的状态
对主节点添加副节点:
指定 host 创建副节点完成数据同步,返回 “ok” : 1 :说明添加成功。
rs.add("192.168.182.147:27018") 对主节点添加副节点
添加副节点成功
使用 rs.status() 查看副本集状态发现副节点已成功添加:
副节点状态正常
添加投票机(仲裁者):
指定 host 创建投票机,返回 “ok” : 1 :说明添加成功。
rs.addArb("192.168.182.147:27019") 对副本集指定投票机
投票机添加成功
使用 rs.status() 查看副本集状态发现投票机已成功添加:
投票机状态正常
数据创建:
登录主节点,创建一个集合进行数据保存:
use articledb
db.articledb.save({
"articleid": "100000",
"content": "今天天气真好,阳光明媚",
"userid": "1001",
"nickname": "Rose",
"createdatetime": new Date(),
"likenum": NumberInt(10),
"state": null
})
登录副节点,默认情况下没有读权限,需要进行设置。如果要取消副节点的读权限,执行 rs.slaveOk(false) 即可 :
/usr/local/mongo/bin/mongo --port 27018 登录副节点
rs.slaveOk() 配置读权限
选择 articledb 集合,查询数据:
数据同步成功
副节点故障测试:
关闭27018副本节点:发现主节点和仲裁节点对27018的心跳失败。因为主节点还在,因此没有触发投票选举。在主节点写入数据后重启副节点,数据将会自动同步。
主节点故障测试:
关闭27017节点,当失败超过10秒,此时因为没有主节点了,会自动发起投票。而副本节点只有27018,因此,候选人只有一个就是27018,开始投票。27019向27018投了一票,27018本身自带一票,因此共两票,超过了“大多数”。27019是仲裁节点,没有选举权,27018不向其自身投票,其票数是0.。最终27018成为主节点。具备读写功能。<br />在27018写入数据查看,再启动27017节点,发现27017变成了从节点,27018仍保持主节点,登录27017节点,发现是从节点了,数据自动从27018同步,从而实现了高可用。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/21405095/1626656393015-6d0eb505-236c-4f46-9966-677dd46966b3.png#clientId=u61206a6e-bbf4-4&from=paste&height=405&id=uf4eece5d&margin=%5Bobject%20Object%5D&name=image.png&originHeight=810&originWidth=758&originalType=binary&ratio=1&size=71286&status=done&style=stroke&taskId=u479611b8-e2b5-46e9-a091-514e17c097f&width=379)<br /> **27017宕机后27018升级为主节点**
仲裁和主节点故障测试:
登录27017后,发现27017仍然是从节点,但副本集中没有主节点了,导致副本集是只读状态,无法写入。
因为27017的票数,没有获得大多数,即没有大于等于2,它只有默认的一票(优先级是1),因此无法触发新的主节点选举,如果要触发选举,随便加入一个成员即可。
如果只加入27019仲裁节点成员,则主节点一定是27017,因为没得选了,仲裁节点不参与选举,但参与投票。如果只加入27018节点,会发起选举。因为27017和27018都是两票,则按照谁数据新,谁当主节点。
仲裁节点和副节点故障测试:
先关掉仲裁节点27019,关掉现在的副本节点27018,10秒后,27017主节点自动降级为副本节点。(服务降级),副本集不可写数据了,已经故障了。
主节点服务降级
SpringBoot集成:
与Redis哨兵的配置类似,slaveOk=true:开启副本节点读的功能,可实现读写分离。connect=replicaSet:自动到副本集中选择读写的主机。如果slaveOK是打开的,则实现了读写分离
spring.data.mongodb.url:mongodb://host1,host2,host3/数据库名?connect=replicaSet&slaveOk=true&replicaSet=副本集名字
以上面案例为例的配置文件语句:
spring.data.mongodb.url:mongodb://192.168.182.147:27017,192.168.182.147:27018,192.168.182.147:27019/articledb?connect=replicaSet&slaveOk=true&replicaSet=myrs
运行时可能提示“com.mongodb.MongoSocketException: localhost.localdomain”,这是由于配置文件自动生成的时候 host 默认成 linux 的主机名,需要修改为具体的 IP 才能正常运行
主节点的Host地址异常
解决方案:
在主节点上修改异常Host值的数据即可
cxf = rs.conf() 将配置文件赋值给变量
cxf.members[0].host="192.168.182.147:27017" 修改异常的一号数据
rs.reconfig(cxf) 重载配置文件
Host异常修复成功
写操作时,只打开主节点连接:
读操作是,同时打开主节点和从节点连接,但使用从节点获取数据:
关闭副本集:
分为手动关闭和强制关闭,强制关闭即kill命令,但有数据损坏风险,若出现该情况需要进行数据修复,数据修复参考《数据分片》章节;手动关闭步骤:依次关闭仲裁节点、副节点、主节点
mongo --port 端口 按顺序进入副本集端口
rs.stepDown() 告知副本集说本机要下线
use admin 切换到admin库
db.shutdownServer() 关闭服务