MongoDB · 2018-04-03 0

MongoDB 任意时间点恢复

简介

Mongodump是MongoDB附带的基础逻辑备份工具。它将备份完整的数据库/集合的BSON副本,并且可以选择备份保持数据库一致性的日志(oplog),备份的oplog记录的是备份执行期间数据库的变更日志。Mongorestore是用于恢复由Mongodump创建的逻辑备份的工具。我将在本文的步骤中使用这俩工具来恢复备份数据。本文假设一个基于mongodump的一致性备份(通过使用 –oplog),并将备份恢复到MongoDB实例。

本实例中,mongodump将会备份恢复基础的集合数据,并单独搜集基于时间点备份恢复所需的oplog变更,恢复时将其应用到数据。

需要指出的是:Percona开发了一个名为 mongodb_consistent_backup 的备份工具,它是’mongodump’的包装,增加了集群范围的备份一致性。 由mongodb_consistent_backup创建的备份(在Dump/Mongodump模式下)可以使用与常规“mongodump”备份相同的步骤进行恢复。
附:mongodb_consistent_backup: https://github.com/Percona-Lab/mongodb_consistent_backup

实验步骤

Stage 1:获得一个Mongodump备份

mongodump参数:

–host/–port (and –user/–password)
即使您使用的是默认主机/端口(localhost:27017),也是必需的。如果启用了授权,则还要添加 –user / –password 参数。

–oplog
任何replset成员都需要!此参数使“mongodump”在备份过程中捕获oplog更改日志,以保持一致的时间点。

–gzip
可选的。适用于mongodump > = 3.2,启用备份文件的内联压缩。

附:–oplog创建一个名为oplog.bson文件作为mongodump结果的一部分,包含了mongodump操作期间的oplog条目,实现了某个时间点的一致性快照。可用mongorestore –oplogReplay进行恢复。它的实际作用是在导出的同时生成一个oplog.bson文件,存放在你开始进行dump到dump结束之间所有的oplog。用图形来说明下oplog.bson的覆盖范围:
mongodump_oplog

注意:mongodump时,–oplog只能在master节点(主从结构)或者副本集的成员中执行。也就是说,mongodump –oplog不能在主从结构的slave上执行(当然,估计也没人用主从了)。

备份命令:

