oplog简介

oplog 是 local 库下的一个固定集合(local.oplog.$main),Secondary 就是通过查看 Primary 的 oplog 这个集合来进行复制的。每个节点都有 oplog,记录着从主节点复制过来的信息,这样每个成员都可以作为同步源给其它节点。

oplog的数据结构

  { ts : ..., op: ..., ns: ..., o: ... o2: ...  }

上面就是一条 oplog 信息,复制机制就是通过这些信息来进行节点间的数据同步并维护数据一致性的,其中:

    ts:8字节的时间戳,由4字节unix timestamp + 4字节自增计数表示。
          这个值很重要,在选举(如master宕机时)新primary时,会选择ts最大的那个secondary作为新primary。
    op:1字节的操作类型,例如i表示insert,d表示delete。
    ns:操作所在的namespace。
    o:操作所对应的document,即当前操作的内容(比如更新操作时要更新的的字段和值)
    o2: 在执行更新操作时的where条件,仅限于update时才有该属性

其中 op 可以是如下几种情形之一:

     "i": insert
     "u": update
     "d": delete
     "c": db cmd
     "db":声明当前数据库 (其中ns 被设置成为=>数据库名称+ '.')
     "n" :no op,即空操作,其会定期执行以确保时效性

复制集数据同步过程

复制集中数据同步的详细过程:Primary节点写入数据,Secondary 通过读取 Primary 的 oplog 得到复制信息,开始复制数据并且将复制信息写到自己的 oplog 中,从而实现 Replication 的功能(同时由于其记录了 Primary 上的操作,故还能将其用作数据恢复)。如果某个操作失败(只有当同步源的数据损坏或者数据与主节点不一致时才可能发生),则备份节点停止从当前数据源复制数据。如果某个备份节点由于某些原因挂掉了,当重新启动后,就会自动从 oplog 的最后一个操作开始同步,同步完成后,将信息写入自己的 oplog,由于复制操作是先复制数据,复制完成后再写入 oplog,有可能相同的操作会同步两份,不过 MongoDB 在设计之初就考虑这个问题,将 oplog 的统一操作执行多次,与执行一次的效果是一样的。

TIP:为了提高复制的效率,复制集中的所有节点之间会相互的心跳检测(ping)。每个节点都可以从其它节点上获取 oplog。

可以简单的将其视作 MySQL 中的 binlog。

oplog 的增长速度

oplog 是固定大小的,它只能保存特定数量的操作日志,通常 oplog 使用空间的增长速度跟系统处理写请求的速度相当,如果主节点上每分钟处理 1KB 的写入数据,那么 oplog 每分钟大约也写入 1KB 数据。如果单次操作影响到了多个文档(比如删除了多个文档或者更新了多个文档)则 oplog 可能就会有多条操作日志。db.xxx.remove() 命令 删除了100个文档,那么 oplog中就会有100条操作日志。如果存在大批量的操作,oplog 有可能很快就会被写满了。

oplog 的大小

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

MongoDB在初始化复制集的时候都会有一个默认的 oplog 大小:

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

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

查看 oplog 的信息

my_repl:PRIMARY> rs.printReplicationInfo()       #查看oplog的状态,输出信息包括oplog日志大小,操作日志记录的起始时间
  configured oplog size:   2048MB              #oplog文件的大小
  log length start to end: 395115secs (109.75hrs)                 #根据目前数据的增加量,预计的oplog还有多长时间就要从头覆盖了。所以最好在这个时间内进行数据全备。oplog至少要保存一轮全备的时间。
  oplog first event time:  Fri Apr 09 2021 05:02:34 GMT+0800 (CST)   #第一个事件日志的产生时间
  oplog last event time:   Tue Apr 13 2021 18:47:49 GMT+0800 (CST)   #最后一个事件日志产生时间
  now:                     Tue Apr 13 2021 18:47:50 GMT+0800 (CST)     #现在的时间

            
my_repl:PRIMARY> db.getReplicationInfo()         #可以用来查看oplog的状态、大小、存储的时间范围
{
	"logSizeMB" : 2048,
	"usedMB" : 0.03,
	"timeDiff" : 395235,
	"timeDiffHours" : 109.79,
	"tFirst" : "Fri Apr 09 2021 05:02:34 GMT+0800 (CST)",
	"tLast" : "Tue Apr 13 2021 18:49:49 GMT+0800 (CST)",
	"now" : "Tue Apr 13 2021 18:49:59 GMT+0800 (CST)"
}

查看slave状态

my_repl:PRIMARY> db.printSlaveReplicationInfo()     #查看slave的同步状态
  source: 10.0.0.51:28018              
	  syncedTo: Tue Apr 13 2021 18:56:09 GMT+0800 (CST)
	  0 secs (0 hrs) behind the primary 
  source: 10.0.0.51:28020
	  no replication info, yet.  State: (not reachable/healthy)
  source: 10.0.0.51:28019
	  syncedTo: Tue Apr 13 2021 18:56:09 GMT+0800 (CST)
	  0 secs (0 hrs) behind the primary 

