MySQL一键升级脚本(5.7-8.0)


脚本内容:

bash 复制代码
#!/bin/bash
# Description: This script is designed to upgrade MySQL 5.7 to MySQL 8.0 (Binary Package Installation)
# Date: 2025-10-22
# ========================================================================
# Precautions before use:
# 1. Test in non-production environment first
# 2. Ensure complete database backup (data directory + logical backup)
# 3. Check application compatibility with MySQL 8.0
# 4. Execute as root user
# ========================================================================
# How to use:
# 1. chmod +x upgrade_mysql80_binary.sh
# 2. ./upgrade_mysql80_binary.sh
# ========================================================================

set -e

# ==============================================
# 请根据实际环境修改以下配置(二进制包安装相关路径)
# ==============================================
MYSQL_57_BASE_DIR="/usr/local/mysql"  # 5.7二进制安装根目录
MYSQL_DATA_DIR="/data/mysql"  # 数据目录(通常在安装目录下)
MYSQL_80_TAR_PATH="/opt/mysql-8.0.42-linux-glibc2.17-x86_64.tar.xz"  # 8.0二进制包路径
MYSQL_80_BASE_DIR="/usr/local/mysql-8.0"  # 8.0安装目录
MYSQL_SOFT_LINK="/usr/local/mysql"  # 软链接路径(指向当前版本)
MYSQL_CONF="/etc/my.cnf"  # 配置文件路径
MYSQL_USER="mysql"  # 运行MySQL的系统用户
MYSQL_GROUP="mysql"  # 运行MySQL的系统组
MYSQL_ROOT_USER="root"  # 数据库root用户
# ==============================================

# 其他关键配置
BACKUP_DIR="/var/backups/mysql_upgrade"
LOG_FILE="/var/log/mysql_upgrade_$(date +%Y%m%d%H%M).log"
SERVICE_SCRIPT="${MYSQL_SOFT_LINK}/support-files/mysql.server"  # 二进制包自带的服务脚本

# 颜色代码
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m'  # No Color

# 步骤0:初始化日志
exec > >(tee -a "$LOG_FILE") 2>&1
echo -e "${YELLOW}[0/13] 开始MySQL二进制包升级流程 $(date)${NC}"

# 确认继续操作
confirm_continue() {
    read -p "$1 (y/n)? " choice
    case "$choice" in
        y|Y) echo -e "${GREEN} 继续执行...${NC}";;
        *)   echo -e "${YELLOW} 操作已取消${NC}"; exit 1;;
    esac
}

# 获取MySQL root密码
get_mysql_password() {
    if [ -z "$MYSQL_ROOT_PASS" ]; then
        read -s -p "输入MySQL root密码: " MYSQL_ROOT_PASS
        echo
    fi
}

# 验证二进制包文件
verify_80_tar() {
    if [ ! -f "$MYSQL_80_TAR_PATH" ]; then
        echo -e "${RED} 错误:MySQL 8.0二进制包不存在($MYSQL_80_TAR_PATH)${NC}"
        exit 1
    fi
# 验证文件格式(是否为tar.xz)
    if ! file "$MYSQL_80_TAR_PATH" | grep -q "XZ compressed data"; then
    echo -e "${RED} 错误:文件不是有效的xz压缩包($MYSQL_80_TAR_PATH)${NC}"
    exit 1
fi
}

# 步骤1: 验证当前用户(必须为root)
echo -e "${YELLOW}[1/13] 验证执行用户${NC}"
if [ "$(id -u)" -ne 0 ]; then
    echo -e "${RED} 错误:此脚本必须以root权限运行!${NC}"
    exit 1
fi
echo -e "${GREEN} 执行用户验证通过${NC}"

# 步骤2: 验证当前MySQL版本(必须为5.7)
echo -e "${YELLOW}[2/13] 检查当前MySQL版本${NC}"
MYSQL_57_BIN="${MYSQL_57_BASE_DIR}/bin/mysql"
if [ ! -f "$MYSQL_57_BIN" ]; then
    echo -e "${RED} 错误:未找到MySQL 5.7二进制文件($MYSQL_57_BIN)${NC}"
    exit 1
