1. 报错现场:为什么连个数据库会栽在“插件未加载”上
先还原一下这个报错的实际场景。某天你用老版本的客户端工具,或者项目里一个用了很久的数据库连接池,去连一台新装好的MySQL服务,结果别人连得好好的,你这儿直接抛出一段让人摸不着头脑的错误:
ERROR 1524 (HY000): Plugin 'mysql_native_password' is not loaded或者在你的应用日志里看到:
java.sql.SQLException: Unable to load authentication plugin 'caching_sha2_password'.顺手再翻一下错误日志,可能还会看到变体:
[Note] Plugin 'mysql_native_password' is not loaded说实话,第一次遇到这个报错的时候,我一瞬间也有点懵。“插件没加载”?我装的MySQL是完整版,又不是精简版,怎么连认证插件都没加载?后来翻了官方文档、做了几次复现实验,才把这条报错的前因后果彻底摸清楚。
这篇东西就是把我排查和解决这个问题的过程完整记录下来。核心结论先放在前面:这个报错几乎都出现在MySQL 8.0及以上版本,原因就是默认认证插件从mysql_native_password换成了caching_sha2_password,而老客户端、老驱动不认新插件,或者是服务端连旧插件都被禁用了。你能用它解决什么?能分清到底该改服务端还是改客户端,该重建账号还是改配置参数,并且知道每个方案背后的利弊。
看完之后,不光能复现解决这一条报错,还能顺带把MySQL账号认证机制的基本盘搞清楚,以后遇到类似认证问题,排查路径基本就是一套。
2. 原理拆解:mysql_native_password到底干了啥,又为什么被替换
2.1 认证插件是什么,它和密码的关系
先别急着改配置。要真正处理这个报错,得先知道mysql_native_password是什么角色。
MySQL的账号登录过程,不是单纯地把密码明文发给服务器去比对。客户端和服务端之间要走一套“挑战-应答”式的认证协议,这套协议的实现方式就被封装在认证插件里。
mysql_native_password是MySQL 5.7及之前版本的默认认证插件。它的算法大致是:服务端存一个密码的哈希值(这个哈希是用SHA1对密码做两次散列再加盐处理得到的,具体细节可以去看MySQL源码里的scramble相关实现),客户端连接时,服务端发一个随机数(scramble),客户端用这个随机数加上自己知道的密码明文,算出一个响应值发给服务端,服务端再用自己存储的哈希值做同样的计算,两边比对,一致就放行。
这个流程本身设计得挺巧妙——密码明文不会在网络上传输,即使被抓包,抓到的也只是加了随机数的响应值,重放攻击基本无效。
2.2 为什么MySQL 8.0要换掉它
mysql_native_password最大的问题不是流程有bug,而是它的哈希算法太弱了。它只用了SHA1,还不是加盐的完整SHA1——在2017年之后,SHA1已经被实践上认为可以在合理算力下被碰撞攻击。对数据库来说,用户密码的哈希值一旦泄露,攻击者完全可以通过彩虹表或者暴力破解来反推出弱密码。
所以MySQL 8.0把默认认证插件切换成了caching_sha2_password。这个新插件用SHA256算法,并且加盐处理,理论上暴力破解的难度会指数级上升。它还缓存了之前认证过的用户连接,减少服务端加解密开销,这也是名字里“caching”的来源。
2.3 插件不加载的真正原因
于是问题就来了:MySQL 8.0虽然提供了caching_sha2_password和mysql_native_password两个插件,但默认只加载前者。也就是说,你在8.0的默认配置里创建的所有新账号,认证插件都是caching_sha2_password。
这时候,老版本客户端(比如MySQL 5.1时代的命令行工具)、老版本驱动(比如很老的PHP mysqli扩展、旧版JDBC驱动、Python的mysql-connector旧版本)来连接,它们只认识mysql_native_password协议。当服务端告诉它“嘿,我的账号用的是caching_sha2_password”,客户端直接傻眼,报出Unable to load authentication plugin 'caching_sha2_password'这种客户端侧错误。
反之,如果你的MySQL服务端配置文件里明确写了default_authentication_plugin=mysql_native_password,但是这个插件实际没被编译进服务端、或者动态加载失败,服务端就会在启动日志里记录Plugin 'mysql_native_password' is not loaded,连接时自然也是同样的报错。
重要提示:启动日志里的“is not loaded”那条记录,在MySQL 8.0.27之前的某些版本里甚至不算错误级别,只是Note级别,但它的直接后果就是所有依赖这个插件的账号都没法登录。
2.4 一个关键认知:账号绑定了插件,不是全局参数说了算
这里非常容易踩坑。很多人以为只要设置了default_authentication_plugin,所有账号就都变成老认证方式了。其实不然。
MySQL的每个用户账号在mysql.user表里都有一行记录,其中plugin字段是创建账号那一刻由全局默认值决定的。你改了全局默认配置,只影响之后新建的账号,旧账号的认证插件字段不会跟着变。所以经常出现这种诡异现象:改了配置文件、重启了服务,老账号还是连接失败,一查mysql.user,plugin字段仍然写着旧值。
明白了这一点,你就能理解下面的所有解决方案,本质上都是在做一件事:让客户端和服务端用的是同一种认证协议。
3. 解决方案全景:按场景选对路子,别乱改一通
3.1 方案一:让服务端加载并启用mysql_native_password(适合老客户端无法升级的场景)
这个方案的核心思路是“向下兼容”。如果你的生产环境里就是有改不动的老客户端,比如运行在十年前旧服务器上的一个PHP 5.3应用,没法升级驱动,那就只能让服务端把旧认证插件打开。
具体操作分三步。
第一步,检查当前服务端到底有没有这个插件。登录MySQL执行:
SHOW PLUGINS;或者查插件目录:
SELECT plugin_name, plugin_status FROM information_schema.plugins WHERE plugin_name LIKE '%native%';如果结果里显示mysql_native_password状态是ACTIVE,说明插件其实是加载了的,问题多半出在账号层面。如果一行都没有,再查一下插件文件是否存在于MySQL的plugin目录(通常叫libmysql_native_password.so或mysql_native_password.dll)。文件在但没加载,可以用INSTALL PLUGIN手动装上:
INSTALL PLUGIN mysql_native_password SONAME 'mysql_native_password.so';第二步,修改配置文件,让默认认证插件变成旧版。编辑MySQL配置文件(Linux常见路径/etc/my.cnf或/etc/mysql/mysql.conf.d/mysqld.cnf),在[mysqld]段下添加:
[mysqld] default_authentication_plugin=mysql_native_password这里有个坑必须提醒:MySQL 8.0.27之后,官方已经在逐步弃用这个参数,有的版本里写这个参数会直接导致mysqld启动失败,报错提示参数未知。我在某次用8.0.34测试时就是这么翻车的。如果遇到这种情况,先用版本兼容的方式处理——见下文方案三。
第三步,重启服务,然后把需要兼容的账号重建或改认证插件。只改全局配置不动账号,你会发现老客户端还是连不上:
ALTER USER 'old_client_user'@'%' IDENTIFIED WITH mysql_native_password BY '你的密码';如果你希望所有根账号都能兼容老客户端:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的root密码';改完之后确认一下:
SELECT user, host, plugin FROM mysql.user WHERE user IN ('root', 'old_client_user');plugin列应该已经变成mysql_native_password了。
3.2 方案二:升级客户端和驱动到支持caching_sha2_password(最优解,推荐)
如果项目里用的都是主流驱动、版本不是特别老,其实没必要让服务端降级。因为caching_sha2_password才是MySQL 8.x的默认方向,未来版本对它的支持只会越来越完善,而mysql_native_password迟早会被移除。
所以第二条路更“政治正确”:升级客户端。
不同技术栈的支持情况大概是这样:
- MySQL官方命令行客户端:8.0以上版本都支持新插件,如果还在用5.x的
mysql命令,升级到8.0.x即可。 - PHP:
mysqlnd从PHP 7.4开始完整支持caching_sha2_password;如果用的是老mysql_*函数(PHP 5时代),那基本只能走方案一。 - Java/JDBC:MySQL Connector/J从8.0.9开始支持新认证,如果项目还在用5.1.x的驱动,建议升级到8.0.x版本。这里要注意,
com.mysql.jdbc.Driver这个类名在Connector/J 8.0里改成了com.mysql.cj.jdbc.Driver。 - Python:
mysql-connector-python8.0以上、PyMySQL从1.0版本左右开始支持新插件。注意有些旧版本PyMySQL对caching_sha2_password支持不完整,连sha256_password也不行,这种只能升级库版本。 - Go:
go-sql-driver/mysql从1.4版本开始支持新认证,升级到1.6+更稳。 - .NET:MySqlConnector这个库很早就支持了,官方Connector/NET较老版本可能需要升级。
升级驱动后,大部分情况下只要连接串里没写死allowPublicKeyRetrieval=false这类参数,就能直接连通。如果用的是JDBC,还有个经典坑:新插件首次连接时需要从服务端获取RSA公钥来加密密码传输,JDBC默认不允许自动获取,会报错。解决办法是在连接串上加:
jdbc:mysql://localhost:3306/db?allowPublicKeyRetrieval=true&useSSL=false或者把公钥文件放到客户端:
jdbc:mysql://localhost:3306/db?serverRSAPublicKeyFile=/path/to/server.pub3.3 方案三:MySQL 8.0.27+版本的特殊处理(如果你没法升级也没法改参数)
前面说了,从8.0.27开始default_authentication_plugin参数被标记为废弃,到了8.0.34我实测中,部分版本里写这个参数直接报错。实际上官方在8.0.27之后引入了新的系统变量调整方式,但mysql_native_password的禁用/启用逻辑发生了变化。
如果你的MySQL版本比较新、又实在需要启用旧插件,正确的操作是确认插件文件存在然后INSTALL PLUGIN,并且不要依赖default_authentication_plugin参数,而是直接对老账号执行ALTER USER ... IDENTIFIED WITH mysql_native_password BY ...。同时也要注意,部分Linux发行版的MySQL二进制包从8.0.28前后就开始去掉对旧插件的编译支持了,那种情况INSTALL PLUGIN也会失败,文件压根不存在。遇到这种,只能老老实实升级客户端,没有第二条路。
实操心得:在MySQL 8.0.34上我遇到过更狠的情况——插件文件都在,但启动时查询
SHOW PLUGINS显示mysql_native_password状态为DISABLED,怎么都启不来。后来发现是发行版的构建去掉了相关模块,只有自己从官方标准二进制包重新安装才解决。所以排查时一定要确认“插件文件存在”不只是存在,还要能被加载执行。
3.4 方案四:账号层面兜底处理(适合临时应急)
还有一种场景是:服务端不想改全局配置,客户端也不能升级,但只有一个特定账号需要兼容。这时候不用动全局配置,直接改这个账号的认证插件就足够了。
ALTER USER 'legacy_app'@'192.168.1.%' IDENTIFIED WITH mysql_native_password BY '临时密码';这会让legacy_app这个账号使用旧插件,而其他所有账号保持新插件,不影响整体安全性。
要注意的细节是:ALTER USER执行后,这个账号的密码会被重置为新密码(即使你用的是BY '临时密码'),所以执行前得确认业务方没有硬编码旧密码,否则会连带出事。如果不希望改密码,可以用:
ALTER USER 'legacy_app'@'192.168.1.%' IDENTIFIED WITH mysql_native_password AS '原密码的哈希值';这个哈希值可以从mysql.user表的authentication_string字段里查出来,复制过来即可。这样只改插件、不改密码。
4. 完整实操复盘:一次模拟环境下的处理全记录
为了把整个过程讲清楚,我自己在本地搭了一个完整环境走了一遍,包括怎么复现、怎么确认、怎么解决,把每个步骤的关键输出都记录在下面。
4.1 环境准备与报错复现
我准备了两台模拟环境:
- 服务端:MySQL 8.0.31,全新安装,未做任何配置修改
- 客户端A:MySQL 5.7命令行工具(模拟老客户端)
- 客户端B:MySQL 8.0命令行工具(模拟新客户端)
在服务端创建测试账号:
CREATE USER 'test_user'@'%' IDENTIFIED BY 'Test@12345'; GRANT ALL ON *.* TO 'test_user'@'%';创建完账号后查一下认证插件:
SELECT user, host, plugin FROM mysql.user WHERE user='test_user';输出:
+-----------+------+-----------------------+ | user | host | plugin | +-----------+------+-----------------------+ | test_user | % | caching_sha2_password | +-----------+------+-----------------------+接着用客户端A(5.7命令行)连接:
mysql -h127.0.0.1 -utest_user -pTest@12345报错:
ERROR 1524 (HY000): Plugin 'mysql_native_password' is not loaded注意,这个报错看起来很像是服务端说“我这边没加载mysql_native_password”,但实际是客户端5.7只支持这种认证,服务端给了新认证,两边对不上。再用客户端B(8.0命令行)连:
mysql -h127.0.0.1 -utest_user -pTest@12345结果:
Welcome to the MySQL monitor...新客户端完全没问题,这进一步确认了问题出在认证插件机制上。
4.2 服务端排查:插件状态、参数检查
首先看服务端到底有没有加载旧插件:
SHOW VARIABLES LIKE 'default_authentication_plugin';输出:
+-------------------------------+-----------------------+ | Variable_name | Value | +-------------------------------+-----------------------+ | default_authentication_plugin | caching_sha2_password | +-------------------------------+-----------------------+再看插件列表:
SELECT plugin_name, plugin_status FROM information_schema.plugins WHERE plugin_name LIKE '%native%';输出为空。这就是很典型的“MySQL 8.0默认不加载mysql_native_password插件”的状态。也就是说,服务端的插件目录里可能有对应的.so文件,但实例没有把它加载进内存。
如果想确认插件文件到底在不在:
ls -l /usr/lib/mysql/plugin/ | grep native或:
SHOW VARIABLES LIKE 'plugin_dir';然后去对应目录里找mysql_native_password.so这类文件名。
4.3 方案落地:让5.7老客户端恢复连接
走完整排查后,我选择了方案一作为演示——因为客户端A是模拟场景下的“没法升级的遗留系统”,这在现实里太常见了。
先尝试手动加载插件:
INSTALL PLUGIN mysql_native_password SONAME 'mysql_native_password.so';如果返回ERROR 1125 (HY000): Function 'mysql_native_password' already exists,说明插件已加载或已存在;如果返回文件找不到,那就确认一下plugin目录里有没有这个.so。在我这个环境下,执行后插件列表变化:
SELECT plugin_name, plugin_status FROM information_schema.plugins WHERE plugin_name LIKE '%native%';输出:
+-----------------------+---------------+ | plugin_name | plugin_status | +-----------------------+---------------+ | mysql_native_password | ACTIVE | +-----------------------+---------------+注意:INSTALL PLUGIN是动态加载,重启MySQL后可能失效。要持久化,得在配置文件里加一行,老版本写法是:
[mysqld] mysql_native_password=ON不过更稳妥的做法是确认default_authentication_plugin参数在当前版本可用时再写入。对于8.0.27+,我建议别写default_authentication_plugin参数,而是直接把需要兼容的账号ALTER掉。也可以写default_authentication_plugin=mysql_native_password,写之前先跑一句看版本:
SELECT VERSION();然后查官方手册确认这个参数是否还生效。现在为了演示稳定,我先只做ALTER:
ALTER USER 'test_user'@'%' IDENTIFIED WITH mysql_native_password BY 'Test@12345';再试客户端A:
mysql -h127.0.0.1 -utest_user -pTest@12345这回连上了。
4.4 记录一下新客户端是否受影响
很多人担心,改成旧插件后,新客户端会不会反而连不上?实测不会。用客户端B(8.0命令行)再连一次:
mysql -h127.0.0.1 -utest_user -pTest@12345同样能连上。这就是一个关键认知:MySQL 8.0的新客户端是兼容旧认证插件的,也就是说,把账号改回旧插件,并不等于把新客户端挡在门外。但反过来,老客户端不兼容新插件。
所以从兼容性优先级来说:能用新客户端就尽量用新客户端,只有被逼无奈才考虑改服务端。
4.5 其他数据库客户端的处理实录
文章到这里,估计有人会遇到“明明我的驱动版本不旧,怎么还报错”的情况。再补充两个实际项目里最常见的变体。
Java连接池场景。某项目用阿里Druid连接池,默认的驱动是com.mysql.jdbc.Driver,连MySQL 8.0时常见报错是:
java.sql.SQLException: Connection driver error: No suitable driver或者:
java.sql.SQLException: Upstream connection error: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Public Key Retrieval is not allowed前者是驱动类名不匹配,后者是allowPublicKeyRetrieval参数问题,都不算认证插件不加载,但经常被误认成同一类报错。实际处理是升级驱动到com.mysql.cj.jdbc.Driver,且连接串加:
?useSSL=false&allowPublicKeyRetrieval=true参数名一眼就能看出来:当客户端用caching_sha2_password认证时需要向服务端请求RSA公钥,默认不允许自动获取,必须显式放行。这里涉及的RSA只是用于传输密码的阶段,不是整个连接开启TLS,所以useSSL=false并不冲突。
Python场景。某项目用PyMySQL连MySQL 8.0,报错:
RuntimeError: 'caching_sha2_password' requires PyMySQL >= 0.7.9但项目环境只能用老版本库。这种就走方案一改账号认证方式,或者在连接时指定auth_plugin_map把caching_sha2_password映射到mysql_native_password。第二种方法其实不推荐,因为密码哈希方式根本不同,很可能失败;实际更稳的做法是直接升级PyMySQL或者改用mysql-connector-python。
5. 常见问题速查与避坑指南
为了后面的人少踩坑,我把这段时间积累的和这个报错相关的典型问题、排查命令、解决方案整理成了一张速查记录。
5.1 问题画像对照表
| 现象 | 常见原因 | 快速排查命令或方法 |
|---|---|---|
服务端日志出现Plugin 'mysql_native_password' is not loaded | 8.0默认未加载该插件;或插件文件缺失、被禁用 | SHOW PLUGINS;ls plugin_dir |
老客户端报ERROR 1524 (HY000) | 账号的plugin字段是caching_sha2_password,老客户端不识别 | SELECT user,host,plugin FROM mysql.user |
新客户端连老账号报Unable to load authentication plugin 'mysql_native_password' | 该场景较少见;可能是客户端库版本过旧或服务端配置强制旧插件 | 升级客户端库 |
连接串提示Public Key Retrieval is not allowed | JDBC连接新认证插件时未允许获取RSA公钥 | 加allowPublicKeyRetrieval=true |
改了default_authentication_plugin但旧账号仍然连不上 | 全局参数只影响新建账号,已有账号需要ALTER USER | ALTER USER ... IDENTIFIED WITH mysql_native_password BY ... |
8.0.27+服务器上写default_authentication_plugin导致启动失败 | 参数在部分新版本上被废弃,解析失败 | 用INSTALL PLUGIN + ALTER USER代替 |
5.2 排查三道手
第一道手:先分清是服务端还是客户端的锅。服务端报错去查mysqld日志,客户端报错看应用日志。如果两边都看到插件相关字样,优先查服务端SHOW PLUGINS,确认插件在不在、ACTIVE还是DISABLED。
第二道手:确定版本兼容边界。列出MySQL服务端版本、客户端工具版本、驱动版本三者的对应关系。也可以参考这张快速判断表:
| 客户端类型 | 支持caching_sha2_password的最低版本 | 备注 |
|---|---|---|
| mysql命令行 | 8.0.x | 5.7及以下不认新插件 |
| JDBC驱动 | 8.0.9+ | 且驱动类名要改成cj |
| mysqlnd (PHP) | PHP 7.4+ | 5.x/7.0-7.3部分支持不完整 |
| PyMySQL | 1.0+ | 旧版本报错直接给具体链接 |
| go-sql-driver | 1.4+ | 建议用新版本 |
| MySqlConnector (.NET) | 1.0+ | 官方Connector/NET较老可能要跟进 |
第三道手:确认账号plugin字段。这个步骤常常被跳过,但最容易被忽视。无论你改全局配置还是改了插件加载状态,账号的plugin字段都可能是旧值残留,所以一定要查:
SELECT user, host, plugin FROM mysql.user WHERE user='目标账号';改完配置后,记得用同一条SQL复查一下,确认已经变成预期值。
5.3 几个容易被坑到的地方
坑一:密码哈希值和plugin字段是两个东西。mysql.user里有authentication_string保存密码哈希,plugin字段决定用什么算法去校验。你改了plugin,哈希值如果不匹配,一样登录失败。所以在ALTER USER换插件时,要么用BY '新密码'重新设置,要么用AS '原哈希'保留旧哈希。
坑二:IDENTIFIED WITH和IDENTIFIED BY的混用解法。一条语句里同时写这两个关键字是允许的,比如:
ALTER USER 'user'@'host' IDENTIFIED WITH mysql_native_password BY 'pass';如果你只写IDENTIFIED WITH mysql_native_password,不带密码,那密码会变成空字符串——这是个极其容易踩中的细节,因为我见过不止一个人手滑执行完,账号登不进去了。
坑三:8.0的caching_sha2_password在首次认证时会走RSA交换,而旧驱动可能因为没开allowPublicKeyRetrieval卡在那里。表面上也报“plugin is not loaded”,实际是公钥获取被拒。排查时不要只盯插件,要把驱动参数也过一遍。
坑四:不要为一两个老账号把整个实例降级成mysql_native_password。这里有个连锁影响:新建的所有账号都会默认使用弱哈希算法,安全性整体下降。而且未来升级MySQL版本时又得清理一批账号。我见过一个项目把所有账号强制改成旧插件,升级到8.4时发现根本没这个插件了,全员登录失败,那叫一个酸爽。
5.4 最佳实践建议
根据我这段时间反复实验和对比,总结出几条通用经验,供参考:
- 如果项目是新启动的,直接用MySQL 8.x的新驱动连MySQL 8.x的服务端,没必要动旧插件。
- 如果项目中只有个别遗留组件必须用老客户端,把这个账号单独ALTER成
mysql_native_password,别动全局配置。 - 如果整个技术栈都停留在MySQL 5.7时代,最简单的路径是服务端加上
default_authentication_plugin=mysql_native_password(只在适配版本里写),然后全库账号统一重建或ALTER。 - 每次改账号认证插件之后,用
SELECT user, host, plugin FROM mysql.user;检查一遍,确保没有账号遗漏。 - 在生产环境操作之前,先在测试环境完整跑一遍:创建账号、改插件、老客户端连接、新客户端连接、应用侧接口回归。
- 对于Java项目,连接串里把
useSSL和allowPublicKeyRetrieval的关系理清:如果开了SSL,就不需要公钥获取;如果没开SSL,最好显式加allowPublicKeyRetrieval=true。 - 连接池初始化时,尽量用8.0以上的驱动,并利用连接池的
connectionTestQuery或者validationQuery探测认证通道是否正常,避免线上突发全部断连。
6. 实用扩展:如何主动预防这类报错再次出现
既然这次踩了坑,后续就得建立一套主动预防的机制。这里说几个我在日常维护中常用的做法。
6.1 初始化MySQL时就把认证插件策略定好
在新装MySQL 8.0时,如果明确知道环境里有老客户端,直接在初始化配置阶段就把认证插件定好:
[mysqld] default_authentication_plugin=mysql_native_password但要注意版本,8.0.27及以上要先用官方文档确认参数可用;如果不可用,就在安装完成后用批量脚本把所有账号ALTER一遍。
批量生成ALTER语句的姿势也分享一下。先查出来需要改的账号:
SELECT CONCAT('ALTER USER ''', user, '''@''', host, ''' IDENTIFIED WITH mysql_native_password AS ''', authentication_string, ''';') FROM mysql.user WHERE plugin = 'caching_sha2_password';把这批SQL导出到文件里,检查一遍后在生产执行。这样能保证密码哈希值不变,业务无感切换。
6.2 定期巡检账号认证插件分布
可以用一条SQL快速看出整个实例的认证插件分布情况:
SELECT plugin, COUNT(*) AS account_count FROM mysql.user GROUP BY plugin;如果某个插件占比异常偏高,说明可能存在大范围兼容性妥协,要关注后续升级风险。
6.3 监控连接失败的错误码
运维层面,可以把MySQL错误日志里的ERROR 1524 (HY000)以及应用侧日志里Unable to load authentication plugin作为监控关键词,接上告警。一旦出现,多半是某台客户端机器的驱动版本太旧、或者有人改动了账号plugin字段,早点发现早点处理。
6.4 升级驱动的顺带检查清单
升级JDBC/PyMySQL/go-sql-driver等驱动时,别只换jar包、wheel包就完事,把连接串参数一并检查清楚。常见要看的参数包括:
- JDBC:
driverClass是否从com.mysql.jdbc.Driver改成com.mysql.cj.jdbc.Driver - JDBC:
allowPublicKeyRetrieval是否设置 - PyMySQL:确认版本是否支持
caching_sha2_password - mysqlnd:确认PHP版本是否在7.4及以上
- 各语言驱动:确认连接超时配置没有被默认值拖垮
7. 写在最后的一点体会
mysql_native_password is not loaded这个报错,说难不难,说简单也不简单。它本质上就是一个“插件机制变更”引发的兼容性问题,但因为牵扯到MySQL版本、账号字段、驱动版本、配置参数好几个层面,排查起来很容易在多条线之间来回折腾。
我个人最深的体会是:在处理这个报错时,先确认账号级别的plugin字段,比先改全局配置靠谱得多。因为很多场景下,你只要精准地把目标账号切换到客户端支持的插件,业务就能立刻恢复;而盲目改全局配置文件,反而可能拖累其他正常账号,甚至因为版本不兼容导致服务起不来。
另外,多多少少要克制“向下兼容”的冲动。新版本把mysql_native_password从默认位置挪走,本来就是安全升级的一部分。如果条件允许,优先升级客户端、升级驱动,让整个技术栈站在新协议的同一侧。全员都用新协议,以后就不会再为这条报错加班了。真遇到“升级成本极高”的遗留系统,再做局部账号的兼容处理,把影响范围控制在最小。
如果这篇文章帮你解决了问题,后续维护中再遇到什么新变体,欢迎带着现场日志回来一起对照。今天踩过的坑,明天可能就是躲过的雷。