0%

MongoDB复制集

MongoDB复制是将数据同步在多个服务器的过程,它提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性,并可以保证数据的安全性。另外,复制还可以从硬件故障和服务中断中恢复数据。

一、基础

      MongoDB复制集由一组mongod实例(进程)组成,包含一个Primary节点和多个Secondary节点,客户端的所有数据都写入Primary,Secondary从Primary同步写入的数据,以保持复制集内所有成员存储相同的数据集,提供数据的高可用。

  1. 选举Primary节点

      复制集通过replSetInitiate命令(或mongo shell的rs.initiate())进行初始化,初始化后各个成员间开始发送心跳消息,并发起Priamry选举操作,获得大多数成员投票支持的节点,会成为Primary,其余节点成为Secondary。正常情况下复制集的Secondary会参与Primary选举(自身也可能会被选为Primary),并从Primary同步最新写入的数据,以保证与Primary存储相同的数据。Secondary可以提供读服务,增加Secondary节点可以提供复制集的读服务能力,同时提升复制集的可用性。

假设复制集内投票成员数量为N,则大多数为N/2 + 1,当复制集内存活成员数量不足大多数时,整个复制集将无法选举出Primary,复制集将无法提供写服务,处于只读状态。

  1. 特殊Secondary节点
    • Arbiter节点:翻译过来为仲裁节点,只参与投票,不能被选为Primary,并且不从Primary同步数据。
    • Priority0节点:Priority0节点的选举优先级为0,不会被选举为Primary,也不能触发选举,更改Priority
    • Vote0节点:从3.0版本开始(目前截止到4.2.5),复制集成员最多50个,参与Primary选举投票的成员最多7个,其他成员即Vote0的vote属性必须设置为0,即不参与投票。
    • Hidden节点:对客户端Driver不可见,且不能被选为主节点(Priority为0)。
    • Delayed节点:主要用于数据恢复,其必须是Hidden节点,则Priority肯定为0,另外members[n].votes必须设为1

从节点基本属性

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"_id" : <num>,
"host" : <hostname:port>,
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 0,
"tags" : {

},
"slaveDelay" : NumberLong(0),
"votes" : 0
}
  1. 常用命令
    • rs.initiate()初始化复制集
    • rs.status()查看复制集状态
    • rs.conf()/config()查看复制集配置信息
    • rs.add()增加节点
    • rs.addArb()增加Arbiter节点
    • rs.remove()移除节点
    • db.isMaster()查看是否为主节点
    • db.getReplicationInfo()查看复制集信息
    • db.getMongo().setSlaveOk()设置从节点为可读

二、搭建(同一台机器不同端口实现)

  1. 准备工作
    • cd /usr/local/src
    • 下载sudo curl -O https://fastdl.mongodb.org/osx/mongodb-osx-ssl-x86_64-4.0.11.tgz
      • 或使用sudo wget https://fastdl.mongodb.org/osx/mongodb-osx-ssl-x86_64-4.0.11.tgz
    • 解压sudo tar -zxvf mongodb-osx-ssl-x86_64-4.0.11.tgz
      • cd到想安装的目录再运行解压命令则省去了移动命令(重命名自己定)
    • 移动并重命名sudo mv /usr/local/src/mongodb-osx-ssl-x86_64-4.0.11 /usr/local/mongodb-4.0.11
    • cd mongodb-4.0.11
    • 分别创建data和logs存放目录
      • sudo mkdir -p data/27017 data/27018 data/27019
      • sudo mkdir -p logs/27017 logs/27018 logs/27019
      • sudo touch logs/27017/mongo.log logs/27018/mongo.log logs/27019/mongo.log
      • sudo chmod -R 777 data
      • sudo chmod -R 777 logs
    • 分别创建27017.conf/27018.conf/27019.conf,内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
dbpath = /usr/local/mongodb-4.0.11/data/27017
logpath = /usr/local/mongodb-4.0.11/logs/27017/mongo.log
port = 27017
fork = true
logappend=true
replSet = my_replset #设置副本集的名字为myrs,同一副本集群的replSet名称必需相同

dbpath = /usr/local/mongodb-4.0.11/data/27018
logpath = /usr/local/mongodb-4.0.11/logs/27018/mongo.log
port = 27018
fork = true
logappend=true
replSet = my_replset #设置副本集的名字为myrs,同一副本集群的replSet名称必需相同

dbpath = /usr/local/mongodb-4.0.11/data/27019
logpath = /usr/local/mongodb-4.0.11/logs/27019/mongo.log
port = 27019
fork = true
logappend=true
replSet = my_replset #设置副本集的名字为myrs,同一副本集群的replSet名称必需相同
  • 分别启动三个节点
    • ./bin/mongod -f 27017.conf
    • ./bin/mongod -f 27018.conf
    • ./bin/mongod -f 27019.conf
  1. 通过shell进入任意一个节点

    • ./bin/mongo --port 27017/27018/27019,不带--port参数默认进入27017
  2. 切换到admin库use admin

  3. 配置复制集节点信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
config={
"_id":"my_replset",
"members":[
{"_id":0,"host":"127.0.0.1:27017"},
{"_id":1,"host":"127.0.0.1:27018"},
{"_id":2,"host":"127.0.0.1:27019"}
]
}

enter后正常情况下会输出:

