#!/bin/bash # ============================================ # LinkShare Blog - 数据库恢复脚本 # ============================================ # 用途: 从备份恢复 PostgreSQL 数据库 # 用法: ./scripts/restore.sh [options] # 示例: ./scripts/restore.sh daily_20260328.sql.gz set -e # 配置 DB_CONTAINER="blog-postgres" DB_NAME="linkshare" DB_USER="blog" # 颜色定义 GREEN='\033[0;32m' RED='\033[0;31m' YELLOW='\033[1;33m' NC='\033[0m' log_info() { echo -e "${GREEN}[INFO]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" } # 显示用法 show_usage() { echo "用法: $0 [options]" echo "" echo "参数:" echo " backup_file 备份文件路径 (必需)" echo "" echo "选项:" echo " --drop 恢复前删除现有数据库 (危险!)" echo " --dry-run 仅验证备份文件,不执行恢复" echo " --help 显示此帮助信息" echo "" echo "示例:" echo " $0 daily_20260328.sql.gz # 恢复备份" echo " $0 daily_20260328.sql.gz --drop # 删除并恢复" echo " $0 daily_20260328.sql.gz --dry-run # 验证备份" echo "" echo "可用备份:" ls -1 backups/*.sql.gz 2>/dev/null || echo " (无备份文件)" } # 检查参数 if [ $# -lt 1 ] || [ "$1" = "--help" ]; then show_usage exit 1 fi BACKUP_FILE=$1 shift # 解析选项 DROP_DB=false DRY_RUN=false while [[ $# -gt 0 ]]; do case $1 in --drop) DROP_DB=true shift ;; --dry-run) DRY_RUN=true shift ;; *) log_error "未知选项: $1" show_usage exit 1 ;; esac done # 检查备份文件 if [ ! -f "$BACKUP_FILE" ]; then log_error "备份文件不存在: $BACKUP_FILE" exit 1 fi # 验证备份文件 log_info "验证备份文件..." if gzip -t "$BACKUP_FILE" 2>/dev/null; then log_info "✓ 备份文件完整性检查通过" else log_error "备份文件损坏或不是有效的 gzip 文件" exit 1 fi # dry-run 模式 if [ "$DRY_RUN" = true ]; then log_info "Dry-run 模式: 不执行恢复" log_info "备份信息:" ls -lh "$BACKUP_FILE" gzip -dc "$BACKUP_FILE" | head -n 20 log_info "备份文件前20行预览完成" exit 0 fi # 检查数据库容器 if ! docker ps | grep -q "$DB_CONTAINER"; then log_error "数据库容器 '$DB_CONTAINER' 未运行" exit 1 fi # 警告确认 echo "" log_warn "即将执行数据库恢复!" log_warn "备份文件: $BACKUP_FILE" log_warn "数据库: $DB_NAME" if [ "$DROP_DB" = true ]; then log_warn "警告: 将删除现有数据库!" fi echo "" read -p "确认继续? (yes/no): " CONFIRM if [ "$CONFIRM" != "yes" ]; then log_info "操作已取消" exit 0 fi # 执行恢复 log_info "开始恢复数据库..." if [ "$DROP_DB" = true ]; then log_info "删除现有数据库..." docker exec "$DB_CONTAINER" psql -U "$DB_USER" -c "DROP DATABASE IF EXISTS $DB_NAME;" docker exec "$DB_CONTAINER" psql -U "$DB_USER" -c "CREATE DATABASE $DB_NAME;" log_info "数据库已重建" fi # 恢复数据 if gzip -dc "$BACKUP_FILE" | docker exec -i "$DB_CONTAINER" psql -U "$DB_USER" "$DB_NAME"; then log_info "✓ 数据库恢复成功!" # 验证恢复 log_info "验证恢复结果..." TABLE_COUNT=$(gzip -dc "$BACKUP_FILE" | grep -c "^CREATE TABLE" || echo "0") log_info "备份中发现的表数量: $TABLE_COUNT" ACTUAL_COUNT=$(docker exec "$DB_CONTAINER" psql -U "$DB_USER" -d "$DB_NAME" -t -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='public';" 2>/dev/null | tr -d '[:space:]') log_info "恢复后的表数量: $ACTUAL_COUNT" if [ "$TABLE_COUNT" -eq "$ACTUAL_COUNT" ] || [ "$ACTUAL_COUNT" -gt 0 ]; then log_info "✓ 验证通过" exit 0 else log_warn "表数量不匹配,请手动验证数据完整性" exit 0 fi else log_error "数据库恢复失败" exit 1 fi