引言:为什么MongoDB备份至关重要

在当今数据驱动的世界中,数据库备份是确保业务连续性和数据安全的生命线。MongoDB作为最受欢迎的NoSQL数据库之一,其灵活的文档模型和水平扩展能力使其在各种应用场景中得到广泛应用。然而,正是这种灵活性也带来了备份策略的复杂性。

想象一下,如果您的电商平台在黑五促销期间突然遭遇数据损坏,或者您的分析系统丢失了数月的用户行为数据,后果将不堪设想。一个完善的MongoDB备份策略不仅能防止数据丢失,还能确保在灾难发生时快速恢复业务。

MongoDB备份面临的主要挑战包括:

  • 数据量巨大:现代应用可能产生TB级别的数据
  • 高可用集群:副本集和分片集群的备份需要特殊考虑
  • 业务连续性要求:7x24小时运行的系统需要最小化停机时间
  • 数据一致性:确保备份数据在时间点上是一致的

本文将深入探讨MongoDB的备份策略,从基础的全量备份到高级的增量备份,并提供实战技巧和常见问题的解决方案。

MongoDB备份基础概念

1. MongoDB数据存储机制

理解MongoDB的备份首先需要了解其数据存储方式。MongoDB使用内存映射文件(Memory-Mapped Files)将数据文件直接映射到进程的虚拟内存空间。

数据文件结构

/data/db/
├── _mdb_catalog.wt       # 元数据目录
├── collection-0-*.wt     # 集合数据文件
├── index-0-*.wt          # 索引文件
├── journal/              # 预写日志(WAL)
│   ├── j._0
│   └── lsn
└── mongod.lock           # 锁文件

关键概念

  • 存储引擎:WiredTiger(默认)和In-Memory
  • Journal日志:预写日志,确保崩溃恢复
  • Oplog:操作日志,用于复制和增量备份

2. 备份类型概述

MongoDB支持多种备份方式,每种都有其适用场景:

备份类型 优点 缺点 适用场景
全量备份 简单可靠,恢复快 耗时,占用存储 定期备份,小数据量
增量备份 节省空间,快速 恢复复杂,依赖基础备份 大数据量,频繁备份
增量日志 实时性好 需要持续写入 高可用要求
逻辑备份 跨版本兼容 性能开销大 数据迁移,跨平台

全量备份策略

1. mongodump工具详解

mongodump是MongoDB官方提供的逻辑备份工具,它通过连接到MongoDB实例并导出BSON格式的数据来实现备份。

基本用法

# 备份整个数据库
mongodump --host localhost --port 27017 --out /backup/mongodb/full_$(date +%Y%m%d)

# 备份指定数据库
mongodump --db myapp --out /backup/myapp_$(date +%Y%m%d)

# 备份指定集合
mongodump --db myapp --collection users --out /backup/users_$(date +%Y%m%d)

# 带认证的备份
mongodump --host localhost --port 27017 \
  --username backupuser --password "securepass" \
  --authenticationDatabase admin \
  --out /backup/mongodb/full_$(date +%Y%m%d)

高级选项

# 压缩备份(节省存储空间)
mongodump --gzip --out /backup/mongodb/compressed

# 查询过滤(备份部分数据)
mongodump --db myapp --query '{ "created_at": { "$gte": { "$date": "2024-01-01T00:00:00Z" } } }'

# 并行导出(提高性能)
mongodump --numParallelCollections=4

# 排除系统数据库
mongodump --excludeDatabase=admin --excludeDatabase=local

2. 文件系统快照备份

文件系统快照提供了一种物理备份方式,可以实现几乎瞬时的备份,特别适合大型数据库。

LVM快照示例(Linux):

#!/bin/bash
# MongoDB LVM快照备份脚本

MONGO_DATA="/var/lib/mongodb"
SNAPSHOT_SIZE="10G"
BACKUP_DIR="/backup/mongodb/snapshots"
DATE=$(date +%Y%m%d_%H%M%S)

# 1. 刷新并锁定数据库(可选,确保一致性)
mongo --eval "db.fsyncLock()"

# 2. 创建LVM快照
lvcreate --size $SNAPSHOT_SIZE --snapshot --name mongo_snap_$DATE $MONGO_DATA

# 3. 解锁数据库
mongo --eval "db.fsyncUnlock()"

