一、简介
mongo副本集即一组mongo实例组成的一个集群,集群中有一个Primary节点和多个Secondary节点
客户端的所有数据都写入Primary节点,Secondary节点从Primary节点同步数据,从而实现数据冗余与高可用
客户端通过driver链接mongo集群时所有节点都写入链接字符串中,mongo集群角色转变,主备切换时客户端并不用做任何配置修改。
-
架构如下
-
客户端的读写请求全部在primary节点上
-
secondary同步primary节点的数据
当然客户端也可以通过配置让secondary处理读请求
-
二、集群角色与选举机制
1. 集群角色
前两种为常见角色
-
primary
主节点,接受所有的写请求,然后把数据修改同步到secondary节点。
一个集群只能有一个主节点。
主节点故障后其他节点会再选出一个主节点。
-
secondary
从节点,同步主节点数据,保存一份数据副本,也可提供读服务。
主节点故障后参与投票并且自身可被选择为主节点。
-
arbiter
仲裁者,不保存数据,仅参与投票,自身不能被选择为主节点
-
priority0
该节点的选举优先级为0,不会被选举为primary节点,比如性能较差或者跨机房部署,防止主节点切到另一个机房,priority参数用于控制选主优先级,数字越大越容易被选为主节点
-
vote0
vote为0的节点不参与投票
-
hidden
hidden节点不能被选为主节点,且对客户端driver不可见,可以用来做些数据备份之类的工作
-
delayed
delayed节点必须是hidden节点,可配置数据与primary节点落后一段时间,这样主节点误删或误修改后可以从这个节点恢复数据
3. 选举机制
-
集群初始化或主节点故障或手动切换主节点时会触发选举。
-
集群故障容忍度
假设集群内参数投票节点数为N
仅当集群内存活的投票节点超过N/2+1时才可以选择出主节点,集群才可用
若小于该数量则集群变为只读状态
投票节点数 N/2+1 容忍故障节点数 1 1 0 2 2 0 3 2 1 4 3 1 5 3 2 6 4 2 从上表可以看出偶数节点集群的故障容忍度与比它少一个节点的奇数节点相同,因此从集群可用性方面讲偶数节点没有任何意义(从数据可靠性来讲还是有意义的必经多了一个数据副本),因此线上环境一般建议部署奇数个节点的集群
三、副本集群搭建与测试
1. 三节点集群搭建测试
(1)配置三个单独的mongo实例
我们在同一台机器上分别使用三个端口启动三个mongo实例来做集群
-
创建mongo用户
useradd mongo echo "mongo" | passwd --stdin mongo
-
解压mongo二进制文件
tar -xzvf mongodb-linux-x86_64-rhel70-4.2.3.tgz -C /usr/local/ cd /usr/local/ mv mongodb-linux-x86_64-rhel70-4.2.3 mongodb chown mongo:mongo mongodb/ -R
-
修改环境变量
- 若允许所有用户都可以使用mongo命令则在
/etc/profile
中加入如下行,然后执行source /etc/profile
生效 - 若仅允许mongo用户使用mongo则切换到mongo用户,在mongo用户家目录中的
.bash_profile
文件中加入如下行,然后source ~/.bash_profile
生效
export PATH=/usr/local/mongodb/bin:$PATH
- 若允许所有用户都可以使用mongo命令则在
-
在
/data/mongo
下新建三个目录,作为mongo的数据目录mkdir -p /data/mongo/2701{ 7,8,9}/{ data,log} chown mongo:mongo -R /data/mongo
创建完成后目录如下:
/data/mongo/ ├── 27017 │ ├── data │ └── log ├── 27018 │ ├── data │ └── log └── 27019 ├── data └── log
-
在27017,27018,27019三个目录下新建
mongo.conf
配置文件,文件内容如下,每个实例修改端口和路径配置// 27017/mongo.conf dbpath=/data/mongo/27017/data logpath=/data/mongo/27017/log/mongo.log fork=true // 后台运行 port=27017 replSet=rs1 // 副本集名称,所有节点都要相同 // 27018/mongo.conf dbpath=/data/mongo/27018/data logpath=/data/mongo/27018/log/mongo.log fork=true port=27018 replSet=rs1 // 27019/mongo.conf dbpath=/data/mongo/27019/data logpath=/data/mongo/27019/log/mongo.log fork=true port=27019 replSet=rs1
-
启动三个实例
// 切换到mongo用户 su - mongo // 启动三个实例 mongod -f /data/mongo/27017/mongo.conf mongod -f /data/mongo/27018/mongo.conf mongod -f /data/mongo/27019/mongo.conf
-
检查
分别执行连接这三个端口看能否正常连接
mongo --port 27017 mongo --port 27018 mongo --port 27019
(2)集群配置
-
登录27017实例,初始化集群
// 登录实例 mongo --port 27017 // 初始化集群 rs.initiate()
初始化之后mongo shell提示符会变成如下状态,先是OTHER然后变成primary,说明我们当前节点已经是主节点了
rs1:OTHER> rs1:PRIMARY> rs1:PRIMARY>
可以使用
rs.status()
查看集群状态结果太多截了一部分,
members
块里面列出的集群成员及状态可以看到
27017
节点状态是primary
-
添加一个节点
rs.add("localhost:27018")
然后在用
rs.status()
看下集群状态可以看到members中有两个节点了,新加入的节点角色是secondary
-
下面我们再加入一个节点
rs.add("localhost:27019")
此时我们再来查看状态可以查看到三个节点的状态
(3)测试
为了便于测试集群可用性,用如下一个小脚本连续插入
这里写了个python脚本,不停的插入数据
(需要安装pymongo模块
pip install pymongo
)#!/usr/bin/python import pymongo mongo_str="mongodb://localhost:27017,localhost:27018,localhost:27019/" myclient=pymongo.MongoClient(mongo_str) mydb=myclient["testdb"] mycol=mydb["test"] mycol.drop() if __name__ == '__main__': count=1 while 1==1: mydict={ "_id":count,"a":count} x=mycol.insert_one(mydict) print(x.inserted_id) count+=1
脚本执行的截图就不贴了,我们手动kill mongo进程观察写入情况
结论:
- 若故障节点为secondary节点,不影响程序执行
- 若故障节点为primary节点,在集群选举新主节点是程序会稍微卡顿以下(1秒以内)
- 若故障两个节点,则程序终止,无法继续执行。(第二个挂掉的如果是从节点,不会立即中断,还可以写入一点数据,然后才会报异常退出)
2. 四节点集群测试
我们继续在刚才的三节点集群基础上,在扩充一个节点,使用27020端口
-
创建mongo目录
mkdir -p /data/mongo/27020/{data,log}
-
创建配置文件
cat /data/mongo/27020/mongo.conf
dbpath=/data/mongo/27020/data logpath=/data/mongo/27020/log/mongo.log fork=true port=27020 replSet=rs1
-
启动这个mongo实例
mongod -f /data/mongo/27020/mongo.conf
-
登录原来的集群加入这个新节点
需要登录到主节点,我这里是27019
// 登录 mongo --port 27019 // 加入新节点 rs.add("localhost:27020") // 查看状态 rs.status()
-
测试
继续用刚才的脚本,在运行起来,然后手动杀掉进程测试执行状态