{
"_id" : "my_replset",
"members" : [
{
"_id" : 0,
"host" : "127.0.0.1:27017"
},
{
"_id" : 1,
"host" : "127.0.0.1:27018"
},
{
"_id" : 2,
"host" : "127.0.0.1:27019"
}
]
}
  1. 初始化复制集rs.initiate(config),正常情况下会有以下输出:
1
2
3
4
5
6
7
8
9
10
11
{
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1586682897, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1586682897, 1)
}
  1. 查看复制集状态rs.status()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
{
"set" : "my_replset",
"date" : ISODate("2020-04-12T09:15:37.371Z"),
"myState" : 1,
"term" : NumberLong(1),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"heartbeatIntervalMillis" : NumberLong(2000),
"majorityVoteCount" : 2,
"writeMajorityCount" : 2,
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1586682928, 1),
"t" : NumberLong(1)
},
"lastCommittedWallTime" : ISODate("2020-04-12T09:15:28.536Z"),
"readConcernMajorityOpTime" : {
"ts" : Timestamp(1586682928, 1),
"t" : NumberLong(1)
},
"readConcernMajorityWallTime" : ISODate("2020-04-12T09:15:28.536Z"),
"appliedOpTime" : {
"ts" : Timestamp(1586682928, 1),
"t" : NumberLong(1)
},
"durableOpTime" : {
"ts" : Timestamp(1586682928, 1),
"t" : NumberLong(1)
},
"lastAppliedWallTime" : ISODate("2020-04-12T09:15:28.536Z"),
"lastDurableWallTime" : ISODate("2020-04-12T09:15:28.536Z")
},
"lastStableRecoveryTimestamp" : Timestamp(1586682908, 3),
"lastStableCheckpointTimestamp" : Timestamp(1586682908, 3),
"electionCandidateMetrics" : {
"lastElectionReason" : "electionTimeout",
"lastElectionDate" : ISODate("2020-04-12T09:15:08.446Z"),
"electionTerm" : NumberLong(1),
"lastCommittedOpTimeAtElection" : {
"ts" : Timestamp(0, 0),
"t" : NumberLong(-1)
},
"lastSeenOpTimeAtElection" : {
"ts" : Timestamp(1586682897, 1),
"t" : NumberLong(-1)
},
"numVotesNeeded" : 2,
"priorityAtElection" : 1,
"electionTimeoutMillis" : NumberLong(10000),
"numCatchUpOps" : NumberLong(0),
"newTermStartDate" : ISODate("2020-04-12T09:15:08.530Z"),
"wMajorityWriteAvailabilityDate" : ISODate("2020-04-12T09:15:09.512Z")
},
"members" : [
{
"_id" : 0,
"name" : "127.0.0.1:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 288,
"optime" : {
"ts" : Timestamp(1586682928, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2020-04-12T09:15:28Z"),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "could not find member to sync from",
"electionTime" : Timestamp(1586682908, 1),
"electionDate" : ISODate("2020-04-12T09:15:08Z"),
"configVersion" : 1,
"self" : true,
"lastHeartbeatMessage" : ""
},
{
"_id" : 1,
"name" : "127.0.0.1:27018",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 40,
"optime" : {
"ts" : Timestamp(1586682928, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1586682928, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2020-04-12T09:15:28Z"),
"optimeDurableDate" : ISODate("2020-04-12T09:15:28Z"),
"lastHeartbeat" : ISODate("2020-04-12T09:15:36.506Z"),
"lastHeartbeatRecv" : ISODate("2020-04-12T09:15:35.512Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "127.0.0.1:27017",
"syncSourceHost" : "127.0.0.1:27017",
"syncSourceId" : 0,
"infoMessage" : "",
"configVersion" : 1
},
{
"_id" : 2,
"name" : "127.0.0.1:27019",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 40,
"optime" : {
"ts" : Timestamp(1586682928, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1586682928, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2020-04-12T09:15:28Z"),
"optimeDurableDate" : ISODate("2020-04-12T09:15:28Z"),
"lastHeartbeat" : ISODate("2020-04-12T09:15:36.506Z"),
"lastHeartbeatRecv" : ISODate("2020-04-12T09:15:35.512Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "127.0.0.1:27017",
"syncSourceHost" : "127.0.0.1:27017",
"syncSourceId" : 0,
"infoMessage" : "",
"configVersion" : 1
}
],
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1586682928, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1586682928, 1)
}
  1. 查看复制集配置信息rs.conf()

  2. 在主节点写入数据

1
2
3
4
5
./bin/mongo 

use study;

db.user.insert({"name":"test"})
  1. 在从节点读取数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
./bin/mongo --port 27018

use study;

db.user.find();

报错
Error: listDatabases failed:{
"operationTime" : Timestamp(1586683256, 1),
"ok" : 0,
"errmsg" : "not master and slaveOk=false",
"code" : 13435,
"codeName" : "NotMasterNoSlaveOk",
"$clusterTime" : {
"clusterTime" : Timestamp(1586683256, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}

默认进入从节点时数据不可读,因为mongodb默认是从主节点读写数据的,需要设置副本节点可以读:db.getMongo().setSlaveOk()

1
db.user.find();# 正常
  1. 自动故障转移(关闭主节点27017服务)
1
2
3
use admin;

db.shutdownServer()
  1. 进入一个从节点查看复制集信息rs.status()

  2. 重新启动27017./bin/mongod -f 27017.conf

  3. 启动27017客户端./bin/mongo

  4. 查看复制集状态rs.status()

三、参考

  1. MongoDB中文社区
  2. MongoDB官网
  3. 参考三