1. 为什么现在必须考虑升级?不仅仅是新特性
如果你还在用MySQL 5.7,最近可能已经收到了各种安全扫描工具的告警,或者云服务商发来的版本支持终止提醒。没错,MySQL 5.7已经在2023年10月结束了标准支持,进入了“扩展支持”阶段。这意味着官方不再提供新的功能更新,关键的安全补丁也会延迟发布。我遇到过不少客户,因为等保测评或安全审计不过关,被逼着必须升级,那种临时抱佛脚的慌乱,真的不想再经历第二次。
但抛开安全合规的压力,升级到MySQL 8.0带来的性能提升和运维便利,才是更实在的驱动力。我实测过一个读写混合的线上业务,从5.7升级到8.0后,仅仅是利用8.0改进后的优化器和更高效的索引管理,整体查询耗时平均下降了15%-20%。特别是那些包含JSON字段操作和复杂关联查询的场景,提升更为明显。8.0的窗口函数和公共表表达式(CTE),能让很多原本需要在应用层拼凑的复杂报表,用一条SQL就优雅地搞定,大大减轻了应用服务器的压力。
不过,升级绝不是“下载新版本、点击下一步”那么简单。它更像是一次精密的数据库“心脏移植手术”,数据就是生命线,容不得半点闪失。网上很多教程只告诉你步骤,但真正折磨人的往往是步骤之间那些意想不到的“坑”。接下来,我就结合自己多次升级的经验,把从准备、操作到验尸的完整流程,以及最常见的几个“坑”和解决办法,给你掰开揉碎了讲清楚。
2. 升级前必做的功课:兼容性检查与完整备份
在动手替换任何一个二进制文件之前,有两件事必须做到位:一是用工具做一次全面的“体检”,二是准备好万无一失的“后悔药”——备份。
2.1 使用官方工具进行深度体检
MySQL官方提供了一个非常强大的工具叫 MySQL Shell,其中的 util.checkForServerUpgrade() 函数就是我们的“体检中心”。它会对当前5.7的实例进行多达28个方面的检查,提前预警兼容性问题。很多朋友觉得麻烦跳过这一步,结果升级后各种报错,回头再找原因更费劲。
具体操作很简单。先去官网下载对应版本的MySQL Shell,解压后运行类似下面的命令:
./mysqlsh -uroot -p -S /tmp/mysql.sock -e "util.checkForServerUpgrade()" > upgrade_check.log
重点看输出报告里的 Error 和 Warning。我遇到最常见的有三个:
- 字符集警告 (Usage of utf8mb3 charset):5.7里的
utf8其实是utf8mb3,最多只支持3字节的UTF-8编码,像一些生僻字和emoji表情存不进去。8.0默认的utf8mb4才是完整的4字节支持。工具会提示你哪些库或表还在用旧的字符集。 - 认证插件警告 (New default authentication plugin considerations):这是个大坑!5.7默认用
mysql_native_password插件认证,而8.0默认变成了caching_sha2_password。如果你的老应用(比如一些旧的PHP框架、Java连接池)没更新驱动,升级后会直接连不上数据库,错误信息还特别隐晦。 - SQL模式提示 (Usage of obsolete sql_mode flags):5.7里常用的
sql_mode可能包含NO_AUTO_CREATE_USER选项,这个在8.0里已经移除了。如果不处理,升级后可能导致一些SQL执行失败。
对于这些警告,我们的策略是:升级前,在5.7环境里尽量按8.0的标准去修复。比如把字符集提前迁移到 utf8mb4,在配置里显式指定 default_authentication_plugin=mysql_native_password 来兼容老应用。
2.2 准备好你的“后悔药”:多层次备份策略
备份是升级的底线,我习惯做三层备份,确保任何一步出错都能回得去。
- 逻辑备份(全量):用
mysqldump导出所有数据和结构。这是最通用的回滚方式。
关键参数mysqldump -uroot -p --all-databases --routines --events --triggers --single-transaction > /backup/full_backup_$(date +%Y%m%d).sql--single-transaction可以在InnoDB表上实现不锁表的快照备份,对线上业务影响小。 - 物理备份(数据目录):直接复制整个MySQL的数据目录(
datadir)。这种方式恢复最快,但必须确保数据库完全关闭,否则文件可能不一致。 - 配置文件备份:把
my.cnf或my.ini备份好。升级后很多参数需要调整,旧配置是重要的参考。
做完备份,一定要验证!我吃过亏,曾经备份文件因为磁盘空间不足没写完整,真到恢复时傻眼了。简单执行一下 grep "CREATE TABLE" /backup/your_backup.sql | head -5,看看备份文件头部是否正常。
3. 核心升级操作:In-Place升级步骤详解
我们采用 In-Place升级,也就是在原服务器上,用8.0的二进制文件替换5.7的文件,然后基于原有的数据目录启动。这种方式好处是快,数据不用搬迁,但操作必须严谨。
3.1 优雅关闭数据库与文件替换
首先,要像送老朋友一样优雅地关闭MySQL 5.7。直接 kill 或者 service mysql stop 可能来不及刷脏页,有数据损坏风险。
-- 连接到5.7实例
mysql> SET GLOBAL innodb_fast_shutdown = 0; -- 设置为0,确保InnoDB完成所有清理工作
mysql> SHUTDOWN;
mysql> EXIT;
执行后,用 ps -ef | grep mysqld 确认进程真的退出了。然后,就是关键的替换环节:将下载好的MySQL 8.0二进制包解压,覆盖旧的安装目录(注意是basedir,不是datadir!)。同时,把数据目录(datadir)的属主改成mysql用户。
tar -xvf mysql-8.0.xx-linux-glibc2.12-x86_64.tar.xz
mv mysql-8.0.xx-linux-glibc2.12-x86_64 /usr/local/mysql8
chown -R mysql:mysql /usr/local/mysql8
chown -R mysql:mysql /data/mysql # 你的数据目录
3.2 关键配置调整:my.cnf的变与不变
接下来是配置文件的调整,这是升级成功的关键。你不能直接用5.7的 my.cnf,但全盘照搬8.0默认配置也可能出问题。我的经验是“继承中调整”。
下面这个对比表,清晰地列出了必须修改的核心参数:
| 配置项 | MySQL 5.7 典型值 | MySQL 8.0 调整建议与说明 |
|---|---|---|
basedir | /usr/local/mysql | 必须改为 /usr/local/mysql8,指向新版本目录 |
character_set_server | utf8 | 建议改为 utf8mb4,以支持更全的字符 |
collation_server | utf8_general_ci | 建议改为 utf8mb4_0900_ai_ci (8.0默认) |
default_authentication_plugin | 未设置 (默认为 mysql_native_password) | 强烈建议显式设置 mysql_native_password,避免连接问题 |
sql_mode | 可能包含 NO_AUTO_CREATE_USER | 必须移除 NO_AUTO_CREATE_USER 选项 |
transaction_isolation | REPEATABLE-READ | 通常保持不变,但8.0对隔离级别有更严格的实现 |
除了这些,你原先在5.7上优化的参数,比如 innodb_buffer_pool_size、innodb_log_file_size 等,可以保持不变。一个完整的8.0配置示例如下:
[mysqld]
basedir=/usr/local/mysql8
datadir=/data/mysql
socket=/tmp/mysql.sock
port=3306
character_set_server=utf8mb4
collation_server=utf8mb4_0900_ai_ci
default_authentication_plugin=mysql_native_password
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
# 以下为原有性能参数,可保留
innodb_buffer_pool_size=4G
innodb_log_file_size=1G
max_connections=1000
3.3 启动与升级数据字典
MySQL 8.0.16以后,不再需要单独运行 mysql_upgrade 命令了。升级过程会在启动时自动完成。我们使用 mysqld_safe 并指定 --upgrade=FORCE 参数来启动,它会强制检查并升级所有系统表和数据字典。
/usr/local/mysql8/bin/mysqld_safe --defaults-file=/etc/my.cnf --user=mysql --upgrade=FORCE &
启动时别着急,打开另一个终端窗口,用 tail -f /data/mysql/error.log 盯着错误日志。你会看到一系列“Upgrading...”的信息,只要没有红色的 ERROR,就耐心等待它完成。这个过程耗时取决于数据量的大小。
当看到日志里出现 “mysqld: ready for connections”,恭喜你,MySQL 8.0已经启动成功了。立刻连接上去验证版本:
mysql -uroot -p -e "SELECT VERSION();"
如果显示 8.0.xx,那么最惊险的一步已经跨过。别忘了把系统的环境变量(如 PATH)和可能存在的服务管理脚本(如 systemd 的 mysqld.service)中的路径,更新到新的 basedir。
4. 升级后高频问题排查与解决
升级成功启动,只是万里长征第一步。接下来应用一接入,各种兼容性问题才会真正暴露。下面这几个是我碰过最多、也最让人头疼的问题。
4.1 连接失败:SSL与认证插件的坑
问题现象:应用日志里报 Public Key Retrieval is not allowed 或者 Authentication plugin 'caching_sha2_password' cannot be loaded 之类的错误,导致应用连不上数据库。
根因分析:这通常是两个问题叠加。第一,MySQL 8.0默认启用了SSL连接,而一些老版本的JDBC驱动或客户端配置不当。第二,就是我们前面提到的认证插件变更,8.0新建用户的默认插件是 caching_sha2_password。
解决方案:分层次解决,从治标到治本。
- 临时应急(修改连接串):在应用的JDBC URL里加上参数,禁用SSL并指定使用旧的认证插件。例如:
jdbc:mysql://localhost:3306/dbname?useSSL=false&allowPublicKeyRetrieval=true&defaultAuthenticationPlugin=mysql_native_password注意:
useSSL=false和allowPublicKeyRetrieval=true在生产环境需谨慎评估安全风险,这仅是临时绕过问题的方法。 - 用户层面修复:修改已有用户的认证插件。
ALTER USER 'your_app_user'@'%' IDENTIFIED WITH mysql_native_password BY 'your_password'; FLUSH PRIVILEGES; - 服务器层面根治(推荐):在
my.cnf中强制指定默认插件,并关闭SSL(如果确实不需要)。
修改配置后重启MySQL服务。这样之后创建的新用户也会默认使用旧插件,一劳永逸。[mysqld] default_authentication_plugin=mysql_native_password skip_ssl
4.2 排序规则冲突:Illegal mix of collations
问题现象:升级后,一些复杂的SQL查询,特别是用到 FIND_IN_SET()、CONCAT() 或者联表查询时,突然报错:ERROR 1267 (HY000): Illegal mix of collations (utf8mb4_general_ci,IMPLICIT) and (utf8mb4_0900_ai_ci,IMPLICIT) for operation 'xxx'。
根因分析:这是因为8.0引入了新的默认排序规则 utf8mb4_0900_ai_ci(更标准、对多语言支持更好),而你的老表、老字段可能还用的是5.7时代的 utf8mb4_general_ci。当查询中同时涉及这两种不同排序规则的数据时,MySQL就懵了,不知道按哪个标准来比较。
解决方案:有三种思路,按影响范围从小到大来选。
- 修改SQL语句(最精准):在报错的查询语句里,用
COLLATE子句强制指定排序规则。比如:
这种方式只影响当前SQL,改动最小,但如果你有成千上万条SQL,改起来会疯掉。SELECT * FROM table_a a JOIN table_b b ON a.name = b.name COLLATE utf8mb4_0900_ai_ci; - 修改数据库/服务器参数(一劳永逸):在
my.cnf中设置服务器和连接的默认排序规则,强制统一。
同时,可以尝试设置[mysqld] collation_server = utf8mb4_general_ci character_set_server = utf8mb4skip-character-set-client-handshake(注意该参数在8.0后期版本已弃用)来忽略客户端握手时的字符集设置,强制使用服务端配置。重启后,新会话的默认排序规则就统一了。 - 修改表结构(最彻底但最重):将相关表和字段的排序规则批量改为
utf8mb4_0900_ai_ci。这需要业务低峰期操作,因为大表改字符集会锁表。
可以先通过ALTER TABLE your_table CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;SHOW CREATE TABLE your_table;和SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLLATION_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE COLLATION_NAME LIKE '%general_ci';来找出所有需要修改的对象。
4.3 性能波动与参数微调
升级后,你可能会发现某些SQL变慢了,或者内存使用比之前高。这很正常,因为优化器变了。我建议在升级后,观察一到两个完整的业务周期(比如24小时)。
重点关注慢查询日志(slow_query_log),对比升级前后同一类SQL的执行计划是否改变。8.0的优化器更智能,但偶尔也会“聪明反被聪明误”。可以利用 OPTIMIZER_TRACE 或者 EXPLAIN FORMAT=JSON 来深入分析。
另外,8.0有一些新的性能相关参数可以调整,比如 innodb_dedicated_server 如果设置为ON,MySQL会自动根据服务器内存调整 innodb_buffer_pool_size 等关键参数,对于新手来说很省心。但如果你有丰富的调优经验,可能更愿意手动精细控制。
5. 回滚方案:万一失败了怎么办?
即使准备得再充分,也要有最坏的打算。如果升级后出现无法解决的严重问题,我们需要能快速回退到5.7。
回滚的前提是:你在升级前做的物理备份(整个数据目录)和配置文件备份都完好无损。切记:一旦用MySQL 8.0启动并写入了数据,这个数据目录就不能再被MySQL 5.7直接使用了,因为数据字典格式已经升级,是单向的。
标准的回滚步骤:
- 停止MySQL 8.0服务。
- 彻底移除或重命名当前被8.0修改过的数据目录(例如
mv /data/mysql /data/mysql_8_bak)。 - 将之前备份的5.7数据目录还原到原位(例如
cp -rp /backup/mysql_data_57_bak /data/mysql)。 - 确保
my.cnf配置改回5.7的版本(特别是basedir要指回5.7的安装目录)。 - 使用MySQL 5.7的二进制文件启动服务。
回滚后,务必用备份的逻辑备份文件(.sql)做一次数据校验,确保数据一致。整个回滚过程应该在运维窗口内反复演练过,确保关键时刻能顶得上。说到底,升级数据库就像一场战役,充分的预案和演练才是信心的来源。别怕麻烦,前期多流汗,升级时才不会“流血”。
162



被折叠的 条评论
为什么被折叠?



