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

在现代应用架构中,MongoDB作为领先的NoSQL数据库,承载着大量关键业务数据。想象一下,如果您的电商平台突然崩溃,用户订单数据丢失,或者金融系统中的交易记录无法恢复,后果将是灾难性的。这就是为什么制定完善的MongoDB备份策略不是可选项,而是必需品。

MongoDB备份不仅仅是简单的数据复制,它需要考虑数据一致性、备份性能、恢复时间点目标(RTO)和恢复点目标(RPO)等多个维度。一个优秀的备份策略应该能够在数据损坏、硬件故障、人为误操作等场景下快速恢复业务,同时平衡存储成本和运维复杂度。

本文将深入探讨MongoDB的全量备份、增量备份、差异备份等策略,提供详细的实战代码示例,并分享常见问题的解决方案,帮助您构建企业级的MongoDB数据保护体系。

MongoDB备份基础概念

MongoDB数据存储原理

理解MongoDB备份首先要了解其数据存储机制。MongoDB使用MMAPv1或WiredTiger存储引擎(WiredTiger是现代版本的默认引擎),数据文件以特定格式存储在磁盘上:

  • 数据文件/data/db/collection-name.ns/data/db/collection-name.<number>
  • Journal日志:预写日志,确保崩溃恢复
  • Oplog:操作日志,用于复制集和增量备份

备份类型概述

MongoDB支持多种备份方式:

  • 文件系统快照:利用LVM、EBS快照等
  • mongodump/mongorestore:逻辑备份工具
  • MongoDB Ops Manager/Cloud Manager:官方企业级工具
  • Percona Backup for MongoDB:开源物理备份工具

全量备份策略

1. 使用mongodump进行逻辑备份

mongodump是最常用的MongoDB备份工具,它导出BSON格式的数据,适合小型到中型数据库。

基本使用示例

# 备份单个数据库
mongodump --host localhost --port 27017 --db myapp --out /backup/mongodb/myapp_$(date +%Y%m%d_%H%M%S)

# 备份所有数据库
mongodump --host localhost --port 27017 --out /backup/mongodb/full_$(date +%Y%m%d_%H%M%S)

# 带认证的备份
mongodump --host localhost --port 27017 --username backupuser --password 'SecurePass123!' --authenticationDatabase admin --db myapp --out /backup/mongodb/

# 压缩备份(使用gzip)
mongodump --host localhost --port 27017 --db myapp --gzip --out /backup/mongodb/myapp_compressed_$(date +%Y%m%d_%H%M%S)

生产环境完整备份脚本

#!/bin/bash
# MongoDB全量备份脚本 - 生产环境版本

# 配置变量
BACKUP_BASE_DIR="/backup/mongodb"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="${BACKUP_BASE_DIR}/${DATE}"
MONGO_HOST="localhost"
MONGO_PORT="27017"
MONGO_USER="backupuser"
MONGO_PASS="SecurePass123!"
RETENTION_DAYS=7

# 创建备份目录
mkdir -p "${BACKUP_DIR}"

# 检查备份目录创建是否成功
if [ $? -ne 0 ]; then
    echo "[$(date)] ERROR: 无法创建备份目录 ${BACKUP_DIR}" | tee -a "${BACKUP_BASE_DIR}/backup.log"
    exit 1
fi

# 执行备份
echo "[$(date)] 开始MongoDB全量备份..." | tee -a "${BACKUP_BASE_DIR}/backup.log"

mongodump \
    --host "${MONGO_HOST}" \
    --port "${MONGO_PORT}" \
    --username "${MONGO_USER}" \
    --password "${MONGO_PASS}" \
    --authenticationDatabase admin \
    --gzip \
    --out "${BACKUP_DIR}" 2>&1 | tee -a "${BACKUP_BASE_DIR}/backup.log"

