MongoDB复制集/集群搭建详解

复制集介绍

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:123456@192.168.75.100:28017,192.168.75.100:28018,192.168.75.100:28019/admin?replicaSet=rs0

在这里插入图片描述



springboot操作复制集配置

spring:
  data:
    mongodb:
        uri: mongodb://hushang:123456@192.168.75.100: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)
  • 15
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值