码迷,mamicode.com
首页 > 数据库 > 详细

MongoDB实战指南(七):MongoDB复制集之复制集工作机制

时间:2016-01-04 00:02:47      阅读:408      评论:0      收藏:0      [点我收藏+]

标签:

http://www.cnblogs.com/longshiyVip/p/5097336.html 概述了复制集,整体上对复制集有了个概念,但是复制集最重要的功能之——自动故障转移是怎么实现的呢?数据同步又是如何实现的?带着这两个问题,下面展开分析。

一. 数据同步

先利用mongo客户端登录到复制集的primary节点上。

>mongo --port 40000

查看实例上所有数据库

rs0:PRIMARY> show dbs
local
0.09375GB

可以看到只有一个local数据库,因为此时还没有在复制集上创建任何其它数据库,local数据库为复制集所有成员节点上默认创建的一个数据库。在primary节点上查看local数据上的集合:

rs0:PRIMARY> show collections

oplog.rs

slaves

startup_log

system.indexes

system.replset

如果是在secondary节点则local数据库上的集合与上面有点不同,secondary节点上没有slaves集合,因为这个集合保存的是需要从primary节点同步数据的secondary节点;secondary节点上会有一个me集合,保存了实例本身所在的服务器名称;secondary节点上还有一个minvalid集合,用于保存对数据库的最新操作的时间截。其它集合primary节点和secondary节点都有,其中startup_log集合表示的是mongod实例每一次的启动信息;system.indexes集合保存的是当前数据库(local)上的所有索引信息;system.replset集合保存的是复制集的成员配置信息,复制集上的命令rs.conf()实际上是从这个集合取的数据返回的。最后要介绍的集合是oplog.rs,这个可是重中之中。

mongoDB就是通过oplog.rs来实现复制集间数据同步的,为了分析数据的变化,先在复制集上的primary节点上创建一个数据库students,然后插入一条记录。

rs0:PRIMARY> use students

switched to db students

rs0:PRIMARY> db.scores.insert({"stuid":1,"subject":"math","score":99});

接着查看一下primary节点上oplog.rs集合的内容:

rs0:PRIMARY> use local

switched to db local

rs0:PRIMARY> db.oplog.rs.find();

返回记录中会多出一条下面这样的记录(里面还有几条记录是复制集初始化时创建的):

