
1. 这个报错到底在说什么先还原一下场景某天你在终端里连 MySQL或者某个应用启动时突然抛出一行红色的错误Plugin mysql_native_password is not loaded。第一次见到的人很容易懵——这个mysql_native_password是什么为什么没有加载我密码没错啊怎么突然连不上了这个报错通常出现在 MySQL 8.0 及以上版本的环境里典型触发路径有两条一条是你手动执行了CREATE USER ... IDENTIFIED WITH mysql_native_password BY ...另一条是从 MySQL 5.7 把整个实例的default_authentication_plugin指到了mysql_native_password。结果服务端告诉你我根本没有这个插件。听起来很像是环境坏了但本质上不是环境问题而是 MySQL 在认证机制上做了一次代际切换你的使用习惯还停留在上一个时代。MySQL 8.0 从发布起默认的认证插件就是caching_sha2_password不再是 5.7 时代人人都在用的mysql_native_password。mysql_native_password这个插件在 8.0 里虽然还带着“兼容模式”继续存在但它默认不会主动加载。如果你没有显式开启服务端在遇到IDENTIFIED WITH mysql_native_password这种写法时就会直接甩出标题里这个错误。到 MySQL 8.4 和 9.0 时代这个插件干脆连存在感都找不到了直接进入禁用/移除序列。这里要先说清楚一个关键认知这个报错不是“MySQL 崩了”也不是“密码错了”更不是“权限问题”。它是 MySQL 的插件机制在告诉你——你指定的认证插件在当前实例里不存在或者未被启用。想解决它你有两条路可以走一是把认证方式改回 MySQL 支持的默认插件二是在 MySQL 里把mysql_native_password插件重新启用。但到底选哪条路背后牵扯到客户端兼容性、安全策略、运维习惯等一系列问题不是随便选一个就完事的。这篇文章会把这个报错从原理到修复、从服务端到客户端、从快速解决到长期规划全部拆开讲一遍。我写的时候会尽量少讲废话多给能直接落地的东西。不管你是刚把 MySQL 5.7 升到 8.0 的 DBA还是正在被同事甩锅说“数据库连接挂了”的后端开发读完应该都能自己判断选择哪种修复策略并且顺手把客户端那一堆隐藏坑也填上。2. 背后的插件机制与设计逻辑2.1 mysql_native_password 的历史地位mysql_native_password是 MySQL 从 4.1 时代开始用的认证插件一直服役到 5.7十几年没有怎么大改过。它的核心原理很简单服务端保存一个密码的哈希值客户端连接时用同样的哈希算法对密码做一次摘要服务端比对摘要结果。整个过程不传明文密码而且计算非常快因此当年它是兼容性最好的选择几乎所有语言的数据库驱动都天然支持它。但是这套机制的安全短板也在业界被诟病了很久它使用的 SHA-1 哈希算法已经被认为强度不够而且它的挑战-响应机制在大量并发连接下存在中间人攻击的暴露面。简单说它是“够用但不安全”的典型代表只是大家用惯了没有动力去换。MySQL 官方在 5.7 时代就开始铺垫替代方案把sha256_password插件塞了进来但一直没转正为默认。真正下决心切换是在 8.0。MySQL 8.0 将默认认证插件改成了caching_sha2_password它能做到和旧的mysql_native_password一样快的连接速度但密码哈希强度更高、支持 RSA 公钥加密传输密码并且更符合当前安全审计的要求。2.2 为什么服务端不愿加载它很多人在报错后第一反应是“那我在 my.cnf 里把默认认证插件改成 mysql_native_password 不就行了”。这个思路没错但在 MySQL 8.0 里你得先确认一件事你的二进制版本里到底还带不带这个插件文件。在 MySQL 8.0 的早期小版本比如 8.0.0 到 8.0.18 左右mysql_native_password作为内置插件随服务端一起提供你说一声它就在。但在某些发行版、某些云厂商的托管数据库实例里这个插件文件可能被有意移除了或者被默认配置成了“不加载”。你在SHOW PLUGINS里看不到它在INFORMATION_SCHEMA.PLUGINS里查不到它自然一引用就报错。到 MySQL 8.4 和更新的版本情况更直接MySQL 官方在 8.0 里宣布mysql_native_password是 deprecated弃用并在 8.4 版本中默认禁用这个插件再到 9.0 直接移除。也就是说如果你用的是 8.4 或 9.x那么在服务端层面已经没有“启用这个插件”这个选项了唯一的选择是让使用者改用caching_sha2_password。MySQL 的设计逻辑其实是合理的一个已经宣布弃用的认证插件继续默认加载只会给所有人一种“我还能继续用”的错误安全感还不如直接推一把让大家迁移到新插件上。问题在于生态里的大量老客户端、老框架、老连接池并不买账这才出现了一批“明知新插件更好但就是连不上”的项目。2.3 报错触发的完整链条这个报错的触发路径我整理下来主要就三条你在 SQL 里显式指定了IDENTIFIED WITH mysql_native_password BY但目标服务端没有开启该插件。你在my.cnf或启动参数里设置了default_authentication_pluginmysql_native_password同样需要插件存在且可被加载。某些第三方管理工具在生成建用户语句时会自动带上IDENTIFIED WITH mysql_native_password你信任地拷贝执行然后撞上这个报错。无论哪条最终 MySQL 的认证插件管理器都找不到对应的插件名于是抛出Plugin xxx is not loaded并且顺带告诉你plugin is not loaded的同时不会继续执行之后的账号创建逻辑。所以你会看到哪怕只是建用户这一个动作后面的授权语句压根不会走到。这里我建议所有人在排查时先分清“语法报错”和“插件报错”因为这类报错和Unknown authentication plugin很容易搞混。3. 修复方案对比与选型分析3.1 方案A重建用户并改用 caching_sha2_password这个方案的核心思路是不与服务端对着干承认mysql_native_password已过时把所有新账号全部改用caching_sha2_password。具体操作很简单。先用一个有CREATE USER权限的管理员账号登入然后执行CREATE USER app_user% IDENTIFIED WITH caching_sha2_password BY StrongPassword123!; GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO app_user%; FLUSH PRIVILEGES;如果是已有的用户想从mysql_native_password或者报错状态里转过来可以这样改ALTER USER app_user% IDENTIFIED WITH caching_sha2_password BY StrongPassword123!;执行完再用客户端工具测试连接。只要能连上说明服务端这边已经搞定了。这个方案最大的优势是符合 MySQL 8.0 以后的默认策略以后升级到 8.4、9.0 都不会再遇到插件兼容问题。但它的前提是你的客户端、JDBC 驱动、ORM 框架、连接池组件都支持caching_sha2_password。目前主流的 MySQL 官方驱动、开源的 ORM 框架基本都兼容了唯一需要特别注意的是一些老旧的、不再维护的内部组件以及部分低版本开发语言驱动。如果你的应用恰好踩中这些雷那就需要在客户端也同步做调整。3.2 方案B服务端重新启用 mysql_native_password这个方案适合短暂过渡。首先确认下你的 MySQL 版本是否还支持这个插件。如果是 8.0 的某个版本可以通过配置文件开启。在my.cnf或my.ini的[mysqld]段加上[mysqld] mysql_native_passwordON或者[mysqld] default_authentication_pluginmysql_native_password修改之后重启 MySQL 服务。重启完登录 MySQL执行SHOW PLUGINS;如果看到一行mysql_native_password | ACTIVE | AUTHENTICATION | NULL说明插件已经加载成功。这时候你再执行CREATE USER IDENTIFIED WITH mysql_native_password就不会报错了。但这个方案有两点必须提醒你第一mysql_native_passwordON这个配置项在 MySQL 8.0 的早期和后期版本写法有差异遇到不生效时需要用default_authentication_plugin兜底。第二如果你使用的是云数据库托管服务很可能根本没权限修改服务端全局配置。那一类环境下方案B直接走不通只能走方案A或者在云平台的控制台参数组里看看有没有同名参数可以改。3.3 两个方案的隐蔽成本从技术动作上看方案B比方案A简单得多一条配置重启就完事。但从项目长期健康度来看方案B是典型的“短期止痛长期留债”。我见过不少团队为了让某个三五年没更新过的内部系统连上 MySQL 8.0直接全局改回mysql_native_password。表面上所有系统都恢复了但隐患在于全局使用mysql_native_password意味着新创建的账号也全是旧认证方式以后想迁回新插件又要逐个清理。安全扫描或等保检查大概率会把这个插件列为高风险项审计的时候很难解释。当 MySQL 版本最终升到 8.4 或 9.x这个配置项直接失效系统会再次面临同样的报错且到时连临时回退方案都没得选。这不是说方案B绝对不能碰而是说你要把它当一个有时间窗口的短期过渡手段而不是长期配置。我个人建议如果只是临时给某个老服务开一条逃生通道用方案B如果是在做一个新项目或计划长期维护一个系统直接用方案A一步到位不折腾。4. 实战修复过程与验证清单4.1 先确认当前服务端的插件状态不管最终选哪个方案动手修复之前第一件事永远是搞清楚 MySQL 实例现在知道哪些插件。这一步不会改任何东西纯查状态用来定位问题边界。登录 MySQL 后执行SHOW PLUGINS;输出的表格里会列出所有插件及状态。重点看最后几行关于sha2_password、caching_sha2_password、mysql_native_password的记录。如果是 8.0 但未见mysql_native_password那就确认了——当前实例没有加载这个插件。也可以用更精确的查询SELECT plugin_name, plugin_status FROM information_schema.plugins WHERE plugin_name LIKE %password% OR plugin_name LIKE %sha2%;这个查询可以让你在SHOW PLUGINS输出太长的情况下快速过滤出认证相关插件。还要顺便看下默认认证插件设置SHOW VARIABLES LIKE default_authentication_plugin;在 MySQL 8.0 里返回值应该是caching_sha2_password。如果这个值已经被改成mysql_native_password而插件又没有加载那就会出现更魔幻的报错不光建用户失败连普通密码登录可能都会异常。4.2 用户级修复的执行细节确认插件状态后走方案A的可以直接操作。这里给一个更完整的示例。假设应用需要连接order_db库账号名叫order_app允许从内网网段192.168.10.%连接CREATE USER order_app192.168.10.% IDENTIFIED WITH caching_sha2_password BY Order2024#Secure; GRANT SELECT, INSERT, UPDATE, DELETE ON order_db.* TO order_app192.168.10.%; FLUSH PRIVILEGES;注意密码里如果包含特殊字符建议用强密码但避开容易在连接串里引起转义问题的符号。比如、#、%在 URL 连接串里如果没转义会直接被解析成特殊用途字符这是最常见的“SQL能建但代码连不上”事故源头。再强调一个细节FLUSH PRIVILEGES在创建和授权语句之后不是必须的但多人操作同一套环境的场景下执行一下能避免一些肉眼看不见的权限缓存问题。它的开销不大就当买个保险。如果是ALTER USER方式修复已有账号尤其要注意不能写漏BY子句。只写ALTER USER x% IDENTIFIED WITH caching_sha2_password;不会报语法错误但会把密码改没了。轻则连接被拒重则整个应用不可用。4.3 全局配置修改的落地操作如果确实要走方案B建议先把配置文件备份一份再改。以 Linux 上的 MySQL 8.0 为例cp /etc/my.cnf /etc/my.cnf.bak.$(date %Y%m%d%H%M%S)随后编辑my.cnf[mysqld] default_authentication_pluginmysql_native_password保存后重启服务systemctl restart mysqld重启后再次进入 MySQL 执行前面的插件状态查询。如果插件状态显示 ACTIVE那就试建一个用户CREATE USER legacy_user% IDENTIFIED WITH mysql_native_password BY LegacyPass!123;没有报错就说明全局启用成功了。如果用的不是 systemd 而是旧式 init 脚本用service mysql restart也行。有些版本重启后可能报错unknown variable default_authentication_pluginmysql_native_password这通常意味着该版本已不支持此参数或者插件文件已被编译时去除。此时继续挣扎没有意义直接返回方案A。4.4 完整验证清单很多人在修复完服务端就立刻去连数据库连上了就说“搞定了”。但真正的完整验证应该包含以下几个维度服务端状态SHOW PLUGINS能看到目标插件状态为ACTIVE。用户信息执行SELECT user, host, plugin FROM mysql.user WHERE user你的账号;确认账号的 plugin 字段与预期一致。明文密码登录用 MySQL 命令行客户端用账号密码真实登录一次不能跳过。应用层连接先用本地脚本或 API 工具连接一次再用业务代码连接一次两层都要通。字符集和时区连接成功后查看SHOW VARIABLES LIKE character_set%和SELECT NOW();防止修复认证插件后暴露出新的配置问题。我自己在实战中遇到最多的情况是命令行列客户端连上了但应用连不上。因为应用侧的连接参数里往往还带着老驱动或旧配置。所以验证清单里应用层连接是绝对不能跳的一步。5. 客户端适配与连接串调参5.1 各类驱动对 caching_sha2_password 的支持差异服务端切到caching_sha2_password之后客户端的支持情况会成为新的瓶颈。拿最常用的几种来说MySQL 官方 JDBC 驱动Connector/J8.0.9 及以上版本完全支持caching_sha2_password无需额外操作。但连接串里强烈建议加allowPublicKeyRetrievaltrue否则在某些非 SSL 连接场景下驱动无法自动获取服务端公钥会报Public Key Retrieval is not allowed。PHP 的 mysqli 和 PDO_MySQLPHP 7.4.20 之后的版本基本支持但老版本 PHP 7.1、7.2 在连接时会报Server sent public key相关错误应对方式是升级 PHP 或在服务端配置mysql_native_password过渡。Python 的 PyMySQL需要安装较新版本或改用mysql-connector-python。使用 PyMySQL 时如果服务端认证插件是caching_sha2_password需要在创建连接时显式指定auth_plugincaching_sha2_password。Go 的 go-sql-driver较新版本兼容但部分旧版本驱动需要加allowCleartextPasswordstrue之类的参数。这个阶段最容易踩的坑是把责任全推给服务端拼命查 SQL 语句。实际上服务端已经改了剩下就是客户端驱动版本的问题。排查建议是先看官方发布日志确认版本对caching_sha2_password的支持状态。5.2 一个典型的 JDBC 连接串改造以 Java 后端最常见的 Spring Boot 项目为例MySQL 8.0 下比较稳妥的 JDBC URL 长这样jdbc:mysql://192.168.10.20:3306/order_db?useUnicodetruecharacterEncodingutf8mb4useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue对比老式连接串新增或改动的关键参数是allowPublicKeyRetrievaltrue。在 MySQL 8.0 默认启用caching_sha2_password后如果连接不是 SSL驱动需要额外获取 RSA 公钥来完成密码传输。这个参数默认是 false会直接导致连接失败。加上它能解决非 SSL 场景的兼容问题。另一个经常被忽略的参数是useSSLfalse和serverTimezone。前者是显式关掉 SSL避免 SSL 相关的手续后者是为了防止时区报错The server time zone value CST is unrecognized。虽然这两个问题和认证插件没有直接关系但在服务端刚完成版本升级后最容易接连出现我先写在这里供参考。如果你所在的企业要求必须走 SSL那么useSSLtrue时还需要配置证书相关参数这又是另一套折腾。我见过不少小团队为了省事最终选择在非 SSL 模式下加allowPublicKeyRetrievaltrue对内部系统来说安全性尚可关键是先把连接跑通。5.3 Python 场景的写法参考Python 项目里用 PyMySQL 时遇到mysql_native_password is not loaded之后如果服务端已经切到caching_sha2_password连接代码建议这样写import pymysql conn pymysql.connect( host192.168.10.20, port3306, userorder_app, passwordOrder2024#Secure, databaseorder_db, charsetutf8mb4, auth_plugincaching_sha2_password )auth_plugin参数不是所有版本的 PyMySQL 都兼容老版本会直接忽略。如果传了还报错优先升级包版本。用mysql-connector-python的话连接参数类似但通常不需要显式指定auth_plugin它会自动从服务端协商。5.4 连接池里那些“诡异”的间歇性失败服务端、驱动都改好了应用也能连上了。但上线后你可能偶尔收到连接池报错“Access denied”或“Communications link failure”。这种情况大概率不是代码问题而是连接池里还缓存着旧密码或旧认证方式。因为连接池在应用启动前就预建好了若干连接你用 SQL 改了用户密码或认证插件连接池并不会自动感知仍然拿着旧凭据做保活校验。这类问题的排查手段是重启应用清空连接池缓存看问题是否消失。或直接执行ALTER USER之后在测试环境不等连接池回收手动KILL曾经建立的空闲连接。有些连接池框架提供了重新校验连接的开关比如 DBCP 的testWhileIdle默认不一定开启。建议在正式环境里打开连接有效性检测避免升级过程中线上应用突然被连接断开砸挂。6. 踩坑实录与排查速查表6.1 我踩过的几个坑第一个坑在 MySQL 8.0.34 版本上直接改my.cnf写mysql_native_passwordON重启后配置被 MySQL 静默忽略没有报错也没有生效。后来查官方手册才发现这个参数在较新的 8.0 里已经被换成default_authentication_plugin作为唯一的控制开关旧写法虽然不报错但直接失效。如果你沿用网上的老教程很容易被这种“不报错但不生效”的情况坑到。第二个坑云数据库实例上执行SHOW PLUGINS看不到mysql_native_password但同样也没权限改default_authentication_plugin。这种情况下方案B完全不可行只能在账号级别使用caching_sha2_password然后把所有老客户端升级到兼容版本。这个坑尤其容易出现在从自建机房迁移上云的场景里。第三个坑账号里设置了IDENTIFIED WITH mysql_native_password但密码本身包含#字符。在 JDBC 连接串里没有做 URL 编码导致驱动解析密码串时截断成空密码报错信息完全指向“Access denied”。排查了半小时服务端结果只是连接串编码问题。血的教训强密码生成后最好先在连接串里用浏览器工具做一次 URL 编码再粘贴进去。第四个坑PHP 老版本 MySQL 8.0 组合。服务端密码没错、插件没错、权限没错但 PHP 7.2 的 mysqli 就是不支持caching_sha2_password。这里一度让我以为是 PHP 扩展没装好后来把报错信息Server sent public key丢给搜索引擎才意识到是驱动版本太老。所以排查时不要只看第一条报错信息要把完整堆栈拉出来看。6.2 排查速查表我把整个排查过程整理成一张表遇到报错可以直接对着操作报错特征可能原因快速处置方法Plugin mysql_native_password is not loaded服务端未启用或移除了旧认证插件查看SHOW PLUGINS改用caching_sha2_password创建用户Public Key Retrieval is not allowed客户端驱动缺少allowPublicKeyRetrieval参数JDBC 连接串加allowPublicKeyRetrievaltrueAccess denied for user密码错 / 账号 host 不匹配 / 连接串密码被特殊字符截断命令行先试登录再逐项检查连接串Server sent public key驱动版本过老不支持新认证插件升级驱动或临时回退到旧认证插件Communications link failure连接池缓存 / 防火墙 / SSL 握手失败重启应用验证检查防火墙端口和 SSL 参数Unknown authentication plugin caching_sha2_password客户端驱动完全不认识新插件必须升级驱动没有其他捷径这张表基于我处理过的多数同类故障能覆盖七成以上的场景。剩下的三成多半是因为业务系统本身有定制化组件没法用通用方案解决那就要回头从“你用的中间件到底支持什么插件”这个原点去查。6.3 关于使用 mysql_native_password 的合规提醒最后多写一段。很多团队在修复完报错后会让服务端长期停留在mysql_native_password方案上。从纯技术角度来说短时间内确实没问题但你要知道MySQL 官方已经把该插件标记为废弃。某安全审计、等保评测时扫描器扫出这个插件是高风险项你会需要提供额外的补偿说明。与其到时候解释一堆不如在第一次碰到报错时就把规划往caching_sha2_password方向靠。如果你的系统里还有一堆不可控的老客户端无法短期内全部升级那我的建议是以账号维度做分层管理新系统一律用caching_sha2_password老系统设定一个“过渡窗口期”临时用mysql_native_password并排期尽快改造而非全局统一回退。这个思路能在“尽快恢复服务”和“长期健康”之间取得一个平衡。7. 从一次报错看到的长远思路处理完这一次Plugin mysql_native_password is not loaded的报错你对 MySQL 认证机制的理解大概率会上一个新台阶。这个报错真正想教你的不是某个命令而是数据库升级从来不是数据库自己升级而是数据库加上它周边的一切客户端、驱动、工具链一起升级。我个人的习惯是每次做 MySQL 大版本升级前先列一张“客户端兼容性矩阵”把项目里用到的所有数据库驱动、ORM、连接池版本列出来逐个确认对目标版本新特性的支持情况。这件事听起来麻烦但比升级到一半被各种稀奇古怪的报错打断要省时得多。再分享一个小技巧如果你在测试环境里模拟这个报错不用专门去卸插件。创建一个用户时显式指定一个不存在的插件名比如CREATE USER test% IDENTIFIED WITH some_fake_plugin BY 123456;就能复现同款Plugin xxx is not loaded报错。用这个方式反复练习你会对 MySQL 的插件加载机制有更直观的感觉。数据库的认证插件这件事本质上就是在安全性和兼容性之间做选择。MySQL 已经用行动告诉了你它选哪边剩下的就是你的项目跟上步伐而已。