fi
CURRENT_VERSION=$("$MYSQL_57_BIN" -V 2>&1 | awk '{print $5}' | tr -d ,)
if [[ ! "$CURRENT_VERSION" =~ 5\.7 ]]; then
    echo -e "${RED} 错误:当前MySQL版本($CURRENT_VERSION)不是5.7,终止升级!${NC}"
    exit 1
fi
echo -e "${GREEN} 当前版本验证通过: $CURRENT_VERSION${NC}"

# 步骤3: 完整数据库备份(逻辑备份+数据目录备份)
echo -e "${YELLOW}[3/13] 执行全量备份${NC}"
mkdir -p "$BACKUP_DIR"
get_mysql_password

# 逻辑备份(使用5.7的mysqldump)
echo -e "${YELLOW} 执行逻辑备份(全库+存储过程+事件)...${NC}"
MYSQLDUMP_57="${MYSQL_57_BASE_DIR}/bin/mysqldump"
"$MYSQLDUMP_57" -u"$MYSQL_ROOT_USER" -p"$MYSQL_ROOT_PASS" \
    --all-databases --routines --events --single-transaction > \
    "$BACKUP_DIR/full_backup_$(date +%F).sql"
if [ $? -eq 0 ]; then
    echo -e "${GREEN} 逻辑备份成功: $BACKUP_DIR/full_backup_$(date +%F).sql${NC}"
else
    echo -e "${RED} 错误:逻辑备份失败!${NC}"
    exit 1
fi

# 数据目录备份(物理备份)
echo -e "${YELLOW} 执行数据目录备份...${NC}"
DATA_BACKUP_DIR="${BACKUP_DIR}/mysql_data_57_$(date +%s)"
cp -a "$MYSQL_DATA_DIR" "$DATA_BACKUP_DIR"
echo -e "${GREEN} 数据目录备份成功: $DATA_BACKUP_DIR${NC}"

# 步骤4: 停止MySQL 5.7服务
echo -e "${YELLOW}[4/13] 停止MySQL 5.7服务${NC}"
if [ -f "$SERVICE_SCRIPT" ]; then
    # 执行停止命令
    "$SERVICE_SCRIPT" stop
    # 等待2秒,确保进程有足够时间退出(解决短暂延迟问题)
    sleep 2
    
    # 验证是否停止成功:优先检查PID文件(服务正常停止会删除PID文件)
    # 从服务脚本中提取PID文件路径(通常在my.cnf或服务脚本中定义)
    PID_FILE=$("$SERVICE_SCRIPT" status | grep "PID file" | awk -F'[()]' '{print $2}')
    # 若服务脚本中未提取到PID文件,使用默认路径(可根据实际环境调整)
    if [ -z "$PID_FILE" ]; then
        PID_FILE="${MYSQL_DATA_DIR}/$(hostname).pid"
    fi

    # 检查进程和PID文件双重验证
    if pgrep -u "$MYSQL_USER" -f "^${MYSQL_57_BASE_DIR}/bin/mysqld$" >/dev/null || [ -f "$PID_FILE" ]; then
        echo -e "${RED} 错误:MySQL 5.7服务停止失败,强制终止进程...${NC}"
        pkill -u "$MYSQL_USER" -f "^${MYSQL_57_BASE_DIR}/bin/mysqld$"
        # 强制终止后再次检查
        if pgrep -u "$MYSQL_USER" -f "^${MYSQL_57_BASE_DIR}/bin/mysqld$" >/dev/null; then
            echo -e "${RED} 错误:强制终止失败,请手动检查进程!${NC}"
            exit 1
        fi
    fi

    echo -e "${GREEN} MySQL 5.7服务已停止${NC}"
else
    echo -e "${RED} 错误:未找到服务脚本($SERVICE_SCRIPT)${NC}"
    exit 1
fi

# 步骤5: 备份5.7配置和二进制目录
echo -e "${YELLOW}[5/13] 备份MySQL 5.7安装目录${NC}"
MV_57_DIR="${MYSQL_57_BASE_DIR}_57_backup_$(date +%s)"
mv "$MYSQL_57_BASE_DIR" "$MV_57_DIR"
echo -e "${GREEN} 5.7安装目录已备份至: $MV_57_DIR${NC}"

