MySQL 8.0复制架构主从自动切换脚本

脚本功能

此脚本是专门用于MySQL8.0数据库主从复制架构下(基于GTID的复制)的自动切换的脚本,配置好相关基本信息(通用用户名,密码,主从数据库的复制用户,用户密码,IP地址,端口号)后,直接手动执行shell脚本即可完成MySQL主从架构下的自动切换功能,是计划内的数据库切换演练有利便捷的执行工具,可大大减少人工切换产生的误操作和切换时间问题。

脚本内容

该脚本名称为mysql_switchover.sh

复制代码
#!/bin/bash# =============================================================================# 基于GTID的MySQL 8.0 一主一从架构主从切换脚本 (支持不同端口)# 版本: 1.0# 修复内容: 表头输出问题、错误处理机制、GTID一致性检查逻辑# =============================================================================# >>>>>>>>>>>> 第一部分:脚本配置区域 (使用前请务必修改) <<<<<<<<<<<<# 数据库连接凭证MYSQL_USER="repluser"MYSQL_PASS="repluser"# 当前主从节点IP地址及端口CURRENT_MASTER_HOST="10.2.8.4"CURRENT_MASTER_PORT="3306"CURRENT_SLAVE_HOST="10.2.8.4"CURRENT_SLAVE_PORT="3307"# 日志文件LOG_FILE="/var/log/mysql_switchover.log"LOCK_FILE="/tmp/mysql_switchover.lock"# 连接超时时间(秒)MYSQL_CONNECT_TIMEOUT=5# >>>>>>>>>>>> 第二部分:核心函数定义 <<<<<<<<<<<<# 日志记录函数log() {    local LEVEL=$1    local MSG=$2    local TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")    echo "[$TIMESTAMP] [$LEVEL] $MSG" | tee -a "$LOG_FILE"}# 错误处理与退出函数error_exit() {    local MSG=$1    log "ERROR" "$MSG"    [ -f "$LOCK_FILE" ] && rm -f "$LOCK_FILE"    exit 1}# 通用的MySQL连接执行函数mysql_exec() {    local HOST=$1    local PORT=$2    local SQL=$3    mysql -h"$HOST" -P"$PORT" -u"$MYSQL_USER" -p"$MYSQL_PASS" --connect-timeout=$MYSQL_CONNECT_TIMEOUT -e "$SQL" 2>/dev/null}# 获取MySQL单值结果 (专门用于获取单个值的函数)get_mysql_value() {    local HOST=$1    local PORT=$2    local SQL=$3    mysql -h"$HOST" -P"$PORT" -u"$MYSQL_USER" -p"$MYSQL_PASS" --connect-timeout=$MYSQL_CONNECT_TIMEOUT -N -s -e "$SQL" 2>/dev/null | tail -1}# 检查MySQL实例是否可连接check_mysql_connectivity() {    local HOST=$1    local PORT=$2    if mysql_exec "$HOST" "$PORT" "SELECT 1;" &> /dev/null; then        log "INFO" "成功连接到MySQL实例: $HOST:$PORT"        return 0    else        log "ERROR" "无法连接到MySQL实例: $HOST:$PORT"        return 1    fi}# 创建锁文件防止脚本重复运行create_lockfile() {    if [ -f "$LOCK_FILE" ]; then        local LOCK_PID=$(cat "$LOCK_FILE")        if ps -p "$LOCK_PID" > /dev/null 2>&1; then            log "ERROR" "脚本已在运行中 (PID: $LOCK_PID),请勿重复执行"            exit 1        else            log "WARNING" "发现残留锁文件,清理后继续"            rm -f "$LOCK_FILE"        fi    fi    echo $$ > "$LOCK_FILE"}# 清理锁文件cleanup_lockfile() {    [ -f "$LOCK_FILE" ] && rm -f "$LOCK_FILE"}# 检查复制延迟并等待追平(基于SHOW REPLICA STATUS)wait_for_replica_catchup() {    local REPLICA_HOST=$1    local REPLICA_PORT=$2    local MAX_DELAY=60    local WAIT_TIMEOUT=300    local WAIT_STEP=5    local TOTAL_WAIT=0    log "INFO" "检查副本延迟并等待追平,最大容忍延迟: ${MAX_DELAY}秒,超时: ${WAIT_TIMEOUT}秒"    while [ $TOTAL_WAIT -lt $WAIT_TIMEOUT ]; do        # 修正:从SHOW REPLICA STATUS获取Seconds_Behind_Source值        local REPLICA_STATUS=$(mysql_exec "$REPLICA_HOST" "$REPLICA_PORT" "SHOW REPLICA STATUS\\G")        local DELAY=$(echo "$REPLICA_STATUS" | grep -i "Seconds_Behind_Source:" | awk '{print $2}')        # 处理空值和NULL情况        if [ "$DELAY" == "NULL" ] || [ -z "$DELAY" ]; then            log "ERROR" "无法获取副本延迟信息或复制已停止"            return 1        fi        # 确保DELAY是数字        if ! [[ "$DELAY" =~ ^[0-9]+$ ]]; then            log "ERROR" "获取的延迟值非数字: $DELAY"            return 1        fi        if [ "$DELAY" -eq 0 ]; then            log "INFO" "副本已完全追平,延迟: 0秒"            return 0        elif [ "$DELAY" -le $MAX_DELAY ]; then            log "INFO" "副本当前延迟: ${DELAY}秒,在容忍范围内"            break        else            log "INFO" "副本当前延迟: ${DELAY}秒,等待追平... 已等待 ${TOTAL_WAIT}秒"            sleep $WAIT_STEP            TOTAL_WAIT=$((TOTAL_WAIT + WAIT_STEP))        fi    done    if [ $TOTAL_WAIT -ge $WAIT_TIMEOUT ]; then        log "WARNING" "等待副本追平超时,当前延迟: ${DELAY}秒"        return 2    fi    return 0}# 检查复制状态 (使用MySQL 8.0的REPLICA语法)check_replica_status() {    local HOST=$1    local PORT=$2    local STATUS=$(mysql_exec "$HOST" "$PORT" "SHOW REPLICA STATUS\\G")    if [ -z "$STATUS" ]; then        log "WARNING" "未找到复制状态信息,主机可能不是副本角色: $HOST:$PORT"        return 2    fi    local IO_THREAD=$(echo "$STATUS" | grep -i "Replica_IO_Running:" | awk '{print $2}')    local SQL_THREAD=$(echo "$STATUS" | grep -i "Replica_SQL_Running:" | awk '{print $2}')    local SECONDS_BEHIND=$(echo "$STATUS" | grep -i "Seconds_Behind_Source:" | awk '{print $2}')    if [[ "$IO_THREAD" == "Yes" ]] && [[ "$SQL_THREAD" == "Yes" ]]; then        log "INFO" "副本复制线程运行正常: $HOST:$PORT, 延迟: ${SECONDS_BEHIND:-N/A} 秒"        return 0    else        log "ERROR" "副本复制线程异常 - IO线程: $IO_THREAD, SQL线程: $SQL_THREAD"        return 1    fi}# 检查主从数据一致性 (基于GTID)check_gtid_consistency() {    local MASTER_HOST=$1    local MASTER_PORT=$2    local SLAVE_HOST=$3    local SLAVE_PORT=$4    log "INFO" "开始检查主从GTID一致性..."    MASTER_GTID_SET=$(get_mysql_value "$MASTER_HOST" "$MASTER_PORT" "SELECT @@GLOBAL.GTID_EXECUTED;")    SLAVE_GTID_SET=$(get_mysql_value "$SLAVE_HOST" "$SLAVE_PORT" "SELECT @@GLOBAL.GTID_EXECUTED;")    if [ -z "$MASTER_GTID_SET" ] || [ -z "$SLAVE_GTID_SET" ]; then        log "ERROR" "获取主库或从库的GTID_EXECUTED失败"        return 1    fi    # 使用GTID_SUBTRACT函数检查从库是否缺失事务    local GTID_DIFF=$(get_mysql_value "$SLAVE_HOST" "$SLAVE_PORT" "SELECT GTID_SUBTRACT('$MASTER_GTID_SET', '$SLAVE_GTID_SET') AS DIFF;")    if [ -n "$GTID_DIFF" ] && [ "$GTID_DIFF" != "" ]; then        log "WARNING" "主从数据存在GTID差异: $GTID_DIFF"        return 2    else        log "INFO" "主从GTID一致性检查通过"        return 0    fi}# 设置实例为只读模式set_read_only() {    local HOST=$1    local PORT=$2    local MODE=$3    if [ "$MODE" == "on" ]; then        log "INFO" "设置实例为只读模式: $HOST:$PORT"        mysql_exec "$HOST" "$PORT" "SET GLOBAL super_read_only=1, read_only=1;"        # 修复:使用改进的连接数查询,避免表头问题        local WRITE_CONNS=$(get_mysql_value "$HOST" "$PORT" "SELECT COUNT(*) FROM performance_schema.processlist where command not in ('Sleep','Binlog Dump','Binlog Dump GTID') AND USER NOT IN ('system user','event_scheduler') AND id <> connection_id();")        # 修复:添加数值检查,避免非数字值比较        if [ "$WRITE_CONNS" -gt 0 ] 2>/dev/null; then            log "WARNING" "发现 $WRITE_CONNS 个活跃写连接,建议处理"        else            log "INFO" "无活跃写连接或获取连接数失败"        fi    else        log "INFO" "关闭实例只读模式: $HOST:$PORT"        mysql_exec "$HOST" "$PORT" "SET GLOBAL read_only=0, super_read_only=0;"    fi}# 停止并重置复制stop_and_reset_replica() {    local HOST=$1    local PORT=$2    log "INFO" "在主机上停止并重置复制: $HOST:$PORT"    mysql_exec "$HOST" "$PORT" "STOP REPLICA;"    mysql_exec "$HOST" "$PORT" "RESET REPLICA ALL;"    if [ $? -eq 0 ]; then        log "INFO" "成功停止并重置复制: $HOST:$PORT"        return 0    else        log "ERROR" "停止或重置复制操作失败: $HOST:$PORT"        return 1    fi}# 提升从库为新主库promote_replica_to_source() {    local REPLICA_HOST=$1    local REPLICA_PORT=$2    log "INFO" "开始提升副本为新主库: $REPLICA_HOST:$REPLICA_PORT"    # 确保复制已停止并重置    stop_and_reset_replica "$REPLICA_HOST" "$REPLICA_PORT" || return 1    # 关闭只读模式,使其可写    set_read_only "$REPLICA_HOST" "$REPLICA_PORT" "off"    log "INFO" "副本提升为新主库操作完成: $REPLICA_HOST:$REPLICA_PORT"}# 配置旧主库作为新主库的从库setup_old_source_as_replica() {    local OLD_MASTER_HOST=$1    local OLD_MASTER_PORT=$2    local NEW_MASTER_HOST=$3    local NEW_MASTER_PORT=$4    log "INFO" "开始配置旧主库作为新主库的副本: $OLD_MASTER_HOST:$OLD_MASTER_PORT -> $NEW_MASTER_HOST:$NEW_MASTER_PORT"    # 检查旧主库连接    check_mysql_connectivity "$OLD_MASTER_HOST" "$OLD_MASTER_PORT" || return 1    # 停止并重置复制(如果之前是主库)    stop_and_reset_replica "$OLD_MASTER_HOST" "$OLD_MASTER_PORT"    # 使用GTID自动定位配置主从复制    mysql_exec "$OLD_MASTER_HOST" "$OLD_MASTER_PORT" "CHANGE REPLICATION SOURCE TO SOURCE_HOST='$NEW_MASTER_HOST',SOURCE_PORT=$NEW_MASTER_PORT,SOURCE_USER='$MYSQL_USER',SOURCE_PASSWORD='$MYSQL_PASS',SOURCE_AUTO_POSITION=1,GET_SOURCE_PUBLIC_KEY=1; START REPLICA;"    if [ $? -eq 0 ]; then        log "INFO" "成功在旧主库上配置指向新主库的复制"        # 短暂等待后检查复制状态        sleep 5        if check_replica_status "$OLD_MASTER_HOST" "$OLD_MASTER_PORT"; then            log "INFO" "旧主库到新主库的复制状态正常"            return 0        else            log "WARNING" "旧主库到新主库的复制状态异常"            return 2        fi    else        log "ERROR" "在旧主库上配置复制关系失败"        return 1    fi}# 检查复制延迟并等待追平 -(基于SHOW REPLICA STATUS)wait_for_replica_catchup() {    local REPLICA_HOST=$1    local REPLICA_PORT=$2    local MAX_DELAY=60    local WAIT_TIMEOUT=300    local WAIT_STEP=5    local TOTAL_WAIT=0    log "INFO" "检查副本延迟并等待追平,最大容忍延迟: ${MAX_DELAY}秒,超时: ${WAIT_TIMEOUT}秒"    while [ $TOTAL_WAIT -lt $WAIT_TIMEOUT ]; do        # 修正:从SHOW REPLICA STATUS获取Seconds_Behind_Source值        local REPLICA_STATUS=$(mysql_exec "$REPLICA_HOST" "$REPLICA_PORT" "SHOW REPLICA STATUS\\G")        local DELAY=$(echo "$REPLICA_STATUS" | grep -i "Seconds_Behind_Source:" | awk '{print $2}')        # 处理空值和NULL情况        if [ "$DELAY" == "NULL" ] || [ -z "$DELAY" ]; then            log "ERROR" "无法获取副本延迟信息或复制已停止"            return 1        fi        # 确保DELAY是数字        if ! [[ "$DELAY" =~ ^[0-9]+$ ]]; then            log "ERROR" "获取的延迟值非数字: $DELAY"            return 1        fi        if [ "$DELAY" -eq 0 ]; then            log "INFO" "副本已完全追平,延迟: 0秒"            return 0        elif [ "$DELAY" -le $MAX_DELAY ]; then            log "INFO" "副本当前延迟: ${DELAY}秒,在容忍范围内"            break        else            log "INFO" "副本当前延迟: ${DELAY}秒,等待追平... 已等待 ${TOTAL_WAIT}秒"            sleep $WAIT_STEP            TOTAL_WAIT=$((TOTAL_WAIT + WAIT_STEP))        fi    done    if [ $TOTAL_WAIT -ge $WAIT_TIMEOUT ]; then        log "WARNING" "等待副本追平超时,当前延迟: ${DELAY}秒"        return 2    fi    return 0}# 切换后验证函数 - 完全重写版verify_switchover_result() {    local NEW_SOURCE_HOST=$1    local NEW_SOURCE_PORT=$2    local OLD_SOURCE_HOST=$3    local OLD_SOURCE_PORT=$4    log "INFO" "开始验证切换结果..."    # 创建临时测试数据库(使用更安全的命名)    local TEST_DB="test_switchover_$(date +%s)"    # 第一层验证:创建数据库    local CREATE_DB_SQL="CREATE DATABASE IF NOT EXISTS $TEST_DB"    if ! mysql_exec "$NEW_SOURCE_HOST" "$NEW_SOURCE_PORT" "$CREATE_DB_SQL"; then        log "ERROR" "新主库创建数据库测试失败: $NEW_SOURCE_HOST:$NEW_SOURCE_PORT"        return 1    fi    log "INFO" "新主库创建数据库测试通过: $NEW_SOURCE_HOST:$NEW_SOURCE_PORT"    # 第二层验证:创建表    #local CREATE_TABLE_SQL="CREATE TABLE $TEST_DB.verify_switchover \(id INT PRIMARY KEY, test_time TIMESTAMP\)"    # 对于复杂的SQL,使用heredoc最安全local CREATE_TABLE_SQL=$(cat <<SQLCREATE TABLE $TEST_DB.verify_switchover (id INT PRIMARY KEY, test_time TIMESTAMP)SQL)    if ! mysql_exec "$NEW_SOURCE_HOST" "$NEW_SOURCE_PORT" "$CREATE_TABLE_SQL"; then        log "ERROR" "新主库创建表测试失败"        mysql_exec "$NEW_SOURCE_HOST" "$NEW_SOURCE_PORT" "DROP DATABASE IF EXISTS $TEST_DB"        return 1    fi    # 第三层验证:插入数据    #local INSERT_SQL="INSERT INTO $TEST_DB.verify_switchover VALUES \(1, NOW\(\)\)"local INSERT_SQL=$(cat <<SQLINSERT INTO $TEST_DB.verify_switchover VALUES (1, NOW())SQL)    if ! mysql_exec "$NEW_SOURCE_HOST" "$NEW_SOURCE_PORT" "$INSERT_SQL"; then        log "ERROR" "新主库插入数据测试失败"        mysql_exec "$NEW_SOURCE_HOST" "$NEW_SOURCE_PORT" "DROP DATABASE IF EXISTS $TEST_DB"        return 1    fi    log "INFO" "新主库写测试完全通过: $NEW_SOURCE_HOST:$NEW_SOURCE_PORT"    # 清理测试数据    mysql_exec "$NEW_SOURCE_HOST" "$NEW_SOURCE_PORT" "DROP DATABASE IF EXISTS $TEST_DB"    # 验证旧主库复制状态    if check_mysql_connectivity "$OLD_SOURCE_HOST" "$OLD_SOURCE_PORT"; then        sleep 3        if check_replica_status "$OLD_SOURCE_HOST" "$OLD_SOURCE_PORT"; then            log "INFO" "旧主库到新主库的复制状态正常"        else            log "WARNING" "旧主库到新主库的复制状态异常,需人工检查"            return 2        fi    else        log "INFO" "旧主库当前不可达,复制状态略过检查"    fi    log "INFO" "切换验证完成"    return 0}# >>>>>>>>>>>> 第三部分:切换主流程 <<<<<<<<<<<<# 切换函数switchover() {    log "INFO" "开始主从切换流程..."    # 1. 前置检查    log "INFO" "步骤1: 执行前置检查"    check_mysql_connectivity "$CURRENT_MASTER_HOST" "$CURRENT_MASTER_PORT" || error_exit "原主库无法连接,切换中止"    check_mysql_connectivity "$CURRENT_SLAVE_HOST" "$CURRENT_SLAVE_PORT" || error_exit "从库无法连接,切换中止"    check_replica_status "$CURRENT_SLAVE_HOST" "$CURRENT_SLAVE_PORT" || error_exit "从库复制状态异常,切换中止"    # 2. 检查数据一致性    log "INFO" "步骤2: 检查主从数据一致性"    check_gtid_consistency "$CURRENT_MASTER_HOST" "$CURRENT_MASTER_PORT" "$CURRENT_SLAVE_HOST" "$CURRENT_SLAVE_PORT"    local CONSISTENCY_RESULT=$?    if [ $CONSISTENCY_RESULT -eq 1 ]; then        error_exit "GTID一致性检查失败,切换中止"    elif [ $CONSISTENCY_RESULT -eq 2 ]; then        log "WARNING" "主从数据存在差异,请确认是否继续切换? \(y/N\)"        read -t 30 user_input        if [[ ! "$user_input" =~ ^[Yy]$ ]]; then            log "INFO" "用户取消切换操作"            exit 1        fi        log "INFO" "用户确认继续切换"    fi    # 3. 设置原主库为只读    log "INFO" "步骤3: 设置原主库为只读模式"    set_read_only "$CURRENT_MASTER_HOST" "$CURRENT_MASTER_PORT" "on"    # 4. 等待从库追平延迟    log "INFO" "步骤4: 检查从库复制延迟"    wait_for_replica_catchup "$CURRENT_SLAVE_HOST" "$CURRENT_SLAVE_PORT"    local CATCHUP_RESULT=$?    if [ $CATCHUP_RESULT -eq 1 ]; then        error_exit "检查副本延迟时发生错误"    elif [ $CATCHUP_RESULT -eq 2 ]; then        log "WARNING" "从库延迟较大,请确认是否继续切换? \(y/N\)"        read -t 30 user_input        if [[ ! "$user_input" =~ ^[Yy]$ ]]; then            # 恢复原主库可写状态            set_read_only "$CURRENT_MASTER_HOST" "$CURRENT_MASTER_PORT" "off"            log "INFO" "用户取消切换操作,已恢复原主库写权限"            exit 1        fi        log "INFO" "用户确认继续切换"    fi    # 5. 提升从库为新主库    log "INFO" "步骤5: 提升从库为新主库"    promote_replica_to_source "$CURRENT_SLAVE_HOST" "$CURRENT_SLAVE_PORT" || error_exit "提升从库失败"    # 6. 配置旧主库为新从库(加强错误处理)    log "INFO" "步骤6: 配置旧主库为新从库"    if ! setup_old_source_as_replica "$CURRENT_MASTER_HOST" "$CURRENT_MASTER_PORT" "$CURRENT_SLAVE_HOST" "$CURRENT_SLAVE_PORT"; then        log "WARNING" "配置旧主库为新从库失败,尝试备用方案..."        # 备用方案:重置GTID并重试        local NEW_MASTER_GTID=$(get_mysql_value "$CURRENT_SLAVE_HOST" "$CURRENT_SLAVE_PORT" "SELECT @@GLOBAL.GTID_EXECUTED;")        if [ -n "$NEW_MASTER_GTID" ]; then            log "INFO" "尝试备用GTID同步方案"            mysql_exec "$CURRENT_MASTER_HOST" "$CURRENT_MASTER_PORT" "STOP REPLICA; RESET REPLICA ALL;"            mysql_exec "$CURRENT_MASTER_HOST" "$CURRENT_MASTER_PORT" "SET GLOBAL gtid_purged='$NEW_MASTER_GTID';"            if setup_old_source_as_replica "$CURRENT_MASTER_HOST" "$CURRENT_MASTER_PORT" "$CURRENT_SLAVE_HOST" "$CURRENT_SLAVE_PORT"; then                log "INFO" "备用方案配置成功"            else                log "ERROR" "备用方案也失败,需要手动干预"                return 2            fi        else            log "ERROR" "无法获取新主库GTID,备用方案无法执行"            return 2        fi    fi    # 7. 最终验证(必须成功)    log "INFO" "步骤7: 执行切换后验证"    if verify_switchover_result "$CURRENT_SLAVE_HOST" "$CURRENT_SLAVE_PORT" "$CURRENT_MASTER_HOST" "$CURRENT_MASTER_PORT"; then        log "INFO" "切换验证通过"    else        error_exit "切换验证失败,新主库可能不可用"    fi    log "INFO" "主从切换流程全部完成!新主库: $CURRENT_SLAVE_HOST:$CURRENT_SLAVE_PORT"}# >>>>>>>>>>>> 第四部分:脚本入口 <<<<<<<<<<<<# 主执行逻辑main() {    # 创建锁文件    create_lockfile    trap cleanup_lockfile EXIT    log "INFO" "==== MySQL主从切换脚本开始执行 ===="    log "INFO" "原主库: $CURRENT_MASTER_HOST:$CURRENT_MASTER_PORT"    log "INFO" "原从库: $CURRENT_SLAVE_HOST:$CURRENT_SLAVE_PORT"    switchover    local result=$?    if [ $result -eq 0 ]; then        log "INFO" "脚本执行成功"    else        log "ERROR" "脚本执行过程中遇到错误"    fi    log "INFO" "==== MySQL主从切换脚本执行结束 ===="    exit $result}# 脚本执行入口main