# source:  从库的IP及端口
# syncedTo:  当前的同步情况,延迟了多久等信息

oplog 配合 mongodump 实现热备

1、准备测试数据

[mongod@db01 ~] $ mongo --port 28017 
my_repl:PRIMARY> use cxs
my_repl:PRIMARY> for(i=1;i<100;i++){db.foo.insert({"id":i});}
        ...
my_repl:PRIMARY> use local
my_repl:PRIMARY> db.oplog.rs.find({"op":"i"}).pretty()         #筛选出oplog中所有 insert操作的记录

2、对 mongodb 数据库中数据进行全备

[mongod@db01 ~] $ mongodump --port 28017 --oplog -o /mongodb/backup  #对28017实例中的数据进行全备
## --oplog参数:会记录备份过程中的数据变化,并以".bson"格式保存下来

3、模拟故障

[mongod@db01 ~] $ mongo --port 28017 
my_repl:PRIMARY> use cxs
my_repl:PRIMARY> db.foo.drop()            #手动删除foo集合

4、数据恢复

[mongod@db01 ~] $ mongorestore  --port 28017 --oplogReplay /mongodb/backup       #使用mongorestore命令恢复数据时,会自动识别/mongodb/backup目录下的 ".bson"文件

 

oplog 高级应用

背景:每天0点进行全备,oplog恢复窗口为48小时。某天上午10 world库下的city集合被删除

恢复思路:

  1. 停应用
  2. 找测试库
  3. 恢复昨天晚上的全备
  4. 截取全备之后到 world.city 误删除时间点的 oplog,并恢复到测试库
  5. 将误删除表导出,恢复到生产库

1、准备数据

[mongod@db01 ~] $ mongo --port 28017 
my_repl:PRIMARY> use world
my_repl:PRIMARY> for(i=1;i<1000;i++){db.city.insert({"id":i});}
        ...

2、周一凌晨0点进行全备

[mongod@db01 ~] $ rm -rf /mongodb/backup/*
[mongod@db01 ~] $ mongodump --port 28017 --oplog -o /mongodb/backup

3、模拟周二新产生的数据

[mongod@db01 ~] $ mongo --port 28017 
my_repl:PRIMARY> use world
my_repl:PRIMARY> db.country.insert({"id":"1"})
my_repl:PRIMARY> db.countrycode.insert({"id":"2"})

4、周二上午10点,误删除了 world 库的 country 集合

[mongod@db01 ~] $ mongo --port 28017 
my_repl:PRIMARY> use world
my_repl:PRIMARY> db.country.drop()

5、备份现有的 oplog.rs 集合

[mongod@db01 ~] $ mongodump --port 28017 -d local -c oplog.rs  -o /mongodb/backup

6、获取到oplog误删除时间点位置

[mongod@db01 ~] $ mongo --port 28017
my_repl:PRIMARY> use local
db.oplog.rs.find({op:"c"}).pretty();         #因为删除操作数据库、集合的操作也记为"c",所以要查找操作为"c"的oplog

{
	"ts" : Timestamp(1553659908, 1),
	"t" : NumberLong(2),
	"h" : NumberLong("-7439981700218302504"),
	"v" : 2,
	"op" : "c",
	"ns" : "world.$cmd",
	"ui" : UUID("db70fa45-edde-4945-ade3-747224745725"),
	"wall" : ISODate("2019-03-27T04:11:48.890Z"),
	"o" : {
		"drop" : "city"
	}
}


#获取到oplog误删除时间点位置: "ts" : Timestamp(1553659908, 1)

7、恢复备份+应用oplog

[mongod@db01 ~]$ cd /mongodb/backup/local/
[mongod@db01 local]$ ls
oplog.rs.bson  oplog.rs.metadata.json


[mongod@db01 ~]$ mongorestore  --port 28020 --oplogReplay /mongodb/backup        #在测试库上恢复昨晚的全备
[mongod@db01 local]$ cp oplog.rs.bson ../oplog.bson       #会将之前全备的oplog.bson一样的内容自动覆盖
[mongod@db01 ~]$ rm -rf /mongodb/backup/local/       #把local目录删掉,因为local并不是我们备份数据中的一部分  
[mongod@db01 ~]$ mongorestore --port 28020  --oplogReplay --oplogLimit "1553659908:1"  --drop   /mongodb/backup/           #在测试库中恢复到"--oplogLimit"参数指定的时间点之前的数据         

#"--drop参数":把数据恢复过程中重复的数据删除掉

8、将误删的表从测试库中导出,恢复带生产中

[mongod@db01 ~] $ mongodump --port 28020 -d world -c country  -o /mongodb/backup   #导出country集合

[mongod@db01 ~] $ mongorestore --port 28017  admin -d world  /mongodb/backup/world            #恢复数据

 

参考文章:

https://www.cnblogs.com/williamjie/p/10410642.html

 

 

 

 

 

 

 

 

 

 

 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