赞
踩
MongoDB
的副本集(Replica Set)
是一种提供数据冗余和高可用性的架构。它是由多个维护相同数据集的mongod
进程(即数据库实例)组成的一个集合,这些实例分布在不同的服务器上。
副本集的设计目标是为了确保在单个服务器发生故障时,数据库服务依然可以继续运作,从而提高了系统的可靠性和容错能力。
一个副本集最多有50个节点。一个副本集最多有7个投票节点,其余节点必须是没有投票权的节点。
副本集的最小推荐配置是三个节点:
- 一个主节点和两个从节点。
- 一个主节点、一个从节点和仲裁节点。
副本集有两种类型三种角色
两种类型
主节点(Primary)类型:数据操作的主要连接点,可读写
次要(辅助、从)节点(Secondaries)类型:数据冗余备份节点,可以读或选举
三种角色
主要成员(Primary):主要接收所有写操作。就是主节点。
副本成员(Replicate):从主节点通过复制操作以维护相同的数据集,即备份数据,不可写操作,但可以读操作(但需要配置)。是默认的一种从节点类型
仲裁者(Arbiter):不保留任何数据的副本,只具有投票选举作用,当然也可以将仲裁服务器维护为副本集的一部分,即副本成员同时也可以是仲裁者。也是一种从节点类型。
建议
如果你的副本+主节点的个数是偶数,建议加一个仲裁者,形成奇数,容易满足大多数的投票。
如果你的副本+主节点的个数是奇数,可以不加仲裁者。
一台机器上部署三个mongodb节点
端口 | 角色 |
---|---|
27017 | 主节点 |
27018 | 副本节点 |
27019 | 仲裁节点 |
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-4.4.6.tgz
tar xzvf mongodb-linux-x86_64-rhel70-4.4.6.tgz -C /usr/local
cd /usr/local/
ln -s /usr/local/mongodb-linux-x86_64-rhel70-4.4.6 /usr/local/mongodb
mkdir -p /usr/local/mongodb/replica_sets/myrs_27017/log
mkdir -p /usr/local/mongodb/replica_sets/myrs_27017/data/db
vim /usr/local/mongodb/replica_sets/myrs_27017/mongod.conf systemLog: destination: file path: "/usr/local/mongodb/replica_sets/myrs_27017/log/mongod.log" logAppend: true storage: dbPath: "/usr/local/mongodb/replica_sets/myrs_27017/data/db" journal: # 启用持久性日志 enabled: true processManagement: fork: true pidFilePath: "/usr/local/mongodb/replica_sets/myrs_27017/log/mongod.pid" net: bindIp: localhost,192.168.112.40 port: 27017 replication: replSetName: myrs
[root@localhost local]# /usr/local/mongodb/bin/mongod -f /usr/local/mongodb/replica_sets/myrs_27017/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 8177
child process started successfully, parent exiting
mkdir -p /usr/local/mongodb/replica_sets/myrs_27018/log
mkdir -p /usr/local/mongodb/replica_sets/myrs_27018/data/db
vim /usr/local/mongodb/replica_sets/myrs_27018/mongod.conf systemLog: destination: file path: "/usr/local/mongodb/replica_sets/myrs_27018/log/mongod.log" logAppend: true storage: dbPath: "/usr/local/mongodb/replica_sets/myrs_27018/data/db" journal: enabled: true processManagement: fork: true pidFilePath: "/usr/local/mongodb/replica_sets/myrs_27018/log/mongod.pid" net: bindIp: localhost,192.168.112.40 port: 27018 replication: replSetName: myrs
[root@localhost ~]# /usr/local/mongodb/bin/mongod -f /usr/local/mongodb/replica_sets/myrs_27018/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 18263
child process started successfully, parent exiting
mkdir -p /usr/local/mongodb/replica_sets/myrs_27019/log
mkdir -p /usr/local/mongodb/replica_sets/myrs_27019/data/db
vim /usr/local/mongodb/replica_sets/myrs_27019/mongod.conf systemLog: destination: file path: "/usr/local/mongodb/replica_sets/myrs_27019/log/mongod.log" logAppend: true storage: dbPath: "/usr/local/mongodb/replica_sets/myrs_27019/data/db" journal: # 启用持久性日志 enabled: true processManagement: fork: true pidFilePath: "/usr/local/mongodb/replica_sets/myrs_27019/log/mongod.pid" net: bindIp: localhost,192.168.112.40 port: 27019 replication: replSetName: myrs
[root@localhost ~]# /usr/local/mongodb/bin/mongod -f /usr/local/mongodb/replica_sets/myrs_27019/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 18314
child process started successfully, parent exiting
vim /etc/profile
export PATH=/usr/local/mongodb/bin/:$PATH
source /etc/profile
[root@localhost ~]# which mongo
/usr/local/mongodb/bin/mongo
mongo --host 192.168.112.40 --port=27017
当前并未选举主节点(primary),并且操作没有设置为允许从辅助节点(secondary)上读取。
所以很多命令无法使用,必须初始化副本集
> show dbs uncaught exception: Error: listDatabases failed:{ "topologyVersion" : { "processId" : ObjectId("662f9e53d74e84faba8865bc"), "counter" : NumberLong(0) }, "ok" : 0, "errmsg" : "not master and slaveOk=false", "code" : 13435, "codeName" : "NotPrimaryNoSecondaryOk" } : _getErrorWithCode@src/mongo/shell/utils.js:25:13 Mongo.prototype.getDBs/<@src/mongo/shell/mongo.js:147:19 Mongo.prototype.getDBs@src/mongo/shell/mongo.js:99:12 shellHelper.show@src/mongo/shell/utils.js:937:13 shellHelper@src/mongo/shell/utils.js:819:15 @(shellhelp2):1:1
初始化副本集
> rs.initiate()
{
"info2" : "no configuration specified. Using a default configuration for the set",
"me" : "192.168.112.40:27017",
"ok" : 1
}
myrs:SECONDARY>
"ok"的值为1,说明创建成功。
命令行提示符发生变化,变成了一个从节点角色,此时默认不能读写。
稍等片刻,发现副本集只有自己一个,变成主节点。
myrs:PRIMARY> rs.conf() { "_id" : "myrs", "version" : 1, "term" : 1, "protocolVersion" : NumberLong(1), "writeConcernMajorityJournalDefault" : true, "members" : [ { "_id" : 0, "host" : "192.168.112.40:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 } ], "settings" : { "chainingAllowed" : true, "heartbeatIntervalMillis" : 2000, "heartbeatTimeoutSecs" : 10, "electionTimeoutMillis" : 10000, "catchUpTimeoutMillis" : -1, "catchUpTakeoverDelayMillis" : 30000, "getLastErrorModes" : { }, "getLastErrorDefaults" : { "w" : 1, "wtimeout" : 0 }, "replicaSetId" : ObjectId("662fa69cd74e84faba8865e0") } }
“_id” : “myrs” :副本集的配置数据存储的主键值,默认就是副本集的名字
“members” :副本集成员数组,此时只有一个:“host” : “192.168.112.40:27017”
该成员不是仲裁节点: “arbiterOnly” : false
优先级(权重值): “priority” : 1
“settings” :副本集的参数配置。
添加副本节点
myrs:PRIMARY> rs.add("192.168.112.40:27018")
{
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1714399646, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1714399646, 1)
}
添加仲裁节点
myrs:PRIMARY> rs.addArb("192.168.112.40:27019")
{
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1714399738, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1714399738, 1)
}
myrs:PRIMARY> rs.conf() { "_id" : "myrs", "version" : 3, "term" : 1, "protocolVersion" : NumberLong(1), "writeConcernMajorityJournalDefault" : true, "members" : [ { "_id" : 0, "host" : "192.168.112.40:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 1, "host" : "192.168.112.40:27018", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 2, "host" : "192.168.112.40:27019", "arbiterOnly" : true, "buildIndexes" : true, "hidden" : false, "priority" : 0, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 } ], "settings" : { "chainingAllowed" : true, "heartbeatIntervalMillis" : 2000, "heartbeatTimeoutSecs" : 10, "electionTimeoutMillis" : 10000, "catchUpTimeoutMillis" : -1, "catchUpTakeoverDelayMillis" : 30000, "getLastErrorModes" : { }, "getLastErrorDefaults" : { "w" : 1, "wtimeout" : 0 }, "replicaSetId" : ObjectId("662fa69cd74e84faba8865e0") } }
- 成员信息:
- 成员1(_id: 0)和成员2(_id: 1)都是数据承载节点,它们位于同一台机器的不同端口(27017 和 27018)。两者都有相同的优先级(“priority” : 1),意味着它们都可以成为主节点。它们都没有延迟复制(“slaveDelay” : NumberLong(0)),并且都参与选举投票(“votes” : 1)。
- 成员3(_id: 2)是一个仲裁者(arbiterOnly: true),位于端口27019。仲裁者的角色是在选举新主节点时起到决定性的一票作用,但它不存储实际数据,也没有优先级,不参与数据复制。
rs.slaveOk()
[root@localhost ~]# mongo --port 27017 myrs:PRIMARY> show dbs admin 0.000GB config 0.000GB local 0.000GB myrs:PRIMARY> use test1 switched to db test1 myrs:PRIMARY> db.q1.insert({"id":"1000","content":"学习部署副本集","userid":"001","name":"zhangsan","createdatatime":new Date(),"state":null}) WriteResult({ "nInserted" : 1 }) myrs:PRIMARY> db.q1.find().pretty() { "_id" : ObjectId("662fb0d727a6cea3c847b26e"), "id" : "1000", "content" : "学习部署副本集", "userid" : "001", "name" : "zhangsan", "createdatatime" : ISODate("2024-04-29T14:38:15.243Z"), "state" : null }
[root@localhost ~]# mongo --port 27018 myrs:SECONDARY> show dbs admin 0.000GB config 0.000GB local 0.000GB test1 0.000GB myrs:SECONDARY> use test1 switched to db test1 myrs:SECONDARY> show tables; q1 myrs:SECONDARY> db.q1.find().pretty() { "_id" : ObjectId("662fb0d727a6cea3c847b26e"), "id" : "1000", "content" : "学习部署副本集", "userid" : "001", "name" : "zhangsan", "createdatatime" : ISODate("2024-04-29T14:38:15.243Z"), "state" : null } myrs:SECONDARY> db.q1.insert({"id":"1001","content":"学习部署副本集","userid":"002","name":"lisi","createdatatime":new Date(),"state":null}) WriteCommandError({ "topologyVersion" : { "processId" : ObjectId("662fa148dfb1efa1f75795c3"), "counter" : NumberLong(4) }, "operationTime" : Timestamp(1714402027, 1), "ok" : 0, "errmsg" : "not master", "code" : 10107, "codeName" : "NotWritablePrimary", "$clusterTime" : { "clusterTime" : Timestamp(1714402027, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } })
无法写入数据
“errmsg” : “not master”
“codeName” : “NotWritablePrimary”
#设置从节点有读取权限
rs.slaveOk()
# 取消从节点的数据读取权限
rs.slaveOk(false)
[root@localhost ~]# mongo --port 27019 myrs:ARBITER> show dbs uncaught exception: Error: listDatabases failed:{ "topologyVersion" : { "processId" : ObjectId("662fa25360629ad0a0f05cc1"), "counter" : NumberLong(1) }, "ok" : 0, "errmsg" : "not master and slaveOk=false", "code" : 13435, "codeName" : "NotPrimaryNoSecondaryOk" } : _getErrorWithCode@src/mongo/shell/utils.js:25:13 Mongo.prototype.getDBs/<@src/mongo/shell/mongo.js:147:19 Mongo.prototype.getDBs@src/mongo/shell/mongo.js:99:12 shellHelper.show@src/mongo/shell/utils.js:937:13 shellHelper@src/mongo/shell/utils.js:819:15 @(shellhelp2):1:1 myrs:ARBITER> rs.slaveOk() WARNING: slaveOk() is deprecated and may be removed in the next major release. Please use secondaryOk() instead. myrs:ARBITER> show dbs uncaught exception: Error: listDatabases failed:{ "topologyVersion" : { "processId" : ObjectId("662fa25360629ad0a0f05cc1"), "counter" : NumberLong(1) }, "ok" : 0, "errmsg" : "node is not in primary or recovering state", "code" : 13436, "codeName" : "NotPrimaryOrSecondary" } : _getErrorWithCode@src/mongo/shell/utils.js:25:13 Mongo.prototype.getDBs/<@src/mongo/shell/mongo.js:147:19 Mongo.prototype.getDBs@src/mongo/shell/mongo.js:99:12 shellHelper.show@src/mongo/shell/utils.js:937:13 shellHelper@src/mongo/shell/utils.js:819:15 @(shellhelp2):1:1
“errmsg” : “node is not in primary or recovering state”
仲裁节点(Arbiter)在MongoDB副本集中仅用于选举过程中的投票,并不存储数据
MongoDB在副本集中,会自动进行主节点的选举,主节点选举的触发条件
主节点故障
主节点网络不可达(默认心跳信息为10秒)
人工干预(rs.stepDown(600)
)primary直接降级在600s内不会把自己选为primary
一旦触发选举,就要根据一定规则来选择主节点。
选举规则是根据票数来决定谁获胜
票数最高,且获得了“大多数”成员的投票支持的节点获胜。
"大多数"的定义为:假设复制集内投票成员时N,则大多数为N/2+1。例如:3个投票成员,则大多数的值是2。当复制集内存活成员数量不足大多数时,整个复制集将无法选举primary,复制集将无法提供写服务,处于只读状态。
若票数相同,且都获得了“大多数”成员的投票支持的,数据新的节点获胜。
数据的新旧是通过操作日志oplog来对比的。
在获得票数的时候,优先级(priority)参数影响重大。
可以通过设置优先级(priority)来设置额外票数。优先级即权重,取值为0-1000,相当于增加0-1000的票数,优先级的值越大,就越可能获得多数成员的投票(votes)数。指定较高的值可使成员更有资格成员主要成员,更低的值可使成员更不符合条件。
默认情况下,优先级的值是1
副本集中没有主节点了,导致此时,副本集是只读状态,无法写入。
因为27017的票数,没有获得大多数,即没有大于等于2,它只有默认的一票(优先级是1)
如果要触发选举,随便加入一个成员即可。
此时:影响正常使用,需要处理
10秒后,27017主节点自动降级为副本节点。(服务降级)
副本集不可写数据了,已经故障了。
此时:影响正常使用,需要处理
集群将处于不完全状态,无法执行写操作,因为剩余的副本节点不足以立即选出新的主节点(假设只剩一个副本节点和仲裁节点)。直到至少有一个额外的副本节点在线并同步,以便选举出新的主节点
此时:影响正常使用,需要处理
整个集群不可用,既不能执行读也不能执行写操作。
这种情况需要手动干预,逐一排查并恢复各个节点,确保至少一个主节点和多数节点(包括仲裁节点)在线,以恢复集群服务。
此时:影响正常使用,需要处理
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。