MongoDB副本集的工作原理

在MongoDB副本集中,主节点负责处理客户端的读写请求,备份节点则负责映射主节点的数据。

备份节点的工作原理过程可以大致描述为,备份节点定期轮询主节点上的数据操作,然后对自己的数据副本进行这些操作,从而保证跟主节点的数据同步。

至于主节点上的所有数据库状态改变的操作,都会存放在一张特定的系统表中。备份节点则是根据这些数据进行自己的数据更新。

oplog

上面提到的数据库状态改变的操作,称为oplog(operation log,主节点操作记录)。oplog存储在local数据库的"oplog.rs"表中。副本集中备份节点异步的从主节点同步oplog,然后重新执行它记录的操作,以此达到了数据同步的作用。

关于oplog有几个注意的地方:

  • oplog只记录改变数据库状态的操作
  • 存储在oplog中的操作并不是和主节点执行的操作完全一样,例如"$inc"操作就会转化为"$set"操作
  • oplog存储在固定集合中(capped collection),当oplog的数量超过oplogSize,新的操作就会覆盖就的操作

下面来看下oplog的一些具体内容,首先删除上一篇的node1-node3文件夹,重新建立副本集,但是这次限制oplogSize为5MB。

mongod.exe --dbpath="c:\mongodb\db\node1" --port=11111 --replSet myReplSet --oplogSize=5
mongod.exe --dbpath="c:\mongodb\db\node2" --port=22222 --replSet myReplSet --oplogSize=5
mongod.exe --dbpath="c:\mongodb\db\node3" --port=33333 --replSet myReplSet --oplogSize=5

然后通过MongoDB shell(连接主节点)插入一些数据

use test
db.person.insert({ "name" : "Will0", "gender" : "Female", "age" : 22 })
db.person.insert({ "name" : "Will1", "gender" : "Female", "age" : 20 })

通过一些命令就可以查看主节点的oplog了,通过oplog可以看到前面两条数据插入操作,备份节点接可以根据这两条记录更新自己的数据集。

use local
show collections
db.oplog.rs.find()

查看oplog表的状态,当前oplog有3条记录,oplog表是一个capped collection(固定大小集合),oplog表的大小是5242880B=5MB。

oplog数据结构

下面来分析一下oplog中字段的含义,通过下面的命令取出一条oplog:

db.oplog.rs.find().skip(1).limit(1).toArray()

  • ts: 8字节的时间戳,由4字节unix timestamp + 4字节自增计数表示。这个值很重要,在选举(如master宕机时)新primary时,会选择ts最大的那个secondary作为新primary
  • op:1字节的操作类型
    • "i": insert
    • "u": update
    • "d": delete
    • "c": db cmd
    • "db":声明当前数据库 (其中ns 被设置成为=>数据库名称+ '.')
    • "n": no op,即空操作,其会定期执行以确保时效性
  • ns:操作所在的namespace
  • o:操作所对应的document,即当前操作的内容(比如更新操作时要更新的的字段和值)
  • o2: 在执行更新操作时的where条件,仅限于update时才有该属性

oplog的大小

capped collection是MongoDB中一种提供高性能插入、读取和删除操作的固定大小集合,当集合被填满的时候,新的插入的文档会覆盖老的文档。

所以,oplog表使用capped collection是合理的,因为不可能无限制的增长oplog。MongoDB在初始化副本集的时候都会有一个默认的oplog大小:

  • 在64位的Linux,Solaris,FreeBSD以及Windows系统上,MongoDB会分配磁盘剩余空间的5%作为oplog的大小,如果这部分小于1GB则分配1GB的空间
  • 在64的OS X系统上会分配183MB
  • 在32位的系统上则只分配48MB

oplog的大小设置是值得考虑的一个问题,如果oplog size过大,会浪费存储空间;如果oplog size过小,老的oplog记录很快就会被覆盖,那么宕机的节点就很容易出现无法同步数据的现象。

比如,基于上面的例子,我们停掉一个备份节点(port=33333),然后通过主节点插入以下记录,然后查看oplog,发现以前的oplog已经被覆盖了。

for(var i=0;i<10000;i++){
    var randAge = parseInt(5*Math.random()) + 20;
    var gender = (randAge%2)?"Male":"Female";
    db.school.students.insert({"name":"Will"+i, "gender": gender, "age": randAge});
}

接下来重新启动上面停掉的备份节点(port=33333),从server的输出中可以看到,oplog已经太新了,备份节点无法进行同步了。

通过MongoDB shell连接上这个节点,会发现这个节点一直处于RECOVERING状态。

 

数据同步

在副本集中,有两种数据同步方式:

  • initial sync(初始化):这个过程发生在当副本集中创建一个新的数据库或其中某个节点刚从宕机中恢复,或者向副本集中添加新的成员的时候,默认的,副本集中的节点会从离它最近的节点复制oplog来同步数据,这个最近的节点可以是primary也可以是拥有最新oplog副本的secondary节点。
    • 该操作一般会重新初始化备份节点,开销较大
  • replication(复制):在初始化后这个操作会一直持续的进行着,以保持各个secondary节点之间的数据同步。

initial sync

当遇到上面例子中无法同步的问题时,只能使用以下两种方式进行initial sync了

  • 第一种方式就是停止该节点,然后删除目录中的文件,重新启动该节点。这样,这个节点就会执行initial sync
    • 注意:通过这种方式,sync的时间是根据数据量大小的,如果数据量过大,sync时间就会很长
    • 同时会有很多网络传输,可能会影响其他节点的工作
  • 第二种方式,停止该节点,然后删除目录中的文件,找一个比较新的节点,然后把该节点目录中的文件拷贝到要sync的节点目录中

通过上面两种方式中的一种,都可以重新恢复"port=33333"的节点。不在进行截图了。

 

副本集管理

查看oplog的信息

通过"db.printReplicationInfo()"命令可以查看oplog的信息

字段说明:

  • configured oplog size: oplog文件大小
  • log length start to end: oplog日志的启用时间段
  • oplog first event time: 第一个事务日志的产生时间
  • oplog last event time: 最后一个事务日志的产生时间
  • now: 现在的时间

查看slave状态

通过"db.printSlaveReplicationInfo()"可以查看slave的同步状态

当我们插入一条新的数据,然后重新检查slave状态时,就会发现sync时间更新了

 

总结

在这篇文章中介绍了副本集的工作原理,通过oplog以及数据同步进一步了解了副本集。

另外,实践中难免会碰到需要修改oplog size的情况,本篇文章没有进行介绍,请参考MongoDB文档中的步骤,修改oplog size 

Ps:例子中所有的命令都可以参考以下链接

https://files.cnblogs.com/wilber2013/oplog.js

 

posted @ 2014-12-09 23:05  田小计划  阅读(4390)  评论(1编辑  收藏  举报
Fork me on GitHub