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) **总 结** 此脚本根据实际环境进行参数配置后,可以自动化的进行针对主从环境的切换演练,并可以适当的通过脚本中内容进行调整,比如可以自定义设置从库延迟的时间,是否可以接受从库延迟等方式,通过反复测试可以正常运行,基本可以替代手工切换过程中的状态检查和繁琐的确认环节,从而减少人为失误。

相关推荐
不知江月待何人..2 小时前
MySQL服务无法启动问题
数据库·mysql
李少兄2 小时前
一文搞懂什么是关系型数据库?什么是非关系型数据库?
数据库·redis·mysql·mongodb·nosql
会开花的二叉树2 小时前
即时通讯系统核心模块实现
数据库·mysql·elasticsearch
勇气要爆发4 小时前
Minio + CDN 架构实战:从入门到避坑
架构
四谎真好看4 小时前
MySQL 学习笔记(运维篇1)
运维·数据库·笔记·学习·mysql·学习笔记
TeleostNaCl5 小时前
Kodi | 如何使用 ADB 无 root 备份 Android 版本 Kodi 的数据并导入到另一台设备中
android·经验分享·adb·电视盒子·智能电视·tv·智能tv
码农水水6 小时前
阿里Java面试被问:慢查询的优化方案
java·adb·面试
墨白曦煜6 小时前
微服务容错设计:Sentinel 全局异常处理与 Feign 降级策略的边界权衡
微服务·架构·sentinel
遇见火星6 小时前
MySQL 5.7/8.0 物理备份实战:XtraBackup 全量+增量+验证+恢复
数据库·mysql·adb·xtrabackup·mysql8.0