MySQL 8.0 升级至 8.4,属于小版本跨越,但 8.4 是一个 LTS 版本,做了若干破坏性变更。结合 PHP 7.3 + Swoole 4.3 的实际情况,整理出以下兼容性问题和对应处理方案。

影响概览
| 问题 | 严重程度 | 影响范围 |
|---|---|---|
mysql_native_password 插件被移除 | 🔴 高 | 连接直接失败 |
| Swoole 连接池持久连接行为异常 | 🔴 高 | 协程环境下连接错乱 |
ONLY_FULL_GROUP_BY 更严格 | 🟡 中 | 部分查询报错 |
| 新增保留关键字 | 🟡 中 | SQL 语法错误 |
| 废弃配置项导致启动失败 | 🟡 中 | MySQL 无法启动 |
| 默认排序规则变更 | 🟢 低 | JOIN 排序规则冲突 |
问题一:mysql_native_password 插件被正式移除
问题描述
MySQL 8.0 已将默认认证插件改为 caching_sha2_password,但 mysql_native_password 仍作为内置插件保留。8.4 将其彻底移除,不再默认加载,需显式启用。
如果 8.0 时期的用户账号是用 mysql_native_password 创建的,升级 8.4 后连接会直接报错:
mysqli::__construct(): The server requested authentication method unknown
to the client [caching_sha2_password] (HY000/2054)PHP 7.3 内置的 mysqlnd 版本对 caching_sha2_password 握手协议支持不完整,即使 MySQL 端改了认证方式,PHP 侧也可能无法正确完成握手。
![]()
修复方案
方案 A(应急):在 my.cnf 中重新加载该插件
[mysqld]
mysql_native_password = ON重启 MySQL 后,将存量用户认证方式回写:
ALTER USER 'your_user'@'%'
IDENTIFIED WITH mysql_native_password BY 'your_password';
FLUSH PRIVILEGES;
-- 验证
SELECT user, host, plugin FROM mysql.user WHERE user = 'your_user';方案 B(长期):升级 PHP,使用 caching_sha2_password
PHP 8.1+ 的 mysqlnd 完整支持 SHA-2 认证,升级后可将账号迁移至新认证方式:
ALTER USER 'your_user'@'%'
IDENTIFIED WITH caching_sha2_password BY 'your_password';问题二:Swoole 4.3 连接池在 MySQL 8.4 下的兼容问题
这是 Swoole 环境特有的问题,也是最容易被忽视的。
2.1 长连接被服务端断开后连接池未感知
MySQL 8.4 对空闲连接的清理更积极,wait_timeout 默认 28800 秒(8小时)。连接池中的空闲连接被 MySQL 服务端单方面断开后,Swoole 4.3 的连接池不能可靠地感知到,下次复用时报:
MySQL server has gone away (errno 2006)修复:取出连接时先 ping,或设置连接最大空闲时间
// 取出连接时检测是否存活
$db = $pool->get();
if (!$db->ping()) {
$db->close();
$db = new mysqli($host, $user, $pass, $dbname);
}// 连接池最大空闲时间设为小于 MySQL wait_timeout
$pool->setMaxIdleTime(3600); // 1小时,小于 MySQL 的 8小时默认值2.2 握手阶段协程切换导致连接状态错乱
Swoole 4.3 对 mysqli 的协程化改造不完整,在 caching_sha2_password 的多步握手过程中,协程切换可能导致连接上下文错乱。具体表现为偶发性认证失败或连接挂起。
修复:强制 mysql_native_password(配合问题一的方案 A),绕过多步握手
单步握手协议下 Swoole 4.3 的行为是稳定的。
2.3 事务状态未清理导致连接污染
协程中开启事务但未正确提交/回滚就归还连接,下一个协程复用该连接时继承了脏状态。MySQL 8.4 对事务隔离级别默认行为有微调,更容易暴露这个问题。
修复:归还连接前强制 rollback
// 归还连接到池之前
if ($db->info !== null) {
$db->rollback(); // 清理未提交事务
}
$pool->put($db);Swoole 版本对比
| Swoole 版本 | PHP 支持 | mysqli 协程化 | 维护状态 |
|---|---|---|---|
| 4.3 | 7.x | 不完整 | 已停止维护 |
| 4.8 | 7.2 ~ 8.0 | 改进 | 仅安全修复 |
| 5.x | 8.1+ | 完整 | 活跃维护 |
根本解决方案是结合 PHP 升级,将 Swoole 一并升级至 5.x。
问题三:ONLY_FULL_GROUP_BY 执行更严格
问题描述
MySQL 8.0 已开启该模式,8.4 执行更严格。SELECT 中出现未在 GROUP BY 中列出、也未被聚合函数包裹的字段,直接报错:
ERROR 1055 (42000): Expression #2 of SELECT list is not in GROUP BY clause
and contains nonaggregated column
问题写法与修复
-- ❌ username 未聚合
SELECT user_id, username, COUNT(*) AS cnt
FROM orders
GROUP BY user_id;
-- ✅ 方案 A:补全 GROUP BY
SELECT user_id, username, COUNT(*) AS cnt
FROM orders
GROUP BY user_id, username;
-- ✅ 方案 B:ANY_VALUE(),适用于该字段在同组内确实相同的场景
SELECT user_id, ANY_VALUE(username), COUNT(*) AS cnt
FROM orders
GROUP BY user_id;在测试环境执行全量回归,通过报错日志逐条定位问题 SQL。
问题四:新增保留关键字
MySQL 8.4 新增了若干保留关键字,若字段名或表名与其重名,SQL 会报语法错误。
常见新增关键字:QUALIFY、ARRAY、VALUE、MEMBER、SYSTEM、INTERSECT、EXCEPT
-- ❌ 字段名为 value
SELECT value FROM config;
-- ERROR 1064: You have an error in your SQL syntax
-- ✅ 加反引号
SELECT `value` FROM `config`;扫描脚本:
SELECT TABLE_NAME, COLUMN_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'your_db'
AND COLUMN_NAME IN (
'qualify','array','value','member','system',
'intersect','except','rank','groups','window'
);问题五:废弃配置项导致 MySQL 无法启动
query_cache_* 系列参数在 MySQL 8.0 已移除,8.4 中如果 my.cnf 仍保留这些配置,MySQL 启动直接失败。
# ❌ 必须删除
query_cache_type = 1
query_cache_size = 64M
query_cache_limit = 2M
innodb_file_format = Barracuda
innodb_large_prefix = ON# expire_logs_days 已废弃,改用:
binlog_expire_logs_seconds = 604800 # 7天升级前先验证配置文件:
mysqld --validate-config --defaults-file=/etc/mysql/my.cnf问题六:默认排序规则变更
MySQL 8.4 新建库/表的默认排序规则为 utf8mb4_0900_ai_ci。旧库用的是 utf8mb4_general_ci,跨库 JOIN 或字符串比较时会报:
ERROR 1267 (HY000): Illegal mix of collations
(utf8mb4_general_ci,IMPLICIT) and (utf8mb4_0900_ai_ci,IMPLICIT)由于是 8.0 → 8.4,旧表排序规则不会自动变更,问题通常出现在新建表与旧表 JOIN 时。
-- 查看所有表的排序规则
SELECT TABLE_NAME, TABLE_COLLATION
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'your_db';
-- 新建表时显式指定,与旧表保持一致
CREATE TABLE new_table (
...
) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;修复执行顺序
第一步:升级前(在 8.0 环境操作)
-- 将所有应用账号改为 mysql_native_password
ALTER USER 'your_user'@'%'
IDENTIFIED WITH mysql_native_password BY 'your_password';
FLUSH PRIVILEGES;# 检查配置文件是否有废弃项
mysqld --validate-config --defaults-file=/etc/mysql/my.cnf第二步:升级 MySQL 至 8.4 后
# my.cnf 加入
[mysqld]
mysql_native_password = ON# 重启后验证连接
php -r "
\$db = new mysqli('127.0.0.1', 'your_user', 'your_password', 'your_db');
echo \$db->connect_error ? 'FAIL: '.\$db->connect_error : 'OK';
"第三步:业务 SQL 修复
在测试环境全量回归,收集报错 SQL,逐条处理:
- GROUP BY 非完全聚合 → 补字段或 ANY_VALUE()
- 保留关键字冲突 → 加反引号
- 新建表排序规则 → 显式指定 COLLATE
第四步:Swoole 连接池加固
- 加入 ping 存活检测
- 设置连接最大空闲时间 < MySQL
wait_timeout - 归还连接前清理未提交事务
Checklist
升级前
- [ ]
mysqld --validate-config检查配置,删除废弃项 - [ ] 所有应用账号改为
mysql_native_password - [ ] 备份数据库
升级后 MySQL
- [ ]
my.cnf加入mysql_native_password = ON - [ ] 验证 MySQL 正常启动
- [ ] 验证应用连接正常
业务 SQL
- [ ] 扫描 GROUP BY 非完全聚合
- [ ] 扫描保留关键字冲突字段/表名
- [ ] 新建表显式指定排序规则
Swoole 连接池
- [ ] 加入 ping 存活检测
- [ ] 设置连接最大空闲时间 < MySQL
wait_timeout - [ ] 归还连接前执行 rollback 清理事务状态
中期规划
- [ ] PHP 升级至 8.1+,Swoole 升级至 5.x
- [ ] 账号认证迁移至
caching_sha2_password - [ ] 删除
my.cnf中的mysql_native_password = ON