重点说明

  1. 版本限制(MySQL8.0.23及以上)

该脚本测试时使用的MySQL版本为8.0.41,主从架构搭建时是采用新的 CHANGE REPLICATION SOURCE TO 语法创建的,在脚本获取相关信息时也是采用 SHOW REPLICA STATUS\G方式获取,由于新的语法是官方从8.0.23版本才进行调整的,故本脚本限制的版本必须是大于等于MySQL8.0.23。

2. 复制限制(GTID模式)

脚本中测试环境复制架构采用的是基于GTID的复制方式,故如果主从架构是基于binlog+position的老方式复制是不适用的。

3. 额外说明

在使用该脚本前,需要对脚本中的开始位置(第一部分:脚本配置区域)进行提前配置好,根据实际环境(复制用户,IP地址,端口,日志路径)进行相关配置才可以运行。

使用方法

  1. 保存脚本并赋权

    [root@VM-8-4-opencloudos ~]# chmod +x mysql_switchover.sh

  2. 手工执行脚本

root@VM-8-4-opencloudos \~\]# ./mysql_switchover.sh 3. 执行过程: [root@VM-8-4-opencloudos ~]# ./mysql_switchover.sh[2025-11-28 17:22:27] [INFO] ==== MySQL主从切换脚本开始执行 ====[2025-11-28 17:22:27] [INFO] 原主库: 10.2.8.4:3306[2025-11-28 17:22:27] [INFO] 原从库: 10.2.8.4:3307[2025-11-28 17:22:27] [INFO] 开始主从切换流程...[2025-11-28 17:22:27] [INFO] 步骤1: 执行前置检查[2025-11-28 17:22:27] [INFO] 成功连接到MySQL实例: 10.2.8.4:3306[2025-11-28 17:22:27] [INFO] 成功连接到MySQL实例: 10.2.8.4:3307[2025-11-28 17:22:27] [INFO] 副本复制线程运行正常: 10.2.8.4:3307, 延迟: 0 秒[2025-11-28 17:22:27] [INFO] 步骤2: 检查主从数据一致性[2025-11-28 17:22:27] [INFO] 开始检查主从GTID一致性...[2025-11-28 17:22:27] [INFO] 主从GTID一致性检查通过[2025-11-28 17:22:27] [INFO] 步骤3: 设置原主库为只读模式[2025-11-28 17:22:27] [INFO] 设置实例为只读模式: 10.2.8.4:3306[2025-11-28 17:22:28] [INFO] 无活跃写连接或获取连接数失败[2025-11-28 17:22:28] [INFO] 步骤4: 检查从库复制延迟[2025-11-28 17:22:28] [INFO] 检查副本延迟并等待追平,最大容忍延迟: 60秒,超时: 300秒[2025-11-28 17:22:28] [INFO] 副本已完全追平,延迟: 0秒[2025-11-28 17:22:28] [INFO] 步骤5: 提升从库为新主库[2025-11-28 17:22:28] [INFO] 开始提升副本为新主库: 10.2.8.4:3307[2025-11-28 17:22:28] [INFO] 在主机上停止并重置复制: 10.2.8.4:3307[2025-11-28 17:22:28] [INFO] 成功停止并重置复制: 10.2.8.4:3307[2025-11-28 17:22:28] [INFO] 关闭实例只读模式: 10.2.8.4:3307[2025-11-28 17:22:28] [INFO] 副本提升为新主库操作完成: 10.2.8.4:3307[2025-11-28 17:22:28] [INFO] 步骤6: 配置旧主库为新从库[2025-11-28 17:22:28] [INFO] 开始配置旧主库作为新主库的副本: 10.2.8.4:3306 -> 10.2.8.4:3307[2025-11-28 17:22:29] [INFO] 成功连接到MySQL实例: 10.2.8.4:3306[2025-11-28 17:22:29] [INFO] 在主机上停止并重置复制: 10.2.8.4:3306[2025-11-28 17:22:29] [INFO] 成功停止并重置复制: 10.2.8.4:3306[2025-11-28 17:22:30] [INFO] 成功在旧主库上配置指向新主库的复制[2025-11-28 17:22:36] [INFO] 副本复制线程运行正常: 10.2.8.4:3306, 延迟: 0 秒[2025-11-28 17:22:36] [INFO] 旧主库到新主库的复制状态正常[2025-11-28 17:22:36] [INFO] 步骤7: 执行切换后验证[2025-11-28 17:22:36] [INFO] 开始验证切换结果...[2025-11-28 17:22:36] [INFO] 新主库创建数据库测试通过: 10.2.8.4:3307[2025-11-28 17:22:39] [INFO] 新主库写测试完全通过: 10.2.8.4:3307[2025-11-28 17:22:40] [INFO] 成功连接到MySQL实例: 10.2.8.4:3306[2025-11-28 17:22:44] [INFO] 副本复制线程运行正常: 10.2.8.4:3306, 延迟: 0 秒[2025-11-28 17:22:44] [INFO] 旧主库到新主库的复制状态正常[2025-11-28 17:22:44] [INFO] 切换验证完成[2025-11-28 17:22:44] [INFO] 切换验证通过[2025-11-28 17:22:44] [INFO] 主从切换流程全部完成!新主库: 10.2.8.4:3307[2025-11-28 17:22:44] [INFO] 脚本执行成功[2025-11-28 17:22:44] [INFO] ==== MySQL主从切换脚本执行结束 ====[root@VM-8-4-opencloudos ~]#说明:本示例测试环境为:MySQL主库:10.2.8.4 端口为 3306MySQL从库:10.2.8.4 端口为 3307复制用户:repluser 本示例中复制用户赋权为所有权限,实际用户可根据情况更换,但必须有相关权限才行(设置参数read_only,复制权限replication slave,建库,建表,增删改查数据,表和库) 示例截图: ![image.png](https://i-blog.csdnimg.cn/img_convert/ab9ff008d9c5ee6ad555b624881388d6.png) **总 结** 此脚本根据实际环境进行参数配置后,可以自动化的进行针对主从环境的切换演练,并可以适当的通过脚本中内容进行调整,比如可以自定义设置从库延迟的时间,是否可以接受从库延迟等方式,通过反复测试可以正常运行,基本可以替代手工切换过程中的状态检查和繁琐的确认环节,从而减少人为失误。