# 检查备份是否成功
if [ ${PIPESTATUS[0]} -eq 0 ]; then
    echo "[$(date)] 备份成功: ${BACKUP_DIR}" | tee -a "${BACKUP_BASE_DIR}/backup.log"
    
    # 清理旧备份
    find "${BACKUP_BASE_DIR}" -maxdepth 1 -type d -mtime +${RETENTION_DAYS} -exec rm -rf {} \; 2>/dev/null
    
    # 生成备份信息文件
    du -sh "${BACKUP_DIR}" > "${BACKUP_DIR}/backup.info"
    echo "Backup completed at: $(date)" >> "${BACKUP_DIR}/backup.info"
else
    echo "[$(date)] ERROR: 备份失败,请检查日志" | tee -a "${BACKUP_BASE_DIR}/backup.log"
    # 删除失败的备份目录
    rm -rf "${BACKUP_DIR}"
    exit 1
fi

备份验证脚本

#!/bin/bash
# 验证备份完整性

BACKUP_DIR=$1
if [ -z "$BACKUP_DIR" ]; then
    echo "用法: $0 <backup_directory>"
    exit 1
fi

# 检查备份目录是否存在
if [ ! -d "$BACKUP_DIR" ]; then
    echo "ERROR: 备份目录不存在: $BACKUP_DIR"
    exit 1
fi

# 检查关键文件
echo "检查备份文件完整性..."
find "$BACKUP_DIR" -name "*.bson" | while read file; do
    # 检查BSON文件是否可读
    if mongodump --quiet --check --file "$file" 2>/dev/null; then
        echo "✓ $file 有效"
    else
        echo "✗ $file 无效或损坏"
        exit 1
    fi
done

echo "备份验证完成"

2. 使用文件系统快照进行物理备份

对于大型数据库,文件系统快照提供了更快的备份速度和更低的性能影响。

Linux LVM快照备份

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

# 配置
MONGO_DATA_LV="/dev/vg0/mongo_data"
MONGO_DATA_MOUNT="/data/db"
SNAPSHOT_NAME="mongo_snap_$(date +%Y%m%d_%H%M%S)"
SNAPSHOT_SIZE="10G"
BACKUP_BASE="/backup/mongodb/snapshots"

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

# 2. 创建LVM快照
lvcreate --size ${SNAPSHOT_SIZE} --snapshot --name ${SNAPSHOT_NAME} ${MONGO_DATA_LV}

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

# 4. 挂载快照
mkdir -p /mnt/mongo_snapshot
mount /dev/vg0/${SNAPSHOT_NAME} /mnt/mongo_snapshot

# 5. 复制数据文件到备份位置
mkdir -p ${BACKUP_BASE}/${SNAPSHOT_NAME}
rsync -av /mnt/mongo_snapshot/ ${BACKUP_BASE}/${SNAPSHOT_NAME}/

# 6. 卸载并删除快照
umount /mnt/mongo_snapshot
lvremove -f /dev/vg0/${SNAPSHOT_NAME}

echo "快照备份完成: ${BACKUP_BASE}/${SNAPSHOT_NAME}"

AWS EBS快照备份(云环境)

#!/bin/bash
# AWS EBS快照备份MongoDB

# 配置
INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)
MONGO_VOLUME_ID="vol-0abcd1234efgh5678"
REGION="us-east-1"
BACKUP_TAG="MongoDB-Backup"

# 1. 刷新数据到磁盘
mongo admin --eval "db.fsyncLock()"

# 2. 创建EBS快照
SNAPSHOT_ID=$(aws ec2 create-snapshot \
    --volume-id ${MONGO_VOLUME_ID} \
    --description "MongoDB Backup $(date)" \
    --region ${REGION} \
    --query 'SnapshotId' \
    --output text)

# 3. 等待快照完成
aws ec2 wait snapshot-completed --snapshot-ids ${SNAPSHOT_ID} --region ${REGION}

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

# 5. 添加标签
aws ec2 create-tags \
    --resources ${SNAPSHOT_ID} \
    --tags Key=Name,Value=${BACKUP_TAG} Key=Date,Value=$(date +%Y-%m-%d) \
    --region ${REGION}

echo "EBS快照创建完成: ${SNAPSHOT_ID}"

3. 使用Percona Backup for MongoDB(企业级物理备份)

Percona Backup for MongoDB支持增量备份和物理备份,适合大型生产环境。

安装和配置

