227 lines
7.0 KiB
Bash
227 lines
7.0 KiB
Bash
#!/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 <backup_file.7z> [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
|