文章目录
复制集介绍
Mongodb复制集(Replication Set)包含一个Primary节点和多个Secondary节点,Mongodb Driver(客户端)的所有数据都写入Primary,Secondary从Primary同步写入的数据,以保证数据一致性。
它的现实依赖于两个方面的功能:
- 数据写入时将数据迅速复制到另一个独立节点上
- 在接收写入的节点发生故障时自动选举出一个新的替代节点
在实现高可用的同时,复制集实现了其他几个附加作用:
- 数据分发: 将数据从一个区域复制到另一个区域,减少另一个区域的读延迟
- 读写分离: 不同类型的压力分别在不同的节点上执行
- 异地容灾: 在数据中心故障时候快速切换到异地
早期版本的MongoDB使用了一种Master-Slave的架构,该做法在MongoDB 3.4版本之后已经废弃。
三节点复制集模式
常见的复制集架构由3个成员节点组成,其中存在几种不同的模式。
Pss模式
官方推荐的模式
PSS模式由一个主节点和两个备节点所组成,即Primary+Secondary+Secondary。
此模式始终提供数据集的两个完整副本,如果主节点不可用,则复制集选择备节点作为主节点并继续正常操作。旧的主节点在可用时重新加入复制集。
PSA模式
PSA模式由一个主节点、一个备节点和一个仲裁者节点组成,即Primary+Secondary+Arbiter
其中,Arbiter节点不存储数据副本,也不提供业务的读写操作。Arbiter节点发生故障不影响业务,仅影响选举投票。此模式仅提供数据的一个完整副本,如果主节点不可用,则复制集将选择备节点作为主节点。
典型三节点复制集环境搭建
注意事项
关于硬件:
- 因为正常的复制集节点都有可能成为主节点,它们的地位是一样的,因此硬件配置上必须一致;
- 为了保证节点不会同时宕机,各节点使用的硬件必须具有独立性。
关于软件:
- 复制集各节点软件版本必须一致,以避免出现不可预知的问题。
- 增加节点不会增加系统写性能
搭建
- 安装 MongoDB并配置好环境变量
- 确保有 10GB 以上的硬盘空间
准备配置文件
复制集的每个mongod进程应该位于不同的服务器。我们现在在一台机器上运行3个进程,因此要为它们各自配置:
- 不同的端口(28017/28018/28019)
- 不同的数据目录
- 不同日志文件路径
[root@localhost mongodb]# mkdir -p /mongodb/db{1,2,3}/data
[root@localhost mongodb]# mkdir -p /mongodb/db{1,2,3}/log
创建配置文件/mongodb/db1/mongod.conf,内容如下:
[root@localhost mongodb]# vi /mongodb/db1/mongod.conf
systemLog:
destination: file
path: /mongodb/db1/log/mongod.log # log path
logAppend: true
storage:
dbPath: /mongodb/db1/data # data directory
net:
bindIp: 0.0.0.0
port: 28017 # port
replication:
replSetName: rs0
processManagement:
fork: true
参考上面配置修改端口,路径,依次配置db2,db3。注意必须是yaml格式
[root@localhost mongodb]# vi /mongodb/db2/mongod.conf
systemLog:
destination: file
path: /mongodb/db2/log/mongod.log # log path
logAppend: true
storage:
dbPath: /mongodb/db2/data # data directory
net:
bindIp: 0.0.0.0
port: 28018 # port
replication:
replSetName: rs0
processManagement:
fork: true
[root@localhost mongodb]# vi /mongodb/db3/mongod.conf
systemLog:
destination: file
path: /mongodb/db3/log/mongod.log # log path
logAppend: true
storage:
dbPath: /mongodb/db3/data # data directory
net:
bindIp: 0.0.0.0
port: 28019 # port
replication:
replSetName: rs0
processManagement:
fork: true
启动 MongoDB 进程
mongod -f /mongodb/db1/mongod.conf
mongod -f /mongodb/db2/mongod.conf
mongod -f /mongodb/db3/mongod.conf
注意:如果启用了 SELinux,可能阻止上述进程启动。简单起见请关闭 SELinux。
# 永久关闭,将SELINUX=enforcing改为SELINUX=disabled,设置后需要重启才能生效
vim /etc/selinux/config
# 查看SELINUX
/usr/sbin/sestatus -v
配置复制集
复制集通过mongosh的rs.initiate()进行初始化,初始化后各个成员间开始发送心跳消息,并发起Priamry选举操作,获得『大多数』成员投票支持的节点,会成为Primary,其余节点成为Secondary。
mongosh --port 28017
# 初始化复制集
> rs.initiate({
_id: "rs0",
members: [{
_id: 0,
host: "192.168.75.100:28017"
},{
_id: 1,
host: "192.168.75.100:28018"
},{
_id: 2,
host: "192.168.75.100:28019"
}]
})
验证主从节点读写操作
-
MongoDB 主节点进行写入,发现能写入成功
# mongosh --port 28017 rs0 [direct: primary] test> db.user.insertMany([{name: "hushang", age: 25},{name: "lisi"}])
-
切换到从节点写入,抛出异常 MongoBulkWriteError: not primary
#mongosh --port 28018 rs0 [direct: primary] test> db.user.insertMany([{name: "hushang2", age: 25},{name: "lisi2"}])
-
MongoDB 从节点进行读
# mongo --port 28018 # 指定从节点可读,如果不执行该命令那么进行读取操作会报错 rs0:SECONDARY> rs.secondaryOk() rs0:SECONDARY> db.user.find()
复制集状态查询
查看复制集整体状态
rs.status()
members一列体现了所有复制集成员的状态,主要如下:
health:成员是否健康,通过心跳进行检测。
state/stateStr:成员的状态,PRIMARY表示主节点,而SECONDARY则表示备节点,如果节点出现故障,则可能出现一些其他的状态,例如RECOVERY。
uptime:成员的启动时间。
optime/optimeDate:成员同步最后一条oplog的时间。
optimeDurable/optimeDurableDate:成员同步最后一条oplog持久化的时间。
pingMs:成员与当前节点的ping时延。
syncingTo:成员的同步来源。
rs0 [direct: secondary] test> rs.status()
{
......
members: [
{
_id: 0,
name: '192.168.75.100:28017',
health: 1, # 成员是否健康,通过心跳进行检测。
state: 1,
stateStr: 'PRIMARY', # PRIMARY表示主节点,而SECONDARY则表示备节点,如果节点出现故障,则可能出现一些其他的状态,例如RECOVERY。
uptime: 931, # 成员的启动时间。
optime: { ts: Timestamp({ t: 1722219597, i: 1 }), t: Long("1") }, # 成员同步最后一条oplog的时间
optimeDurable: { ts: Timestamp({ t: 1722219597, i: 1 }), t: Long("1") }, # 成员同步最后一条oplog持久化的时间。
optimeDate: ISODate("2024-07-29T02:19:57.000Z"),
optimeDurableDate: ISODate("2024-07-29T02:19:57.000Z"),
lastAppliedWallTime: ISODate("2024-07-29T02:19:57.639Z"),
lastDurableWallTime: ISODate("2024-07-29T02:19:57.639Z"),
lastHeartbeat: ISODate("2024-07-29T02:19:57.827Z"),
lastHeartbeatRecv: ISODate("2024-07-29T02:19:58.236Z"),
pingMs: Long("0"), # 成员与当前节点的ping时延。
lastHeartbeatMessage: '',
syncSourceHost: '',
syncSourceId: -1,
infoMessage: '',
electionTime: Timestamp({ t: 1722218677, i: 1 }),
electionDate: ISODate("2024-07-29T02:04:37.000Z"),
configVersion: 1,
configTerm: 1
},
{
_id: 1,
name: '192.168.75.100:28018',
health: 1,
state: 2,
stateStr: 'SECONDARY',
uptime: 1054,
optime: { ts: Timestamp({ t: 1722219597, i: 1 }), t: Long("1") },
optimeDate: ISODate("2024-07-29T02:19:57.000Z"),
lastAppliedWallTime: ISODate("2024-07-29T02:19:57.639Z"),
lastDurableWallTime: ISODate("2024-07-29T02:19:57.639Z"),
syncSourceHost: '192.168.75.100:28017', # 主节点信息
syncSourceId: 0,
infoMessage: '',
configVersion: 1,
configTerm: 1,
self: true,
lastHeartbeatMessage: ''
},
{
_id: 2,
name: '192.168.75.100:28019',
health: 1,
state: 2,
stateStr: 'SECONDARY',
uptime: 931,
optime: { ts: Timestamp({ t: 1722219597, i: 1 }), t: Long("1") },
optimeDurable: { ts: Timestamp({ t: 1722219597, i: 1 }), t: Long("1") },
optimeDate: ISODate("2024-07-29T02:19:57.000Z"),
optimeDurableDate: ISODate("2024-07-29T02:19:57.000Z"),
lastAppliedWallTime: ISODate("2024-07-29T02:19:57.639Z"),
lastDurableWallTime: ISODate("2024-07-29T02:19:57.639Z"),
lastHeartbeat: ISODate("2024-07-29T02:19:57.827Z"),
lastHeartbeatRecv: ISODate("2024-07-29T02:19:57.822Z"),
pingMs: Long("0"),
lastHeartbeatMessage: '',
syncSourceHost: '192.168.75.100:28017', # 主节点信息
syncSourceId: 0,
infoMessage: '',
configVersion: 1,
configTerm: 1
}
],
ok: 1,
'$clusterTime': {
clusterTime: Timestamp({ t: 1722219597, i: 1 }),
signature: {
hash: Binary(Buffer.from("0000000000000000000000000000000000000000", "hex"), 0),
keyId: Long("0")
}
},
operationTime: Timestamp({ t: 1722219597, i: 1 })
}
查看当前节点角色:
db.isMaster()
rs0 [direct: secondary] test> db.isMaster()
{
topologyVersion: {
processId: ObjectId("66a6f830b855ea99e3f52fea"),
counter: Long("4")
},
hosts: [ # 整个复制集的成员列表
'192.168.75.100:28017',
'192.168.75.100:28018',
'192.168.75.100:28019'
],
setName: 'rs0',
setVersion: 1,
ismaster: false,
secondary: true,
primary: '192.168.75.100:28017', # 主节点
me: '192.168.75.100:28018', # 当前节点
lastWrite: {
opTime: { ts: Timestamp({ t: 1722220007, i: 1 }), t: Long("1") },
lastWriteDate: ISODate("2024-07-29T02:26:47.000Z"),
majorityOpTime: { ts: Timestamp({ t: 1722220007, i: 1 }), t: Long("1") },
majorityWriteDate: ISODate("2024-07-29T02:26:47.000Z")
},
maxBsonObjectSize: 16777216,
maxMessageSizeBytes: 48000000,
maxWriteBatchSize: 100000,
localTime: ISODate("2024-07-29T02:26:54.021Z"),
logicalSessionTimeoutMinutes: 30,
connectionId: 29,
minWireVersion: 0,
maxWireVersion: 17,
readOnly: false,
ok: 1,
'$clusterTime': {
clusterTime: Timestamp({ t: 1722220007, i: 1 }),
signature: {
hash: Binary(Buffer.from("0000000000000000000000000000000000000000", "hex"), 0),
keyId: Long("0")
}
},
operationTime: Timestamp({ t: 1722220007, i: 1 }),
isWritablePrimary: false
}
复制集常用命令
命令 | 描述 |
---|---|
rs.add() | 为复制集新增节点 |
rs.addArb() | 为复制集新增一个 arbiter |
rs.conf() | 返回复制集配置信息 |
rs.freeze() | 防止当前节点在一段时间内选举成为主节点 |
rs.help() | 返回 replica set 的命令帮助 |
rs.initiate() | 初始化一个新的复制集 |
rs.printReplicationInfo() | 以主节点的视角返回复制的状态报告 |
rs.printSecondaryReplicationInfo() | 以从节点的视角返回复制状态报告 |
rs.reconfig() | 通过重新应用复制集配置来为复制集更新配置 |
rs.remove() | 从复制集中移除一个节点 |
rs.secondaryOk() | 为当前的连接设置 从节点可读 |
rs.status() | 返回复制集状态信息。 |
rs.stepDown() | 让当前的 primary 变为从节点并触发 election |
rs.syncFrom() | 设置复制集节点从哪个节点处同步数据,将会覆盖默认选取逻辑 |
安全认证
权限名 | 描述 |
---|---|
read | 允许用户读取指定数据库 |
readWrite | 允许用户读写指定数据库 |
dbAdmin | 允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profile |
dbOwner | 允许用户在指定数据库中执行任意操作,增、删、改、查等 |
userAdmin | 允许用户向system.users集合写入,可以在指定数据库里创建、删除和管理用户 |
clusterAdmin | 只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限 |
readAnyDatabase | 只在admin数据库中可用,赋予用户所有数据库的读权限 |
readWriteAnyDatabase | 只在admin数据库中可用,赋予用户所有数据库的读写权限 |
userAdminAnyDatabase | 只在admin数据库中可用,赋予用户所有数据库的userAdmin权限 |
dbAdminAnyDatabase | 只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限 |
root | 只在admin数据库中可用。超级账号,超级权限 |
创建用户
切换至主节点
use admin
#创建用户
db.createUser( {
user: "hushang",
pwd: "123456",
roles: [ { role: "clusterAdmin", db: "admin" } ,
{ role: "userAdminAnyDatabase", db: "admin"},
{ role: "dbAdminAnyDatabase", db: "admin"},
{ role: "readWriteAnyDatabase", db: "admin"}]
})
创建keyFile文件
keyFile文件的作用: 集群之间的安全认证,增加安全认证机制KeyFile(开启keyfile认证就默认开启了auth认证了)。
注意:创建keyFile前,需要先停掉复制集中所有主从节点的mongod服务,然后再创建,否则有可能出现服务启动不了的情况。
将主节点中的keyfile文件拷贝到复制集其他从节点服务器中,路径地址对应mongo.conf配置文件中的keyFile字段地址,并设置keyfile权限为600
#mongo.key采用随机算法生成,用作节点内部通信的密钥文件。
[root@localhost mongodb]# openssl rand -base64 756 > /mongodb/mongo.key
#权限必须是600
[root@localhost mongodb]# chmod 600 /mongodb/mongo.key
当我停止主节点之后,连接从节点时,发现它就已经自动变为了新的从节点,当我把所有节点都停止后,再重启,发现三个节点就自动搭建了集群并选举了新的Primary
启动mongod
mongod -f /mongodb/db1/mongod.conf --keyFile /mongodb/mongo.key
mongod -f /mongodb/db2/mongod.conf --keyFile /mongodb/mongo.key
mongod -f /mongodb/db3/mongod.conf --keyFile /mongodb/mongo.key
测试
# 未携带用户名 密码方式
# 进入第一个mongodb实例,发现重启之后28017变为了从节点,执行下面的命令会提示需要我们认证
mongosh --port 28017
切换到主节点,一样是需要认证
接下来使用用户名密码登录主节点,发现就能查询了
[root@localhost mongodb]# mongosh --port 28018 -u hushang -p 123456
复制集连接方式
方式一:客户端直接连接 Primary 节点,正常情况下可读写 MongoDB,但主节点故障切换后,无法正常访问
mongosh -u hushang -p 123456 192.168.75.100:28018
方式二(强烈推荐):通过高可用 Uri 的方式连接 MongoDB,当 Primary 故障切换后,MongoDB Driver 可自动感知并把流量路由到新的 Primary 节点
mongosh mongodb://hushang:[email protected]:28017,192.168.75.100:28018,192.168.75.100:28019/admin?replicaSet=rs0
springboot操作复制集配置
spring:
data:
mongodb:
uri: mongodb://hushang:[email protected]:28017,192.168.75.100:28018,192.168.75.100:28019/test?authSource=admin&replicaSet=rs0
复制集成员角色
属性
复制集里面有多个节点,每个节点拥有不同的职责。
在看成员角色之前,先了解两个重要属性:
属性一:Priority = 0
当 Priority 等于 0 时,它不可以被复制集选举为主,Priority 的值越高,则被选举为主的概率更大。
通常,在跨机房方式下部署复制集可以使用该特性。假设使用了机房A和机房B,由于主要业务与机房A更近,则可以将机房B的复制集成员Priority设置为0,这样主节点就一定会是A机房的成员。
属性二:Vote = 0
不可以参与选举投票,此时该节点的 Priority 也必须为 0,即它也不能被选举为主。
由于一个复制集中最多只有7个投票成员,因此多出来的成员则必须将其vote属性值设置为0,即这些成员将无法参与投票。
成员角色
-
Primary
主节点,其接收所有的写请求,然后把修改同步到所有备节点。
一个复制集只能有一个主节点,当主节点“挂掉”后,其他节点会重新选举出来一个主节点。
-
Secondary
备节点,与主节点保持同样的数据集。当主节点“挂掉”时,参与竞选主节点。
分为以下三个不同类型:
-
Hidden = false:正常的只读节点,是否可选为主,是否可投票,取决于 Priority,Vote 的值;
-
Hidden = true:隐藏节点,对客户端不可见, 可以参与选举,但是 Priority 必须为 0,即不能被提升为主。
由于隐藏节点不会接受业务访问,因此可通过隐藏节点做一些数据备份、离线计算的任务,这并不会影响整个复制集。
-
Delayed :延迟节点,必须同时具备隐藏节点和Priority0的特性,
会延迟一定的时间(secondaryDelaySecs 配置决定)从上游复制增量,常用于快速回滚场景。
-
-
Arbiter
仲裁节点,只用于参与选举投票,本身不承载任何数据,只作为投票角色。
比如你部署了2个节点的复制集,1个 Primary,1个Secondary,任意节点宕机,复制集将不能提供服务了(无法选出Primary),这时可以给复制集添加⼀个 Arbiter节点,即使有节点宕机,仍能选出Primary。 Arbiter本身不存储数据,是非常轻量级的服务,当复制集成员为偶数时,最好加入⼀个Arbiter节点,以提升复制集可用性。
配置隐藏节点
很多情况下将节点设置为隐藏节点是用来协助 delayed members 的。如果我们仅仅需要防止该节点成为主节点,我们可以通过 priority 0 member 来实现。
cfg = rs.conf()
cfg.members[2].priority = 0
cfg.members[2].hidden = true
rs.reconfig(cfg)
设置完毕后,该从节点的优先级将变为 0 来防止其升职为主节点,同时其也是对应用程序不可见的。在其他节点上执行 db.isMaster() 将不会显示隐藏节点。
当然rs.conf()
和rs.status()
还是会显示三个节点信息的
配置延时节点
当我们配置一个延时节点的时候,复制过程与该节点的 oplog 都将延时。延时节点中的数据集将会比复制集中主节点的数据延后。
举个例子,现在是09:52,如果延时节点延后了1小时,那么延时节点的数据集中将不会有08:52之后的操作。
cfg = rs.conf()
cfg.members[2].priority = 0
cfg.members[2].hidden = true
# 延迟1分钟
cfg.members[2].secondaryDelaySecs = 60
rs.reconfig(cfg)
查看复制延迟
如果希望查看当前节点oplog的情况,则可以使用rs.printReplicationInfo()
命令
# 登录延迟节点
[root@localhost mongodb]# mongosh --port 28019 -u hushang -p 123456
rs0 [direct: other] test> rs.printReplicationInfo()
actual oplog size
'990 MB'
---
configured oplog size
'990 MB'
---
log length start to end
'5340 secs (1.48 hrs)'
---
oplog first event time
'Mon Jul 29 2024 10:04:25 GMT+0800 (China Standard Time)'
---
oplog last event time
'Mon Jul 29 2024 11:33:25 GMT+0800 (China Standard Time)'
---
now
'Mon Jul 29 2024 11:34:32 GMT+0800 (China Standard Time)'
这里清晰地描述了oplog的大小、最早一条oplog以及最后一条oplog的产生时间,log length start to end所指的是一个复制窗口(时间差)。
通常在oplog大小不变的情况下,业务写操作越频繁,复制窗口就会越短。
在主节点上执行rs.printSecondaryReplicationInfo()
命令,可以一并列出所有备节点成员的同步延迟情况
rs0 [direct: primary] test> rs.printSecondaryReplicationInfo()
source: 192.168.75.100:28017
{
syncedTo: 'Mon Jul 29 2024 11:37:15 GMT+0800 (China Standard Time)',
replLag: '0 secs (0 hrs) behind the primary '
}
---
source: 192.168.75.100:28019
{
syncedTo: 'Mon Jul 29 2024 11:36:15 GMT+0800 (China Standard Time)',
replLag: '-60 secs (-0.02 hrs) behind the primary '
}
添加投票节点
# 为仲裁节点创建数据目录,存放配置数据。该目录将不保存数据集
[root@localhost ~]# mkdir -p /mongodb/arb
# 启动仲裁节点,指定数据目录和复制集名称 --replSet指定复制集
[root@localhost ~]# mongod --port 30000 --dbpath /mongodb/arb --replSet rs0
# 进入mongo shell,添加仲裁节点到复制集
[root@localhost ~]# mongosh -u hushang -p 123456 --port 28018
rs0 [direct: primary] test> rs.addArb("ip:30000")
如果添加节点遇到下面的错误:
MongoServerError: 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.
# 执行命令
db.adminCommand( {"setDefaultRWConcern" : 1, "defaultWriteConcern" : { "w" : 2 } } )
使用rs.conf()
命令查看新添加的节点,其中arbiterOnly就表示仲裁节点
移除复制集节点
使用 rs.remove() 来移除节点
# 1.关闭节点实例
# 2.连接主节点,执行下面命令
rs.remove("ip:port")
通过 rs.reconfig() 来移除节点
# 1.关闭节点实例
# 2.连接主节点,执行下面命令
cfg = rs.conf()
cfg.members.splice(2,1) #从下标索引2开始移除1个元素
rs.reconfig(cfg)
就比如把我上面新添加的仲裁节点移除掉
cfg = rs.conf()
cfg.members.splice(3,1) # 仲裁节点的下标为3
rs.reconfig(cfg)
更改复制集节点
cfg = rs.conf()
cfg.members[0].host = "ip:port"
rs.reconfig(cfg)
标签:mongod,rs,MongoDB,2024,详解,集群,复制,mongodb,节点
From: https://blog.csdn.net/qq_44027353/article/details/140768417