强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

09 - 备份与恢复

09 - 备份与恢复

9.1 备份策略

9.1.1 为什么备份如此重要

风险场景后果备份能否解决
世界文件损坏玩家建筑丢失
恶意破坏(Grief)区域被毁
误操作删除数据丢失
插件数据损坏配置丢失
服务器硬件故障全部数据丢失✅(异地备份)
勒索软件攻击数据被加密✅(离线备份)

9.1.2 备份策略 3-2-1 原则

3 份副本:数据本身 + 2 份备份
2 种介质:本地 + 异地(或不同存储设备)
1 份离线:至少 1 份离线备份(防勒索软件)

9.1.3 备份频率建议

服务器类型建议频率保留时间备份类型
小型私人服每天 1 次7 天全量
中型社区服每 6 小时14 天增量 + 全量
大型公共服每 2 小时30 天增量 + 全量
生产/商业服每小时90 天增量 + 全量 + 异地

9.2 全量备份

9.2.1 简单备份脚本

#!/bin/bash
# simple-backup.sh - 简单全量备份脚本

# ============ 配置 ============
SERVER_DIR="/opt/minecraft/paper"
BACKUP_DIR="/opt/minecraft/backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_NAME="mc_backup_${TIMESTAMP}"
SCREEN_NAME="minecraft"
KEEP_COUNT=10                        # 保留最近 N 个备份

# ============ 初始化 ============
mkdir -p "$BACKUP_DIR"
echo "[$(date)] === 开始备份 ==="

# ============ 通知服务器 ============
screen -S "$SCREEN_NAME" -p 0 -X eval 'stuff "say §e[备份] 正在创建备份,请稍候...\015"'
screen -S "$SCREEN_NAME" -p 0 -X eval 'stuff "save-off\015"'
screen -S "$SCREEN_NAME" -p 0 -X eval 'stuff "save-all\015"'
sleep 5

# ============ 执行备份 ============
echo "[$(date)] 正在备份世界文件..."
tar -czf "${BACKUP_DIR}/${BACKUP_NAME}.tar.gz" \
  -C "$SERVER_DIR" \
  --exclude='cache' \
  --exclude='libraries' \
  --exclude='logs/latest.log' \
  world world_nether world_the_end \
  plugins config \
  server.properties \
  bukkit.yml spigot.yml \
  banned-players.json banned-ips.json \
  ops.json whitelist.json \
  2>/dev/null

# ============ 恢复自动保存 ============
screen -S "$SCREEN_NAME" -p 0 -X eval 'stuff "save-on\015"'

# ============ 检查备份结果 ============
if [ -f "${BACKUP_DIR}/${BACKUP_NAME}.tar.gz" ]; then
    SIZE=$(du -h "${BACKUP_DIR}/${BACKUP_NAME}.tar.gz" | cut -f1)
    echo "[$(date)] 备份成功: ${BACKUP_NAME}.tar.gz (${SIZE})"
    screen -S "$SCREEN_NAME" -p 0 -X eval 'stuff "say §a[备份] 备份完成!\015"'
else
    echo "[$(date)] 备份失败!"
    screen -S "$SCREEN_NAME" -p 0 -X eval 'stuff "say §c[备份] 备份失败!\015"'
    exit 1
fi

# ============ 清理旧备份 ============
echo "[$(date)] 清理旧备份(保留最近 ${KEEP_COUNT} 个)..."
ls -t ${BACKUP_DIR}/mc_backup_*.tar.gz 2>/dev/null | tail -n +$((KEEP_COUNT + 1)) | while read f; do
    echo "[$(date)] 删除旧备份: $(basename $f)"
    rm -f "$f"
done

echo "[$(date)] === 备份完成 ==="

9.2.2 完整服务器备份

#!/bin/bash
# full-backup.sh - 完整服务器备份(包含 JAR 和所有配置)

SERVER_DIR="/opt/minecraft/paper"
BACKUP_DIR="/opt/minecraft/backups/full"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)

mkdir -p "$BACKUP_DIR"

