#!/bin/bash TAG="" while getopts "t:" opt; do case $opt in t) TAG="$OPTARG" ;; *) echo "Invalid option: -$OPTARG" >&2; exit 1 ;; esac done shift $((OPTIND-1)) cd /data/backup/mysql || exit 1 if [ -n "$TAG" ]; then BACKUP_DIR="./$(date +%Y%m%d%H%M%S)_${TAG}" else BACKUP_DIR="./$(date +%Y%m%d%H%M%S)" fi mkdir -p "$BACKUP_DIR" MYSQL_USER="root" MYSQL_PASSWORD="ENC(VN13kACOdhyl7xYqbgDMTMP0fK4dwoCkFe0AQW0VEt7/tpwx96GzRVY6eIpwlASM)" MYSQL_HOST="127.0.0.1" if [[ "$MYSQL_PASSWORD" =~ ^ENC\(.*\)$ ]]; then encrypted_pwd="${MYSQL_PASSWORD:4:-1}" 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 } # Set trap to ensure MySQL state is restored even on error trap restore_mysql_state EXIT ERR # Pre-backup: Set read-only and stop slave 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" exit 1 fi READ_ONLY_SET="true" echo "Stopping slave replication..." if ! mysql_exec "STOP REPLICA;"; then echo "[Warning] Failed to stop replica (may not be a slave server or already stopped)" fi # Get GTID position using SELECT @@global.gtid_executed; echo "Getting GTID position..." GTID_EXECUTED=$(mysql_exec "SELECT @@global.gtid_executed;") if [ -n "$GTID_EXECUTED" ]; then # Remove newlines and literal \n strings GTID_EXECUTED=$(echo "$GTID_EXECUTED" | tr -d '\n' | sed 's/\\n//g' | tr -s ' ') echo "GTID Executed: $GTID_EXECUTED" # Write GTID to file for restore use echo "$GTID_EXECUTED" > "$BACKUP_DIR/gtid_executed.txt" else echo "[Warning] Failed to get GTID executed information" fi # Get database list for backup if [ $# -eq 0 ]; then # When no arguments, get all non-system databases databases=$(mysql_exec "SHOW DATABASES;" | grep -Ev "(Database|information_schema|performance_schema|mysql|skywalking|sys)") # Convert to array and check empty values readarray -t db_list <<< "$databases" || { echo "ERROR: No valid databases found or failed to retrieve database list" restore_mysql_state exit 1 } else # Use arguments as database list when provided db_list=("$@") fi # Check if database list is empty if [ ${#db_list[@]} -eq 0 ]; then echo "ERROR: No databases to backup" restore_mysql_state exit 1 fi backup_list_str="$BACKUP_DIR.7z" # Perform backup for db in "${db_list[@]}"; do echo "Backing up database: $db" backup_list_str="$backup_list_str $db" if ! docker exec -i mysql mysqldump -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" -h "$MYSQL_HOST" --set-gtid-purged=OFF --single-transaction --databases "$db" > "$BACKUP_DIR/$db.sql"; then echo "[Critical Error] Failed to backup database $db, aborting" restore_mysql_state exit 1 fi done # Post-backup: Restore MySQL state (before compression) echo "Restoring MySQL to read-write mode..." mysql_exec "SET GLOBAL read_only = OFF;" || echo "[Warning] Failed to set read_only = OFF" # Clear the trap since we've successfully restored READ_ONLY_SET="false" trap - EXIT ERR # Compress backup files if ! 7zz a "$BACKUP_DIR.7z" "$BACKUP_DIR" >/dev/null; then echo "Compression failed. Please check disk space or 7z installation" exit 1 fi # Cleanup temporary files rm -rf "$BACKUP_DIR" echo "Backup completed: $BACKUP_DIR.7z" echo $backup_list_str >> backup_list.txt echo "Backup process completed successfully"