#!/bin/bash # PostgreSQL 生产环境数据库配置脚本 # 用于初始化生产环境数据库配置、连接池、备份策略等 set -e # 颜色定义 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # 日志函数 log() { echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" } success() { echo -e "${GREEN}[SUCCESS]${NC} $1" } warning() { echo -e "${YELLOW}[WARNING]${NC} $1" } error() { echo -e "${RED}[ERROR]${NC} $1" } # 配置变量 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" CONFIG_DIR="$PROJECT_ROOT/configs" BACKUP_DIR="/var/backups/photography" LOG_DIR="/var/log/photography" # 默认配置 DB_HOST="${DB_HOST:-localhost}" DB_PORT="${DB_PORT:-5432}" DB_NAME="${DB_NAME:-photography}" DB_USER="${DB_USER:-postgres}" DB_PASSWORD="${DB_PASSWORD:-}" DB_POOL_SIZE="${DB_POOL_SIZE:-20}" DB_MAX_CONNECTIONS="${DB_MAX_CONNECTIONS:-100}" # 检查依赖 check_dependencies() { log "检查系统依赖..." local missing_deps=() # 检查 PostgreSQL 客户端 if ! command -v psql &> /dev/null; then missing_deps+=("postgresql-client") fi # 检查 Docker(如果使用容器部署) if ! command -v docker &> /dev/null; then warning "Docker 未安装,跳过容器相关配置" fi # 检查 systemctl if ! command -v systemctl &> /dev/null; then warning "systemctl 未找到,跳过服务配置" fi if [ ${#missing_deps[@]} -ne 0 ]; then error "缺少依赖: ${missing_deps[*]}" echo "请运行以下命令安装依赖:" echo "sudo apt-get update && sudo apt-get install -y ${missing_deps[*]}" exit 1 fi success "依赖检查完成" } # 创建必要目录 create_directories() { log "创建必要目录..." # 创建备份目录 sudo mkdir -p "$BACKUP_DIR" sudo chown -R postgres:postgres "$BACKUP_DIR" sudo chmod 755 "$BACKUP_DIR" # 创建日志目录 sudo mkdir -p "$LOG_DIR" sudo chown -R postgres:postgres "$LOG_DIR" sudo chmod 755 "$LOG_DIR" # 创建配置目录 mkdir -p "$CONFIG_DIR/postgres" success "目录创建完成" } # 测试数据库连接 test_connection() { log "测试数据库连接..." if [ -z "$DB_PASSWORD" ]; then error "数据库密码未设置,请设置 DB_PASSWORD 环境变量" exit 1 fi export PGPASSWORD="$DB_PASSWORD" if psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -c "SELECT version();" &> /dev/null; then success "数据库连接成功" else error "数据库连接失败,请检查配置" exit 1 fi } # 创建数据库配置 create_database_config() { log "创建数据库配置..." # 生产环境配置文件 cat > "$CONFIG_DIR/postgres/postgresql.conf" << EOF # PostgreSQL 生产环境配置 # Photography Portfolio 项目 # 连接设置 listen_addresses = '*' port = $DB_PORT max_connections = $DB_MAX_CONNECTIONS superuser_reserved_connections = 3 # 内存设置 shared_buffers = 256MB effective_cache_size = 1GB work_mem = 4MB maintenance_work_mem = 64MB # WAL 设置 wal_buffers = 16MB checkpoint_completion_target = 0.9 wal_writer_delay = 200ms # 查询规划器 random_page_cost = 1.1 effective_io_concurrency = 200 default_statistics_target = 100 # 日志设置 logging_collector = on log_directory = '$LOG_DIR' log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' log_truncate_on_rotation = on log_rotation_age = 1d log_rotation_size = 100MB log_min_duration_statement = 1000 log_checkpoints = on log_connections = on log_disconnections = on log_lock_waits = on log_temp_files = 10MB # 性能监控 shared_preload_libraries = 'pg_stat_statements' track_activities = on track_counts = on track_io_timing = on track_functions = all # 自动清理 autovacuum = on autovacuum_max_workers = 3 autovacuum_naptime = 1min autovacuum_vacuum_threshold = 50 autovacuum_analyze_threshold = 50 # 时区设置 timezone = 'Asia/Shanghai' log_timezone = 'Asia/Shanghai' # 字符集设置 default_text_search_config = 'pg_catalog.simple' EOF # 创建 pg_hba.conf 配置 cat > "$CONFIG_DIR/postgres/pg_hba.conf" << EOF # PostgreSQL Client Authentication Configuration File # Photography Portfolio 项目 # TYPE DATABASE USER ADDRESS METHOD # 本地连接 local all postgres peer local all all md5 # IPv4 本地连接 host all all 127.0.0.1/32 md5 host all all ::1/128 md5 # 应用连接 host $DB_NAME $DB_USER 0.0.0.0/0 md5 # 复制连接 host replication postgres 127.0.0.1/32 md5 host replication postgres ::1/128 md5 EOF success "数据库配置创建完成" } # 配置连接池 setup_connection_pool() { log "配置数据库连接池..." # 使用 PgBouncer 作为连接池 if command -v pgbouncer &> /dev/null; then cat > "$CONFIG_DIR/postgres/pgbouncer.ini" << EOF [databases] $DB_NAME = host=$DB_HOST port=$DB_PORT dbname=$DB_NAME [pgbouncer] listen_port = 6432 listen_addr = 127.0.0.1 auth_type = md5 auth_file = $CONFIG_DIR/postgres/userlist.txt logfile = $LOG_DIR/pgbouncer.log pidfile = /var/run/pgbouncer/pgbouncer.pid admin_users = postgres stats_users = postgres # 连接池配置 pool_mode = transaction max_client_conn = 100 default_pool_size = $DB_POOL_SIZE min_pool_size = 5 reserve_pool_size = 5 reserve_pool_timeout = 5 server_lifetime = 3600 server_idle_timeout = 600 # 性能调优 server_connect_timeout = 15 server_login_retry = 15 query_timeout = 0 query_wait_timeout = 120 client_idle_timeout = 0 client_login_timeout = 60 autodb_idle_timeout = 3600 # 日志设置 log_connections = 1 log_disconnections = 1 log_pooler_errors = 1 EOF # 创建用户认证文件 echo "\"$DB_USER\" \"$DB_PASSWORD\"" > "$CONFIG_DIR/postgres/userlist.txt" chmod 600 "$CONFIG_DIR/postgres/userlist.txt" success "PgBouncer 连接池配置完成" else warning "PgBouncer 未安装,跳过连接池配置" echo "安装 PgBouncer: sudo apt-get install pgbouncer" fi } # 配置自动备份 setup_backup_strategy() { log "配置自动备份策略..." # 创建备份脚本 cat > "$BACKUP_DIR/backup.sh" << 'EOF' #!/bin/bash # PostgreSQL 自动备份脚本 # Photography Portfolio 项目 set -e # 配置 DB_HOST="${DB_HOST:-localhost}" DB_PORT="${DB_PORT:-5432}" DB_NAME="${DB_NAME:-photography}" DB_USER="${DB_USER:-postgres}" BACKUP_DIR="/var/backups/photography" LOG_FILE="$BACKUP_DIR/backup.log" RETENTION_DAYS=30 # 日志函数 log() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" } # 执行备份 backup_database() { local backup_date=$(date +'%Y%m%d_%H%M%S') local backup_file="$BACKUP_DIR/${DB_NAME}_${backup_date}.sql" local compressed_file="${backup_file}.gz" log "开始备份数据库: $DB_NAME" # 执行 pg_dump export PGPASSWORD="$DB_PASSWORD" if pg_dump -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" \ -f "$backup_file" \ --verbose --clean --no-owner --no-privileges \ "$DB_NAME" 2>> "$LOG_FILE"; then # 压缩备份文件 gzip "$backup_file" # 验证备份文件 if [ -f "$compressed_file" ] && [ -s "$compressed_file" ]; then log "备份成功: $compressed_file" log "备份大小: $(du -h "$compressed_file" | cut -f1)" else log "ERROR: 备份文件创建失败" exit 1 fi else log "ERROR: 数据库备份失败" exit 1 fi } # 清理旧备份 cleanup_old_backups() { log "清理 $RETENTION_DAYS 天前的备份文件" find "$BACKUP_DIR" -name "${DB_NAME}_*.sql.gz" -mtime +$RETENTION_DAYS -delete log "旧备份清理完成" } # 主函数 main() { log "=== PostgreSQL 自动备份开始 ===" backup_database cleanup_old_backups log "=== PostgreSQL 自动备份完成 ===" } # 执行主函数 main "$@" EOF # 设置执行权限 chmod +x "$BACKUP_DIR/backup.sh" # 创建 crontab 条目 cat > "$CONFIG_DIR/postgres/backup.crontab" << EOF # Photography Portfolio 数据库备份计划任务 # 每天凌晨 2 点执行备份 0 2 * * * $BACKUP_DIR/backup.sh >> $LOG_DIR/cron.log 2>&1 # 每周日凌晨 3 点执行完整备份 0 3 * * 0 $BACKUP_DIR/backup.sh full >> $LOG_DIR/cron.log 2>&1 EOF success "自动备份策略配置完成" echo "要启用自动备份,请运行: sudo crontab $CONFIG_DIR/postgres/backup.crontab" } # 配置监控 setup_monitoring() { log "配置数据库监控..." # 创建监控脚本 cat > "$CONFIG_DIR/postgres/monitor.sh" << 'EOF' #!/bin/bash # PostgreSQL 监控脚本 # Photography Portfolio 项目 set -e # 配置 DB_HOST="${DB_HOST:-localhost}" DB_PORT="${DB_PORT:-5432}" DB_NAME="${DB_NAME:-photography}" DB_USER="${DB_USER:-postgres}" ALERT_EMAIL="${ALERT_EMAIL:-admin@photography.com}" # 检查数据库连接 check_connection() { export PGPASSWORD="$DB_PASSWORD" if ! psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" \ -c "SELECT 1;" &> /dev/null; then echo "CRITICAL: 数据库连接失败" return 1 fi echo "OK: 数据库连接正常" return 0 } # 检查数据库大小 check_database_size() { local size_mb=$(psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" \ -t -c "SELECT pg_size_pretty(pg_database_size('$DB_NAME'));" | tr -d ' ') echo "INFO: 数据库大小: $size_mb" } # 检查慢查询 check_slow_queries() { local slow_count=$(psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" \ -t -c "SELECT count(*) FROM pg_stat_statements WHERE mean_time > 1000;" | tr -d ' ') if [ "$slow_count" -gt 10 ]; then echo "WARNING: 发现 $slow_count 个慢查询" else echo "OK: 慢查询数量正常 ($slow_count)" fi } # 检查连接数 check_connections() { local conn_count=$(psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" \ -t -c "SELECT count(*) FROM pg_stat_activity;" | tr -d ' ') local max_conn=$(psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" \ -t -c "SHOW max_connections;" | tr -d ' ') local usage_percent=$((conn_count * 100 / max_conn)) if [ $usage_percent -gt 80 ]; then echo "WARNING: 连接数使用率过高: $conn_count/$max_conn ($usage_percent%)" else echo "OK: 连接数正常: $conn_count/$max_conn ($usage_percent%)" fi } # 主监控函数 main() { echo "=== PostgreSQL 监控报告 $(date) ===" if ! check_connection; then echo "数据库连接失败,停止监控" exit 1 fi check_database_size check_slow_queries check_connections echo "=== 监控完成 ===" } # 执行监控 main "$@" EOF chmod +x "$CONFIG_DIR/postgres/monitor.sh" success "数据库监控配置完成" } # 配置性能优化 setup_performance_tuning() { log "配置性能优化..." # 创建性能调优脚本 cat > "$CONFIG_DIR/postgres/tune.sql" << EOF -- PostgreSQL 性能调优脚本 -- Photography Portfolio 项目 -- 创建性能监控扩展 CREATE EXTENSION IF NOT EXISTS pg_stat_statements; CREATE EXTENSION IF NOT EXISTS pg_buffercache; -- 创建索引优化函数 CREATE OR REPLACE FUNCTION analyze_missing_indexes() RETURNS TABLE ( schemaname text, tablename text, seq_scan bigint, seq_tup_read bigint, idx_scan bigint, idx_tup_fetch bigint, missing_index_suggestion text ) AS \$\$ BEGIN RETURN QUERY SELECT s.schemaname, s.tablename, s.seq_scan, s.seq_tup_read, s.idx_scan, s.idx_tup_fetch, CASE WHEN s.seq_scan > s.idx_scan AND s.seq_tup_read > 10000 THEN '建议添加索引' ELSE '索引使用正常' END as missing_index_suggestion FROM pg_stat_user_tables s WHERE s.schemaname = 'public' ORDER BY s.seq_scan DESC, s.seq_tup_read DESC; END; \$\$ LANGUAGE plpgsql; -- 创建慢查询分析函数 CREATE OR REPLACE FUNCTION analyze_slow_queries() RETURNS TABLE ( query text, calls bigint, total_time double precision, mean_time double precision, rows bigint ) AS \$\$ BEGIN RETURN QUERY SELECT left(s.query, 100) as query, s.calls, s.total_time, s.mean_time, s.rows FROM pg_stat_statements s WHERE s.mean_time > 100 ORDER BY s.mean_time DESC LIMIT 20; END; \$\$ LANGUAGE plpgsql; -- 创建表空间使用分析函数 CREATE OR REPLACE FUNCTION analyze_table_sizes() RETURNS TABLE ( schemaname text, tablename text, size_pretty text, size_bytes bigint ) AS \$\$ BEGIN RETURN QUERY SELECT s.schemaname, s.tablename, pg_size_pretty(pg_total_relation_size(s.schemaname||'.'||s.tablename)), pg_total_relation_size(s.schemaname||'.'||s.tablename) FROM pg_tables s WHERE s.schemaname = 'public' ORDER BY pg_total_relation_size(s.schemaname||'.'||s.tablename) DESC; END; \$\$ LANGUAGE plpgsql; -- 更新表统计信息 ANALYZE; -- 重建索引(如需要) -- REINDEX DATABASE $DB_NAME; EOF success "性能优化配置完成" } # 创建启动脚本 create_startup_script() { log "创建服务启动脚本..." cat > "$CONFIG_DIR/postgres/photography-db.service" << EOF [Unit] Description=Photography Portfolio Database Service After=postgresql.service Requires=postgresql.service [Service] Type=oneshot RemainAfterExit=yes User=postgres Group=postgres # 环境变量 Environment=DB_HOST=$DB_HOST Environment=DB_PORT=$DB_PORT Environment=DB_NAME=$DB_NAME Environment=DB_USER=$DB_USER # 启动前检查 ExecStartPre=/bin/bash -c 'until pg_isready -h $DB_HOST -p $DB_PORT; do sleep 1; done' # 启动命令 ExecStart=/bin/bash $CONFIG_DIR/postgres/monitor.sh # 停止命令 ExecStop=/bin/true [Install] WantedBy=multi-user.target EOF success "服务启动脚本创建完成" echo "要启用服务,请运行:" echo "sudo cp $CONFIG_DIR/postgres/photography-db.service /etc/systemd/system/" echo "sudo systemctl enable photography-db.service" echo "sudo systemctl start photography-db.service" } # 生成配置摘要 generate_summary() { log "生成配置摘要..." cat > "$CONFIG_DIR/postgres/SETUP_SUMMARY.md" << EOF # PostgreSQL 生产环境配置摘要 ## 配置文件位置 - PostgreSQL 配置: \`$CONFIG_DIR/postgres/postgresql.conf\` - 认证配置: \`$CONFIG_DIR/postgres/pg_hba.conf\` - PgBouncer 配置: \`$CONFIG_DIR/postgres/pgbouncer.ini\` - 备份脚本: \`$BACKUP_DIR/backup.sh\` - 监控脚本: \`$CONFIG_DIR/postgres/monitor.sh\` ## 数据库信息 - 主机: $DB_HOST - 端口: $DB_PORT - 数据库: $DB_NAME - 用户: $DB_USER - 最大连接数: $DB_MAX_CONNECTIONS - 连接池大小: $DB_POOL_SIZE ## 目录结构 - 备份目录: $BACKUP_DIR - 日志目录: $LOG_DIR - 配置目录: $CONFIG_DIR/postgres ## 自动化任务 - 每日备份: 凌晨 2:00 - 每周完整备份: 周日凌晨 3:00 - 备份保留期: 30 天 ## 监控指标 - 数据库连接状态 - 数据库大小 - 慢查询统计 - 连接数使用率 ## 性能优化 - 已启用 pg_stat_statements 扩展 - 已创建性能分析函数 - 已配置合理的内存参数 - 已优化 WAL 和检查点设置 ## 下一步操作 1. 复制配置文件到 PostgreSQL 目录 2. 重启 PostgreSQL 服务 3. 启用自动备份 crontab 4. 配置监控服务 5. 运行性能调优脚本 ## 维护命令 \`\`\`bash # 手动备份 $BACKUP_DIR/backup.sh # 监控检查 $CONFIG_DIR/postgres/monitor.sh # 性能分析 psql -d $DB_NAME -f $CONFIG_DIR/postgres/tune.sql # 查看慢查询 psql -d $DB_NAME -c "SELECT * FROM analyze_slow_queries();" # 查看表大小 psql -d $DB_NAME -c "SELECT * FROM analyze_table_sizes();" \`\`\` 配置完成时间: $(date) EOF success "配置摘要生成完成: $CONFIG_DIR/postgres/SETUP_SUMMARY.md" } # 主函数 main() { log "开始 PostgreSQL 生产环境配置..." check_dependencies create_directories test_connection create_database_config setup_connection_pool setup_backup_strategy setup_monitoring setup_performance_tuning create_startup_script generate_summary success "PostgreSQL 生产环境配置完成!" echo "" echo "配置文件位置: $CONFIG_DIR/postgres/" echo "查看配置摘要: cat $CONFIG_DIR/postgres/SETUP_SUMMARY.md" echo "" echo "要应用配置,请参考配置摘要中的'下一步操作'部分" } # 处理命令行参数 case "${1:-}" in "test") test_connection ;; "backup") setup_backup_strategy ;; "monitor") setup_monitoring ;; "tune") setup_performance_tuning ;; *) main ;; esac