相关推荐
代码游侠4 小时前
嵌入式开发——ARM Cortex-A7内核和i.MX6处理器相关的底层头文件
arm开发·笔记·嵌入式硬件·学习·架构
a努力。4 小时前
虾皮Java面试被问:分布式Top K问题的解决方案
java·后端·云原生·面试·rpc·架构
liux35284 小时前
MySQL主从复制技术全面解析:从基础原理到高级架构实践(八)
mysql
HyEISN4 小时前
Android 9 开启远程adb
android·adb
a程序小傲4 小时前
听说前端又死了?
开发语言·前端·mysql·算法·postgresql·深度优先
小Mie不吃饭5 小时前
MySQL慢查询日志全解析:从配置到优化实践
mysql
2501_944711435 小时前
前端向架构突围系列 - 工程化(五):企业级脚手架的设计与落地
前端·架构
明月醉窗台6 小时前
Ryzen AI --- AMD XDNA架构的部署框架
人工智能·opencv·目标检测·机器学习·计算机视觉·架构
代码煮茶君7 小时前
MySQL 数据库死锁及核心机制全解析
android·数据库·mysql
拆房老料7 小时前
实战复盘:自研 Office / PDF 文档处理平台的高坑预警与 AI Agent 时代架构思考
人工智能·架构·pdf·编辑器·开源软件