$ mongodump --host localhost --port 27017 --oplog --gzip
2016-08-15T12:32:28.930+0200    writing wikipedia.pages to
2016-08-15T12:32:31.932+0200    [#########...............]  wikipedia.pages  674/1700   (39.6%)
2016-08-15T12:32:34.931+0200    [####################....]  wikipedia.pages  1436/1700  (84.5%)
2016-08-15T12:32:37.509+0200    [########################]  wikipedia.pages  2119/1700  (124.6%)
2016-08-15T12:32:37.510+0200    done dumping wikipedia.pages (2119 documents)
2016-08-15T12:32:37.521+0200    writing captured oplog to
2016-08-15T12:32:37.931+0200    [##......................]  .oplog  44/492   (8.9%)
2016-08-15T12:32:39.648+0200    [########################]  .oplog  504/492  (102.4%)
2016-08-15T12:32:39.648+0200    dumped 504 oplog entries

stage 2 恢复备份的数据

步骤:

1 找到分片的PRIMARY成员(肯定要在主节点做恢复)

2 三重检查是否将正确的备份恢复至正确的分片/主机

3 将基于mongodump的备份还原到PRIMARY节点(具体可参考官方文档)

4 检查是否有错误

5 检查所有SECONDARY成员是否与PRIMARY同步。

stage 3 获取时间点恢复的Oplogs

在这个阶段,我们将收集从备份时间点(向前滚动数据)到我们想要恢复的时间点(oplog位置)所需的更改。

在下面的这个例子中,假设有人在oplog时间戳“Timestamp(1470923942,3)”中意外删除了整个集合,我们想修复它。如果我们递减“时间戳(1470923942,3)”的增量(第2个数字),我们将获取到在意外命令之前进行的最后更改,在本例中为“时间戳(1470923942,2)”。 通过使用时间戳,我们可以捕获并重放oplog,从备份时间点到问题/错误操作之前。

我们需要开始和结束时间戳来获取oplog中的数据。在所有情况下,这都需要手动收集。

用到的脚本:
【dump_oplog_range.sh】

#!/bin/bash
#
# This tool will dump out a BSON file of MongoDB oplog changes based on a range of Timestamp() objects.
# The captured oplog changes can be applied to a host using 'mongorestore --oplogReplay --dir /path/to/dump'.
set -e
TS_START=$1
TS_END=$2
MONGODUMP_EXTRA=$3
function usage_exit() {
  echo "Usage $0: [Start-BSON-Timestamp] [End-BSON-Timestamp] [Extra-Mongodump-Flags (in quotes for multiple)]"
  exit 1
}
function check_bson_timestamp() {
  local TS=$1
  echo "$TS" | grep -qP "^Timestamp(d+,sd+)$"
  if [ $? -gt 0 ]; then
    echo "ERROR: Both timestamp fields must be in BSON Timestamp format, eg: 'Timestamp(########, #)'!"
    usage_exit
  fi
}
if [ -z "$TS_START" ] || [ -z "$TS_END" ]; then
  usage_exit
else
  check_bson_timestamp "$TS_START"
  check_bson_timestamp "$TS_END"
fi
MONGODUMP_QUERY='{ "ts" : { "$gte" : '$TS_START' }, "ts" : { "$lte" : '$TS_END' } }'
MONGODUMP_FLAGS='--db=local --collection=oplog.rs'
[ ! -z "$MONGODUMP_EXTRA" ] && MONGODUMP_FLAGS="$MONGODUMP_FLAGS $MONGODUMP_EXTRA"
if [ -d dump ]; then
  echo "'dump' subdirectory already exists! Exiting!"
  exit 1
fi
echo "# Dumping oplogs from '$TS_START' to '$TS_END'..."
mkdir dump
mongodump $MONGODUMP_FLAGS --query "$MONGODUMP_QUERY" --out - >dump/oplog.bson
if [ -f dump/oplog.bson ]; then
  echo "# Done!"
else
  echo "ERROR: Cannot find oplog.bson file! Exiting!"
  exit 1
fi

脚本用法:

$ ./dump_oplog_range.sh
Usage ./dump_oplog_range.sh: [Start-BSON-Timestamp] [End-BSON-Timestamp] [Extra-Mongodump-Flags (in quotes for multiple)]

操作步骤:
1 找到包含PITR(point-in-time-recovery)还原所需的oplog的PRIMARY成员。

2 确定需要恢复的“end”Timestamp()。 这个oplog时间应该在问题出现之前。

3 确定备份开始之前的“start”Timestamp()。
这个时间戳不需要精确,所以Timestamp()对象等于“在备份开始前几分钟”都没有问题,但时间越精确,您需要重放的更改就更少(可以节省恢复时间)。

4 使用MongoToolsAndSnippets脚本: “get_oplog_range.sh”(就是上面贴的“帮助脚本”)将需要恢复的oplog时间范围dump到您选择的时间点。 在这个例子中,我在两个时间点之间收集oplog(同时在引号中传入–username / –password作为第三个参数):
1) 起始时间戳:本示例中的“阶段2:恢复集合数据”中的mongodump备份之前的BSON时间戳。在本例中,“时间戳(1470923918,0)”是在mongodump执行之前几秒钟的时间(不需要精确)。
2) 结束时间戳:在此示例中,恢复到哪个时间点的BSON时间戳。“时间戳(1470923942,2)”是发生问题之前的最后一次oplog更改。

$ wget -q https://raw.githubusercontent.com/percona/MongoToolsAndSnippets/master/rdba/dump_oplog_range.sh
$ bash ./dump_oplog_range.sh 'Timestamp(1470923918, 0)' 'Timestamp(1470923942, 2)' '--username=secret --password=secret --host=mongo01.example.com --port=27024'
# Dumping oplogs from 'Timestamp(1470923918, 0)' to 'Timestamp(1470923942, 2)'...
2016-08-12T13:11:17.676+0200    writing local.oplog.rs to stdout
2016-08-12T13:11:18.120+0200    dumped 22 documents
# Done!

注意:所有额外的mongodump 参数(第三个参数)必须用引号括起来!

5 通过查找 ‘oplog.bson’ 文件并检查文件中是否有一些数据(下面的例子中为168mb)来检查它的工作情况:

$ ls -alh dump/oplog.bson
-rw-rw-r--. 1 tim tim 168M Aug 12 13:11 dump/oplog.bson

stage 4 应用Oplogs进行时间点恢复(PITR)

在此阶段,我们将在阶段3中收集的基于时间范围的oplog应用于恢复的数据集,以便将数据从备份时间带到发生问题之前的特定时间点。

mongorestore 参数:

–host/–port(–user / –password)
即使您使用的是默认主机/端口(localhost:27017),也是必需的。如果启用授权,则还要添加 –user / –password 参数。

–oplogReplay
必须。这一步用于重放oplog。

–dir
必须。 mongodump数据的路径。

步骤:
1 将仅包含“oplog.bson”文件(在阶段3中捕获)的“dump”目录复制到需要应用oplog更改的主机(还原主机)。

2 在“dump”目录中运行“mongorestore”以将oplog重放到实例中。注意确保“dump”目录只包含“oplog.bson”文件。

$ mongorestore --host localhost --port 27017 --oplogReplay --dir ./dump
2016-08-12T13:12:28.105+0200    building a list of dbs and collections to restore from dump dir
2016-08-12T13:12:28.106+0200    replaying oplog
2016-08-12T13:12:31.109+0200    oplog   80.0 MB
2016-08-12T13:12:34.109+0200    oplog   143.8 MB
2016-08-12T13:12:35.501+0200    oplog   167.8 MB
2016-08-12T13:12:35.501+0200    done

3 使用任何可能的方式验证恢复的数据,(例如:.count()查询,一些随机的.find()查询等)。


本文译自:MongoDB point-in-time backups made easy