# 安装Percona Backup for MongoDB
wget https://repo.percona.com/apt/percona-release_latest.$(lsb_release -sc)_all.deb
sudo dpkg -i percona-release_latest.$(lsb_release -sc)_all.deb
sudo apt-get update
sudo apt-get install percona-backup-mongodb

# 配置备份存储
cat > /etc/percona-backup-mongodb/config.yaml <<EOF
storage:
  type: s3
  s3:
    region: us-east-1
    bucket: my-mongodb-backups
    prefix: pbm
    credentials:
      access-key-id: AKIAIOSFODNN7EXAMPLE
      secret-access-key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
EOF

执行备份

# 启动备份
pbm backup --type=full --compression=gzip

# 查看备份列表
pbm list

# 恢复备份
pbm restore 2024-01-15T10:30:00Z

增量备份策略

1. 基于Oplog的增量备份

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

Oplog基础概念

Oplog是MongoDB复制集中的特殊集合,存储在local数据库中:

// 查看Oplog信息
use local
db.oplog.rs.find().sort({$natural: -1}).limit(1)

// 查看Oplog大小和状态
db.getReplicationInfo()

增量备份脚本实现

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

# 配置
BACKUP_BASE="/backup/mongodb/incremental"
LAST_BACKUP_FILE="${BACKUP_BASE}/last_backup_timestamp"
MONGO_HOST="localhost"
MONGO_PORT="27017"
OPLOG_NS="local.oplog.rs"

# 获取上次备份时间戳
if [ -f "${LAST_BACKUP_FILE}" ]; then
    LAST_TS=$(cat "${LAST_BACKUP_FILE}")
else
    # 如果是第一次备份,获取一小时前的时间戳
    LAST_TS=$(date -d "1 hour ago" +%s)
fi

# 获取当前Oplog最后时间戳
CURRENT_TS=$(mongo --quiet --eval "db.getSiblingDB('local').oplog.rs.find().sort({\$natural: -1}).limit(1).next().ts.getTime()")

# 创建备份目录
BACKUP_DIR="${BACKUP_BASE}/$(date +%Y%m%d_%H%M%S)"
mkdir -p "${BACKUP_DIR}"

# 导出Oplog增量
mongodump \
    --host "${MONGO_HOST}" \
    --port "${MONGO_PORT}" \
    --db local \
    --collection oplog.rs \
    --query "{ts: {\$gte: Timestamp(${LAST_TS}, 1)}}" \
    --out "${BACKUP_DIR}"

# 保存当前时间戳用于下次备份
echo "${CURRENT_TS}" > "${LAST_BACKUP_FILE}"

echo "增量备份完成: ${BACKUP_DIR}"

2. 基于时间点的增量恢复

恢复脚本实现

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

# 配置
BACKUP_BASE="/backup/mongodb/incremental"
MONGO_HOST="localhost"
MONGO_PORT="27017"

# 选择要恢复的备份
echo "可用的增量备份:"
ls -l "${BACKUP_BASE}" | grep -v "last_backup_timestamp"

read -p "输入要恢复的备份目录名: " BACKUP_NAME
BACKUP_DIR="${BACKUP_BASE}/${BACKUP_NAME}"

if [ ! -d "${BACKUP_DIR}" ]; then
    echo "备份目录不存在: ${BACKUP_DIR}"
    exit 1
fi

# 恢复Oplog
mongorestore \
    --host "${MONGO_HOST}" \
    --port "${MONGO_PORT}" \
    --oplogReplay \
    --oplogLimit="${TIMESTAMP_LIMIT}" \
    "${BACKUP_DIR}"

echo "增量恢复完成"

3. 使用MongoDB Change Streams实现实时增量备份

对于需要近乎实时备份的场景,可以使用Change Streams。

Change Streams备份示例

// Node.js Change Streams备份脚本
const { MongoClient } = require('mongodb');
const fs = require('fs');

class ChangeStreamBackup {
    constructor(uri, backupPath) {
        this.uri = uri;
        this.backupPath = backupPath;
        this.client = null;
    }

    async connect() {
        this.client = await MongoClient.connect(this.uri, {
            useNewUrlParser: true,
            useUnifiedTopology: true
        });
        return this.client;
    }