# 4. 挂载快照
mkdir -p /mnt/mongo_snapshot
mount /dev/vg0/mongo_snap_$DATE /mnt/mongo_snapshot

# 5. 复制数据到备份目录
rsync -av /mnt/mongo_snapshot/ $BACKUP_DIR/full_$DATE/

# 6. 清理
umount /mnt/mongo_snapshot
lvremove -f /dev/vg0/mongo_snap_$DATE

echo "Backup completed: $BACKUP_DIR/full_$DATE"

AWS EBS快照示例

#!/bin/bash
# AWS EBS快照备份脚本

INSTANCE_ID="i-0abcd1234efgh5678"
VOLUME_ID="vol-0123456789abcdef0"
BACKUP_DIR="/backup/mongodb/ebs"
DATE=$(date +%Y%m%d_%H%M%S)

# 创建快照
SNAPSHOT_ID=$(aws ec2 create-snapshot \
  --volume-id $VOLUME_ID \
  --description "MongoDB backup $DATE" \
  --query 'SnapshotId' \
  --output text)

# 等待快照完成
aws ec2 wait snapshot-completed --snapshot-ids $SNAPSHOT_ID

# 记录快照信息
echo "$SNAPSHOT_ID $DATE" >> $BACKUP_DIR/snapshots.log

echo "EBS snapshot completed: $SNAPSHOT_ID"

3. 副本集全量备份最佳实践

对于副本集,推荐使用Secondary节点进行备份,避免影响Primary节点的性能。

备份脚本示例

#!/bin/bash
# 副本集备份脚本

REPLSET="rs0"
SECONDARY="mongodb-secondary.example.com:27017"
BACKUP_DIR="/backup/mongodb/replset"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=7

# 1. 检查节点状态
PRIMARY=$(mongo --host $SECONDARY --eval "
  rs.isMaster().ismaster" --quiet)

if [ "$PRIMARY" = "true" ]; then
  echo "Error: Connected to primary, aborting"
  exit 1
fi

# 2. 执行备份
mongodump --host $SECONDARY \
  --oplog \
  --out $BACKUP_DIR/full_$DATE

# 3. 验证备份
if [ $? -eq 0 ]; then
  # 4. 清理旧备份
  find $BACKUP_DIR -type d -mtime +$RETENTION_DAYS -exec rm -rf {} \;
  
  echo "Backup completed successfully: $BACKUP_DIR/full_$DATE"
else
  echo "Backup failed"
  exit 1
fi

增量备份策略

1. 基于Oplog的增量备份

MongoDB的Oplog(操作日志)记录了所有数据变更操作,是实现增量备份的基础。

Oplog结构

{
  "ts": Timestamp(1640995200, 1),  // 时间戳
  "h": NumberLong("-123456789"),    // 哈希值
  "v": 2,                          // 操作版本
  "op": "i",                       // 操作类型:i=insert, u=update, d=delete
  "ns": "myapp.users",             // 命名空间(数据库.集合)
  "o": { "_id": ObjectId("..."), "name": "John" }  // 操作对象
}

增量备份脚本

#!/bin/bash
# MongoDB增量备份脚本

BACKUP_DIR="/backup/mongodb/incremental"
BASE_BACKUP_DIR="/backup/mongodb/base"
DATE=$(date +%Y%m%d_%H%M%S)
LAST_BACKUP_FILE="$BACKUP_DIR/last_backup.txt"

# 1. 获取上次备份的时间戳
if [ -f "$LAST_BACKUP_FILE" ]; then
  LAST_TS=$(cat $LAST_BACKUP_FILE)
else
  # 如果没有上次备份,使用基础备份的时间戳
  LAST_TS=$(mongo --eval "db.adminCommand({getCmdLineOpts: 1}).parsed.replication.oplogSize" --quiet)
fi

# 2. 获取当前Oplog状态
CURRENT_TS=$(mongo --eval "db.getReplicationInfo().lastOpTimeEntry" --quiet)

# 3. 导出Oplog
mongodump --host localhost --port 27017 \
  --db local --collection oplog.rs \
  --query "{ ts: { \$gte: Timestamp($LAST_TS, 1) } }" \
  --out $BACKUP_DIR/oplog_$DATE

# 4. 记录当前时间戳
echo $CURRENT_TS > $LAST_BACKUP_FILE

echo "Incremental backup completed: $BACKUP_DIR/oplog_$DATE"

2. 基于WiredTiger检查点的增量备份

WiredTiger存储引擎支持增量备份,通过比较检查点来识别变化的数据块。

WiredTiger增量备份原理

  • 每次检查点(Checkpoint)会标记数据文件的最新状态
  • 增量备份只复制自上次检查点以来变化的块
  • 需要基础备份作为恢复起点

实现示例

#!/bin/bash
# WiredTiger增量备份

MONGO_DATA="/var/lib/mongodb"
BACKUP_DIR="/backup/mongodb/wt_incremental"
DATE=$(date +%Y%m%d_%H%M%S)

# 1. 执行WiredTiger增量备份
# 注意:需要MongoDB 4.2+ 和WiredTiger引擎
mongodump --host localhost --port 27017 \
  --oplog \
  --gzip \
  --archive=$BACKUP_DIR/incremental_$DATE.archive

# 2. 记录备份元数据
echo "{
  \"timestamp\": \"$DATE\",
  \"type\": \"incremental\",
  \"base_backup\": \"$(ls -t $BACKUP_DIR/base_* | head -1)\"
}" > $BACKUP_DIR/meta_$DATE.json

