脚本功能
此脚本是专门用于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
重点说明
- 版本限制(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地址,端口,日志路径)进行相关配置才可以运行。
使用方法
-
保存脚本并赋权
[root@VM-8-4-opencloudos ~]# chmod +x mysql_switchover.sh
-
手工执行脚本
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,建库,建表,增删改查数据,表和库) 示例截图:  **总 结** 此脚本根据实际环境进行参数配置后,可以自动化的进行针对主从环境的切换演练,并可以适当的通过脚本中内容进行调整,比如可以自定义设置从库延迟的时间,是否可以接受从库延迟等方式,通过反复测试可以正常运行,基本可以替代手工切换过程中的状态检查和繁琐的确认环节,从而减少人为失误。