Replica Sets复制集

 

                                               Replica Sets 复制集
MongoDB 支持在多个机器中通过异步复制达到故障转移和实现冗余。多机器中同一时刻只
有一台是用于写操作。正是由于这个情况,为 MongoDB 提供了数据一致性的保障。担当
Primary 角色的机器能把读操作分发给 slave
MongoDB 高可用可用分两种 :
 Master-Slave 主从复制:
只需要在某一个服务启动时加上– master 参数,而另一个服务加上– slave 与– source 参数,
即可实现同步。 MongoDB 的最新版本已不再推荐此方案。
 Replica Sets 复制集:
MongoDB 1.6 版本对开发了新功能 replica set ,这比之前的 replication 功能要强大一
些,增加了故障自动切换和自动修复成员节点,各个 DB 之间数据完全一致,大大降低了维
护成功。 auto shard 已经明确说明不支持 replication paris ,建议使用 replica set replica set
故障切换完全自动。
如果上图所示, Replica Sets 的结构非常类似一个集群。是的,你完全可以把它当成集群,因
为它确实跟集群实现的作用是一样的,其中一个节点如果出现故障,其它节点马上会将业务
接过来而无须停机操作。
21.1 部署 Replica Sets
接下来将一步一步的给大家分享一下实施步骤:
 
63 / 91
1 创建数据文件存储路径
[root@localhost ~]# mkdir -p /data/data/r0
[root@localhost ~]# mkdir -p /data/data/r1
[root@localhost ~]# mkdir -p /data/data/r2
2 创建日志文件路径
[root@localhost ~]# mkdir -p /data/log
3 、创建主从 key 文件,用于标识集群的私钥的完整路径,如果各个实例的 key file 内容不一致,程序将不能正常用。
[root@localhost ~]# mkdir -p /data/key
[root@localhost ~]# echo "this is rs1 super secret key" > /data/key/r0
[root@localhost ~]# echo "this is rs1 super secret key" > /data/key/r1
[root@localhost ~]# echo "this is rs1 super secret key" > /data/key/r2
[root@localhost ~]# chmod 600 /data/key/r*
4 、启动 3 个实例
[root@localhost ~]# /Apps/mongo/bin/mongod --replSet rs1 --keyFile /data/key/r0 --fork --port
28010 --dbpath /data/data/r0 --logpath=/data/log/r0.log –logappend --oplogSize=4096
 
all output going to: /data/log/r0.log
forked process: 6573
[root@localhost ~]# /Apps/mongo/bin/mongod --replSet rs1 --keyFile /data/key/r1 --fork --port
28011 --dbpath /data/data/r1 --logpath=/data/log/r1.log –logappend --oplogSize=4096
 
all output going to: /data/log/r1.log
forked process: 6580
[root@localhost ~]# /Apps/mongo/bin/mongod --replSet rs1 --keyFile /data/key/r2 --fork --port
28012 --dbpath /data/data/r2 --logpath=/data/log/r2.log –logappend  --oplogSize=4096
all output going to: /data/log/r2.log
forked process: 6585
[root@localhost ~]#
5 、配置及初始化 Replica Sets
[root@localhost bin]# /Apps/mongo/bin/mongo -port 28010
MongoDB shell version: 1.8.1
connecting to: 127.0.0.1:28010/test
> config_rs1 = {_id: 'rs1', members: [
. .. {_id: 0, host: 'localhost:28010', priority:1}, -- 成员IP 及端口,priority=1 PRIMARY
.. . {_id: 1, host: 'localhost:28011'},
... {_id: 2, host: 'localhost:28012'}]
... }
{
"_id" : "rs1",
"members" : [
{
 
64 / 91
"_id" : 0,
"host" : "localhost:28010"
},
{
"_id" : 1,
"host" : "localhost:28011"
},
{
"_id" : 2,
"host" : "localhost:28012"
}
]
}
> rs.initiate(config_rs1); -- 初始化配置
{
"info" : "Config now saved locally. Should come online in about a minute.",
"ok" : 1
}
6 、查看复制集状态
> rs.status()
{
"set" : "rs1",
"date" : ISODate("2012-05-31T09:49:57Z"),
"myState" : 1,
"members" : [
{
"_id" : 0,
"name" : "localhost:28010",
"health" : 1, --1 表明正常 ; 0 表明异常
"state" : 1, -- 1 表明是 Primary; 2 表明是 Secondary;
"stateStr" : "PRIMARY", -- 表明此机器是主库
"optime" : {
"t" : 1338457763000,
"i" : 1
},
"optimeDate" : ISODate("2012-05-31T09:49:23Z"),
"self" : true
},
{
"_id" : 1,
"name" : "localhost:28011",
"health" : 1,
 
65 / 91
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 23,
"optime" : {
"t" : 1338457763000,
"i" : 1
},
"optimeDate" : ISODate("2012-05-31T09:49:23Z"),
"lastHeartbeat" : ISODate("2012-05-31T09:49:56Z")
},
{
"_id" : 2,
"name" : "localhost:28012",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 23,
"optime" : {
"t" : 1338457763000,
"i" : 1
},
"optimeDate" : ISODate("2012-05-31T09:49:23Z"),
"lastHeartbeat" : ISODate("2012-05-31T09:49:56Z")
}
],
"ok" : 1
}
rs1:PRIMARY>
还可以用 isMaster 查看 Replica Sets 状态。
rs1:PRIMARY> rs.isMaster()
{
"setName" : "rs1",
"ismaster" : true,
"secondary" : false,
"hosts" : [
"localhost:28010",
"localhost:28012",
"localhost:28011"
],
"maxBsonObjectSize" : 16777216,
"ok" : 1
}
rs1:PRIMARY>
 
