引言:为什么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备份策略需要根据业务需求、数据规模、恢复时间要求等因素综合考虑。关键要点包括:
- 分层策略:全量备份+增量备份是最佳实践
- 定期测试:备份必须定期验证和恢复测试
- 监控告警:建立完善的备份监控体系
- 安全考虑:加密、权限控制、传输安全
- 文档化:详细记录备份策略和恢复流程
通过本文提供的脚本和策略,您可以构建一个可靠、高效、安全的MongoDB备份体系,确保在任何灾难场景下都能快速恢复业务数据。记住,没有经过测试的备份等于没有备份!
