Karp 的技术博客

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


2026-04-03T15:37:43.png

影响概览

问题严重程度影响范围
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 侧也可能无法正确完成握手。

caching_sha2_password 认证方式说明

修复方案

方案 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.37.x不完整已停止维护
4.87.2 ~ 8.0改进仅安全修复
5.x8.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

MySQL ONLY_FULL_GROUP_BY 模式

问题写法与修复

-- ❌ 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 会报语法错误。

常见新增关键字:QUALIFYARRAYVALUEMEMBERSYSTEMINTERSECTEXCEPT

-- ❌ 字段名为 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

参考

mysql

版权属于:karp
作品采用:本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
更新于: 2026年04月03日 15:38
0

目录

来自 《MySQL 8.0 升级 8.4:PHP 7.3 + Swoole 4.3 兼容性问题与修复方案》