21.2 主从操作日志 oplog
MongoDB Replica Set 架构是通过一个日志来存储写操作的,这个日志就叫做” oplog ”。
oplog.rs 是一个固定长度的 capped collection ,它存在于” local ”数据库中,用于记录 Replica
Sets 操作日志。在默认情况下 , 对于 64 MongoDB,oplog 是比较大的,可以达到 5% 的磁
盘空间。 oplog 的大小是可以通过 mongod 的参数” oplogSize ”来改变 oplog 的日志大小
Oplog 内容样例 :
rs1:PRIMARY> use local
switched to db local
rs1:PRIMARY> show collections
oplog.rs
system.replset
rs1:PRIMARY> db.oplog.rs.find()
{ "ts" : { "t" : 1338457763000, "i" : 1 }, "h" : NumberLong(0), "op" : "n", "ns" : "", "o" : { "msg" :
"initiating set" } }
{ "ts" : { "t" : 1338459114000, "i" : 1 }, "h" : NumberLong("5493127699725549585"), "op" : "i",
"ns" : "test.c1", "o" : { "_id" : ObjectId("4fc743e9aea289af709ac6b5"), "age" : 29, "name" :
"Tony" } }
rs1:PRIMARY>
字段说明 :
 ts: 某个操作的时间戳
 op: 操作类型,如下:
[1] i: insert
[1] d: delete
[1] u: update
 ns: 命名空间,也就是操作的 collection name
 o: document 的内容
查看 master oplog 元数据信息:
rs1:PRIMARY> db.printReplicationInfo()
configured oplog size: 47.6837158203125MB
log length start to end: 1351secs (0.38hrs)
oplog first event time: Thu May 31 2012 17:49:23 GMT+0800 (CST)
oplog last event time: Thu May 31 2012 18:11:54 GMT+0800 (CST)
now: Thu May 31 2012 18:21:58 GMT+0800 (CST)
rs1:PRIMARY>
字段说明 :
 configured oplog size: 配置的 oplog 文件大小
 log length start to end: oplog 日志的启用时间段
 oplog first event time: 第一个事务日志的产生时间
 oplog last event time: 最后一个事务日志的产生时间
 now: 现在的时间
查看 slave 的同步状态:
rs1:PRIMARY> db.printSlaveReplicationInfo()
source: localhost:28011
syncedTo: Thu May 31 2012 18:11:54 GMT+0800 (CST)
= 884secs ago (0.25hrs)
source: localhost:28012
syncedTo: Thu May 31 2012 18:11:54 GMT+0800 (CST)
= 884secs ago (0.25hrs)
rs1:PRIMARY>
字段说明 :
 source: 从库的 IP 及端口
 syncedTo: 目前的同步情况,延迟了多久等信息
21.3 主从配置信息
local 库中不仅有主从日志 oplog 集合,还有一个集合用于记录主从配置信息
system.replset
rs1:PRIMARY> use local
switched to db local
rs1:PRIMARY> show collections
oplog.rs
system.replset
rs1:PRIMARY> db.system.replset.find()
{ "_id" : "rs1", "version" : 1, "members" : [
{
"_id" : 0,
"host" : "localhost:28010"
},
{
"_id" : 1,
"host" : "localhost:28011"
},
{
"_id" : 2,
"host" : "localhost:28012"
}
] }
rs1:PRIMARY>
从这个集合中可以看出, Replica Sets 的配置信息,也可以在任何一个成员实例上执行 rs.conf()
来查看配置信息
 
