前言
本文章主要介绍了 MongoDB 5.0 副本集的部署与配置,基于上一篇 MongoDB CentOS 7 安装部署
主从复制和副本集区别
主从集群和副本集最大的区别就是副本集没有固定的主节点;整个集群会选出一个主节点,当其挂掉后,又在剩下的从节点中选中其他节点为主节点,副本集总有一个主节点和一个或多个备份节点
副本集配置
架构
一主一副本一仲裁
角色 | ip | port |
---|
Primary | 192.168.1.142 | 27017 | Secondary | 192.168.1.143 | 27017 | Arbiter | 192.168.1.144 | 27017 |
系统配置
- 设置主机名
hostnamectl set-hostname node1
- 在 /etc/hosts (主机名查询静态表)配置主机名与ip映射关系
vi /etc/hosts
192.168.1.142 node1
192.168.1.143 node2
192.168.1.144 node3
注意事项
- 副本集的集合名 replSetName 必须是相同的 这里统一为 rs
- 机器的防火墙要么关闭,要么将相应的端口开放
创建节点
新增修改配置文件 mongodb.conf
systemLog:
destination: file
path: "/usr/local/mongodb-5.0.2/log/mongodb.log"
logAppend: true
storage:
dbPath: "/usr/local/mongodb-5.0.2/data"
journal:
enabled: true
processManagement:
fork: true
pidFilePath: "/usr/local/mongodb-5.0.2/run/mongod.pid"
net:
bindIp: 0.0.0.0
port: 27017
replication:
replSetName: rs
三台服务相同即可
启动节点服务
/usr/local/mongodb-5.0.2/bin/mongod -f /usr/local/mongodb-5.0.2/conf/mongodb.conf
[root@localhost /]
about to fork child process, waiting until server is ready for connections.
forked process: 38140
child process started successfully, parent exiting
三个节点以同样的方式启动即可
初始化配置副本集和主节点
使用客户端命令连接主节点
/usr/local/mongodb-5.0.2/bin/mongo --port 27017
准备初始化新的副本集
语法
rs.initiate(configuration)
Parameter | Type | Description |
---|
configuration | document | Optional. A document that specifies configuration for the new replica set. If a configuration is not specified, MongoDB uses a default replica set configuration. |
使用默认的配置初始化副本集
rs.initiate()
> rs.initiate()
{
"info2" : "no configuration specified. Using a default configuration for the set",
"me" : "node1:27017",
"ok" : 1
}
rs:SECONDARY>
rs:PRIMARY>
说明: 1)“ok” 的值为 1,说明创建成功。 2)“me” 的值便是当前的 主机名+端口 3)命令行提示符发生变化,变成了一个从节点角色,此时默认不能读写。稍等片刻,回车,变成主节点
查看副本集的配置内容
返回包含当前副本集配置的文档
rs.conf(configuration)
rs.config() 是该方法的别名。 configuration:可选,如果没有配置,则使用默认主节点配置。
rs:PRIMARY> rs.conf()
{
"_id" : "rs",
"version" : 1,
"term" : 1,
"members" : [
{
"_id" : 0,
"host" : "node1:27017",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {
},
"secondaryDelaySecs" : NumberLong(0),
"votes" : 1
}
],
"protocolVersion" : NumberLong(1),
"writeConcernMajorityJournalDefault" : true,
"settings" : {
"chainingAllowed" : true,
"heartbeatIntervalMillis" : 2000,
"heartbeatTimeoutSecs" : 10,
"electionTimeoutMillis" : 10000,
"catchUpTimeoutMillis" : -1,
"catchUpTakeoverDelayMillis" : 30000,
"getLastErrorModes" : {
},
"getLastErrorDefaults" : {
"w" : 1,
"wtimeout" : 0
},
"replicaSetId" : ObjectId("614585a268fddfacd19367d0")
}
}
说明: 1) “_id” : “rs” :副本集的配置数据存储的主键值,默认就是副本集的名字 2) “members” :副本集成员数组,此时只有一个: “host” : “node1:27017” ,该成员不是仲裁节点: “arbiterOnly” : false ,优先级(权重值): “priority” : 1, 3) “settings” :副本集的参数配置。
【提示】 副本集配置的查看命令,本质是查询的是 system.replset 的表中的数据
master:PRIMARY> use local
switched to db local
master:PRIMARY> show collections
oplog.rs
replset.election
replset.initialSyncId
replset.minvalid
replset.oplogTruncateAfterPoint
startup_log
system.replset
system.rollback.id
system.tenantMigration.oplogView
system.views
master:PRIMARY> db.system.replset.find()
{ "_id" : "master", "version" : 1, "term" : 1, "members" : [ { "_id" : 0, "host" : "localhost.localdomain:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "secondaryDelaySecs" : NumberLong(0), "votes" : 1 } ], "protocolVersion" : NumberLong(1), "writeConcernMajorityJournalDefault" : true, "settings" : { "chainingAllowed" : true, "heartbeatIntervalMillis" : 2000, "heartbeatTimeoutSecs" : 10, "electionTimeoutMillis" : 10000, "catchUpTimeoutMillis" : -1, "catchUpTakeoverDelayMillis" : 30000, "getLastErrorModes" : { }, "getLastErrorDefaults" : { "w" : 1, "wtimeout" : 0 }, "replicaSetId" : ObjectId("614486497c0afbdf3172ebfe") } }
查看副本集状态
检查副本集状态 说明: 返回包含状态信息的文档。此输出使用从副本集的其他成员发送的心跳包中获得的数据反映副本集的当 前状态。 语法:
rs.status()
rs:PRIMARY> rs.status()
{
"set" : "rs",
"date" : ISODate("2021-09-18T06:27:46.387Z"),
"myState" : 1,
"term" : NumberLong(1),
"syncSourceHost" : "",
"syncSourceId" : -1,
"heartbeatIntervalMillis" : NumberLong(2000),
"majorityVoteCount" : 1,
"writeMajorityCount" : 1,
"votingMembersCount" : 1,
"writableVotingMembersCount" : 1,
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1631946456, 1),
"t" : NumberLong(1)
},
"lastCommittedWallTime" : ISODate("2021-09-18T06:27:36.551Z"),
"readConcernMajorityOpTime" : {
"ts" : Timestamp(1631946456, 1),
"t" : NumberLong(1)
},
"appliedOpTime" : {
"ts" : Timestamp(1631946456, 1),
"t" : NumberLong(1)
},
"durableOpTime" : {
"ts" : Timestamp(1631946456, 1),
"t" : NumberLong(1)
},
"lastAppliedWallTime" : ISODate("2021-09-18T06:27:36.551Z"),
"lastDurableWallTime" : ISODate("2021-09-18T06:27:36.551Z")
},
"lastStableRecoveryTimestamp" : Timestamp(1631946436, 1),
"electionCandidateMetrics" : {
"lastElectionReason" : "electionTimeout",
"lastElectionDate" : ISODate("2021-09-18T06:22:26.463Z"),
"electionTerm" : NumberLong(1),
"lastCommittedOpTimeAtElection" : {
"ts" : Timestamp(0, 0),
"t" : NumberLong(-1)
},
"lastSeenOpTimeAtElection" : {
"ts" : Timestamp(1631946146, 1),
"t" : NumberLong(-1)
},
"numVotesNeeded" : 1,
"priorityAtElection" : 1,
"electionTimeoutMillis" : NumberLong(10000),
"newTermStartDate" : ISODate("2021-09-18T06:22:26.470Z"),
"wMajorityWriteAvailabilityDate" : ISODate("2021-09-18T06:22:26.476Z")
},
"members" : [
{
"_id" : 0,
"name" : "node1:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 388,
"optime" : {
"ts" : Timestamp(1631946456, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2021-09-18T06:27:36Z"),
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"electionTime" : Timestamp(1631946146, 2),
"electionDate" : ISODate("2021-09-18T06:22:26Z"),
"configVersion" : 1,
"configTerm" : 1,
"self" : true,
"lastHeartbeatMessage" : ""
}
],
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1631946456, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1631946456, 1)
}
说明: 1) “set” : “rs” :副本集的名字 2) “myState” : 1:说明状态正常 3) “members” :副本集成员数组,此时只有一个: “name” : “node1:27017” ,该成员的角色是 “stateStr” : “PRIMARY”, 该节点是健康的: “health” : 1
添加副本从节点
在主节点添加从节点,将其他成员加入到副本集 语法:
rs.add(host, arbiterOnly)
Parameter | Type | Description |
---|
host | string or document | 要添加到副本集的新成员。 指定为字符串或配置文档:1)如 果是一个字符串,则需要指定新成员的主机名和可选的端口 号;2)如果是一个文档,请指定在members数组中找到的副 本集成员配置文档。 您必须在成员配置文档中指定主机字段。 有关文档配置字段的说明,详见下方文档:“主机成员的配置文档” | arbiterOnly | boolean | 可选的。 仅在 值为字符串时适用。 如果为true,则添加的主机是仲裁者。 |
【示例】 将 192.168.1.143 的副本节点添加到副本集中
rs:PRIMARY> rs.add("node2:27017")
{
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1631948744, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1631948744, 1)
}
说明: ok : 1 说明添加成功 但并不代表指定的副本节点已经正常添加到副本集中
查看副本集状态
rs:PRIMARY> rs.status()
{
"set" : "rs",
"date" : ISODate("2021-09-18T08:03:52.132Z"),
"myState" : 1,
"term" : NumberLong(1),
"syncSourceHost" : "",
"syncSourceId" : -1,
"heartbeatIntervalMillis" : NumberLong(2000),
"majorityVoteCount" : 2,
"writeMajorityCount" : 2,
"votingMembersCount" : 2,
"writableVotingMembersCount" : 2,
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1631952227, 1),
"t" : NumberLong(1)
},
"lastCommittedWallTime" : ISODate("2021-09-18T08:03:47.058Z"),
"readConcernMajorityOpTime" : {
"ts" : Timestamp(1631952227, 1),
"t" : NumberLong(1)
},
"appliedOpTime" : {
"ts" : Timestamp(1631952227, 1),
"t" : NumberLong(1)
},
"durableOpTime" : {
"ts" : Timestamp(1631952227, 1),
"t" : NumberLong(1)
},
"lastAppliedWallTime" : ISODate("2021-09-18T08:03:47.058Z"),
"lastDurableWallTime" : ISODate("2021-09-18T08:03:47.058Z")
},
"lastStableRecoveryTimestamp" : Timestamp(1631952207, 1),
"electionCandidateMetrics" : {
"lastElectionReason" : "electionTimeout",
"lastElectionDate" : ISODate("2021-09-18T06:22:26.463Z"),
"electionTerm" : NumberLong(1),
"lastCommittedOpTimeAtElection" : {
"ts" : Timestamp(0, 0),
"t" : NumberLong(-1)
},
"lastSeenOpTimeAtElection" : {
"ts" : Timestamp(1631946146, 1),
"t" : NumberLong(-1)
},
"numVotesNeeded" : 1,
"priorityAtElection" : 1,
"electionTimeoutMillis" : NumberLong(10000),
"newTermStartDate" : ISODate("2021-09-18T06:22:26.470Z"),
"wMajorityWriteAvailabilityDate" : ISODate("2021-09-18T06:22:26.476Z")
},
"members" : [
{
"_id" : 0,
"name" : "node1:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 6154,
"optime" : {
"ts" : Timestamp(1631952227, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2021-09-18T08:03:47Z"),
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"electionTime" : Timestamp(1631946146, 2),
"electionDate" : ISODate("2021-09-18T06:22:26Z"),
"configVersion" : 15,
"configTerm" : 1,
"self" : true,
"lastHeartbeatMessage" : ""
},
{
"_id" : 1,
"name" : "node2:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 667,
"optime" : {
"ts" : Timestamp(1631952227, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1631952227, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2021-09-18T08:03:47Z"),
"optimeDurableDate" : ISODate("2021-09-18T08:03:47Z"),
"lastHeartbeat" : ISODate("2021-09-18T08:03:50.682Z"),
"lastHeartbeatRecv" : ISODate("2021-09-18T08:03:51.135Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncSourceHost" : "node1:27017",
"syncSourceId" : 0,
"infoMessage" : "",
"configVersion" : 15,
"configTerm" : 1
}
],
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1631952227, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1631952227, 1)
}
可以看到 members 数组 变成了 两位 【注意】 若 optimeDate optimeDurableDate lastHeartbeatRecv 是 1970 的 则是因为 从服务器 无法连通主服务器 我这边地原因是 vi /etc/hosts 第一次只是配置的本机的 主机名和 ip ,将三个节点全部添加是即可解决
若 stateStr 的 值 没有 变为 SECONDARY 也是节点之间网络环境的问题
更多副本集命令
rs:PRIMARY> rs.help()
rs.status() { replSetGetStatus : 1 } checks repl set status
rs.initiate() { replSetInitiate : null } initiates set with default settings
rs.initiate(cfg) { replSetInitiate : cfg } initiates set with configuration cfg
rs.conf() get the current configuration object from local.system.replset
rs.reconfig(cfg, opts) updates the configuration of a running replica set with cfg, using the given opts (disconnects)
rs.reconfigForPSASet(memberIndex, cfg, opts) updates the configuration of a Primary-Secondary-Arbiter (PSA) replica set while preserving majority writes
memberIndex: index of the node being updated; cfg: the desired new config; opts: options passed in with the reconfig
Not to be used with every configuration
rs.add(hostportstr) add a new member to the set with default attributes (disconnects)
rs.add(membercfgobj) add a new member to the set with extra attributes (disconnects)
rs.addArb(hostportstr) add a new member which is arbiterOnly:true (disconnects)
rs.stepDown([stepdownSecs, catchUpSecs]) step down as primary (disconnects)
rs.syncFrom(hostportstr) make a secondary sync from the given member
rs.freeze(secs) make a node ineligible to become primary for the time specified
rs.remove(hostportstr) remove a host from the replica set (disconnects)
rs.secondaryOk() allow queries on secondary nodes
rs.printReplicationInfo() check oplog size and time range
rs.printSecondaryReplicationInfo() check replica set members and replication lag
db.isMaster() check who is primary
db.hello() check who is primary
reconfiguration helpers disconnect from the database so the shell will display
an error, even if the command succeeds.
添加仲裁节点
rs.addArb("node3:27017")
添加仲裁节点一直无响应,很长时间后返回错误信息 “errmsg” : “Reconfig attempted to install a config that would change the implicit default write concern. Use the setDefaultRWConcern command to set a cluster-wide write concern and try the reconfig again.”
rs:PRIMARY> rs.addArb("node3:27017")
assert.soon failed: function() {
var cfg = hostport;
var local = db.getSiblingDB("local");
assert(local.system.replset.count() <= 1,
"error: local.system.replset has unexpected contents");
var c = local.system.replset.findOne();
assert(c, "no config object retrievable from local.system.replset");
const attemptedVersion = c.version++;
var max = 0;
for (var i in c.members) {
// Omit 'newlyAdded' field if it exists in the config.
delete c.members[i].newlyAdded;
if (c.members[i]._id > max)
max = c.members[i]._id;
}
if (isString(hostport)) {
cfg = {_id: max + 1, host: hostport};
if (arb)
cfg.arbiterOnly = true;
} else if (arb == true) {
throw Error(
"Expected first parameter to be a host-and-port string of arbiter, but got " +
tojson(hostport));
}
if (cfg._id == null) {
cfg._id = max + 1;
}
c.members.push(cfg);
res = self._runCmd({replSetReconfig: c});
if (res === "") {
// _runCmd caught an exception.
return true;
}
if (res.ok) {
return true;
}
if (res.code === ErrorCodes.ConfigurationInProgress ||
res.code === ErrorCodes.CurrentConfigNotCommittedYet) {
return false; // keep retrying
}
if (res.code === ErrorCodes.NewReplicaSetConfigurationIncompatible) {
// We will retry only if this error was due to our config version being too low.
const cfgState = local.system.replset.findOne();
if (cfgState.version >= attemptedVersion) {
return false; // keep retrying
}
}
// Take no action on other errors.
return true;
} : {
"ok" : 0,
"errmsg" : "Reconfig attempted to install a config that would change the implicit default write concern. Use the setDefaultRWConcern command to set a cluster-wide write concern and try the reconfig again.",
"code" : 103,
"codeName" : "NewReplicaSetConfigurationIncompatible",
"$clusterTime" : {
"clusterTime" : Timestamp(1631968111, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1631968111, 1)
} The hang analyzer is automatically called in assert.soon functions. If you are *expecting* assert.soon to possibly fail, call assert.soon with {runHangAnalyzer: false} as the fifth argument (you can fill unused arguments with `undefined`). Running hang analyzer from assert.soon.
Skipping runHangAnalyzer: no TestData (not running from resmoke)
Error: assert.soon failed: function() {
var cfg = hostport;
var local = db.getSiblingDB("local");
assert(local.system.replset.count() <= 1,
"error: local.system.replset has unexpected contents");
var c = local.system.replset.findOne();
assert(c, "no config object retrievable from local.system.replset");
const attemptedVersion = c.version++;
var max = 0;
for (var i in c.members) {
// Omit 'newlyAdded' field if it exists in the config.
delete c.members[i].newlyAdded;
if (c.members[i]._id > max)
max = c.members[i]._id;
}
if (isString(hostport)) {
cfg = {_id: max + 1, host: hostport};
if (arb)
cfg.arbiterOnly = true;
} else if (arb == true) {
throw Error(
"Expected first parameter to be a host-and-port string of arbiter, but got " +
tojson(hostport));
}
if (cfg._id == null) {
cfg._id = max + 1;
}
c.members.push(cfg);
res = self._runCmd({replSetReconfig: c});
if (res === "") {
// _runCmd caught an exception.
return true;
}
if (res.ok) {
return true;
}
if (res.code === ErrorCodes.ConfigurationInProgress ||
res.code === ErrorCodes.CurrentConfigNotCommittedYet) {
return false; // keep retrying
}
if (res.code === ErrorCodes.NewReplicaSetConfigurationIncompatible) {
// We will retry only if this error was due to our config version being too low.
const cfgState = local.system.replset.findOne();
if (cfgState.version >= attemptedVersion) {
return false; // keep retrying
}
}
// Take no action on other errors.
return true;
} : {
"ok" : 0,
"errmsg" : "Reconfig attempted to install a config that would change the implicit default write concern. Use the setDefaultRWConcern command to set a cluster-wide write concern and try the reconfig again.",
"code" : 103,
"codeName" : "NewReplicaSetConfigurationIncompatible",
"$clusterTime" : {
"clusterTime" : Timestamp(1631968111, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1631968111, 1)
} The hang analyzer is automatically called in assert.soon functions. If you are *expecting* assert.soon to possibly fail, call assert.soon with {runHangAnalyzer: false} as the fifth argument (you can fill unused arguments with `undefined`). :
doassert@src/mongo/shell/assert.js:20:14
assert.soon@src/mongo/shell/assert.js:382:17
rs.add@src/mongo/shell/utils.js:1626:5
rs.addArb@src/mongo/shell/utils.js:1698:12
@(shell):1:1
解决办法 在 主节点 设置
db.adminCommand({
"setDefaultRWConcern" : 1,
"defaultWriteConcern" : {
"w" : 2
}
})
参考地址 MongoDB 官网 解决无法添加仲裁节点 天宇轩-王
|