#!/bin/bash # Define variables MYSQL_USER="root" MYSQL_PASSWORD="ENC(VN13kACOdhyl7xYqbgDMTMP0fK4dwoCkFe0AQW0VEt7/tpwx96GzRVY6eIpwlASM)" MYSQL_HOST="127.0.0.1" BACKUP_DIR="/data/backup/mysql" TEMP_DIR="/tmp/mysql_restore_temp" if [[ "$MYSQL_PASSWORD" =~ ^ENC\(.*\)$ ]]; then encrypted_pwd="${MYSQL_PASSWORD:4:-1}" # Decrypt password if ! MYSQL_PASSWORD=$(java -cp /data/secure/jasypt-agent.jar com.jasypt.agent.DecryptionCLI $encrypted_pwd \ 2>&1); then echo -e "\033[31m[Fatal Error] Password decryption failed. Reason:" echo "$MYSQL_PASSWORD" | grep -iE "error|exception" exit 1 fi fi # Function to execute MySQL command mysql_exec() { docker exec -i mysql mysql -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" -h "$MYSQL_HOST" -N -e "$1" 2>/dev/null } # Function to restore MySQL state (called on exit) restore_mysql_state() { if [ "$READ_ONLY_SET" = "true" ]; then echo "Restoring MySQL state..." mysql_exec "SET GLOBAL read_only = OFF;" || echo "[Warning] Failed to set read_only = OFF" echo "MySQL state restored" fi } # Check parameters if [ $# -lt 1 ]; then echo "Usage: $0 [sql_file]" echo "Example: $0 20250414105525.7z nacos.sql" exit 1 fi BACKUP_FILE="$1" SQL_FILE="$2" # Verify backup file existence if [ ! -f "$BACKUP_DIR/$BACKUP_FILE" ]; then echo "Error: Backup file $BACKUP_DIR/$BACKUP_FILE not found" exit 1 fi # Extract base backup name BACKUP_NAME=$(basename "$BACKUP_FILE" .7z) # Create temporary directory mkdir -p "$TEMP_DIR" echo "Creating temporary directory: $TEMP_DIR" # Extract backup file echo "Extracting $BACKUP_FILE to $TEMP_DIR..." 7zz x "$BACKUP_DIR/$BACKUP_FILE" -o"$TEMP_DIR" -y # Check extraction status if [ $? -ne 0 ]; then echo "Error: Decompression failed" rm -rf "$TEMP_DIR" exit 1 fi echo "Extraction completed" # Handle nested directory NESTED_DIR="$TEMP_DIR/$BACKUP_NAME" if [ -d "$NESTED_DIR" ]; then echo "Detected nested directory: $NESTED_DIR" SQL_DIR="$NESTED_DIR" else echo "No nested directory found, using root extraction path" SQL_DIR="$TEMP_DIR" fi # Verify SQL files existence SQL_COUNT=$(find "$SQL_DIR" -name "*.sql" | wc -l) if [ "$SQL_COUNT" -eq 0 ]; then echo "Error: No SQL files found in extracted directory" rm -rf "$TEMP_DIR" exit 1 fi # Pre-restore: Set read-only, stop replica, and reset GTID echo "Setting MySQL to read-only mode..." if ! mysql_exec "SET GLOBAL read_only = ON;"; then echo "[Critical Error] Failed to set read_only = ON" rm -rf "$TEMP_DIR" exit 1 fi READ_ONLY_SET="true" # Set trap to ensure MySQL state is restored even on error trap restore_mysql_state EXIT ERR echo "Stopping replica replication..." if ! mysql_exec "STOP REPLICA;"; then echo "[Warning] Failed to stop replica (may not be a replica server or already stopped)" fi echo "Resetting binary logs and GTIDs..." if ! mysql_exec "RESET BINARY LOGS AND GTIDS;"; then echo "[Critical Error] Failed to reset binary logs and GTIDs" restore_mysql_state rm -rf "$TEMP_DIR" exit 1 fi # Database import logic if [ -n "$SQL_FILE" ]; then # Import specific SQL file if [ -f "$SQL_DIR/$SQL_FILE" ]; then echo "Importing $SQL_FILE..." db_name=$(basename "$SQL_FILE" .sql) echo "Using database: $db_name" # Create database if not exists docker exec -i mysql mysql --init-command="SET SQL_LOG_BIN=0;" -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" -h"$MYSQL_HOST" --connect-expired-password -e "CREATE DATABASE IF NOT EXISTS \`$db_name\`;" # Import SQL file docker exec -i mysql mysql --init-command="SET SQL_LOG_BIN=0;" -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" -h"$MYSQL_HOST" --connect-expired-password "$db_name" < "$SQL_DIR/$SQL_FILE" if [ $? -eq 0 ]; then echo "Successfully imported $SQL_FILE" else echo "Error: Failed to import $SQL_FILE" restore_mysql_state rm -rf "$TEMP_DIR" exit 1 fi else echo "Error: SQL file $SQL_DIR/$SQL_FILE not found" restore_mysql_state rm -rf "$TEMP_DIR" exit 1 fi else # Import all SQL files echo "Importing all SQL files..." for sql_file in $(find "$SQL_DIR" -name "*.sql" | sort); do db_name=$(basename "$sql_file" .sql) echo "Importing $sql_file to database $db_name..." # Create database if not exists docker exec -i mysql mysql --init-command="SET SQL_LOG_BIN=0;" -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" -h"$MYSQL_HOST" --connect-expired-password -e "CREATE DATABASE IF NOT EXISTS \`$db_name\`;" # Import SQL file docker exec -i mysql mysql --init-command="SET SQL_LOG_BIN=0;" -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" -h"$MYSQL_HOST" --connect-expired-password "$db_name" < "$sql_file" if [ $? -eq 0 ]; then echo "Successfully imported $sql_file to $db_name" else echo "Error: Failed to import $sql_file to $db_name" restore_mysql_state rm -rf "$TEMP_DIR" exit 1 fi done fi # Post-restore: Set GTID purged from backup file GTID_FILE="$SQL_DIR/gtid_executed.txt" if [ -f "$GTID_FILE" ]; then echo "Reading GTID from $GTID_FILE..." GTID_EXECUTED=$(cat "$GTID_FILE" | tr -d '\n' | sed 's/\\n//g' | tr -s ' ' | xargs) if [ -n "$GTID_EXECUTED" ]; then echo "Setting GTID purged to: $GTID_EXECUTED" if ! mysql_exec "SET GLOBAL gtid_purged = '$GTID_EXECUTED';"; then echo "[Critical Error] Failed to set gtid_purged" restore_mysql_state rm -rf "$TEMP_DIR" exit 1 fi echo "GTID purged set successfully" else echo "[Warning] GTID file is empty" fi else echo "[Warning] GTID file $GTID_FILE not found, skipping GTID purged setting" fi # Restore MySQL to read-write mode echo "Restoring MySQL to read-write mode..." mysql_exec "SET GLOBAL read_only = OFF;" || echo "[Warning] Failed to set read_only = OFF" READ_ONLY_SET="false" # Start replica echo "Starting replica replication..." if ! mysql_exec "START REPLICA;"; then echo "[Warning] Failed to start replica (may not be a replica server)" else echo "Waiting 3 seconds for replica to initialize..." sleep 3 echo "Checking replica status..." REPLICA_STATUS=$(mysql_exec "SHOW REPLICA STATUS\G") if [ -n "$REPLICA_STATUS" ]; then echo "Replica Status:" echo "$REPLICA_STATUS" # Check for errors in replica status ERROR_COUNT=$(echo "$REPLICA_STATUS" | grep -i "Last_Error\|Last_IO_Error\|Last_SQL_Error" | grep -v "No error" | wc -l) if [ "$ERROR_COUNT" -gt 0 ]; then echo "[Warning] Replica status shows errors, please check the output above" else echo "Replica status appears normal" fi else echo "[Warning] Failed to get replica status" fi fi # Clear the trap since we've successfully restored trap - EXIT ERR # Cleanup echo "Cleaning temporary directory..." rm -rf "$TEMP_DIR" echo "Database restore operation completed" exit 0