{ "ts" : { "t" : 1376838296, "i" : 1 }, "h" : NumberLong("6357586994520331181"),

 "v" : 2, "op" : "i", "ns" : "students.scores", "o" : { "_id" : ObjectId("5210e2

98d7b419b44afa58cc"), "stuid" : 1, "subject" : "math", "score" : 99 } }

里面有几个重要字段,其中"ts"表示是这条记录的时间截,"t"是秒数,"i"每秒操作的次数;字段"op"表示的是操作码,值为"i"表示的是insert操作;"ns"表示插入操作发生的命名空间,这里值为: "students.scores",由数据库和集合名构成;"o"表示的是此插入操作包含的文档对象;

当primary节点完成插入操作后,secondary节点为了保证数据的同步也会完成一些动作:

所有secondary节点检查自己的local数据上oplog.rs集合,找出最近的一条记录的时间截;接着它会查询primary节点上的oplog.rs集合,找出所有大于此时间截的记录;最后它将这些找到的记录插入到自己的oplog.rs集合中并执行这些记录所代表的操作;通过这三步策略,就能保证secondary节点上的数据与primary节点上的数据同步了。整个流程如下图所示:

技术分享

          复制集数据同步流程

查看一下secondary节点上的数据,证明上面的分析是正确的。

rs0:SECONDARY> show dbs

local   0.09375GB

students        0.0625GB

  在secondary节点上新插入了一个数据库students。但是有一点要注意:现在还不能在secondary节点上直接查询students集合上的内容,默认情况下mongoDB的所有读写操作都是在primary节点上完成的,后面也会介绍通过设置从secondary节点上来读,这将引入一个新的主题,后面再分析。

关于oplog.rs集合还有一个很重要的方面,那就是它的大小是固定的,mongoDB这样设置也是有道理的,假如大小没限制,那么随着时间的推移,在数据库上的操作会逐渐累积,oplog.rs集合中保存的记录也会逐渐增多,这样会消耗大量的存储空间,同时对于某个时间点以前的操作记录,早已同步到secondary节点上,也没有必要一直保存这些记录,因此mongoDB将oplog.rs集合设置成一个capped类型的集合,实际上就是一个循环使用的缓冲区。

固定大小的oplog.rs会带来新的问题,考虑下面这种场景:假如一个secondary节点因为宕机,长时间不能恢复,而此时大量的写操作发生在primary节点上,当secondary节点恢复时,利用自己oplog.rs集合上最新的时间截去查找primary节点上的oplog.rs集合,会出现找不到任何记录。因为长时间不在线,primary节点上的oplog.rs集合中的记录早已全部刷新了一遍,这样就不得不手动重新同步数据了。因此oplog.rs的大小是很重要,在32位的系统上默认大小是50MB,在64位的机器上默认是5%的空闲磁盘空间大小,也可以在mongod启动命令中通过项—oplogSize设置其大小。

二. 故障转移

上面的介绍的数据同步相当于传统数据库中的备份策略,mongoDB在此基础还有自动故障转移的功能。在复制集概述那一节提到过心跳"lastHeartbeat"字段,mongoDB就是靠它来实现自动故障转移的。 mongod实例每隔2秒就向其它成员发送一个心跳包以及通过rs.staus()中返回的成员的”health”值来判断成员的状态。如果出现复制集中primary节点不可用了,那么复制集中所有secondary的节点就会触发一次选举操作,选出一个新的primary节点。如上所配置的复制集中如果primary节点宕机了,那么就会选举secondary节点成为primary节点,arbiter节点只是参与选举其它成员成为primary节点,自己永远不会成为primary节点。如果secondary节点有多个则会选择拥有最新时间截的oplog记录或较高权限的节点成为primary节点。oplog记录在前面复制集概述中已经描述过,关于复制集中节点权限配置的问题可在复制集启动的时候进行设置,也可以在启动后重新配置,这里先略过这一点,集中精力讨论故障转移。

如果是某个secondary节点失败了,只要复制集中还有其它secondary节点或arbiter节点存在,就不会发生重新选举primary节点的过程。

下面模拟两种失败场景:一是secondary节点的失败,然后过一段时间后重启(时间不能无限期,否则会导致oplog.rs集合严重滞后的问题,需要手动才能同步);二是primary节点失败,故障转移发生。

先分析第一种情况的测试,当前复制集的配置情况如下:

(1)

rs0:PRIMARY> rs.conf()

{

"_id" : "rs0",

"version" : 3,

"members" : [

{

"_id" : 0,

"host" : "Guo:40000" //primary节点

},

{

"_id" : 1,

"host" : "Guo:40001" //secondary节点

},

{

"_id" : 2,

"host" : "Guo:40002", //arbiter节点

"arbiterOnly" : true

}

]

}

(2)通过Kill掉secondary节点所在的mongod实例,模拟第一种故障情况,如下图所示:

技术分享

    模拟secondary节点故障

通过rs.status()命令查看复制集状态,secondary节点状态信息如下:

"_id" : 1,

"name" : "Guo:40001",

"health" : 0,

"state" : 8, //表示成员已经down机

"stateStr" : "(not reachable/healthy)",

"uptime" : 0,

"optime" : {

"t" : 1376838296,

"i" : 1

},

"optimeDate" : ISODate("2013-08-18T15:04:56Z")

(3)接着通过primary节点插入一条记录:

rs0:PRIMARY> db.scores.insert({stuid:2,subject:"english",score:100})

(4)再次查看复制集状态信息rs.status(),可以看到primary成员节点上oplpog信息如下:

"optime" : {

"t" : 1376922730,

"i" : 1

},

"optimeDate" : ISODate("2013-08-19T14:32:10Z"),

与上面down机的成员节点比较,optime已经不一样,primary节点上要新于down机的节点。

(5)重新启动Kill掉的节点

>mongod --config E:\mongodb-win32-i386-2.4.3\configs_rs0\rs0_1.conf

查询复制集状态信息rs.status(),观看节点"Guo:40001"的状态信息如下:

"_id" : 1,

"name" : "GUO:40001",

"health" : 1,

"state" : 2,

"stateStr" : "SECONDARY",

"uptime" : 136,

"optime" : {

"t" : 1376922730, //与上面primary节点一致了

"i" : 1

},

"optimeDate" : ISODate("2013-08-19T14:32:10Z"),

说明secondary节点已经恢复,并且从primary节点同步到了最新的操作数据。进一步通过查询secondary节点上local数据库上的oplog.rs集合来进行验证,发现多了一条下面这样的记录:

{ "ts" : { "t" : 1376922730, "i" : 1 }, "h" : NumberLong("-451684574732211704"),

"v" : 2, "op" : "i", "ns" : "students.scores", "o" : { "_id" : ObjectId("52122c

6a99c5a3ae472a6900"), "stuid" : 2, "subject" : "english", "score" : 100 } }

这正是在primary节点上插入的记录,再次证明数据确实同步过来了。

接下来测试第二种情况,假如primary节点故障,流程变化如下图所示:

技术分享

      模拟primary节点失败并恢复后

(1)将primary节点Kill掉。

查询复制集的状态信息rs.status()

"name" : "Guo:40000",

"health" : 0,

"state" : 8,

"stateStr" : "(not reachable/healthy)"

字段"health"的值为0,说明原来的primary节点已经down机了。

"name" : "Guo:40001",

"health" : 1,

"state" : 1,

"stateStr" : "PRIMARY"

字段"stateStr"值为"PRIMARY",说明原来secondary节点变成了primary节点。

(2)在新的primary节点上插入一条记录

rs0:PRIMARY> db.scores.insert({stuid:3,subject:"computer",score:99})

(3)重新恢复"Guo:40000"节点(原来的primary节点)

>mongod --config E:\mongodb-win32-i386-2.4.3\configs_rs0\rs0_0.conf

再次查看复制集状态rs.status()

"name" : "Guo:40000",

"health" : 1,

"state" : 2,

"stateStr" : "SECONDARY",

"uptime" : 33,

"optime" : {

"t" : 1376924110,

"i" : 1

},

当"Guo:40000"实例被重新激活后,变成了secondary节点,oplog也被同步成最新的了。说明当primary节点故障时,复制集能自动转移故障,将其中一个secondary节点变为primary节点,读写操作继续在新的primary节点上进行。原来primary节点恢复后,在复制集中变成了secondary节点。

上面两中情况都得到了验证,但是有一点要注意,mongDB默认情况下只能在primary节点上进行读写操作,如下图所示:

技术分享

    默认的读写流程图

  对于客户端应用程序来说,对复制集的读写操作是透明的,默认情况它总是在primary节点上进行。 mongoDB提供了很多种常见编程语言的驱动程序,驱动程序位于应用程序与mongod实例之间,应用程发起与复制集的连接,驱动程序自动选择primary节点。当primary节点失效,复制集发生故障转移时,复制集将先关闭与所有客户端的socket连接,驱动程序将返回一个异常,应用程序收到这个异常,这个时候需要应用程序开发人员去处理这些异常,同时驱动程序会尝试重新与primary节点建立连接(这个动作对应用程序来说是透明的)。假如这个时候正在发生一个读操作,在异常处理中你可以重新发起读数据命令,因为读操作不会改变数据库的数据;假如这个时候发生的是写操作,情况就变得微妙起来,如果是非安全模式下的写,就会产生不确定因素,写是否成功不确定,如果是安全模式,驱动程序会通过getlasterror命令知道哪些写操作成功了,哪些失败,驱动程序会返回失败的信息给应用程序,针对这个异常信息,应用程序可以决定怎样处置这个写操作,可以重新执行写操作,也可以直接给用户暴出这个错误。

MongoDB实战指南(七):MongoDB复制集之复制集工作机制

标签:

原文地址:http://www.cnblogs.com/longshiyVip/p/5097379.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!