21.4 管理维护 Replica Sets
21.4.1 读写分离
有一些第三方的工具,提供了一些可以让数据库进行读写分离的工具。我们现在是否有一个
疑问,从库要是能进行查询就更好了,这样可以分担主库的大量的查询请求。
1 先向主库中插入一条测试数据
[root@localhost bin]# ./mongo --port 28010
MongoDB shell version: 1.8.1
connecting to: 127.0.0.1:28010/test
rs1:PRIMARY> db.c1.insert({age:30})
db.c2rs1:PRIMARY> db.c1.find()
{ "_id" : ObjectId("4fc77f421137ea4fdb653b4a"), "age" : 30 }
2 在从库进行查询等操作
[root@localhost bin]# ./mongo --port 28011
MongoDB shell version: 1.8.1
connecting to: 127.0.0.1:28011/test
rs1:SECONDARY> show collections
Thu May 31 22:27:17 uncaught exception: error: { "$err" : "not master and slaveok=false",
"code" : 13435 }
rs1:SECONDARY>
当查询时报错了,说明是个从库且不能执行查询的操作
3 让从库可以读,分担主库的压力
rs1:SECONDARY> db.getMongo().setSlaveOk()
not master and slaveok=false
rs1:SECONDARY> show collections
c1
system.indexes
rs1:SECONDARY> db.c1.find()
{ "_id" : ObjectId("4fc77f421137ea4fdb653b4a"), "age" : 30 }
rs1:SECONDARY>
看来我们要是执行 db.getMongo().setSlaveOk() 我们就可查询从库了。
21.4.2 故障转移
复制集比传统的 Master-Slave 有改进的地方就是他可以进行故障的自动转移,如果我们停掉
复制集中的一个成员,那么剩余成员会再自动选举出一个成员,做为主库,例如 :
我们将 28010 这个主库停掉,然后再看一下复制集的状态
 
1 、杀掉 28010 端口的 MongoDB
[root@localhost bin]# ps aux|grep mongod
root 6706 1.6 6.9 463304 6168 Sl 21:49 0:26
/Apps/mongo/bin/mongod --replSet rs1 --keyFile /data/key/r0 --fork --port 28010
root 6733 0.4 6.7 430528 6044 ? Sl 21:50 0:06
/Apps/mongo/bin/mongod --replSet rs1 --keyFile /data/key/r1 --fork --port 28011
root 6747 0.4 4.7 431548 4260 ? Sl 21:50 0:06
/Apps/mongo/bin/mongod --replSet rs1 --keyFile /data/key/r2 --fork --port 28012
root 7019 0.0 0.7 5064 684 pts/2 S+ 22:16 0:00 grep mongod
[root@localhost bin]# kill -9 6706
2 查看复制集状态
[root@localhost bin]# ./mongo --port 28011
MongoDB shell version: 1.8.1
connecting to: 127.0.0.1:28011/test
rs1:SECONDARY> rs.status()
{
"set" : "rs1",
"date" : ISODate("2012-05-31T14:17:03Z"),
"myState" : 2,
"members" : [
{
"_id" : 0,
"name" : "localhost:28010",
"health" : 0,
"state" : 1,
"stateStr" : "(not reachable/healthy)",
"uptime" : 0,
"optime" : {
"t" : 1338472279000,
"i" : 1
},
"optimeDate" : ISODate("2012-05-31T13:51:19Z"),
"lastHeartbeat" : ISODate("2012-05-31T14:16:42Z"),
"errmsg" : "socket exception"
},
{
"_id" : 1,
"name" : "localhost:28011",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"optime" : {
 
70 / 91
"t" : 1338472279000,
"i" : 1
},
"optimeDate" : ISODate("2012-05-31T13:51:19Z"),
"self" : true
},
{
"_id" : 2,
"name" : "localhost:28012",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 1528,
"optime" : {
"t" : 1338472279000,
"i" : 1
},
"optimeDate" : ISODate("2012-05-31T13:51:19Z"),
"lastHeartbeat" : ISODate("2012-05-31T14:17:02Z")
}
],
"ok" : 1
}
rs1:SECONDARY>
可以看到 28010 这个端口的 MongoDB 出现了异常,而系统自动选举了 28012 这个端口为主,
所以这样的故障处理机制,能将系统的稳定性大大提高。
21.4.3 增减节点
MongoDB Replica Sets 不仅提供高可用性的解决方案,它也同时提供负载均衡的解决方案,
增减 Replica Sets 节点在实际应用中非常普遍,例如当应用的读压力暴增时, 3 台节点的环
境已不能满足需求,那么就需要增加一些节点将压力平均分配一下;当应用的压力小时,可
以减少一些节点来减少硬件资源的成本;总之这是一个长期且持续的工作。
21.4.3.1 增加节点
官方给我们提了 2 个方案用于增加节点,一种是通过 oplog 来增加节点,一种是通过数据库
快照 (--fastsync) oplog 来增加节点,下面将分别介绍。
21.4.3.1.1 通过 oplog 增加节点
①、配置并启动新节点,启用 28013 这个端口给新的节点
[root@localhost ~]# mkdir -p /data/data/r3
 
