PostgreSQL 备份脚本
📋 概述
这个脚本专门用于 PostgreSQL 数据库的备份操作,支持全量备份、增量备份和压缩备份。
🚀 备份脚本
#!/bin/bash
# PostgreSQL 备份脚本
set -euo pipefail
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# 日志函数
log_info() {
echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}
# 环境变量配置
DB_HOST="${DB_HOST:-localhost}"
DB_PORT="${DB_PORT:-5432}"
DB_NAME="${DB_NAME:-postgres}"
DB_USER="${DB_USER:-postgres}"
DB_PASSWORD="${DB_PASSWORD:-}"
BACKUP_DIR="${BACKUP_DIR:-/backup/postgres}"
BACKUP_RETENTION_DAYS="${BACKUP_RETENTION_DAYS:-7}"
COMPRESS_BACKUP="${COMPRESS_BACKUP:-true}"
BACKUP_TYPE="${BACKUP_TYPE:-full}" # full, schema, data
# 检查依赖
check_dependencies() {
log_info "检查依赖..."
if ! command -v pg_dump &> /dev/null; then
log_error "pg_dump 未安装"
exit 1
fi
if ! command -v psql &> /dev/null; then
log_error "psql 未安装"
exit 1
fi
if [[ "$COMPRESS_BACKUP" == "true" ]] && ! command -v gzip &> /dev/null; then
log_warning "gzip 未安装,将跳过压缩"
COMPRESS_BACKUP=false
fi
log_success "依赖检查完成"
}
# 测试数据库连接
test_connection() {
log_info "测试数据库连接..."
if ! PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -c "SELECT 1;" &> /dev/null; then
log_error "无法连接到数据库"
exit 1
fi
log_success "数据库连接测试通过"
}
# 获取数据库信息
get_db_info() {
log_info "获取数据库信息..."
local db_size=$(PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -t -c "
SELECT pg_size_pretty(pg_database_size('$DB_NAME'));
" | xargs)
local table_count=$(PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -t -c "
SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public';
" | xargs)
log_info "数据库大小: $db_size"
log_info "表数量: $table_count"
}
# 创建备份目录
create_backup_dir() {
log_info "创建备份目录..."
mkdir -p "$BACKUP_DIR"
log_success "备份目录创建完成: $BACKUP_DIR"
}
# 全量备份
perform_full_backup() {
log_info "执行全量备份..."
local timestamp=$(date '+%Y%m%d_%H%M%S')
local backup_file="$BACKUP_DIR/${DB_NAME}_full_${timestamp}.sql"
local pg_dump_args=(
-h "$DB_HOST"
-p "$DB_PORT"
-U "$DB_USER"
-d "$DB_NAME"
--verbose
--clean
--if-exists
--create
--no-owner
--no-privileges
--format=custom
-f "$backup_file"
)
if PGPASSWORD="$DB_PASSWORD" pg_dump "${pg_dump_args[@]}"; then
log_success "全量备份完成: $backup_file"
# 压缩备份文件
if [[ "$COMPRESS_BACKUP" == "true" ]]; then
log_info "压缩备份文件..."
gzip "$backup_file"
backup_file="${backup_file}.gz"
log_success "压缩完成: $backup_file"
fi
echo "$backup_file"
else
log_error "全量备份失败"
exit 1
fi
}
# Schema 备份
perform_schema_backup() {
log_info "执行 Schema 备份..."
local timestamp=$(date '+%Y%m%d_%H%M%S')
local backup_file="$BACKUP_DIR/${DB_NAME}_schema_${timestamp}.sql"
local pg_dump_args=(
-h "$DB_HOST"
-p "$DB_PORT"
-U "$DB_USER"
-d "$DB_NAME"
--verbose
--schema-only
--no-owner
--no-privileges
-f "$backup_file"
)
if PGPASSWORD="$DB_PASSWORD" pg_dump "${pg_dump_args[@]}"; then
log_success "Schema 备份完成: $backup_file"
# 压缩备份文件
if [[ "$COMPRESS_BACKUP" == "true" ]]; then
log_info "压缩备份文件..."
gzip "$backup_file"
backup_file="${backup_file}.gz"
log_success "压缩完成: $backup_file"
fi
echo "$backup_file"
else
log_error "Schema 备份失败"
exit 1
fi
}
# 数据备份
perform_data_backup() {
log_info "执行数据备份..."
local timestamp=$(date '+%Y%m%d_%H%M%S')
local backup_file="$BACKUP_DIR/${DB_NAME}_data_${timestamp}.sql"
local pg_dump_args=(
-h "$DB_HOST"
-p "$DB_PORT"
-U "$DB_USER"
-d "$DB_NAME"
--verbose
--data-only
--no-owner
--no-privileges
-f "$backup_file"
)
if PGPASSWORD="$DB_PASSWORD" pg_dump "${pg_dump_args[@]}"; then
log_success "数据备份完成: $backup_file"
# 压缩备份文件
if [[ "$COMPRESS_BACKUP" == "true" ]]; then
log_info "压缩备份文件..."
gzip "$backup_file"
backup_file="${backup_file}.gz"
log_success "压缩完成: $backup_file"
fi
echo "$backup_file"
else
log_error "数据备份失败"
exit 1
fi
}
# 增量备份(基于 WAL)
perform_incremental_backup() {
log_info "执行增量备份..."
# 检查是否启用了 WAL 归档
local wal_archive_mode=$(PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -t -c "
SHOW wal_level;
" | xargs)
if [[ "$wal_archive_mode" != "replica" && "$wal_archive_mode" != "logical" ]]; then
log_warning "WAL 级别不是 replica 或 logical,无法执行增量备份"
log_info "建议在 postgresql.conf 中设置: wal_level = replica"
return 1
fi
# 执行 pg_basebackup
local timestamp=$(date '+%Y%m%d_%H%M%S')
local backup_dir="$BACKUP_DIR/${DB_NAME}_incremental_${timestamp}"
if PGPASSWORD="$DB_PASSWORD" pg_basebackup \
-h "$DB_HOST" \
-p "$DB_PORT" \
-U "$DB_USER" \
-D "$backup_dir" \
--verbose \
--progress; then
log_success "增量备份完成: $backup_dir"
echo "$backup_dir"
else
log_error "增量备份失败"
exit 1
fi
}
# 验证备份文件
verify_backup() {
local backup_file=$1
log_info "验证备份文件: $backup_file"
if [[ ! -f "$backup_file" ]]; then
log_error "备份文件不存在"
return 1
fi
# 检查文件大小
local file_size=$(du -h "$backup_file" | cut -f1)
log_info "备份文件大小: $file_size"
# 如果是压缩文件,检查完整性
if [[ "$backup_file" == *.gz ]]; then
if gzip -t "$backup_file"; then
log_success "压缩文件完整性验证通过"
else
log_error "压缩文件损坏"
return 1
fi
fi
log_success "备份文件验证通过"
}
# 清理旧备份
cleanup_old_backups() {
log_info "清理旧备份文件..."
local deleted_count=0
# 清理 SQL 备份文件
if [[ -d "$BACKUP_DIR" ]]; then
deleted_count=$(find "$BACKUP_DIR" -name "*.sql*" -mtime +"$BACKUP_RETENTION_DAYS" | wc -l)
find "$BACKUP_DIR" -name "*.sql*" -mtime +"$BACKUP_RETENTION_DAYS" -delete
fi
# 清理增量备份目录
if [[ -d "$BACKUP_DIR" ]]; then
local incremental_deleted=$(find "$BACKUP_DIR" -name "*_incremental_*" -type d -mtime +"$BACKUP_RETENTION_DAYS" | wc -l)
find "$BACKUP_DIR" -name "*_incremental_*" -type d -mtime +"$BACKUP_RETENTION_DAYS" -exec rm -rf {} +
deleted_count=$((deleted_count + incremental_deleted))
fi
log_success "清理完成,删除了 $deleted_count 个旧备份文件"
}
# 生成备份报告
generate_backup_report() {
local backup_file=$1
log_info "生成备份报告..."
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
local report_file="$BACKUP_DIR/backup_report_$(date '+%Y%m%d').txt"
cat >> "$report_file" << EOF
=== PostgreSQL 备份报告 ===
备份时间: $timestamp
数据库: $DB_NAME
主机: $DB_HOST:$DB_PORT
备份类型: $BACKUP_TYPE
备份文件: $backup_file
文件大小: $(du -h "$backup_file" | cut -f1)
压缩: $COMPRESS_BACKUP
EOF
log_success "备份报告已生成: $report_file"
}
# 主函数
main() {
log_info "开始 PostgreSQL 备份..."
check_dependencies
test_connection
get_db_info
create_backup_dir
local backup_file=""
case "$BACKUP_TYPE" in
"full")
backup_file=$(perform_full_backup)
;;
"schema")
backup_file=$(perform_schema_backup)
;;
"data")
backup_file=$(perform_data_backup)
;;
"incremental")
backup_file=$(perform_incremental_backup)
;;
*)
log_error "不支持的备份类型: $BACKUP_TYPE"
log_info "支持的备份类型: full, schema, data, incremental"
exit 1
;;
esac
# 验证备份
if [[ -n "$backup_file" ]]; then
verify_backup "$backup_file"
generate_backup_report "$backup_file"
fi
# 清理旧备份
cleanup_old_backups
log_success "PostgreSQL 备份完成!"
}
# 脚本入口
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi
🔧 使用方法
1. 环境变量配置
# 数据库配置
export DB_HOST="localhost"
export DB_PORT="5432"
export DB_NAME="my_database"
export DB_USER="postgres"
export DB_PASSWORD="password"
# 备份配置
export BACKUP_DIR="/backup/postgres"
export BACKUP_RETENTION_DAYS="7"
export COMPRESS_BACKUP="true"
export BACKUP_TYPE="full" # full, schema, data, incremental
2. 执行备份
# 全量备份
export BACKUP_TYPE="full"
./postgres-backup.sh
# Schema 备份
export BACKUP_TYPE="schema"
./postgres-backup.sh
# 数据备份
export BACKUP_TYPE="data"
./postgres-backup.sh
# 增量备份
export BACKUP_TYPE="incremental"
./postgres-backup.sh
3. 定时备份
# 每天凌晨2点执行全量备份
0 2 * * * /path/to/postgres-backup.sh >> /var/log/postgres-backup.log 2>&1
# 每小时执行增量备份
0 * * * * export BACKUP_TYPE="incremental" && /path/to/postgres-backup.sh >> /var/log/postgres-backup.log 2>&1
📊 备份类型说明
备份类型 | 说明 | 适用场景 |
---|---|---|
full |
全量备份,包含所有数据和结构 | 完整备份,灾难恢复 |
schema |
仅备份数据库结构 | 部署新环境,结构迁移 |
data |
仅备份数据,不包含结构 | 数据迁移,数据恢复 |
incremental |
基于 WAL 的增量备份 | 频繁备份,最小化数据丢失 |