第二节:MongoDB复制集(主从复制)剖析和快速上手搭建
一. 复制集剖析
1. 什么是复制集
复制集(replica set)提供了数据冗余和高可用,它是一组mongod进程,它的作用如下:
保障数据的安全性
数据高可用性 (24*7)
灾难恢复
无需停机维护(如备份,重建索引,压缩)
分布式读取数据
副本集对应用层是透明的
一个复制集里面有很多数据节点(data bearing node)和一个可选择的仲裁节点(arbiter node)。数据节点的角色也分为主节点(primary node)从节点(secondary node)。所有写操作都是在主节点上的,并记录oplog(operation log)。从节点通过主节点的oplog重放来保持数据的最终一致性,这个“主从复制”是异步的。当主节点挂了,二级节点会进行新的选举(基于Raft算法),从中找一个二级节点来充当主节点的角色。仲裁节点只负责参与选举,而不存储数据。
2. 架构图
MongoDB推荐的最小架构是:一个主节点,两个从节点,最大可以有50个成员节点。
读写分离架构:
二. 快速搭建
目标:
搭建1主2从的复制集 集群,主节点(Primary)端口为:27018,从节点(Secondary)端口为:27019、27020.
完成主从数据的复制
完成主节点宕机,从节点自动选举为主节点的
1. 准备3个配置文件
(1). 事先创建相关路径
A. 配置文件路径: D:\Program Files\MongoDB\Server\5.0\bin\CopyConf
B. 日志路径: D:\Program Files\MongoDB\Server\5.0\myCopy_log
C. 数据路径:D:\Program Files\MongoDB\Server\5.0\myCopy_data
(2). 修改数据存储路径 (以27018端口为例)
storage:
dbPath: D:\Program Files\MongoDB\Server\5.0\myCopy_data\27018
journal:
enabled: true
(3). 修改日志存储路径
systemLog:
destination: file
logAppend: true
path: D:\Program Files\MongoDB\Server\5.0\myCopy_log\27018\mongod.log
(4). 修改端口
net:
port: 27018
bindIp: 127.0.0.1
(5). 开启复制集合
#开启复制集(名字为:ypfCopy)
replication:
replSetName: ypfCopy
27018配置文件
# mongod.conf # for documentation of all options, see: # http://docs.mongodb.org/manual/reference/configuration-options/ # Where and how to store data. storage: dbPath: D:\Program Files\MongoDB\Server\5.0\myCopy_data\27018 journal: enabled: true # engine: # wiredTiger: # where to write logging data. systemLog: destination: file logAppend: true path: D:\Program Files\MongoDB\Server\5.0\myCopy_log\27018\mongod.log # network interfaces net: port: 27018 bindIp: 127.0.0.1 #processManagement: #security: #operationProfiling: #开启复制集(名字为:ypfCopy) replication: replSetName: ypfCopy #sharding: ## Enterprise-Only Options: #auditLog: #snmp:
27019配置文件
# mongod.conf # for documentation of all options, see: # http://docs.mongodb.org/manual/reference/configuration-options/ # Where and how to store data. storage: dbPath: D:\Program Files\MongoDB\Server\5.0\myCopy_data\27019 journal: enabled: true # engine: # wiredTiger: # where to write logging data. systemLog: destination: file logAppend: true path: D:\Program Files\MongoDB\Server\5.0\myCopy_log\27019\mongod.log # network interfaces net: port: 27019 bindIp: 127.0.0.1 #processManagement: #security: #operationProfiling: #开启复制集(名字为:ypfCopy) replication: replSetName: ypfCopy #sharding: ## Enterprise-Only Options: #auditLog: #snmp:
27020配置文件
# mongod.conf # for documentation of all options, see: # http://docs.mongodb.org/manual/reference/configuration-options/ # Where and how to store data. storage: dbPath: D:\Program Files\MongoDB\Server\5.0\myCopy_data\27020 journal: enabled: true # engine: # wiredTiger: # where to write logging data. systemLog: destination: file logAppend: true path: D:\Program Files\MongoDB\Server\5.0\myCopy_log\27020\mongod.log # network interfaces net: port: 27020 bindIp: 127.0.0.1 #processManagement: #security: #operationProfiling: #开启复制集(名字为:ypfCopy) replication: replSetName: ypfCopy #sharding: ## Enterprise-Only Options: #auditLog: #snmp:
2. 启动三个实例
在cmd中依次启动项下面三个实例:
【mongod.exe –config "D:\Program Files\MongoDB\Server\5.0\bin\CopyConf\mongod-27018.cfg"】
【mongod.exe –config "D:\Program Files\MongoDB\Server\5.0\bin\CopyConf\mongod-27019.cfg"】
【mongod.exe –config "D:\Program Files\MongoDB\Server\5.0\bin\CopyConf\mongod-27020.cfg"】
3. 设置主从节点
(1). 先通过指令连接到27018节点
【 mongo.exe –host 127.0.0.1 –port 27018】
(2). 将连接到的当前节点(27018)初始化为主节点(Primary)
【rs.initiate()】
查看主节点状态:【rs.status()】, 从而切换到主节点
(3). 添加从节点27019
【 rs.add("127.0.0.1:27019") 】
(4). 添加从节点27020
【 rs.add("127.0.0.1:27020") 】
(5). 查看状态
【rs.status()】
{
"set" : "ypfCopy",
"date" : ISODate("2022-08-29T09:33:48.857Z"),
"myState" : 1,
"term" : NumberLong(1),
"syncSourceHost" : "",
"syncSourceId" : -1,
"heartbeatIntervalMillis" : NumberLong(2000),
"majorityVoteCount" : 2,
"writeMajorityCount" : 2,
"votingMembersCount" : 3,
"writableVotingMembersCount" : 3,
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1661765620, 1),
"t" : NumberLong(1)
},
"lastCommittedWallTime" : ISODate("2022-08-29T09:33:40.074Z"),
"readConcernMajorityOpTime" : {
"ts" : Timestamp(1661765620, 1),
"t" : NumberLong(1)
},
"appliedOpTime" : {
"ts" : Timestamp(1661765620, 1),
"t" : NumberLong(1)
},
"durableOpTime" : {
"ts" : Timestamp(1661765620, 1),
"t" : NumberLong(1)
},
"lastAppliedWallTime" : ISODate("2022-08-29T09:33:40.074Z"),
"lastDurableWallTime" : ISODate("2022-08-29T09:33:40.074Z")
},
"lastStableRecoveryTimestamp" : Timestamp(1661765575, 1),
"electionCandidateMetrics" : {
"lastElectionReason" : "electionTimeout",
"lastElectionDate" : ISODate("2022-08-29T09:27:59.979Z"),
"electionTerm" : NumberLong(1),
"lastCommittedOpTimeAtElection" : {
"ts" : Timestamp(1661765279, 1),
"t" : NumberLong(-1)
},
"lastSeenOpTimeAtElection" : {
"ts" : Timestamp(1661765279, 1),
"t" : NumberLong(-1)
},
"numVotesNeeded" : 1,
"priorityAtElection" : 1,
"electionTimeoutMillis" : NumberLong(10000),
"newTermStartDate" : ISODate("2022-08-29T09:27:59.997Z"),
"wMajorityWriteAvailabilityDate" : ISODate("2022-08-29T09:28:00.048Z")
},
"members" : [
{
"_id" : 0,
"name" : "127.0.0.1:27018",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 832,
"optime" : {
"ts" : Timestamp(1661765620, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2022-08-29T09:33:40Z"),
"lastAppliedWallTime" : ISODate("2022-08-29T09:33:40.074Z"),
"lastDurableWallTime" : ISODate("2022-08-29T09:33:40.074Z"),
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"electionTime" : Timestamp(1661765279, 2),
"electionDate" : ISODate("2022-08-29T09:27:59Z"),
"configVersion" : 5,
"configTerm" : 1,
"self" : true,
"lastHeartbeatMessage" : ""
},
{
"_id" : 1,
"name" : "127.0.0.1:27019",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 71,
"optime" : {
"ts" : Timestamp(1661765620, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1661765620, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2022-08-29T09:33:40Z"),
"optimeDurableDate" : ISODate("2022-08-29T09:33:40Z"),
"lastAppliedWallTime" : ISODate("2022-08-29T09:33:40.074Z"),
"lastDurableWallTime" : ISODate("2022-08-29T09:33:40.074Z"),
"lastHeartbeat" : ISODate("2022-08-29T09:33:47.060Z"),
"lastHeartbeatRecv" : ISODate("2022-08-29T09:33:47.062Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncSourceHost" : "127.0.0.1:27018",
"syncSourceId" : 0,
"infoMessage" : "",
"configVersion" : 5,
"configTerm" : 1
},
{
"_id" : 2,
"name" : "127.0.0.1:27020",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 53,
"optime" : {
"ts" : Timestamp(1661765620, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1661765620, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2022-08-29T09:33:40Z"),
"optimeDurableDate" : ISODate("2022-08-29T09:33:40Z"),
"lastAppliedWallTime" : ISODate("2022-08-29T09:33:40.074Z"),
"lastDurableWallTime" : ISODate("2022-08-29T09:33:40.074Z"),
"lastHeartbeat" : ISODate("2022-08-29T09:33:47.060Z"),
"lastHeartbeatRecv" : ISODate("2022-08-29T09:33:47.570Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncSourceHost" : "127.0.0.1:27019",
"syncSourceId" : 1,
"infoMessage" : "",
"configVersion" : 5,
"configTerm" : 1
}
],
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1661765620, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1661765620, 1)
}
注:
截止此处一主两从的集群就已经搭建完毕了,下次直接开启上述三个实例,该主从关系依旧存在,不需要重新搭建了。
4. 使用Compass客户端进行链接
(1). 连接字符串为
(2). 连接成功,如下图:
5. 代码实操测试
(1). 连接集群
public HomeController()
{
// 1. 建立MongoDB连接
//var client = new MongoClient("mongodb://localhost:27017"); //单个节点
var client = new MongoClient("mongodb://localhost:27018,localhost:27019,localhost:27020"); //复制集- 1主2从 【正常使用】
// 2. 获取数据库ShipDB 【若没有,则自动创建,插入数据的时候才生效】
var database = client.GetDatabase("ShipDB");
// 3. 获取表 ShipInfo【若没有,则自动创建,插入数据的时候才生效】
shipService = database.GetCollection<ShipInfo>("ShipInfo");
}
说明:
如上代码,就可以实现连接到复制集集群上。
(2). 测试能否只连接主节点
经测试,单独连接到主节点(Primary) 27018 上,可以正常进行crud。
(3). 测试能否直连接从节点
经测试,从节点(Secondary) 27019、27020,不允许进行单独连接。
6. 测试主节点宕机,从节点选举
主节点27018宕机, 从节点27019会被选举为主节点。
结论:默认只能主节点读写,从节点不能读也不能写!! 当执行指令 secondaryOk(),执行后,从节点可以读了。【正确!!!】
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。