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)"
相关推荐
怪兽20146 小时前
Android View, SurfaceView, GLSurfaceView 的区别
android·面试
龚礼鹏7 小时前
android 图像显示框架二——流程分析
android
玩机达人887 小时前
2025年新版ADB工具箱下载+驱动+ADB指令集+fastboot刷机ROOT工具
adb
消失的旧时光-19437 小时前
kmp需要技能
android·设计模式·kotlin
友友马7 小时前
『 数据库 』MySQL复习 - 内置函数详解
数据库·mysql
ANYOLY7 小时前
慢查询优化
mysql
帅得不敢出门7 小时前
Linux服务器编译android报no space left on device导致失败的定位解决
android·linux·服务器
雨白8 小时前
协程间的通信管道 —— Kotlin Channel 详解
android·kotlin
凸头9 小时前
MySQL 的四种 Binlog 日志处理工具:Canal、Maxwell、Databus和 阿里云 DTS
数据库·mysql·阿里云