71 / 91
[root@localhost ~]# echo "this is rs1 super secret key" > /data/key/r3
[root@localhost ~]# chmod 600 /data/key/r3
[root@localhost ~]# /Apps/mongo/bin/mongod --replSet rs1 --keyFile /data/key/r3 --fork --port
28013 --dbpath /data/data/r3 --logpath=/data/log/r3.log --logappend
all output going to: /data/log/r3.log
forked process: 10553
[root@localhost ~]#
②、添加此新节点到现有的 Replica Sets
rs1:PRIMARY> rs.add("localhost:28013")
{ "ok" : 1 }
③、查看 Replica Sets 我们可以清晰的看到内部是如何添加 28013 这个新节点的 .
步骤一 : 进行初始化
rs1: PRIMARY > rs.status()
{
"set" : "rs1",
"date" : ISODate("2012-05-31T12:17:44Z"),
"myState" : 1,
"members" : [
……
{
"_id" : 3,
"name" : "localhost:28013",
"health" : 0,
"state" : 6,
"stateStr" : "(not reachable/healthy)",
"uptime" : 0,
"optime" : {
"t" : 0,
"i" : 0
},
"optimeDate" : ISODate("1970-01-01T00:00:00Z"),
"lastHeartbeat" : ISODate("2012-05-31T12:17:43Z"),
"errmsg" : "still initializing"
}
],
"ok" : 1
}
步骤二 : 进行数据同步
rs1:PRIMARY> rs.status()
{
"set" : "rs1",
 
72 / 91
"date" : ISODate("2012-05-31T12:18:07Z"),
"myState" : 1,
"members" : [
……
{
"_id" : 3,
"name" : "localhost:28013",
"health" : 1,
"state" : 3,
"stateStr" : "RECOVERING",
"uptime" : 16,
"optime" : {
"t" : 0,
"i" : 0
},
"optimeDate" : ISODate("1970-01-01T00:00:00Z"),
"lastHeartbeat" : ISODate("2012-05-31T12:18:05Z"),
"errmsg" : "initial sync need a member to be primary or secondary
to do our initial sync"
}
],
"ok" : 1
}
步骤三 : 初始化同步完成
rs1:PRIMARY> rs.status()
{
"set" : "rs1",
"date" : ISODate("2012-05-31T12:18:08Z"),
"myState" : 1,
"members" : [
……
{
"_id" : 3,
"name" : "localhost:28013",
"health" : 1,
"state" : 3,
"stateStr" : "RECOVERING",
"uptime" : 17,
"optime" : {
"t" : 1338466661000,
"i" : 1
},
"optimeDate" : ISODate("2012-05-31T12:17:41Z"),
 
73 / 91
"lastHeartbeat" : ISODate("2012-05-31T12:18:07Z"),
"errmsg" : "initial sync done"
}
],
"ok" : 1
}
步骤四 : 节点添加完成,状态正常
rs1:PRIMARY> rs.status()
{
"set" : "rs1",
"date" : ISODate("2012-05-31T12:18:10Z"),
"myState" : 1,
"members" : [
……
{
"_id" : 3,
"name" : "localhost:28013",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 19,
"optime" : {
"t" : 1338466661000,
"i" : 1
},
"optimeDate" : ISODate("2012-05-31T12:17:41Z"),
"lastHeartbeat" : ISODate("2012-05-31T12:18:09Z")
}
],
"ok" : 1
}
④、验证数据已经同步过来了
[root@localhost data]# /Apps/mongo/bin/mongo -port 28013
MongoDB shell version: 1.8.1
connecting to: 127.0.0.1:28013/test
rs1:SECONDARY> rs.slaveOk()
rs1:SECONDARY> db.c1.find()
{ "_id" : ObjectId("4fc760d2383ede1dce14ef86"), "age" : 10 }
rs1:SECONDARY>
21.4.3.1.2 通过数据库快照 (--fastsync) oplog 增加节点
通过 oplog 直接进行增加节点操作简单且无需人工干预过多,但 oplog capped collection
 