echo "WiredTiger incremental backup completed"

3. 增量恢复实战

增量备份的恢复需要按顺序应用所有增量备份。

恢复脚本示例

#!/bin/bash
# MongoDB增量恢复脚本

BASE_BACKUP="/backup/mongodb/base_20240101"
INCREMENTAL_DIR="/backup/mongodb/incremental"
RESTORE_DIR="/var/lib/mongodb_restore"

# 1. 恢复基础备份
mongorestore --host localhost --port 27017 \
  --dir $BASE_BACKUP \
  --drop

# 2. 按时间顺序应用增量备份
for OPLOG in $(ls -t $INCREMENTAL_DIR/oplog_*); do
  echo "Applying $OPLOG"
  mongorestore --host localhost --port 27017 \
    --oplogReplay \
    --oplogLimit=1640995200:1 \
    --archive=$OPLOG
done

echo "Restore completed"

高级备份策略

1. 分片集群备份

分片集群的备份需要协调多个组件,确保数据一致性。

分片集群备份步骤

  1. 锁定Config服务器(元数据)
  2. 备份Config服务器
  3. 备份每个分片
  4. 解锁Config服务器

分片集群备份脚本

#!/bin/bash
# 分片集群备份脚本

CONFIG_SERVER="config1.example.com:27017"
SHARDS=("shard1.example.com:27017" "shard2.example.com:27017")
BACKUP_DIR="/backup/mongodb/sharded"
DATE=$(date +%Y%m%d_%H%M%S)

# 1. 锁定Config服务器(元数据)
mongo --host $CONFIG_SERVER --eval "db.fsyncLock()"

# 2. 备份Config服务器
mongodump --host $CONFIG_SERVER \
  --out $BACKUP_DIR/config_$DATE

# 3. 备份每个分片(并行)
for SHARD in "${SHARDS[@]}"; do
  mongodump --host $SHARD \
    --oplog \
    --out $BACKUP_DIR/shard_${SHARD}_$DATE &
done
wait

# 4. 解锁Config服务器
mongo --host $CONFIG_SERVER --eval "db.fsyncUnlock()"

# 5. 记录备份信息
echo "{
  \"config_backup\": \"config_$DATE\",
  \"shard_backups\": [\"shard1_$DATE\", \"shard2_$DATE\"],
  \"timestamp\": \"$DATE\"
}" > $BACKUP_DIR/backup_info_$DATE.json

echo "Sharded cluster backup completed"

2. 热备份与最小停机时间

对于生产环境,需要实现热备份以最小化对业务的影响。

热备份策略

  • 使用Secondary节点进行备份
  • 采用文件系统快照(瞬时完成)
  • 使用Oplog实现时间点恢复

热备份脚本

#!/bin/bash
# MongoDB热备份脚本(无停机)

SECONDARY="mongodb-secondary.example.com:27017"
BACKUP_DIR="/backup/mongodb/hot"
DATE=$(date +%Y%m%d_%H%M%S)