# 步骤6: 安装MySQL 8.0二进制包
echo -e "${YELLOW}[6/13] 安装MySQL 8.0二进制包${NC}"
verify_80_tar

# 解压二进制包(用tar xJf简化格式,-C指定目标父目录)
echo -e "${YELLOW} 解压MySQL 8.0二进制包...${NC}"
mkdir -p "$(dirname "$MYSQL_80_BASE_DIR")"
# 执行解压(x=解压,J=xz格式,f=指定文件,C=指定解压到的父目录)
tar xJf "$MYSQL_80_TAR_PATH" -C "$(dirname "$MYSQL_80_BASE_DIR")"

# 精准获取解压后的主目录名(tar -tf仅列出压缩包内容,无多余日志)
TAR_EXTRACT_DIR=$(tar -tf "$MYSQL_80_TAR_PATH" | head -1 | cut -d/ -f1)
TAR_EXTRACT_FULL_PATH="$(dirname "$MYSQL_80_BASE_DIR")/$TAR_EXTRACT_DIR"

# 验证解压目录存在性,避免空路径错误
if [ ! -d "$TAR_EXTRACT_FULL_PATH" ]; then
    echo -e "${RED} 错误:未找到解压后的目录($TAR_EXTRACT_FULL_PATH)${NC}"
    exit 1
fi

# 重命名解压目录(统一为配置的MYSQL_80_BASE_DIR路径)
mv "$TAR_EXTRACT_FULL_PATH" "$MYSQL_80_BASE_DIR"

# 创建软链接(指向8.0版本)
ln -snf "$MYSQL_80_BASE_DIR" "$MYSQL_SOFT_LINK"
echo -e "${GREEN} 8.0二进制包安装完成,软链接: $MYSQL_SOFT_LINK -> $MYSQL_80_BASE_DIR${NC}"

# 步骤7: 调整权限(关键!二进制包依赖mysql用户权限)
echo -e "${YELLOW}[7/13] 配置目录权限${NC}"
# 确保mysql用户组存在
if ! id -u "$MYSQL_USER" >/dev/null 2>&1; then
    groupadd "$MYSQL_GROUP"
    useradd -r -g "$MYSQL_GROUP" "$MYSQL_USER"
fi
# 递归设置权限(数据目录+安装目录)
chown -R "$MYSQL_USER:$MYSQL_GROUP" "$MYSQL_80_BASE_DIR"
chown -R "$MYSQL_USER:$MYSQL_GROUP" "$MYSQL_DATA_DIR"  # 数据目录保留原数据
chmod -R 755 "$MYSQL_80_BASE_DIR"
echo -e "${GREEN} 权限配置完成${NC}"

# 步骤8: 调整配置文件(适配8.0兼容参数)
echo -e "${YELLOW}[8/13] 调整my.cnf配置(兼容8.0)${NC}"
# 备份原配置
cp "$MYSQL_CONF" "${MYSQL_CONF}_57_backup"

# 添加/修改8.0兼容参数(关键:解决认证插件和语法兼容问题)
# 1. 禁用query_cache(8.0已移除)
sed -i '/query_cache/d' "$MYSQL_CONF"
# 2. 设置默认认证插件为5.7兼容模式(避免应用连接失败)
if ! grep -q "default_authentication_plugin" "$MYSQL_CONF"; then
    sed -i '/\[mysqld\]/a default_authentication_plugin=mysql_native_password' "$MYSQL_CONF"
else
    sed -i 's/default_authentication_plugin=.*/default_authentication_plugin=mysql_native_password/' "$MYSQL_CONF"
fi
# 3. 移除8.0不支持的参数(如sql_mode中的NO_AUTO_CREATE_USER)
sed -i 's/NO_AUTO_CREATE_USER//' "$MYSQL_CONF"

echo -e "${GREEN} 配置文件调整完成${NC}"

# 步骤9: 启动MySQL 8.0并升级系统表
echo -e "${YELLOW}[9/13] 启动MySQL 8.0并升级系统表${NC}"
# 启动8.0服务(使用二进制包自带的服务脚本)
NEW_SERVICE_SCRIPT="${MYSQL_SOFT_LINK}/support-files/mysql.server"
"$NEW_SERVICE_SCRIPT" start
sleep 5  # 等待服务启动

