参考:
https://www.cnblogs.com/kevingrace/p/7881496.html
https://www.jianshu.com/p/f9f1454f251f
一、介绍
MongoDB复制集是一个带有故障转移的主从集群。是从现有的主从模式演变而来,增加了自动故障转移和节点成员自动恢复。MongoDB复制集模式中没有固定的主节点,在启动后,多个服务节点间将自动选举产生一个主节点。该主节点被称为primary,一个或多个从节点被称为secondaries。primary节点基本上就是master结点,不同之处在于primary结点在不同时间可能是不同的服务器。如果当前的主结点失效了,复制集中的其余结点将会试图选出一个新的主结点。
MongoDB复制集模式的好处:
- 一切自动化。首先,复制集模式本身做了大量的管理工作,自动管理从节点,确保数据不会不一致。
- 主节点挂掉后,会自动判断集群中的服务器并进行故障转移,推举新的主节点。
- 一个复制集集群支持1-7台服务器,在一个复制集中各个服务器数据保持完全一致。
在一个MongoDB复制集集群中,各个服务器有以下几种状态:
- Primary 主节点,一个复制集有且仅有一台服务器处于Primary状态,只有主节点才对外提供读写服务。如果主节点挂掉,复制集将投票选出一个备节点成为新的主节点。
- Secondary 备用节点,复制集允许有多台Secondary,每个备用节点的数据与主节点的数据是完全同步的。Recovering 恢复中,当复制集中某台服务器挂掉或者掉线后数据无法同步,重新恢复服务后从其他成员复制数据,这时就处于恢复过程,数据同步后,该节点又回到备用状态。
- Arbiter 仲裁节点,该类节点可以不用单独存在,如果配置为仲裁节点,就主要负责在复本集中监控其他节点状态,投票选出主节点。该节点将不会用于存放数据。如果没有仲裁节点,那么投票工作将由所有节点共同进行。
- Down 无效节点,当服务器挂掉或掉线时就会处于该状态。复制集的从节点读请求,也是在各个Driver层设置slaveOk的值来实现的。
如上介绍所知,mongodb中的复制可以在多台服务器中同步数据。复制提供了冗余和增加了数据的高可用性,防止单个节点易丢失数据的可能性,也可以用来进行读写分离提高客户端操作性能。复制集中各节点的mongodb实例有相同的数据集副本。主节点可以接收客户端所有写操作记录到日志中,从库复制主库的操作日志记录应用到其数据库中。一个客户端只能有一个主节点,如果主节点不可用(10秒内无法连接),复制集中将选一个成员节点作为主节点。MongoDB主备+仲裁的基本结构如下:
- 主节点(Primary)
在复制集中,主节点是唯一能够接收写请求的节点。MongoDB在主节点进行写操作,并将这些操作记录到主节点的oplog中。而从节点将会从oplog复制到其本机,并将这些操作应用到自己的数据集上。(复制集最多只能拥有一个主节点) - 从节点(Secondaries)
从节点通过应用主节点传来的数据变动操作来保持其数据集与主节点一致。从节点也可以通过增加额外参数配置来对应特殊需求。例如,从节点可以是non-voting或是priority 0. - 仲裁节点(ARBITER)
仲裁节点即投票节点,其本身并不包含数据集,且也无法晋升为主节点。但是,旦当前的主节点不可用时,投票节点就会参与到新的主节点选举的投票中。仲裁节点使用最小的资源并且不要求硬件设备。投票节点的存在使得复制集可以以偶数个节点存在,而无需为复制集再新增节点 不要将投票节点运行在复制集的主节点或从节点机器上。 投票节点与其他 复制集节点的交流仅有:选举过程中的投票,心跳检测和配置数据。这些交互都是不加密的。
心跳检测
复制集成员每两秒向复制集中其他成员进行心跳检测。如果某个节点在10秒内没有返回,那么它将被标记为不可用。
二、安装
1.下载mongodb
https://www.mongodb.com/download-center/community
选择版本,本例选择最新4.2.3,服务器上用wget下载
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-4.2.3.tgz
2.配置
- 解压
tar -xzvf mongodb-linux-x86_64-rhel70-4.2.3.tgz
- 重命名为
mv mongodb-linux-x86_64-rhel70-4.2.3/ mongodb
- 在mongodb目录新建data logs conf文件夹
cd mongodb
mkdir data logs conf
- conf中新建配置文件
vi conf/mongodb.conf
#内容如下:
systemLog:
destination: file
path: "/home/cluster/mongodb/logs/mongod.log"
logAppend: true
storage:
dbPath: "/home/cluster/mongodb/data"
journal:
enabled: true
processManagement:
fork: true # fork and run in background
timeZoneInfo: /usr/share/zoneinfo
net:
bindIp: 192.168.62.15
port: 28017
replication:
oplogSizeMB: 1024
replSetName: rs0
3.添加用户环境变量
vi .bash_profile
#文件末尾加上
export PATH=$PATH:/home/cluster/mongodb/bin
4.注册为系统服务
vi /lib/systemd/system/mongodb.service
#内容如下:
[Unit]
Description=MongoDB Database Server
Documentation=https://docs.mongodb.org/manual
After=network.target remote-fs.target nss-lookup.target
[Service]
Type=forking
User=cluster
Group=cluster
ExecStart=/home/cluster/mongodb/bin/mongod --config /home/cluster/mongodb/conf/mongodb.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/home/cluster/mongodb/bin/mongod --config /home/cluster/mongodb/conf/mongodb.conf --shutdown
PrivateTmp=true
[Install]
WantedBy=multi-user.target
三、启动
#关闭mongodb
mongod -f /home/cluster/mongodb/conf/mongodb.conf --shutdown
#使用配置文件启动mongodb
mongod -f /home/cluster/mongodb/conf/mongodb.conf
systemctl start mongodb.service
systemctl stop mongodb.service
四、配置集群
192.168.62.37 Primary
192.168.62.15 Secondary
192.168.62.95 Arbiter
#连接主节点
mongo 192.168.62.15:28037
#初始化集群
rs.initiate({_id: "rs0",members: [{ _id: 0 , host: "192.168.62.37:28017" }]})
#添加副节点
rs.add("192.168.62.15:28017")
#添加仲裁节点
rs.addArb("192.168.62.95:28017")
#查看状态
rs0:PRIMARY> rs.status();
{
"set" : "rs0",
"date" : ISODate("2020-03-23T09:22:37.763Z"),
"myState" : 1,
"term" : NumberLong(5),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"heartbeatIntervalMillis" : NumberLong(2000),
"majorityVoteCount" : 2,
"writeMajorityCount" : 2,
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1584955352, 1),
"t" : NumberLong(5)
},
"lastCommittedWallTime" : ISODate("2020-03-23T09:22:32.126Z"),
"readConcernMajorityOpTime" : {
"ts" : Timestamp(1584955352, 1),
"t" : NumberLong(5)
},
"readConcernMajorityWallTime" : ISODate("2020-03-23T09:22:32.126Z"),
"appliedOpTime" : {
"ts" : Timestamp(1584955352, 1),
"t" : NumberLong(5)
},
"durableOpTime" : {
"ts" : Timestamp(1584955352, 1),
"t" : NumberLong(5)
},
"lastAppliedWallTime" : ISODate("2020-03-23T09:22:32.126Z"),
"lastDurableWallTime" : ISODate("2020-03-23T09:22:32.126Z")
},
"lastStableRecoveryTimestamp" : Timestamp(1584955322, 1),
"lastStableCheckpointTimestamp" : Timestamp(1584955322, 1),
"electionCandidateMetrics" : {
"lastElectionReason" : "electionTimeout",
"lastElectionDate" : ISODate("2020-03-23T09:11:51.967Z"),
"electionTerm" : NumberLong(5),
"lastCommittedOpTimeAtElection" : {
"ts" : Timestamp(1584954671, 1),
"t" : NumberLong(3)
},
"lastSeenOpTimeAtElection" : {
"ts" : Timestamp(1584954671, 1),
"t" : NumberLong(3)
},
"numVotesNeeded" : 2,
"priorityAtElection" : 1,
"electionTimeoutMillis" : NumberLong(10000),
"numCatchUpOps" : NumberLong(0),
"newTermStartDate" : ISODate("2020-03-23T09:11:52.111Z"),
"wMajorityWriteAvailabilityDate" : ISODate("2020-03-23T09:14:02.372Z")
},
"members" : [
{
"_id" : 0,
"name" : "192.168.62.37:28017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 755,
"optime" : {
"ts" : Timestamp(1584955352, 1),
"t" : NumberLong(5)
},
"optimeDate" : ISODate("2020-03-23T09:22:32Z"),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"electionTime" : Timestamp(1584954711, 1),
"electionDate" : ISODate("2020-03-23T09:11:51Z"),
"configVersion" : 3,
"self" : true,
"lastHeartbeatMessage" : ""
},
{
"_id" : 1,
"name" : "192.168.62.15:28017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 515,
"optime" : {
"ts" : Timestamp(1584955352, 1),
"t" : NumberLong(5)
},
"optimeDurable" : {
"ts" : Timestamp(1584955352, 1),
"t" : NumberLong(5)
},
"optimeDate" : ISODate("2020-03-23T09:22:32Z"),
"optimeDurableDate" : ISODate("2020-03-23T09:22:32Z"),
"lastHeartbeat" : ISODate("2020-03-23T09:22:36.033Z"),
"lastHeartbeatRecv" : ISODate("2020-03-23T09:22:36.372Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "192.168.62.37:28017",
"syncSourceHost" : "192.168.62.37:28017",
"syncSourceId" : 0,
"infoMessage" : "",
"configVersion" : 3
},
{
"_id" : 2,
"name" : "192.168.62.95:28017",
"health" : 1,
"state" : 7,
"stateStr" : "ARBITER",
"uptime" : 753,
"lastHeartbeat" : ISODate("2020-03-23T09:22:35.972Z"),
"lastHeartbeatRecv" : ISODate("2020-03-23T09:22:36.371Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"configVersion" : 3
}
],
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1584955352, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1584955352, 1)
}
五、开启验证
在副本集内成员之间需要用keyFile认证,集群所有mongod和mongos实例使用内容相同的keyFile文件;由于启用了认证,需要建立一个管理员帐号,才能从远程登录。建立管理员帐户,利用管理员账户从远程登录后,需要建立一个可以操作某个数据库的用户,客户端就用这个用户访问数据库。
- 1.建立管理员账户
use admin
db.createUser({
user:"root",
pwd:"123456",
roles: [ { role: "root",db:"admin"}]
})
db.auth("root","123456")//认证该用户
- 2.用openssl生成keyFile认证文件
openssl rand -base64 700 > mdb-keyfile.jks
#长度不能太长,否则会报如下错误,此处定义700长度:
#security key in /home/cluster/mongodb/conf/mdb-keyfile.jks has length 1368, must be between 6 and 1024 chars
- 3.更改keyfile权限
chmod 600 mdb-keyfile.jks
- 4.拷贝到其他主机
scp -P 22 mdb-keyfile.jks [email protected]:/home/cluster/mongodb/conf
scp -P 22 mdb-keyfile.jks [email protected]:/home/cluster/mongodb/conf
- 5.配置mongodb.conf文件开启auth认证
#配置文件增加:
security:
authorization: enabled
keyFile: /home/cluster/mongodb/conf/mdb-keyfile.jks
- 6.登陆
mongo 192.168.62.37:28017/admin -u root -p 123456
mongo 192.168.62.15:28017/admin -u root -p 123456
- 7.连接字符串
mongodb://192.168.62.37:28017,192.168.62.15:28017/?replicaSet=rs0
六、常用操作语句
show dbs;显示所有存在的数据库
use test;进入数据库
db;显示当前所在数据库
show collections;显示当前数据库所有集合
//向当前数据库,students集合中插入一个学生文档
db.students.insert({"name":"孙悟空","age":18,"sex":"男"});
//向当前数据库,students集合中插入多个学生文档
db.students.insert([{"name":"紫霞","age":16,"sex":"女"},{"name":"牛魔","age":23,"sex":"男"}]);
db.students.find();查询当前数据库,students集合中所有的文档
db.students.find({"sex":"男"});查询当前数据库,students集合中符合条件的文档
db.students.find({"name":/牛/});模糊查询
db.students.find().count();查询当前数据库,students集合中所有的文档的总数
db.students.find().sort({"age":1});根据字段排序,1正序 -1倒序
db.students.update({"name":"牛魔"}, {$set:{"name":"牛魔魔"}});部分字段更新,默认只修改一条记录
db.students.updateMany({"sex":"男"}, {$set:{"sex":"男生"}});部分字段更新,修改多条记录
db.students.remove({"name":"牛魔魔"});删除符合条件的文档
db.students.drop();删除当前集合
db.dropDatabase();删除当前数据库