    async startBackup() {
        const db = this.client.db('myapp');
        const collection = db.collection('users');

        // 创建Change Stream
        const changeStream = collection.watch([
            { $match: { operationType: { $in: ['insert', 'update', 'delete'] } } }
        ]);

        console.log('开始监听数据变更...');

        changeStream.on('change', async (change) => {
            const timestamp = new Date().toISOString();
            const backupFile = `${this.backupPath}/change_${Date.now()}.json`;
            
            const backupData = {
                timestamp: timestamp,
                operation: change.operationType,
                documentKey: change.documentKey,
                fullDocument: change.fullDocument,
                updateDescription: change.updateDescription
            };

            // 写入备份文件
            fs.writeFileSync(backupFile, JSON.stringify(backupData, null, 2));
            console.log(`备份变更: ${change.operationType} - ${change.documentKey._id}`);
        });

        return changeStream;
    }
}

// 使用示例
const backup = new ChangeStreamBackup(
    'mongodb://localhost:27017/myapp',
    '/backup/mongodb/realtime'
);

backup.connect().then(() => {
    backup.startBackup();
});

混合备份策略:全量+增量

1. 综合备份策略设计

最佳实践是采用全量备份与增量备份结合的方式:

  • 每周日:执行全量备份
  • 每天:执行增量备份
  • 保留策略:全量备份保留4周,增量备份保留7天

2. 综合备份脚本

#!/bin/bash
# MongoDB综合备份策略脚本

# 配置
BACKUP_BASE="/backup/mongodb"
FULL_BACKUP_DIR="${BACKUP_BASE}/full"
INC_BACKUP_DIR="${BACKUP_BASE}/incremental"
DAY_OF_WEEK=$(date +%u)  # 1=Monday, 7=Sunday
DATE=$(date +%Y%m%d_%H%M%S)
MONGO_HOST="localhost"
MONGO_PORT="27017"

# 确保目录存在
mkdir -p "${FULL_BACKUP_DIR}" "${INC_BACKUP_DIR}"

# 全量备份函数
perform_full_backup() {
    echo "[$(date)] 执行全量备份..."
    local backup_path="${FULL_BACKUP_DIR}/full_${DATE}"
    
    mongodump \
        --host "${MONGO_HOST}" \
        --port "${MONGO_PORT}" \
        --gzip \
        --out "${backup_path}"
    
    if [ $? -eq 0 ]; then
        echo "[$(date)] 全量备份成功: ${backup_path}"
        # 清理超过30天的全量备份
        find "${FULL_BACKUP_DIR}" -maxdepth 1 -type d -mtime +30 -exec rm -rf {} \;
        return 0
    else
        echo "[$(date)] 全量备份失败"
        return 1
    fi
}

# 增量备份函数
perform_incremental_backup() {
    echo "[$(date)] 执行增量备份..."
    local backup_path="${INC_BACKUP_DIR}/inc_${DATE}"
    
    # 获取上次备份时间戳
    local last_ts_file="${BACKUP_BASE}/last_inc_ts"
    local last_ts
    
    if [ -f "${last_ts_file}" ]; then
        last_ts=$(cat "${last_ts_file}")
    else
        last_ts=$(date -d "1 hour ago" +%s)
    fi
    
    # 导出Oplog
    mongodump \
        --host "${MONGO_HOST}" \
        --port "${MONGO_PORT}" \
        --db local \
        --collection oplog.rs \
        --query "{ts: {\$gte: Timestamp(${last_ts}, 1)}}" \
        --out "${backup_path}"
    
    if [ $? -eq 0 ]; then
        echo "[$(date)] 增量备份成功: ${backup_path}"
        # 更新时间戳
        current_ts=$(mongo --quiet --eval "db.getSiblingDB('local').oplog.rs.find().sort({\$natural: -1}).limit(1).next().ts.getTime()")
        echo "${current_ts}" > "${last_ts_file}"
        
        # 清理超过7天的增量备份
        find "${INC_BACKUP_DIR}" -maxdepth 1 -type d -mtime +7 -exec rm -rf {} \;
        return 0
    else
        echo "[$(date)] 增量备份失败"
        return 1
    fi
}