# 验证启动状态
if ! pgrep -f "/usr/local/mysql/bin/mysqld" >/dev/null; then
    echo -e "${RED} 错误:MySQL 8.0启动失败,请检查日志(${MYSQL_DATA_DIR}/error.err)${NC}"
    exit 1
fi

# 步骤9.1: 验证服务器自动升级是否成功(核心判断)
echo -e "${YELLOW} 验证数据目录自动升级结果...${NC}"
ERROR_LOG="${MYSQL_DATA_DIR}/mysql.err"
sleep 5  # 等待日志写入

# 匹配日志中"从50700升级到80042(或其他8.0.x版本)并完成"的记录,包含关键事件码MY-013381
if grep -q "MY-013381.*Server upgrade from '50700' to '8[0-9]*' completed" "$ERROR_LOG"; then
    echo -e "${GREEN} 数据目录自动升级成功(日志验证通过)${NC}"
else
    if grep -q "MY-013381.*Server upgrade.*failed" "$ERROR_LOG"; then
        echo -e "${RED} 错误:数据目录升级失败,日志详情:${NC}"
        grep "MY-013381.*upgrade.*failed" "$ERROR_LOG"
    else
        echo -e "${RED} 错误:未在日志中找到升级完成标志,请检查错误日志:$ERROR_LOG ${NC}"
        # 输出日志中升级相关记录,方便排查
        echo -e "${YELLOW} 日志中升级相关记录:${NC}"
        grep -i "upgrade" "$ERROR_LOG"
    fi
    exit 1
fi

# 步骤10: 重置root密码(处理8.0密码加密方式)
echo -e "${YELLOW}[10/13] 重置root密码(适配8.0加密)${NC}"

# 【新增代码】定义MySQL 8.0客户端二进制文件路径(关键修复)
MYSQL_80_BIN="${MYSQL_SOFT_LINK}/bin/mysql"

# 可选:手动指定socket路径(若默认路径不匹配,可从my.cnf中查socket参数)
MYSQL_SOCKET=$(grep "socket" "$MYSQL_CONF" | grep -v "#" | awk -F'=' '{gsub(/ /,"",$2);print $2}')

# 颜色代码(注:此处颜色代码重复定义,可删除以精简,不影响功能)
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m'

MYSQL_ROOT_PASS="123123"

# 步骤1:检查并添加免密配置
echo -e "${YELLOW}1/6 检查免密配置(skip-grant-tables)...${NC}"
if ! grep -q "skip-grant-tables" "$MYSQL_CONF"; then
    sed -i '/\[mysqld\]/a skip-grant-tables' "$MYSQL_CONF"
    echo -e "${YELLOW}  已添加免密配置${NC}"
else
    echo -e "${GREEN}  免密配置已存在${NC}"
fi

# 步骤2:重启MySQL服务
echo -e "${YELLOW}2/6 重启MySQL服务...${NC}"
"$NEW_SERVICE_SCRIPT" stop >/dev/null 2>&1
"$NEW_SERVICE_SCRIPT" start
sleep 5

# 步骤3:查询实际root用户(获取真实host)
echo -e "${YELLOW}3/6 查询实际root用户...${NC}"
ROOT_USERS=$("$MYSQL_80_BIN" -u"$MYSQL_ROOT_USER" -Nse "SELECT host FROM mysql.user WHERE user='$MYSQL_ROOT_USER';")
if [ -z "$ROOT_USERS" ]; then
    echo -e "${RED}  错误:未查询到root用户!${NC}"
    sed -i '/skip-grant-tables/d' "$MYSQL_CONF"
    "$NEW_SERVICE_SCRIPT" restart
    exit 1
fi
echo -e "${GREEN}  查询到root用户的host:${NC}"
echo "$ROOT_USERS" | while read -r host; do echo "    - $host"; done

# 步骤4:重置root密码(针对所有真实host)
echo -e "${YELLOW}4/6 重置root密码...${NC}"
SQL="FLUSH PRIVILEGES;"
while read -r host; do
    SQL+="ALTER USER '$MYSQL_ROOT_USER'@'$host' IDENTIFIED WITH mysql_native_password BY '$MYSQL_ROOT_PASS';"
