博客文章除注明转载外,均为原创。转载请注明出处。
本文链接地址:http://blog.chinaunix.net/uid-31396856-id-5780054.html
一、mongo副本集的原理
主节点是
集中接收写入操作的唯一成员。MongoDB中的应用连接到主节点进行写入操作,然后记录在主节点操作oplog。从节点成员复制此日志并将操作应用于其数据集。 在以下三个副本集中,primary节点接收所有写入操作。然后secondary节点复制oplog以应用到他们的数据集。
mongo副本集的基础原理:副本集依赖于两个基础机制:oplog和heartbeat。oplog负责进行数据的复制;而心跳则监控各节点的健康状况并触发故障转移。
1.oplog
副本集的复制是通过操作日志oplog实现的,oplog包含了节点的每一次写操作,记录。oplog是位于每个节点的lcoal数据库中的一个固定collection。所有副本集成员都在集合中包含oplog的副本,备份节点通过查询这个集合就可以知道需要进行复制的集合。
每次客户端向主节点写入数据,就会自动向主节点的oplog集合里面添加一个条目,记录当前的写操作。一旦写操作被复制到从节点,从节点的oplog也会保存一条关于写入的记录。每个oplog条目都由一个BSON时间戳进行标识,所有从节点都使用这个时间戳来追踪它们最后的应用条目。
2.heartbeat心跳检测
副本集的心跳检测是进行选举和故障转移的基础。默认情况,每个副本集成员每两秒ping一次其它成员,做一次心跳请求(heartbeat request)。这样,每个成员就可以知道其它成员的状态:哪个是主节点?哪个可以作为同步源?哪个挂掉了?
心跳最重要的功能就是让主节点知道自己是否满足集合"大多数"的条件。如果主节点不在得到大多数支持,就会退位,变成备份节点。
在运行re.status()时候,可以看到每个节点上次心跳检测的时间戳和健康状况。
3.选举(Elections)
当任意的故障切换发生,都会伴随着一个选举的出现,mongo副本集采用Bully选举算法,以此来决定哪个成员成为primary。
选举提供了一种机制,用于副本集中的成员无需管理员的干预,自动的选出一个新的primary。选举可以让副本集快速和坚决的从故障中恢复。
当primary变为不可达时,secondary成员发起选举,第一个收到大多数选票的成员成为新的primary(最大投票成员数)。
4.副本集的特征:
N 个节点的集群(N为奇数)
任何节点可作为主节点
所有写入操作都在主节点上
自动故障转移
二、创建mongo副本集
1、安装并启动mongo:
安装过程(略)
[root@mongo 27017]# mongod -f /data/mongo/27017/conf/mongodb.conf
about to fork child process, waiting until server is ready for connections.
forked process: 1930
child process started successfully, parent exiting
[root@mongo 27018]# mongod -f /data/mongo/27018/conf/mongodb.conf
about to fork child process, waiting until server is ready for connections.
forked process: 1845
child process started successfully, parent exiting
[root@mongo 27019]# mongod -f /data/mongo/27019/conf/mongodb.conf
about to fork child process, waiting until server is ready for connections.
forked process: 1886
child process started successfully, parent exiting
状态如下:
[root@mongo ~]# ps -ef|grep mongo|grep -v grep
root 1845 1 0 11:34 ? 00:00:01 mongod -f /data/mongo/27018/conf/mongodb.conf
root 1886 1 0 11:36 ? 00:00:00 mongod -f /data/mongo/27019/conf/mongodb.conf
root 1930 1 2 11:38 ? 00:00:00 mongod -f /data/mongo/27017/conf/mongodb.conf
mongo的监听状态:
[root@mongo ~]# netstat -antlp|grep mongo
tcp 0 0 0.0.0.0:27017 0.0.0.0:* LISTEN 1930/mongod
tcp 0 0 0.0.0.0:27018 0.0.0.0:* LISTEN 1845/mongod
tcp 0 0 0.0.0.0:27019 0.0.0.0:* LISTEN 1886/mongod
2.选择主节点登陆进行配置副本集
(1)选择admin数据库
> use admin
switched to db admin
(2)创建副本集
> rsConfig = {_id: "repl", members: []}
{ "_id" : "repl", "members" : [ ] }
(3)添加副本集成员
> rsConfig.members.push({_id: 0, host: "localhost:27017"})
1
> rsConfig.members.push({_id: 1, host: "localhost:27018"})
2
> rsConfig.members.push({_id: 2, host: "localhost:27019"})
3
(4)初始化副本集
> rs.initiate(rsConfig);
{ "ok" : 1 }
创建副本集配置各项参数说明:
"_id": 副本集的名称
"members": 副本集的服务器列表
"_id": 服务器的唯一ID
"host": 服务器主机
"priority": 是优先级,默认为1,优先级0为被动节点,不能成为活跃节点。优先级不位0则按照有大到小选出活跃节点。
"arbiterOnly": 仲裁节点,只参与投票,不接收数据,也不能成为活跃节点。
3.配置完成后,检查副本集配置信息和状态
repl:PRIMARY> rs.isMaster()
{
"hosts" : [
"localhost:27017",
"localhost:27018",
"localhost:27019"
],
"setName" : "repl",
"setVersion" : 1,
"ismaster" : true,
"secondary" : false,
"primary" : "localhost:27017",
"me" : "localhost:27017",
"electionId" : ObjectId("7fffffff0000000000000001"),
"lastWrite" : {
"opTime" : {
"ts" : Timestamp(1511500882, 1),
"t" : NumberLong(1)
},
"lastWriteDate" : ISODate("2017-11-24T05:21:22Z")
},
"maxBsonObjectSize" : 16777216,
"maxMessageSizeBytes" : 48000000,
"maxWriteBatchSize" : 1000,
"localTime" : ISODate("2017-11-24T05:21:23.313Z"),
"maxWireVersion" : 5,
"minWireVersion" : 0,
"readOnly" : false,
"ok" : 1
}
repl:PRIMARY> rs.status()
{
"set" : "repl",
"date" : ISODate("2017-11-24T05:19:38.643Z"),
"myState" : 1,
"term" : NumberLong(1),
"heartbeatIntervalMillis" : NumberLong(2000),
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1511500772, 1),
"t" : NumberLong(1)
},
"appliedOpTime" : {
"ts" : Timestamp(1511500772, 1),
"t" : NumberLong(1)
},
"durableOpTime" : {
"ts" : Timestamp(1511500772, 1),
"t" : NumberLong(1)
}
},
"members" : [
{
"_id" : 0,
"name" : "localhost:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 1041,
"optime" : {
"ts" : Timestamp(1511500772, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2017-11-24T05:19:32Z"),
"electionTime" : Timestamp(1511500650, 1),
"electionDate" : ISODate("2017-11-24T05:17:30Z"),
"configVersion" : 1,
"self" : true
},
{
"_id" : 1,
"name" : "localhost:27018",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 139,
"optime" : {
"ts" : Timestamp(1511500772, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1511500772, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2017-11-24T05:19:32Z"),
"optimeDurableDate" : ISODate("2017-11-24T05:19:32Z"),
"lastHeartbeat" : ISODate("2017-11-24T05:19:38.571Z"),
"lastHeartbeatRecv" : ISODate("2017-11-24T05:19:37.703Z"),
"pingMs" : NumberLong(0),
"syncingTo" : "localhost:27017",
"configVersion" : 1
},
{
"_id" : 2,
"name" : "localhost:27019",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 139,
"optime" : {
"ts" : Timestamp(1511500772, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1511500772, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2017-11-24T05:19:32Z"),
"optimeDurableDate" : ISODate("2017-11-24T05:19:32Z"),
"lastHeartbeat" : ISODate("2017-11-24T05:19:38.571Z"),
"lastHeartbeatRecv" : ISODate("2017-11-24T05:19:37.730Z"),
"pingMs" : NumberLong(0),
"syncingTo" : "localhost:27018",
"configVersion" : 1
}
],
"ok" : 1
}
说明:
setName:指明复制集的名称,本例中的取值是"rs1"。
ismaster:指明当前连接的实例是否是primary角色。它是一个布尔类型:"true"说明此实例是primary角色,"false"说明此实例不是primary角色。
secondary:指明当前连接的实例是否是slave角色。它是一个布尔类型:"true"说明此实例是slave角色,"false"说明此实例不是slave角色。
hosts:指明Replica Sets各成员IP及端口信息。
ok:表明复制集的状态。“1”说明状态正常,“0”说明状态异常。
4.测试副本集
(1)测试数据复制
首先在主节点插入记录
use dbwatcher
for (i = 5000; i < 100000; i++) {
db.users.insert({
"i": i,
"userName": "user" + i,
"age": Math.floor(Math.random() * 120),
"created": new Date(),
total: Math.floor(Math.random() * 100) * i
})
}
(2)检查复制状态
repl:SECONDARY> db.printSlaveReplicationInfo()
source: localhost:27018
syncedTo: Fri Nov 24 2017 13:37:30 GMT+0800 (CST)
0 secs (0 hrs) behind the primary
source: localhost:27019
syncedTo: Fri Nov 24 2017 13:37:32 GMT+0800 (CST)
-2 secs (0 hrs) behind the primary
(3)在从库节点上检查数据
repl:SECONDARY> db.getCollectionNames()
[ "users" ]
repl:SECONDARY> db.users.find()
{ "_id" : ObjectId("5a17b00210e7ebcda3d09c32"), "i" : 5000, "userName" : "user5000", "age" : 5, "created" : ISODate("2017-11-24T05:37:06.684Z"), "total" : 305000 }
{ "_id" : ObjectId("5a17b00210e7ebcda3d09c33"), "i" : 5001, "userName" : "user5001", "age" : 31, "created" : ISODate("2017-11-24T05:37:06.715Z"), "total" : 355071 }
{ "_id" : ObjectId("5a17b00210e7ebcda3d09c35"), "i" : 5003, "userName" : "user5003", "age" : 87, "created" : ISODate("2017-11-24T05:37:06.717Z"), "total" : 295177 }
{ "_id" : ObjectId("5a17b00210e7ebcda3d09c36"), "i" : 5004, "userName" : "user5004", "age" : 52, "created" : ISODate("2017-11-24T05:37:06.717Z"), "total" : 75060 }
{ "_id" : ObjectId("5a17b00210e7ebcda3d09c34"), "i" : 5002, "userName" : "user5002", "age" : 44, "created" : ISODate("2017-11-24T05:37:06.716Z"), "total" : 70028 }
{ "_id" : ObjectId("5a17b00210e7ebcda3d09c38"), "i" : 5006, "userName" : "user5006", "age" : 53, "created" : ISODate("2017-11-24T05:37:06.718Z"), "total" : 440528 }
{ "_id" : ObjectId("5a17b00210e7ebcda3d09c37"), "i" : 5005, "userName" : "user5005", "age" : 91, "created" : ISODate("2017-11-24T05:37:06.718Z"), "total" : 465465 }
{ "_id" : ObjectId("5a17b00210e7ebcda3d09c39"), "i" : 5007, "userName" : "user5007", "age" : 104, "created" : ISODate("2017-11-24T05:37:06.719Z"), "total" : 45063 }
{ "_id" : ObjectId("5a17b00210e7ebcda3d09c3a"), "i" : 5008, "userName" : "user5008", "age" : 1, "created" : ISODate("2017-11-24T05:37:06.719Z"), "total" : 310496 }
{ "_id" : ObjectId("5a17b00210e7ebcda3d09c3e"), "i" : 5012, "userName" : "user5012", "age" : 102, "created" : ISODate("2017-11-24T05:37:06.727Z"), "total" : 210504 }
{ "_id" : ObjectId("5a17b00210e7ebcda3d09c4a"), "i" : 5024, "userName" : "user5024", "age" : 43, "created" : ISODate("2017-11-24T05:37:06.732Z"), "total" : 85408 }
{ "_id" : ObjectId("5a17b00210e7ebcda3d09c59"), "i" : 5039, "userName" : "user5039", "age" : 1, "created" : ISODate("2017-11-24T05:37:06.765Z"), "total" : 342652 }
{ "_id" : ObjectId("5a17b00210e7ebcda3d09c58"), "i" : 5038, "userName" : "user5038", "age" : 14, "created" : ISODate("2017-11-24T05:37:06.764Z"), "total" : 20152 }
{ "_id" : ObjectId("5a17b00210e7ebcda3d09c3f"), "i" : 5013, "userName" : "user5013", "age" : 42, "created" : ISODate("2017-11-24T05:37:06.727Z"), "total" : 170442 }
{ "_id" : ObjectId("5a17b00210e7ebcda3d09c65"), "i" : 5051, "userName" : "user5051", "age" : 19, "created" : ISODate("2017-11-24T05:37:06.772Z"), "total" : 50510 }
{ "_id" : ObjectId("5a17b00210e7ebcda3d09c4c"), "i" : 5026, "userName" : "user5026", "age" : 14, "created" : ISODate("2017-11-24T05:37:06.733Z"), "total" : 55286 }
{ "_id" : ObjectId("5a17b00210e7ebcda3d09c62"), "i" : 5048, "userName" : "user5048", "age" : 25, "created" : ISODate("2017-11-24T05:37:06.770Z"), "total" : 479560 }
{ "_id" : ObjectId("5a17b00210e7ebcda3d09c46"), "i" : 5020, "userName" : "user5020", "age" : 25, "created" : ISODate("2017-11-24T05:37:06.730Z"), "total" : 316260 }
{ "_id" : ObjectId("5a17b00210e7ebcda3d09c49"), "i" : 5023, "userName" : "user5023", "age" : 59, "created" : ISODate("2017-11-24T05:37:06.731Z"), "total" : 421932 }
{ "_id" : ObjectId("5a17b00210e7ebcda3d09c55"), "i" : 5035, "userName" : "user5035", "age" : 72, "created" : ISODate("2017-11-24T05:37:06.763Z"), "total" : 176225 }
Type "it" for more
repl:SECONDARY> db.users.insert({username: "dbwatcher", age: 25})
WriteResult({ "writeError" : { "code" : 10107, "errmsg" : "not master" } })
repl:SECONDARY>
---The END!