util.checkForServerUpgrade() 可信但有局限:仅检查MySQL Server 8.0+升级前的SQL兼容性(如废弃语法、系统表变更),不覆盖应用代码、UDF、备份策略等;需用目标版本mysqlsh执行,依赖SELECT和LOCK TABLES权限,且必须配合mysqldump --no-data人工核查字符集/排序规则、JSON类型及时区表加载。

mysql-shell 的 util.checkForServerUpgrade() 能不能信
能,但只信一半。它检查的是 MySQL Server 8.0+ 升级前的兼容性问题,比如废弃语法、不兼容的 SQL 模式、系统表结构变更等,但不会检测你的应用代码、备份策略或自定义 UDF 是否适配。
常见错误现象:util.checkForServerUpgrade() 报 ERROR: Unsupported data type in column 'xxx',其实是某张表用了 ENUM 或 SET 值含非 ASCII 字符,而新版本对排序规则更严格;或者报 WARNING: Column uses old temporal type,说明还有 DATETIME 列没设精度(如 DATETIME(3)),虽不阻断升级,但会影响时区行为一致性。
- 必须用升级目标版本对应的
mysqlsh运行(比如升到 8.4,就得用 8.4 自带的 mysql-shell) - 连接用户需有
SELECT权限遍历information_schema和所有业务库,以及LOCK TABLES(用于临时加锁校验) - 不检查存储过程体内的动态 SQL,也不扫描注释或日志中的 SQL 片段
检查前为什么必须先导出 mysqldump --no-data
因为 util.checkForServerUpgrade() 不解析表定义里的字符集/排序规则继承链,比如你建表时没显式写 CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_cs,它可能放过,但实际升级后因默认排序规则变更导致索引失效或查询结果错乱。
使用场景:你在 MySQL 5.7 上跑了多年,SHOW CREATE TABLE 看起来都正常,但 mysqldump --no-data 导出后一搜 CHARSET latin1 或 COLLATE utf8_general_ci,基本就是雷。
- 执行命令:
mysqldump -u root -p --no-data --skip-triggers --skip-routines --databases db1 db2 > schema.sql - 重点 grep:
grep -i "charset\|collate" schema.sql | grep -v "utf8mb4_0900_as_cs" - 特别注意
JSON字段是否被误定义为TEXT—— 升级后JSON_VALID()行为会变,但检查工具不报
checkForServerUpgrade() 报 ERROR: Binary logging is enabled but log_bin_trust_function_creators is OFF 怎么办
这不是数据兼容性问题,而是安全配置冲突。MySQL 8.0.23+ 强制要求开启 log_bin_trust_function_creators 才允许创建含不确定函数(如 NOW()、UUID())的存储函数,否则主从复制可能不一致。
性能影响:开启后不会降低性能,但若你真有不安全函数,升级后可能被拒绝执行 —— 工具只是提前预警。
- 临时修复(仅验证用):
SET GLOBAL log_bin_trust_function_creators = ON; - 永久修复:在
my.cnf的[mysqld]段落加一行:log_bin_trust_function_creators=ON - 更稳妥的做法是重写函数,用
DETERMINISTIC显式声明,或改用触发器替代
为什么检查完还得手动跑 mysql_upgrade(8.0.16 之后已弃用)
因为 util.checkForServerUpgrade() 是只读检查,它不修改任何东西。而真正升级 MySQL Server 二进制后,mysql_system_tables 结构可能已变(比如 8.0.30 新增了 replication_asynchronous_connection_failover_managed 表),必须由 mysqld --upgrade(新版叫这个)来同步元数据。
容易踩的坑:有人以为检查通过就等于“可直接切流”,结果上线后发现 performance_schema 里某些监控项为空,或 INFORMATION_SCHEMA.PROCESSLIST 缺字段 —— 那是因为忘了运行升级命令,或者用错了用户权限(必须用 root@localhost 启动)。
- 升级后首次启动命令示例:
mysqld --upgrade=FORCE --user=mysql - 不要用
mysql_upgrade脚本(8.0.16+ 已移除),它会静默失败且无提示 - 如果启用了
validate_password插件,升级后密码策略会重置为默认强度,需手动SET GLOBAL validate_password.policy = MEDIUM;
最常被忽略的是:检查工具不验证时区表(mysql.time_zone*)是否已加载。如果你用 CONVERT_TZ(),升级前必须确认 mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql 已执行,否则检查通过、升级后函数全返回 NULL。










