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_pluginmysql_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.userplugin字段仍然写着旧值。明白了这一点你就能理解下面的所有解决方案本质上都是在做一件事让客户端和服务端用的是同一种认证协议。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_pluginmysql_native_password这里有个坑必须提醒MySQL 8.0.27之后官方已经在逐步弃用这个参数有的版本里写这个参数会直接导致mysqld启动失败报错提示参数未知。我在某次用8.0.34测试时就是这么翻车的。如果遇到这种情况先用版本兼容的方式处理——见下文方案三。第三步重启服务然后把需要兼容的账号重建或改认证插件。只改全局配置不动账号你会发现老客户端还是连不上ALTER USER old_client_user% IDENTIFIED WITH mysql_native_password BY 你的密码;如果你希望所有根账号都能兼容老客户端ALTER USER rootlocalhost 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即可。PHPmysqlnd从PHP 7.4开始完整支持caching_sha2_password如果用的是老mysql_*函数PHP 5时代那基本只能走方案一。Java/JDBCMySQL Connector/J从8.0.9开始支持新认证如果项目还在用5.1.x的驱动建议升级到8.0.x版本。这里要注意com.mysql.jdbc.Driver这个类名在Connector/J 8.0里改成了com.mysql.cj.jdbc.Driver。Pythonmysql-connector-python8.0以上、PyMySQL从1.0版本左右开始支持新插件。注意有些旧版本PyMySQL对caching_sha2_password支持不完整连sha256_password也不行这种只能升级库版本。Gogo-sql-driver/mysql从1.4版本开始支持新认证升级到1.6更稳。.NETMySqlConnector这个库很早就支持了官方Connector/NET较老版本可能需要升级。升级驱动后大部分情况下只要连接串里没写死allowPublicKeyRetrievalfalse这类参数就能直接连通。如果用的是JDBC还有个经典坑新插件首次连接时需要从服务端获取RSA公钥来加密密码传输JDBC默认不允许自动获取会报错。解决办法是在连接串上加jdbc:mysql://localhost:3306/db?allowPublicKeyRetrievaltrueuseSSLfalse或者把公钥文件放到客户端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_app192.168.1.% IDENTIFIED WITH mysql_native_password BY 临时密码;这会让legacy_app这个账号使用旧插件而其他所有账号保持新插件不影响整体安全性。要注意的细节是ALTER USER执行后这个账号的密码会被重置为新密码即使你用的是BY 临时密码所以执行前得确认业务方没有硬编码旧密码否则会连带出事。如果不希望改密码可以用ALTER USER legacy_app192.168.1.% IDENTIFIED WITH mysql_native_password AS 原密码的哈希值;这个哈希值可以从mysql.user表的authentication_string字段里查出来复制过来即可。这样只改插件、不改密码。4. 完整实操复盘一次模拟环境下的处理全记录为了把整个过程讲清楚我自己在本地搭了一个完整环境走了一遍包括怎么复现、怎么确认、怎么解决把每个步骤的关键输出都记录在下面。4.1 环境准备与报错复现我准备了两台模拟环境服务端MySQL 8.0.31全新安装未做任何配置修改客户端AMySQL 5.7命令行工具模拟老客户端客户端BMySQL 8.0命令行工具模拟新客户端在服务端创建测试账号CREATE USER test_user% IDENTIFIED BY Test12345; GRANT ALL ON *.* TO test_user%;创建完账号后查一下认证插件SELECT user, host, plugin FROM mysql.user WHERE usertest_user;输出---------------------------------------- | user | host | plugin | ---------------------------------------- | test_user | % | caching_sha2_password | ----------------------------------------接着用客户端A5.7命令行连接mysql -h127.0.0.1 -utest_user -pTest12345报错ERROR 1524 (HY000): Plugin mysql_native_password is not loaded注意这个报错看起来很像是服务端说“我这边没加载mysql_native_password”但实际是客户端5.7只支持这种认证服务端给了新认证两边对不上。再用客户端B8.0命令行连mysql -h127.0.0.1 -utest_user -pTest12345结果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_passwordON不过更稳妥的做法是确认default_authentication_plugin参数在当前版本可用时再写入。对于8.0.27我建议别写default_authentication_plugin参数而是直接把需要兼容的账号ALTER掉。也可以写default_authentication_pluginmysql_native_password写之前先跑一句看版本SELECT VERSION();然后查官方手册确认这个参数是否还生效。现在为了演示稳定我先只做ALTERALTER USER test_user% IDENTIFIED WITH mysql_native_password BY Test12345;再试客户端Amysql -h127.0.0.1 -utest_user -pTest12345这回连上了。4.4 记录一下新客户端是否受影响很多人担心改成旧插件后新客户端会不会反而连不上实测不会。用客户端B8.0命令行再连一次mysql -h127.0.0.1 -utest_user -pTest12345同样能连上。这就是一个关键认知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且连接串加?useSSLfalseallowPublicKeyRetrievaltrue参数名一眼就能看出来当客户端用caching_sha2_password认证时需要向服务端请求RSA公钥默认不允许自动获取必须显式放行。这里涉及的RSA只是用于传输密码的阶段不是整个连接开启TLS所以useSSLfalse并不冲突。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 loaded8.0默认未加载该插件或插件文件缺失、被禁用SHOW PLUGINSls 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 allowedJDBC连接新认证插件时未允许获取RSA公钥加allowPublicKeyRetrievaltrue改了default_authentication_plugin但旧账号仍然连不上全局参数只影响新建账号已有账号需要ALTER USERALTER 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.x5.7及以下不认新插件JDBC驱动8.0.9且驱动类名要改成cjmysqlnd (PHP)PHP 7.45.x/7.0-7.3部分支持不完整PyMySQL1.0旧版本报错直接给具体链接go-sql-driver1.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 userhost 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_pluginmysql_native_password只在适配版本里写然后全库账号统一重建或ALTER。每次改账号认证插件之后用SELECT user, host, plugin FROM mysql.user;检查一遍确保没有账号遗漏。在生产环境操作之前先在测试环境完整跑一遍创建账号、改插件、老客户端连接、新客户端连接、应用侧接口回归。对于Java项目连接串里把useSSL和allowPublicKeyRetrieval的关系理清如果开了SSL就不需要公钥获取如果没开SSL最好显式加allowPublicKeyRetrievaltrue。连接池初始化时尽量用8.0以上的驱动并利用连接池的connectionTestQuery或者validationQuery探测认证通道是否正常避免线上突发全部断连。6. 实用扩展如何主动预防这类报错再次出现既然这次踩了坑后续就得建立一套主动预防的机制。这里说几个我在日常维护中常用的做法。6.1 初始化MySQL时就把认证插件策略定好在新装MySQL 8.0时如果明确知道环境里有老客户端直接在初始化配置阶段就把认证插件定好[mysqld] default_authentication_pluginmysql_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包就完事把连接串参数一并检查清楚。常见要看的参数包括JDBCdriverClass是否从com.mysql.jdbc.Driver改成com.mysql.cj.jdbc.DriverJDBCallowPublicKeyRetrieval是否设置PyMySQL确认版本是否支持caching_sha2_passwordmysqlnd确认PHP版本是否在7.4及以上各语言驱动确认连接超时配置没有被默认值拖垮7. 写在最后的一点体会mysql_native_password is not loaded这个报错说难不难说简单也不简单。它本质上就是一个“插件机制变更”引发的兼容性问题但因为牵扯到MySQL版本、账号字段、驱动版本、配置参数好几个层面排查起来很容易在多条线之间来回折腾。我个人最深的体会是在处理这个报错时先确认账号级别的plugin字段比先改全局配置靠谱得多。因为很多场景下你只要精准地把目标账号切换到客户端支持的插件业务就能立刻恢复而盲目改全局配置文件反而可能拖累其他正常账号甚至因为版本不兼容导致服务起不来。另外多多少少要克制“向下兼容”的冲动。新版本把mysql_native_password从默认位置挪走本来就是安全升级的一部分。如果条件允许优先升级客户端、升级驱动让整个技术栈站在新协议的同一侧。全员都用新协议以后就不会再为这条报错加班了。真遇到“升级成本极高”的遗留系统再做局部账号的兼容处理把影响范围控制在最小。如果这篇文章帮你解决了问题后续维护中再遇到什么新变体欢迎带着现场日志回来一起对照。今天踩过的坑明天可能就是躲过的雷。