74 / 91
采用循环的方式进行日志处理,所以采用 oplog 的方式进行增加节点,有可能导致数据的不
一致,因为日志中存储的信息有可能已经刷新过了。不过没关系,我们可以通过数据库快照
(--fastsync) oplog 结合的方式来增加节点,这种方式的操作流程是,先取某一个复制集成
员的物理文件来做为初始化数据,然后剩余的部分用 oplog 日志来追,最终达到数据一致性
①、取某一个复制集成员的物理文件来做为初始化数据
[root@localhost ~]# scp -r /data/data/r3 /data/data/r4
[root@localhost ~]# echo "this is rs1 super secret key" > /data/key/r4
[root@localhost ~]# chmod 600 /data/key/r4
②、在取完物理文件后,在 c1 集中插入一条新文档,用于最后验证此更新也同步了
rs1:PRIMARY> db.c1.find()
{ "_id" : ObjectId("4fc760d2383ede1dce14ef86"), "age" : 10 }
rs1:PRIMARY> db.c1.insert({age:20})
rs1:PRIMARY> db.c1.find()
{ "_id" : ObjectId("4fc760d2383ede1dce14ef86"), "age" : 10 }
{ "_id" : ObjectId("4fc7748f479e007bde6644ef"), "age" : 20 }
rs1:PRIMARY>
③、启用 28014 这个端口给新的节点
/Apps/mongo/bin/mongod --replSet rs1 --keyFile /data/key/r4 --fork --port 28014 --dbpath
/data/data/r4 --logpath=/data/log/r4.log --logappend --fastsync
④、添加 28014 节点
rs1:PRIMARY> rs.add("localhost:28014")
{ "ok" : 1 }
⑤、验证数据已经同步过来了
[root@localhost data]# /Apps/mongo/bin/mongo -port 28014
MongoDB shell version: 1.8.1
connecting to: 127.0.0.1:28014/test
rs1:SECONDARY> rs.slaveOk()
rs1:SECONDARY> db.c1.find()
{ "_id" : ObjectId("4fc760d2383ede1dce14ef86"), "age" : 10 }
{ "_id" : ObjectId("4fc7748f479e007bde6644ef"), "age" : 20 }
rs1:SECONDARY>
21.4.3.2 减少节点
下面将刚刚添加的两个新节点 28013 28014 从复制集中去除掉,只需执行 rs.remove 指令
就可以了,具体如下 :
rs1:PRIMARY> rs.remove("localhost:28014")
 
75 / 91
{ "ok" : 1 }
rs1:PRIMARY> rs.remove("localhost:28013")
{ "ok" : 1 }
查看复制集状态,可以看到现在只有 28010 28011 28012 这三个成员,原来的 28013
28014 都成功去除了
rs1:PRIMARY> rs.status()
{
"set" : "rs1",
"date" : ISODate("2012-05-31T14:08:29Z"),
"myState" : 1,
"members" : [
{
"_id" : 0,
"name" : "localhost:28010",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"optime" : {
"t" : 1338473273000,
"i" : 1
},
"optimeDate" : ISODate("2012-05-31T14:07:53Z"),
"self" : true
},
{
"_id" : 1,
"name" : "localhost:28011",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 34,
"optime" : {
"t" : 1338473273000,
"i" : 1
},
"optimeDate" : ISODate("2012-05-31T14:07:53Z"),
"lastHeartbeat" : ISODate("2012-05-31T14:08:29Z")
},
{
"_id" : 2,
"name" : "localhost:28012",
"health" : 1,
"state" : 2,
 
76 / 91
"stateStr" : "SECONDARY",
"uptime" : 34,
"optime" : {
"t" : 1338473273000,
"i" : 1
},
"optimeDate" : ISODate("2012-05-31T14:07:53Z"),
"lastHeartbeat" : ISODate("2012-05-31T14:08:29Z")
}
],
"ok" : 1
}
rs1:PRIMARY>

你可能感兴趣的:(mongodb复制集)