done <<< "$ROOT_USERS"
SQL+="FLUSH PRIVILEGES;"

"$MYSQL_80_BIN" -u"$MYSQL_ROOT_USER" -e "$SQL" 2>&1
if [ $? -eq 0 ]; then
    echo -e "${GREEN}  密码重置成功(新密码:$MYSQL_ROOT_PASS)${NC}"
else
    echo -e "${RED}  错误:密码重置失败!SQL:$SQL${NC}"
    sed -i '/skip-grant-tables/d' "$MYSQL_CONF"
    "$NEW_SERVICE_SCRIPT" restart
    exit 1
fi

# 步骤5:移除免密配置并重启
echo -e "${YELLOW}5/6 移除免密配置并重启服务...${NC}"
sed -i '/skip-grant-tables/d' "$MYSQL_CONF"
"$NEW_SERVICE_SCRIPT" stop >/dev/null 2>&1
"$NEW_SERVICE_SCRIPT" start
sleep 5


echo -e "${GREEN} root密码重置完成${NC}"


# 步骤11: 验证MySQL 8.0版本
echo -e "${YELLOW}[11/13] 验证升级后版本${NC}"
# 用正则匹配提取版本号(匹配Ver后的数字版本,如8.0.42)
NEW_VERSION=$("$MYSQL_80_BIN" -V 2>&1 | grep -oP '(?<=Ver )\d+\.\d+\.\d+')
if [[ "$NEW_VERSION" =~ 8\.0 ]]; then
    echo -e "${GREEN} 版本验证通过:$NEW_VERSION${NC}"
else
    echo -e "${RED} 错误:升级后版本不是8.0(当前:$NEW_VERSION)${NC}"
    exit 1
fi

# 步骤12: 验证数据库可用性
echo -e "${YELLOW}[12/13] 验证数据库连接${NC}"
"$MYSQL_80_BIN" -u"$MYSQL_ROOT_USER" -p"$MYSQL_ROOT_PASS" -e "SELECT VERSION();" >/dev/null
if [ $? -eq 0 ]; then
    echo -e "${GREEN} 数据库连接成功,升级后可用${NC}"
else
    echo -e "${RED} 错误:无法连接升级后的数据库!${NC}"
    exit 1
fi

# 步骤13: 升级完成提示
echo -e "${YELLOW}[13/13] 升级流程结束${NC}"
echo -e "${GREEN}====================================================${NC}"
echo -e "${GREEN} MySQL 5.7 -> 8.0 二进制包升级成功!${NC}"
echo -e "${GREEN}====================================================${NC}"
echo "关键信息:"
echo "1. 备份文件路径:$BACKUP_DIR"
echo "2. 升级日志:$LOG_FILE"
echo "3. 8.0安装目录:$MYSQL_80_BASE_DIR"
echo "4. 建议操作:"
echo "   - 验证应用连接(注意认证插件兼容性)"
echo "   - 检查错误日志:${MYSQL_DATA_DIR}/error.log"
echo "   - 必要时优化8.0新特性(如innodb_dedicated_server)"
相关推荐
李艺为20 小时前
根据apk包名动态修改Android品牌与型号
android·开发语言
Tom4i21 小时前
【网络优化】Android 如何监听系统网络连接成功
android·网络
FrameNotWork1 天前
HarmonyOS 与 Android 架构对比:从“写页面”到“设计系统”的差异
android·架构·harmonyos
TAEHENGV1 天前
基本设置模块 Cordova 与 OpenHarmony 混合开发实战
android·java·数据库
Leo1871 天前
MySQL 回表(Back to Table)详解
数据库·mysql
遇见火星1 天前
MySQL 8.0复制架构主从自动切换脚本
mysql·adb·架构·mysql8.0·mysql主从
不知江月待何人..1 天前
MySQL服务无法启动问题
数据库·mysql
李少兄1 天前
一文搞懂什么是关系型数据库?什么是非关系型数据库?
数据库·redis·mysql·mongodb·nosql
会开花的二叉树1 天前
即时通讯系统核心模块实现
数据库·mysql·elasticsearch
屏息1 天前
Android 低延迟流媒体播放器实战:基于 FFmpeg 6.1.1 的 RTSP/RTMP 解决方案
android