# 主逻辑
if [ "${DAY_OF_WEEK}" -eq 7 ]; then
    # 周日执行全量备份
    perform_full_backup
else
    # 其他时间执行增量备份
    perform_incremental_backup
fi

备份验证与恢复测试

1. 备份验证脚本

#!/bin/bash
# 备份验证脚本

BACKUP_DIR=$1
if [ -z "$BACKUP_DIR" ]; then
    echo "用法: $0 <backup_directory>"
    exit 1
fi

echo "开始验证备份: ${BACKUP_DIR}"

# 检查备份目录结构
echo "1. 检查目录结构..."
if [ ! -d "${BACKUP_DIR}" ]; then
    echo "ERROR: 备份目录不存在"
    exit 1
fi

# 检查数据库目录
for db_dir in "${BACKUP_DIR}"/*; do
    if [ -d "$db_dir" ]; then
        db_name=$(basename "$db_dir")
        echo "  验证数据库: ${db_name}"
        
        # 检查集合文件
        for coll_file in "${db_dir}"/*.bson; do
            if [ -f "$coll_file" ]; then
                coll_name=$(basename "$coll_file" .bson)
                echo "    检查集合: ${coll_name}"
                
                # 验证BSON文件
                if ! mongodump --quiet --check --file "$coll_file" 2>/dev/null; then
                    echo "    ERROR: ${coll_file} 损坏"
                    exit 1
                fi
                
                # 检查元数据文件
                meta_file="${db_dir}/${coll_name}.metadata.json"
                if [ ! -f "$meta_file" ]; then
                    echo "    WARNING: 缺少元数据文件 ${meta_file}"
                fi
            fi
        done
    fi
done

echo "备份验证通过: ${BACKUP_DIR}"

2. 恢复测试脚本

#!/bin/bash
# MongoDB恢复测试脚本

# 配置
TEST_MONGO_PORT="27018"
TEST_DATA_DIR="/data/test_mongodb"
BACKUP_DIR=$1

if [ -z "$BACKUP_DIR" ]; then
    echo "用法: $0 <backup_directory>"
    exit 1
fi

# 启动测试实例
echo "启动测试MongoDB实例..."
mkdir -p "${TEST_DATA_DIR}"
mongod --port "${TEST_MONGO_PORT}" --dbpath "${TEST_DATA_DIR}" --logpath "${TEST_DATA_DIR}/mongod.log" --fork

# 等待启动
sleep 5

# 恢复备份
echo "恢复备份到测试实例..."
mongorestore \
    --host localhost \
    --port "${TEST_MONGO_PORT}" \
    --gzip \
    --dir "${BACKUP_DIR}"

if [ $? -eq 0 ]; then
    echo "恢复成功,执行数据验证..."
    
    # 连接测试实例进行验证
    mongo --port "${TEST_MONGO_PORT}" --eval "
        dbs = db.adminCommand('listDatabases').databases;
        print('数据库数量: ' + dbs.length);
        dbs.forEach(function(db) {
            print('数据库: ' + db.name + ' 大小: ' + (db.sizeOnDisk/1024/1024).toFixed(2) + ' MB');
        });
    "
    
    echo "恢复测试完成"
else
    echo "恢复失败"
fi

# 清理测试实例
echo "清理测试实例..."
mongod --port "${TEST_MONGO_PORT}" --shutdown
rm -rf "${TEST_DATA_DIR}"

备份自动化与监控

1. 使用Cron定时任务

# 编辑crontab
crontab -e

# 添加以下任务
# 每天凌晨2点执行全量备份(周日)
0 2 * * 0 /scripts/mongodb_full_backup.sh

# 每天凌晨3点执行增量备份(周一至周六)
0 3 * * 1-6 /scripts/mongodb_incremental_backup.sh

# 每小时验证最新备份
0 * * * * /scripts/mongodb_verify_backup.sh $(ls -td /backup/mongodb/full/* | head -1)

# 每周日凌晨4点执行恢复测试
0 4 * * 0 /scripts/mongodb_restore_test.sh $(ls -td /backup/mongodb/full/* | head -1)

2. 备份监控脚本

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

# 配置
BACKUP_BASE="/backup/mongodb"
ALERT_EMAIL="dba@company.com"
LOG_FILE="${BACKUP_BASE}/monitor.log"

# 检查最近24小时的备份
check_recent_backup() {
    local backup_type=$1
    local backup_dir="${BACKUP_BASE}/${backup_type}"
    local cutoff_time=$(date -d "24 hours ago" +%s)
    
    # 查找最新的备份
    latest_backup=$(find "${backup_dir}" -maxdepth 1 -type d -name "*20*" | sort | tail -1)
    
    if [ -z "$latest_backup" ]; then
        echo "ERROR: 未找到${backup_type}备份" | tee -a "${LOG_FILE}"
        return 1
    fi
    
    # 检查备份时间
    backup_time=$(stat -c %Y "${latest_backup}")
    if [ "${backup_time}" -lt "${cutoff_time}" ]; then
        echo "ERROR: ${backup_type}备份过旧: ${latest_backup}" | tee -a "${LOG_FILE}"
        return 1
    fi
    
    # 检查备份大小
    backup_size=$(du -sh "${latest_backup}" | cut -f1)
    echo "OK: ${backup_type}备份正常 - ${latest_backup} (${backup_size})" | tee -a "${LOG_FILE}"
    return 0
}

# 检查磁盘空间
check_disk_space() {
    disk_usage=$(df /backup | tail -1 | awk '{print $5}' | sed 's/%//')
    if [ "${disk_usage}" -gt 80 ]; then
        echo "WARNING: 备份磁盘使用率超过80%: ${disk_usage}%" | tee -a "${LOG_FILE}"
        echo "请清理旧备份或扩容磁盘" | mail -s "MongoDB备份空间告警" "${ALERT_EMAIL}"
        return 1
    fi
    return 0
}

# 主监控逻辑
echo "[$(date)] 开始备份监控检查..." | tee -a "${LOG_FILE}"
check_recent_backup "full"
check_recent_backup "incremental"
check_disk_space
echo "[$(date)] 监控检查完成" | tee -a "${LOG_FILE}"

常见问题与解决方案

1. 备份失败:权限不足

问题描述

$ mongodump --host localhost --port 27017 --db myapp --out /backup/
Error: couldn't connect to server localhost:27017, connection attempt failed: SocketException: connect failed

解决方案

# 1. 创建专用备份用户
mongo admin --eval "
    db.createUser({
        user: 'backupuser',
        pwd: 'SecurePass123!',
        roles: [
            { role: 'backup', db: 'admin' },
            { role: 'clusterMonitor', db: 'admin' },
            { role: 'readAnyDatabase', db: 'admin' }
        ]
    })
"

# 2. 验证用户权限
mongo --port 27017 -u backupuser -p 'SecurePass123!' --authenticationDatabase admin --eval "db.runCommand({connectionStatus: 1})"

2. 备份文件损坏

问题描述

$ mongorestore --gzip --dir /backup/myapp
error: invalid BSONObj size

解决方案

#!/bin/bash
# 修复损坏的备份文件

BACKUP_DIR=$1
TEMP_DIR="/tmp/repaired_backup"

mkdir -p "${TEMP_DIR}"

# 逐个检查并修复BSON文件
for bson_file in "${BACKUP_DIR}"/*.bson; do
    if [ -f "$bson_file" ]; then
        collection_name=$(basename "$bson_file" .bson)
        echo "处理集合: ${collection_name}"
        
        # 使用bsondump尝试读取并重新生成
        bsondump "$bson_file" > "${TEMP_DIR}/${collection_name}.json" 2>/dev/null
        
        if [ $? -eq 0 ]; then
            # 重新导入到临时数据库
            mongoimport --db temp_db --collection "${collection_name}" --file "${TEMP_DIR}/${collection_name}.json"
            
            # 重新导出
            mongodump --db temp_db --collection "${collection_name}" --out "${TEMP_DIR}/repaired"
            
            # 复制修复后的文件
            cp "${TEMP_DIR}/repaired/temp_db/${collection_name}.bson" "${BACKUP_DIR}/"
            cp "${TEMP_DIR}/repaired/temp_db/${collection_name}.metadata.json" "${BACKUP_DIR}/"
            
            echo "✓ ${collection_name} 修复完成"
        else
            echo "✗ ${collection_name} 无法修复"
        fi
    fi
done

# 清理临时文件
rm -rf "${TEMP_DIR}"
mongo --eval "db.getSiblingDB('temp_db').dropDatabase()"

3. 备份性能影响

问题描述:备份期间数据库性能下降明显

解决方案

# 1. 使用--oplog选项确保一致性
mongodump --host localhost --port 27017 --oplog --gzip --out /backup/

# 2. 在从节点备份(复制集环境)
mongodump --host secondary-node --port 27017 --slaveOk --gzip --out /backup/

# 3. 限制备份速度(避免IO饱和)
mongodump --host localhost --port 27017 --gzip --out /backup/ | pv -L 10m

# 4. 使用Percona Backup for MongoDB(物理备份,性能影响更小)
pbm backup --type=full --compression=gzip

4. 恢复时Oplog不足

问题描述

$ mongorestore --oplogReplay --oplogLimit="2024-01-15T10:30:00Z"
error: oplog is too short, cannot reach timestamp

解决方案

# 1. 增加Oplog大小(需要重启)
mongo admin --eval "db.adminCommand({replSetResizeOplog: 1, size: 10240})"

# 2. 从更早的全量备份开始恢复
# 3. 使用--oplogArchived选项指定Oplog文件
mongorestore --oplogReplay --oplogArchived=/backup/oplog.bson /backup/full/

# 4. 手动应用Oplog
mongorestore --oplogReplay --oplogLimit="2024-01-15T10:30:00Z" --dir /backup/full/

5. 备份存储空间不足

问题描述:备份磁盘空间不足

解决方案

#!/bin/bash
# 自动清理旧备份

BACKUP_BASE="/backup/mongodb"
DAYS_TO_KEEP=7

# 清理全量备份(保留最近3个)
cd "${BACKUP_BASE}/full"
ls -t | tail -n +4 | xargs rm -rf

# 清理增量备份
find "${BACKUP_BASE}/incremental" -maxdepth 1 -type d -mtime +${DAYS_TO_KEEP} -exec rm -rf {} \;

# 清理日志文件
find "${BACKUP_BASE}" -name "*.log" -mtime +30 -delete

# 压缩旧备份(超过30天的)
find "${BACKUP_BASE}/full" -maxdepth 1 -type d -mtime +30 -name "*20*" | while read dir; do
    if [ ! -f "${dir}.tar.gz" ]; then
        tar -czf "${dir}.tar.gz" -C "$(dirname "$dir")" "$(basename "$dir")"
        rm -rf "$dir"
    fi
done

高级备份策略

1. 复制集环境备份策略

在复制集环境中,最佳实践是在Secondary节点进行备份:

#!/bin/bash
# 复制集环境备份脚本

# 配置
PRIMARY_HOST="mongodb-primary.example.com:27017"
SECONDARY_HOST="mongodb-secondary.example.com:27017"
BACKUP_BASE="/backup/mongodb"

# 检查节点状态
is_secondary() {
    local node=$1
    local status=$(mongo --host "$node" --quiet --eval "db.isMaster().secondary")
    [ "$status" = "true" ]
}

# 等待Secondary追上Primary
wait_for_sync() {
    local secondary=$1
    echo "等待 ${secondary} 同步完成..."
    
    while true; do
        lag=$(mongo --host "$secondary" --quiet --eval "
            var status = db.isMaster();
            if (status.secondary) {
                var optime = status.optimeDate;
                var now = new Date();
                Math.floor((now - optime) / 1000);
            } else {
                0;
            }
        ")
        
        if [ "$lag" -lt 10 ]; then
            echo "同步完成,延迟: ${lag}秒"
            break
        fi
        
        echo "当前延迟: ${lag}秒,等待中..."
        sleep 10
    done
}

# 在Secondary节点执行备份
perform_backup_on_secondary() {
    wait_for_sync "${SECONDARY_HOST}"
    
    # 使用--slaveOk选项
    mongodump \
        --host "${SECONDARY_HOST}" \
        --slaveOk \
        --gzip \
        --out "${BACKUP_BASE}/$(date +%Y%m%d_%H%M%S)"
}

perform_backup_on_secondary

2. 分片集群备份策略

分片集群备份需要协调多个组件:

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

# 配置
CONFIG_SERVERS="config1.example.com:27019,config2.example.com:27019,config3.example.com:27019"
SHARDS="shard1.example.com:27018,shard2.example.com:27018,shard3.example.com:27018"
MONGOS="mongos.example.com:27017"
BACKUP_BASE="/backup/mongodb/sharded"

# 1. 备份Config Servers
backup_config() {
    echo "备份Config Servers..."
    for config in ${CONFIG_SERVERS//,/ }; do
        mongodump --host "$config" --gzip --out "${BACKUP_BASE}/config_${config}_$(date +%Y%m%d_%H%M%S)" &
    done
    wait
}

# 2. 备份Shards
backup_shards() {
    echo "备份Shards..."
    for shard in ${SHARDS//,/ }; do
        mongodump --host "$shard" --gzip --out "${BACKUP_BASE}/shard_${shard}_$(date +%Y%m%d_%H%M%S)" &
    done
    wait
}

# 3. 备份Metadata
backup_metadata() {
    echo "备份Metadata..."
    mongodump --host "${MONGOS}" --db config --gzip --out "${BACKUP_BASE}/metadata_$(date +%Y%m%d_%H%M%S)"
}

# 执行备份
backup_config
backup_shards
backup_metadata

echo "分片集群备份完成"

备份安全最佳实践

1. 备份加密

#!/bin/bash
# 备份加密脚本

BACKUP_DIR=$1
ENCRYPT_KEY="your-256-bit-encryption-key"

# 使用GPG加密
encrypt_backup() {
    local backup_file="${BACKUP_DIR}.tar.gz"
    tar -czf - "${BACKUP_DIR}" | gpg --cipher-algo AES256 --compress-algo 1 --symmetric --batch --passphrase "${ENCRYPT_KEY}" > "${backup_file}.gpg"
    
    # 删除原始备份
    rm -rf "${BACKUP_DIR}"
    
    echo "备份已加密: ${backup_file}.gpg"
}

# 解密备份
decrypt_backup() {
    local encrypted_file=$1
    local output_dir=$2
    
    gpg --decrypt --batch --passphrase "${ENCRYPT_KEY}" "${encrypted_file}" | tar -xzf - -C "${output_dir}"
    
    echo "备份已解密到: ${output_dir}"
}

2. 备份传输安全

#!/bin/bash
# 安全传输备份到远程存储

BACKUP_FILE=$1
REMOTE_HOST="backup-server.example.com"
REMOTE_PATH="/remote/backup/mongodb"

# 使用rsync over SSH
rsync -avz -e "ssh -i /path/to/ssh-key" "${BACKUP_FILE}" "${REMOTE_HOST}:${REMOTE_PATH}/"

# 或使用scp
scp -i /path/to/ssh-key "${BACKUP_FILE}" "${REMOTE_HOST}:${REMOTE_PATH}/"

# 验证传输完整性
ssh -i /path/to/ssh-key "${REMOTE_HOST}" "md5sum ${REMOTE_PATH}/$(basename "${BACKUP_FILE}")"

总结

MongoDB备份策略需要根据业务需求、数据规模、恢复时间要求等因素综合考虑。关键要点包括:

  1. 分层策略:全量备份+增量备份是最佳实践
  2. 定期测试:备份必须定期验证和恢复测试
  3. 监控告警:建立完善的备份监控体系
  4. 安全考虑:加密、权限控制、传输安全
  5. 文档化:详细记录备份策略和恢复流程

通过本文提供的脚本和策略,您可以构建一个可靠、高效、安全的MongoDB备份体系,确保在任何灾难场景下都能快速恢复业务数据。记住,没有经过测试的备份等于没有备份!