# 备份整个服务器目录(排除缓存和日志)
tar -czf "${BACKUP_DIR}/full_${TIMESTAMP}.tar.gz" \
  -C "$(dirname $SERVER_DIR)" \
  --exclude='*.cache' \
  --exclude='logs/*.log' \
  --exclude='cache' \
  --exclude='libraries' \
  "$(basename $SERVER_DIR)"

echo "完整备份完成: full_${TIMESTAMP}.tar.gz"
ls -lh "${BACKUP_DIR}/full_${TIMESTAMP}.tar.gz"

9.3 增量备份

增量备份仅备份自上次备份后变更的文件,节省空间和时间。

9.3.1 使用 rsync 增量备份

#!/bin/bash
# incremental-backup.sh - 使用 rsync 增量备份

SERVER_DIR="/opt/minecraft/paper"
BACKUP_DIR="/opt/minecraft/backups/incremental"
LATEST_LINK="${BACKUP_DIR}/latest"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)

mkdir -p "$BACKUP_DIR"

# rsync 增量备份(硬链接未变更文件)
rsync -a --delete \
  --link-dest="$LATEST_LINK" \
  --exclude='cache' \
  --exclude='libraries' \
  --exclude='logs/latest.log' \
  "$SERVER_DIR/" \
  "${BACKUP_DIR}/${TIMESTAMP}/"

# 更新 latest 链接
rm -f "$LATEST_LINK"
ln -s "${BACKUP_DIR}/${TIMESTAMP}" "$LATEST_LINK"

echo "增量备份完成: ${TIMESTAMP}"
echo "备份大小: $(du -sh ${BACKUP_DIR}/${TIMESTAMP} | cut -f1)"

9.3.2 使用 restic 增量备份

# 安装 restic
sudo apt install restic    # Debian/Ubuntu
sudo dnf install restic    # Fedora

# 初始化备份仓库
restic init --repo /opt/minecraft/backups/restic

# 执行增量备份
restic backup /opt/minecraft/paper \
  --repo /opt/minecraft/backups/restic \
  --exclude cache \
  --exclude libraries \
  --tag minecraft

# 查看备份列表
restic snapshots --repo /opt/minecraft/backups/restic

# 恢复到指定时间点
restic restore latest \
  --repo /opt/minecraft/backups/restic \
  --target /opt/minecraft/paper-restored

# 设置保留策略
restic forget \
  --repo /opt/minecraft/backups/restic \
  --keep-daily 7 \
  --keep-weekly 4 \
  --keep-monthly 6 \
  --prune

9.4 WorldEdit 区域还原

对于局部破坏(如熊孩子破坏),WorldEdit 的还原比全量备份恢复更精准。

9.4.1 WorldEdit 备份与还原

# 1. 选中要保护的区域
//pos1                  # 选择第一个角
//pos2                  # 选择第二个角

# 2. 创建快照(保存到文件)
//save my_backup        # 保存为 my_backup.schematic

# 3. 还原区域
//load my_backup        # 加载快照
//paste                 # 粘贴到当前位置

# 4. 还原并匹配空气
//paste -a              # 粘贴(包含空气方块)

9.4.2 CoreProtect 区域回滚

CoreProtect 是回滚玩家操作的首选工具:

# 查看 50 格范围内最近 24 小时的操作
/co lookup u:Griefer t:24h r:50

# 回滚特定玩家的操作
/co rollback u:Griefer t:24h r:50

# 回滚方块破坏
/co rollback u:Griefer t:24h r:50 a:break

# 回滚方块放置
/co rollback u:Griefer t:24h r:50 a:place

# 回滚容器操作(拿取物品)
/co rollback u:Griefer t:24h r:50 a:container

# 恢复回滚(如果回滚错误)
/co restore u:Griefer t:24h r:50

# 查看回滚结果
/co lookup u:Griefer t:24h r:50 a:+break,-place

9.4.3 CoreProtect 操作查询

# 检查特定方块
/co inspect                  # 开启检查模式
# 然后点击方块查看操作历史

# 查看谁破坏了某个方块
/co lookup t:7d r:5 a:break

# 查看最近的 TNT 爆炸
/co lookup a:explode t:1h r:100

# 查看容器被谁打开过
/co lookup a:container t:24h r:20

9.5 云备份方案

9.5.1 使用 rclone 上传到云存储