# 1. 检查Secondary延迟
DELAY=$(mongo --host $SECONDARY --eval "
  rs.status().members.find(m => m.name === '$SECONDARY').optimeDate" --quiet)

# 2. 如果延迟过大,等待或警告
if [ "$DELAY" -gt 300 ]; then
  echo "Warning: Secondary lag is $DELAY seconds"
fi

# 3. 执行备份(使用--oplog确保一致性)
mongodump --host $SECONDARY \
  --oplog \
  --gzip \
  --archive=$BACKUP_DIR/hot_$DATE.archive

# 4. 验证备份完整性
mongorestore --host $SECONDARY \
  --archive=$BACKUP_DIR/hot_$DATE.archive \
  --dryRun

echo "Hot backup completed: $BACKUP_DIR/hot_$DATE.archive"

3. 云原生备份方案

现代云平台提供了专门的备份服务,可以简化备份管理。

AWS Backup for MongoDB

# AWS Backup计划配置
BackupPlan:
  BackupPlanName: MongoDB-Backup-Plan
  Rules:
    - RuleName: Daily-Backup
      TargetBackupVaultName: MongoDB-Vault
      ScheduleExpression: "cron(0 2 * * ? *)"
      Lifecycle:
        DeleteAfterDays: 30
      CopyActions:
        - DestinationBackupVaultArn: arn:aws:backup:us-east-1:123456789012:backup-vault:cross-region-vault
          Lifecycle:
            DeleteAfterDays: 90

MongoDB Atlas备份

// Atlas API创建备份
const axios = require('axios');

const config = {
  url: 'https://cloud.mongodb.com/api/atlas/v1.0/groups/{groupId}/clusters/{clusterName}/backup/snapshots',
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ' + process.env.ATLAS_API_KEY
  },
  data: {
    "retentionInDays": 7
  }
};

axios(config)
  .then(response => {
    console.log('Snapshot created:', response.data.id);
  })
  .catch(error => {
    console.error('Error:', error.response.data);
  });

备份自动化与监控

1. 自动化备份脚本

完整的自动化备份系统

#!/bin/bash
# MongoDB自动化备份系统

# 配置
BACKUP_DIR="/backup/mongodb"
LOG_FILE="/var/log/mongodb_backup.log"
RETENTION_DAYS=7
NOTIFY_EMAIL="admin@example.com"

# 日志函数
log() {
  echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a $LOG_FILE
}

# 错误处理
error_exit() {
  log "ERROR: $1"
  echo "Backup failed" | mail -s "MongoDB Backup Failed" $NOTIFY_EMAIL
  exit 1
}

# 主备份函数
perform_backup() {
  local backup_type=$1
  local date=$(date +%Y%m%d_%H%M%S)
  
  log "Starting $backup_type backup"
  
  case $backup_type in
    "full")
      mongodump --host localhost --port 27017 \
        --oplog \
        --gzip \
        --archive=$BACKUP_DIR/full_$date.archive \
        || error_exit "Full backup failed"
      ;;
    "incremental")
      # 实现增量备份逻辑
      ;;
    *)
      error_exit "Unknown backup type: $backup_type"
      ;;
  esac
  
  log "Backup completed: $BACKUP_DIR/${backup_type}_$date.archive"
}

# 清理旧备份
cleanup_old_backups() {
  log "Cleaning up backups older than $RETENTION_DAYS days"
  find $BACKUP_DIR -type f -mtime +$RETENTION_DAYS -delete
  log "Cleanup completed"
}

# 主流程
main() {
  local backup_type=${1:-"full"}
  
  log "=== MongoDB Backup Started ==="
  
  # 执行备份
  perform_backup $backup_type
  
  # 清理旧备份
  cleanup_old_backups
  
  # 验证备份
  verify_backup
  
  log "=== MongoDB Backup Completed Successfully ==="
}