# 安装 rclone
curl https://rclone.org/install.sh | sudo bash

# 配置云存储(以 Google Drive 为例)
rclone config
# 按提示选择 Google Drive 并完成授权

# 上传备份
rclone copy /opt/minecraft/backups/mc_backup_latest.tar.gz \
  remote:minecraft-backups/ \
  --progress

# 自动同步
rclone sync /opt/minecraft/backups/ \
  remote:minecraft-backups/ \
  --include "*.tar.gz" \
  --progress

9.5.2 支持的云存储

服务配置命令特点
Google Driverclone config15GB 免费
OneDriverclone config5GB 免费
Dropboxrclone config2GB 免费
S3 (AWS)rclone config按量付费
阿里云 OSSrclone config国内首选
腾讯云 COSrclone config国内备选
坚果云rclone configWebDAV 协议

9.5.3 使用 Duplicity 加密备份

# 安装
sudo apt install duplicity

# 加密备份到云存储
duplicity /opt/minecraft/paper/world \
  s3://s3.amazonaws.com/my-bucket/minecraft-backup \
  --encrypt-key YOUR_GPG_KEY \
  --full-if-older-than 7D

# 恢复
duplicity restore \
  s3://s3.amazonaws.com/my-bucket/minecraft-backup \
  /opt/minecraft/paper/world-restored

9.6 自动备份定时任务

9.6.1 Crontab 配置

# 编辑 crontab
crontab -e

# 每 6 小时全量备份
0 */6 * * * /opt/minecraft/scripts/backup.sh >> /var/log/mc-backup.log 2>&1

# 每天凌晨 2 点做完整备份
0 2 * * * /opt/minecraft/scripts/full-backup.sh >> /var/log/mc-backup.log 2>&1

# 每周日凌晨 3 点上传到云存储
0 3 * * 0 /opt/minecraft/scripts/cloud-upload.sh >> /var/log/mc-backup.log 2>&1

# 每月 1 日清理旧云备份
0 4 1 * * rclone delete remote:minecraft-backups/ --min-age 90d

9.6.2 Systemd Timer(替代 Crontab)

# /etc/systemd/system/mc-backup.timer

[Unit]
Description=Minecraft Backup Timer

[Timer]
OnCalendar=*-*-* */6:00:00
Persistent=true

[Install]
WantedBy=timers.target
# /etc/systemd/system/mc-backup.service

[Unit]
Description=Minecraft Backup Service

[Service]
Type=oneshot
ExecStart=/opt/minecraft/scripts/backup.sh
User=minecraft
sudo systemctl daemon-reload
sudo systemctl enable mc-backup.timer
sudo systemctl start mc-backup.timer

# 查看定时器状态
sudo systemctl list-timers mc-backup.timer

9.7 备份验证

9.7.1 自动验证脚本

#!/bin/bash
# verify-backup.sh - 验证备份完整性

BACKUP_FILE="$1"
VERIFY_DIR="/tmp/mc-backup-verify"

if [ -z "$BACKUP_FILE" ]; then
    echo "用法: $0 <备份文件路径>"
    exit 1
fi

echo "=== 备份验证 ==="
echo "文件: $BACKUP_FILE"

# 1. 检查文件是否存在
if [ ! -f "$BACKUP_FILE" ]; then
    echo "错误: 备份文件不存在"
    exit 1
fi

# 2. 检查文件大小
SIZE=$(stat -c%s "$BACKUP_FILE")
if [ "$SIZE" -lt 1024 ]; then
    echo "错误: 备份文件过小 (${SIZE} bytes)"
    exit 1
fi
echo "文件大小: $(du -h "$BACKUP_FILE" | cut -f1)"

# 3. 验证压缩完整性
if [[ "$BACKUP_FILE" == *.tar.gz ]]; then
    if gzip -t "$BACKUP_FILE" 2>/dev/null; then
        echo "压缩完整性: 通过"
    else
        echo "错误: 压缩文件损坏"
        exit 1
    fi
fi

# 4. 检查关键文件是否存在
rm -rf "$VERIFY_DIR"
mkdir -p "$VERIFY_DIR"
tar -xzf "$BACKUP_FILE" -C "$VERIFY_DIR" 2>/dev/null