# 验证备份
verify_backup() {
  local latest_backup=$(ls -t $BACKUP_DIR/*.archive | head -1)
  
  if [ -f "$latest_backup" ]; then
    log "Verifying backup: $latest_backup"
    mongorestore --archive=$latest_backup --dryRun || error_exit "Backup verification failed"
    log "Backup verified successfully"
  else
    error_exit "No backup file found"
  fi
}

# 执行主函数
main "$@"

2. 监控与告警

备份监控脚本

#!/bin/bash
# MongoDB备份监控脚本

# 配置
BACKUP_DIR="/backup/mongodb"
LOG_FILE="/var/log/mongodb_backup.log"
SLACK_WEBHOOK="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"

# 检查最近备份
check_last_backup() {
  local last_backup=$(find $BACKUP_DIR -type f -name "*.archive" -mmin -1440 | head -1)
  
  if [ -z "$last_backup" ]; then
    send_alert "No backup found in last 24 hours"
    return 1
  fi
  
  # 检查备份大小
  local size=$(stat -c%s "$last_backup")
  if [ $size -lt 1000000 ]; then
    send_alert "Backup file too small: $size bytes"
    return 1
  fi
  
  return 0
}

# 发送告警
send_alert() {
  local message=$1
  
  # Slack告警
  curl -X POST -H 'Content-type: application/json' \
    --data "{\"text\":\"MongoDB Backup Alert: $message\"}" \
    $SLACK_WEBHOOK
  
  # 邮件告警
  echo "$message" | mail -s "MongoDB Backup Alert" admin@example.com
}

# 检查MongoDB状态
check_mongodb_status() {
  local status=$(mongo --eval "db.adminCommand({ping: 1}).ok" --quiet)
  
  if [ "$status" != "1" ]; then
    send_alert "MongoDB is not reachable"
    return 1
  fi
  
  return 0
}

# 主监控函数
main() {
  if ! check_mongodb_status; then
    exit 1
  fi
  
  if ! check_last_backup; then
    exit 1
  fi
  
  echo "All backup checks passed"
}

main

3. 使用Cron进行定时备份

Cron表达式示例

# 每天凌晨2点执行全量备份
0 2 * * * /opt/mongodb/scripts/backup.sh full >> /var/log/mongodb_backup.log 2>&1

# 每4小时执行增量备份
0 */4 * * * /opt/mongodb/scripts/backup.sh incremental >> /var/log/mongodb_backup.log 2>&1

# 每周日执行清理
0 3 * * 0 /opt/mongodb/scripts/cleanup.sh >> /var/log/mongodb_backup.log 2>&1

# 每月1号生成备份报告
0 4 1 * * /opt/mongodb/scripts/backup_report.sh >> /var/log/mongodb_backup.log 2>&1

备份恢复实战

1. 全量恢复

全量恢复脚本

#!/bin/bash
# MongoDB全量恢复脚本

BACKUP_FILE="/backup/mongodb/full_20240101_020000.archive"
RESTORE_HOST="localhost"
RESTORE_PORT="27017"
TEMP_DIR="/tmp/mongodb_restore"

# 1. 准备恢复环境
echo "Preparing restore environment..."
mkdir -p $TEMP_DIR

# 2. 验证备份文件
echo "Verifying backup file..."
if [ ! -f "$BACKUP_FILE" ]; then
  echo "Error: Backup file not found: $BACKUP_FILE"
  exit 1
fi

# 3. 停止MongoDB服务(可选,确保数据一致性)
echo "Stopping MongoDB service..."
systemctl stop mongod

# 4. 清理现有数据(危险操作,谨慎执行)
echo "Cleaning existing data..."
rm -rf /var/lib/mongodb/*

# 5. 执行恢复
echo "Starting restore..."
mongorestore --host $RESTORE_HOST --port $RESTORE_PORT \
  --archive=$BACKUP_FILE \
  --gzip \
  --oplogReplay \
  --drop \
  --numParallelCollections=4

# 6. 验证恢复
echo "Verifying restore..."
mongo --eval "db.adminCommand({ping: 1})" --quiet

# 7. 启动MongoDB服务
echo "Starting MongoDB service..."
systemctl start mongod

echo "Restore completed successfully"

2. 时间点恢复(PITR)

时间点恢复允许恢复到特定时间点,对于误操作恢复特别有用。

时间点恢复步骤

  1. 恢复基础全量备份
  2. 应用Oplog直到目标时间点

时间点恢复脚本

#!/bin/bash
# MongoDB时间点恢复脚本

BASE_BACKUP="/backup/mongodb/full_20240101_020000.archive"
OPLOG="/backup/mongodb/oplog_20240101_020000.bson"
TARGET_TIME="2024-01-01T14:30:00Z"
RESTORE_DIR="/var/lib/mongodb_pitr"

# 1. 恢复基础备份
mongorestore --host localhost --port 27017 \
  --archive=$BASE_BACKUP \
  --gzip \
  --drop

# 2. 提取Oplog到目标时间点
mongodump --host localhost --port 27017 \
  --db local --collection oplog.rs \
  --query "{ ts: { \$lte: Timestamp(1641040200, 1) } }" \
  --out /tmp/oplog_pitr

# 3. 应用Oplog
mongorestore --host localhost --port 27017 \
  --oplogReplay \
  --oplogLimit=1641040200:1 \
  /tmp/oplog_pitr/local/oplog.rs.bson

echo "Point-in-time recovery completed to: $TARGET_TIME"

3. 单集合恢复

单集合恢复示例

#!/bin/bash
# 恢复单个集合

BACKUP_DIR="/backup/mongodb/full_20240101_020000"
DB_NAME="myapp"
COLLECTION_NAME="users"

# 1. 恢复指定集合
mongorestore --host localhost --port 27017 \
  --db $DB_NAME \
  --collection $COLLECTION_NAME \
  --drop \
  $BACKUP_DIR/myapp/users.bson

# 2. 重建索引(如果需要)
mongo --eval "
  db.getSiblingDB('$DB_NAME').$COLLECTION_NAME.reIndex()
"

echo "Collection $COLLECTION_NAME restored"

常见问题与解决方案

1. 备份失败问题

问题1:权限不足

# 错误信息:not authorized on admin to execute command { getCmdLineOpts: 1 }

# 解决方案:创建专用备份用户
use admin
db.createUser({
  user: "backupUser",
  pwd: "securePassword",
  roles: [
    { role: "backup", db: "admin" },
    { role: "clusterMonitor", db: "admin" },
    { role: "readAnyDatabase", db: "admin" }
  ]
})

# 使用备份用户执行备份
mongodump --username backupUser --password securePassword \
  --authenticationDatabase admin \
  --out /backup/mongodb

问题2:磁盘空间不足

# 错误信息:No space left on device

# 解决方案:清理旧备份和临时文件
# 1. 查找大文件
find /backup -type f -size +1G -exec ls -lh {} \;

# 2. 清理超过30天的备份
find /backup -type f -mtime +30 -delete

# 3. 压缩旧备份
find /backup -type f -name "*.archive" -mtime +7 -exec gzip {} \;

# 4. 监控磁盘使用
df -h /backup

问题3:网络中断导致备份不完整

# 解决方案:使用--archive参数和重试机制

# 带重试的备份脚本
max_retries=3
retry_count=0

while [ $retry_count -lt $max_retries ]; do
  mongodump --host localhost --port 27017 \
    --archive=/backup/mongodb/full.archive \
    --gzip
  
  if [ $? -eq 0 ]; then
    echo "Backup successful"
    break
  else
    retry_count=$((retry_count + 1))
    echo "Backup failed, retrying ($retry_count/$max_retries)..."
    sleep 60
  fi
done

if [ $retry_count -eq $max_retries ]; then
  echo "Backup failed after $max_retries attempts"
  exit 1
fi

2. 恢复失败问题

问题1:版本不兼容

# 错误信息:BSON version mismatch

# 解决方案:使用兼容版本的mongorestore
# 检查版本
mongodump --version
mongorestore --version

# 如果版本不匹配,使用Docker运行对应版本
docker run --rm -v /backup:/backup \
  mongo:4.4 mongorestore --archive=/backup/full.archive --gzip

问题2:索引重建缓慢

# 解决方案:并行重建索引

# 方法1:使用--numParallelCollections
mongorestore --host localhost --port 27017 \
  --archive=/backup/full.archive \
  --gzip \
  --numParallelCollections=8

# 方法2:恢复后手动重建索引
# 先恢复数据,再重建索引
mongorestore --host localhost --port 27017 \
  --archive=/backup/full.archive \
  --gzip \
  --noIndexRestore

# 然后并行重建索引
mongo --eval "
  dbs = db.adminCommand({listDatabases: 1}).databases;
  dbs.forEach(function(db) {
    if (db.name !== 'admin' && db.name !== 'local' && db.name !== 'config') {
      var colls = db.getSiblingDB(db.name).getCollectionNames();
      colls.forEach(function(coll) {
        if (!coll.startsWith('system.')) {
          db.getSiblingDB(db.name)[coll].reIndex();
        }
      });
    }
  });
"

问题3:Oplog不足

# 错误信息:oplog too short, cannot do point-in-time recovery

# 解决方案:增加Oplog大小
# 1. 查看当前Oplog大小
db.getReplicationInfo()

# 2. 调整Oplog大小(需要重启)
# 修改mongod.conf
replication:
  oplogSizeMB: 20480  # 20GB

# 或者动态调整(MongoDB 4.0+)
db.adminCommand({
  replSetUpdateOplogSize: 20480
})

3. 性能问题

问题1:备份速度慢

# 解决方案:优化备份参数

# 1. 使用并行导出
mongodump --numParallelCollections=8

# 2. 增加写缓冲区
mongodump --numParallelCollections=8 --gzip

# 3. 从Secondary备份
mongodump --host secondary.example.com:27017

# 4. 使用文件系统快照(瞬时完成)
# 见前面的LVM快照脚本

问题2:恢复时CPU/IO过高

# 解决方案:限制恢复资源使用

# 1. 限制并行度
mongorestore --numParallelCollections=2

# 2. 分批恢复
# 先恢复小集合,再恢复大集合
mongorestore --db small_db --collection small_coll /backup/small.bson
mongorestore --db large_db --collection large_coll /backup/large.bson

# 3. 使用ionice和nice(Linux)
ionice -c2 -n7 mongorestore --archive=/backup/full.archive --gzip
nice -n19 mongorestore --archive=/backup/full.archive --gzip

备份最佳实践总结

1. 备份策略设计原则

3-2-1备份法则

  • 3:至少3份数据副本
  • 2:使用2种不同的存储介质
  • 1:1份异地备份

备份频率建议

  • 全量备份:每周一次(周日凌晨)
  • 增量备份:每天多次(每4小时)
  • 日志备份:实时(Oplog转发)

保留策略

  • 全量备份:保留4周
  • 增量备份:保留7天
  • 日志:保留48小时

2. 安全考虑

备份加密

# 使用GPG加密备份
mongodump --archive=/backup/mongodb/full.archive --gzip | \
  gpg --cipher-algo AES256 --compress-algo 1 --symmetric --output /backup/mongodb/full.archive.gpg

# 解密
gpg --decrypt /backup/mongodb/full.archive.gpg | \
  mongorestore --archive=/dev/stdin --gzip

访问控制

  • 备份存储在专用的、访问受限的服务器上
  • 使用IAM角色和策略限制云存储访问
  • 定期轮换备份凭证

3. 文档与测试

备份文档应包含

  • 备份脚本和配置
  • 恢复步骤和验证方法
  • 联系人和升级路径
  • 故障排除指南

定期测试

# 每月执行一次恢复测试
#!/bin/bash
# 恢复测试脚本

TEST_SERVER="mongodb-test.example.com"
BACKUP_FILE="/backup/mongodb/latest.archive"

# 1. 恢复到测试环境
mongorestore --host $TEST_SERVER --port 27017 \
  --archive=$BACKUP_FILE \
  --gzip

# 2. 验证数据完整性
mongo --host $TEST_SERVER --eval "
  // 检查关键集合
  var collections = ['users', 'orders', 'products'];
  collections.forEach(function(coll) {
    var count = db.getSiblingDB('myapp')[coll].count();
    print(coll + ': ' + count + ' documents');
  });
"

# 3. 运行应用测试
# 执行自动化测试套件
echo "Recovery test completed"

结论

MongoDB备份是一个需要综合考虑数据量、业务需求、恢复时间目标(RTO)和恢复点目标(RPO)的复杂话题。一个完善的备份策略应该包括:

  1. 多层次备份:结合全量、增量和日志备份
  2. 自动化:使用脚本和调度工具实现无人值守
  3. 监控告警:实时监控备份状态和健康度
  4. 定期测试:确保备份可恢复
  5. 安全加固:保护备份数据的安全性

记住,没有经过测试的备份等于没有备份。定期进行恢复演练是确保备份有效性的关键。

随着MongoDB版本的不断演进,新的备份特性(如WiredTiger增量备份、云原生备份服务)也在不断完善。建议定期评估和优化备份策略,以适应业务增长和技术变化。

最后,备份不仅仅是技术问题,更是组织纪律。建立完善的备份管理制度,明确责任分工,才能在真正的灾难面前从容应对。