MISSING=0
for f in world/level.dat plugins; do
    if [ ! -e "${VERIFY_DIR}/$f" ]; then
        echo "警告: 缺少 $f"
        MISSING=$((MISSING + 1))
    fi
done

if [ "$MISSING" -eq 0 ]; then
    echo "关键文件: 全部存在"
else
    echo "警告: 缺少 ${MISSING} 个关键文件"
fi

# 5. 清理
rm -rf "$VERIFY_DIR"

echo "=== 验证完成 ==="

9.8 备份管理最佳实践

9.8.1 备份存储策略

时间段保留策略存储位置
最近 24 小时每 6 小时保留本地 SSD
最近 7 天每天保留本地 HDD
最近 30 天每周保留NAS/云存储
30 天以上每月保留冷存储/归档

9.8.2 备份清单文档

## 备份清单

| 备份类型 | 频率 | 保留时间 | 存储位置 | 负责人 |
|----------|------|----------|----------|--------|
| 世界增量 | 每 6h | 7 天 | 本地 | 自动脚本 |
| 全量备份 | 每天 | 30 天 | NAS | 自动脚本 |
| 异地备份 | 每周 | 90 天 | 云存储 | 手动触发 |
| 配置备份 | 每次变更 | 永久 | Git | 开发者 |

## 恢复测试

- [ ] 每月执行一次恢复测试
- [ ] 验证恢复后服务器可正常启动
- [ ] 验证玩家数据完整性

9.9 灾难恢复流程

9.9.1 完整恢复步骤

# 1. 停止服务器
sudo systemctl stop minecraft

# 2. 备份当前损坏的数据(以防万一)
mv /opt/minecraft/paper /opt/minecraft/paper_damaged_$(date +%s)

# 3. 从备份恢复
BACKUP_FILE="/opt/minecraft/backups/full/full_20260510.tar.gz"
mkdir -p /opt/minecraft/paper
tar -xzf "$BACKUP_FILE" -C /opt/minecraft/

# 4. 确认权限
chown -R minecraft:minecraft /opt/minecraft/paper

# 5. 启动服务器测试
sudo systemctl start minecraft

# 6. 检查日志
sudo journalctl -u minecraft -f

# 7. 验证服务器状态
# 在游戏内连接并检查世界是否正常

9.9.2 部分恢复(仅恢复世界)

# 1. 停止服务器
sudo systemctl stop minecraft

# 2. 备份当前世界
mv /opt/minecraft/paper/world /opt/minecraft/paper/world_broken

# 3. 从备份中仅提取世界
tar -xzf /opt/minecraft/backups/latest.tar.gz -C /opt/minecraft/paper/ world/

# 4. 启动服务器
sudo systemctl start minecraft

9.10 常见问题

Q1:备份时服务器卡顿?

# 使用 nice 降低备份进程优先级
nice -n 19 /opt/minecraft/scripts/backup.sh

# 使用 ionice 降低 IO 优先级
ionice -c 3 /opt/minecraft/scripts/backup.sh

# 使用 pigz 多线程压缩
tar -cf - world | pigz -p 4 > backup.tar.gz

Q2:备份文件太大?

# 使用 zstd 压缩(比 gzip 更快更小)
tar --zstd -cf backup.tar.zst world/

# 修剪世界中未使用的区块(详见第 04 章)
java -jar mcaselector.jar --mode delete --world world --radius 10000 --center 0,0

# 排除不必要的文件
tar -czf backup.tar.gz --exclude='world/region/r.10.*' world/

Q3:如何恢复被删除的玩家数据?

# 从 CoreProtect 恢复(如果有记录)
/co restore u:<玩家名> t:7d

# 从备份中提取玩家数据
tar -xzf backup.tar.gz world/playerdata/<uuid>.dat

# 从 LuckPerms 恢复权限
/lp user <玩家名> import

9.11 本章小结

要点说明
遵循 3-2-1 原则3 份副本、2 种介质、1 份离线
定期自动备份Crontab 或 Systemd Timer
增量备份节省空间rsync 或 restic
定期验证备份确保备份可恢复
CoreProtect 用于局部回滚比全量恢复更精准
云备份防灾难异地存储是最后防线

扩展阅读