遇到Plugin 'mysql_native_password' is not loaded这个报错,通常意味着你正在跟 MySQL 8.0 及以上版本较劲,而且多半是在做账号认证、数据迁移或者客户端连接的时候撞上的。这条报错不算冷门,但第一次碰到的人很容易蒙圈,因为错误消息本身只说“插件没加载”,却没告诉你为什么没加载、去哪里加载、怎么才能绕过它。
我用几种不同的环境实测过这个报错,包括全新安装的 MySQL、从 5.7 升级上来的实例、还有 Docker 容器里跑的数据库。这篇就把根因、排查思路、三种可落地的解决方案一次性梳理清楚,顺便把那些容易误入的坑也标出来。
1. 问题成因拆解:这个报错到底在说什么
1.1 mysql_native_password 的身份
要搞懂这条报错,先得明白mysql_native_password是什么角色。它是 MySQL 历史上默认使用的身份认证插件,从 MySQL 4.1 开始就被广泛应用,一直到 MySQL 5.7 都是默认认证方式。它的工作原理是对用户密码做两次 SHA-1 加盐哈希,然后在服务端验证哈希值,认证过程不需要在服务端保存明文密码,也不需要通过网络传输明文密码。
这个插件用了二十多年,兼容性极好,几乎市面上所有老版本的客户端、驱动、中间件都认识它。但问题恰恰出在这个“老”字上。
1.2 为什么 MySQL 8.0 默认不再加载它
MySQL 8.0 发布时,Oracle 把默认认证插件换成了caching_sha2_password,理由很明确:mysql_native_password的 SHA-1 哈希算法安全性已经跟不上时代,而且它不支持下 TLS 保护密码传输之外的更安全的公钥交换机制。caching_sha2_password在认证速度、密码存储安全性上都有明显提升,所以 MySQL 团队决定把它设为默认值。
但需要注意,MySQL 8.0 并不是把mysql_native_password直接从代码里删掉了。插件本身还在,只是默认不启用、默认不加载。这就像工具箱里有一把螺丝刀,但厂家默认没把它摆在桌面上,你需要自己去抽屉里翻出来用。
1.3 触发这个报错的典型场景
根据我实测的经历,下面这些场景最容易踩中:
从 MySQL 5.7(或更老版本)迁移过来的账号。老实例里创建的账号,认证插件字段存的是
mysql_native_password。当你用文件系统拷贝数据目录或者导入旧备份的方式迁移到 MySQL 8.0 时,8.0 实例在验证这些老账号时,发现自己的插件列表里没有mysql_native_password,于是直接抛错。手动创建用户时显式指定了插件,比如写了
CREATE USER 'xxx'@'%' IDENTIFIED WITH mysql_native_password BY '123456',但如果当前实例根本没有加载这个插件,同样会报错。某些发行版或云厂商提供的 MySQL 8.0 版本,为了安全策略,干脆把
mysql_native_password插件从plugin目录里移除了。我在某个精简版镜像里就遇到过,SHOW PLUGINS输出里根本看不到它,mysql.plugin表里也没有记录。这种情况光靠SET PASSWORD改账号是没用的,要么把插件文件放回去,要么彻底换认证方式。PHP 老版本驱动、老代码连接数据库时报错。这类场景名义上是客户端报错,实际上服务端日志里也经常出现
plugin is not loaded的记录。
把这些场景放在一起看,你会发现根因其实是同一个:服务端 MySQL 实例不认识mysql_native_password这个认证插件,所以在认证阶段直接拒绝。
2. 排查与预检:动手修改之前的必做步骤
2.1 确认 MySQL 版本与插件加载情况
无论你打算走哪条修复路线,第一步永远是确认当前实例的真实状态,不能靠猜。登录 MySQL 后执行:
SELECT VERSION();得到类似8.0.x的版本号后,再看插件表:
SHOW PLUGINS;重点关注输出里有没有mysql_native_password。如果看到这一行,说明插件在内存里是可用的;如果列表里没有,再看mysql.plugin表:
SELECT * FROM mysql.plugin WHERE PLUGIN_NAME = 'mysql_native_password';如果表里也没有记录,那基本可以判断插件文件可能缺失,问题比想象中麻烦一点。
2.2 确认具体账号的认证插件状态
光知道全局情况还不够,你还要看看到底是哪个账号在报错。查询方式:
SELECT user, host, plugin FROM mysql.user;正常情况下,MySQL 8.0 新建的账号plugin列应该是caching_sha2_password。如果发现某些账号的plugin列是mysql_native_password,这些就是潜在的报错源。
2.3 查看 MySQL 错误日志
在改任何配置之前,建议先翻一下错误日志,通常位于数据目录下的*.err文件,或者通过SHOW VARIABLES LIKE 'log_error';查到路径。我遇到过一种情况:表面上报错信息只出现在客户端,但日志里其实早就记录了类似[ERROR] [MY-010330] Plugin mysql_native_password reported: ''的提示,能帮你更快定位是插件加载失败还是账号引用问题。
如果你用的是 Docker 部署的 MySQL,日志看这里:
docker logs 容器名 --tail 1002.4 备份与回滚预案
修改认证插件属于变更操作,原则上要留后路。最稳妥的做法是先把mysql.user表里受影响账号的元数据导出来:
mysqldump -u root -p --no-tablespaces --no-create-info --skip-triggers mysql user > user_backup.sql这个备份文件很小,但在你操作失误时能帮你恢复原状。另外,任何涉及my.cnf的修改,都建议先复制一份原文件:
cp /etc/my.cnf /etc/my.cnf.bak.$(date +%Y%m%d)3. 解决方案一:修改账号认证插件指向新默认插件
3.1 适用场景与核心思路
这个方案最适合的场景是:你的 MySQL 8.0 实例本身没有缺失插件文件,只是老账号仍然写着mysql_native_password,导致认证失败。核心思路很简单——把账号的认证插件改成caching_sha2_password,并重新设置密码。
之所以推荐优先做这个操作,是因为 MySQL 8.0 的caching_sha2_password才是官方主推的方向,长期维护、性能更好,跟未来版本兼容性也更强。
3.2 操作步骤
先用管理员账号登录 MySQL(通常是 root 或者其他有CREATE USER/ALTER USER权限的账号),执行:
ALTER USER '目标用户名'@'目标主机' IDENTIFIED WITH caching_sha2_password BY '新密码';举个例子,假如报错账号是'appuser'@'%',你想把密码改成App@2024Pass,就执行:
ALTER USER 'appuser'@'%' IDENTIFIED WITH caching_sha2_password BY 'App@2024Pass';如果不想改密码,只想换认证插件,可以用:
ALTER USER 'appuser'@'%' IDENTIFIED WITH caching_sha2_password;但这招的前提是账号原本的密码哈希可以被重算出来——实际上 MySQL 在切换插件时会对已有密码进行转换,如果旧密码哈希算法不兼容,命令可能失败。这时候老老实实加上BY '新密码'最稳妥。
改完以后执行:
FLUSH PRIVILEGES;3.3 批量修改的技巧
如果你要处理的账号不止一两个,手工敲ALTER USER太浪费时间。可以用动态 SQL 批量处理。先查出所有需要改的账号:
SELECT CONCAT('ALTER USER ''', user, '''@''', host, ''' IDENTIFIED WITH caching_sha2_password BY ''临时密码'';') FROM mysql.user WHERE plugin = 'mysql_native_password';把输出的 SQL 语句复制出来执行即可。但要注意,这个办法会让所有被改账号统一使用同一个临时密码,改完以后必须让对应业务方或开发人员自己去改密码。
3.4 这个方案的副作用与避坑提醒
最直接的副作用是:所有使用老版本客户端的连接方可能连不上数据库。caching_sha2_password要求客户端驱动支持新的认证协议。JDBC 8.0.x 以上的驱动、Python 的mysql-connector-python8.0 版本、PHP 7.4 以上配合较新的 mysqli 扩展,这些才支持。如果你的应用还停留在非常老的版本,选这个方案要提前做驱动升级。
在我实测中,Java 应用最常见的问题是依赖了mysql-connector-java5.1.x 版本,连接新认证账号时直接抛Public Key Retrieval is not allowed。赶上这种情况,要么升级驱动,要么只能走后文提到的第二个方案。
4. 解决方案二:在 MySQL 8.0 中启用 mysql_native_password 插件
4.1 适用场景与核心思路
如果你的业务系统里有一堆老客户端短期内没法升级,甚至有些驱动已经停止维护,那么强制迁移到caching_sha2_password根本不可行。这时候就需要在 MySQL 8.0 中重新启用mysql_native_password插件。
MySQL 8.0.34 之前的版本,默认把mysql_native_password标记为 deprecated,但只要插件文件存在,你通过配置就能把它加载进来。核心思路是在my.cnf里显式声明要加载这个插件。
4.2 直接修改 my.cnf
打开 MySQL 配置文件,一般路径是/etc/my.cnf、/etc/mysql/my.cnf或者/etc/mysql/mysql.conf.d/mysqld.cnf,在[mysqld]段落里加一行:
[mysqld] mysql_native_password=ON注意写法——不是plugin-load=mysql_native_password.so(虽然这样写理论上也能生效,但更推荐用mysql_native_password=ON这种开关式配置,语义更清晰,也更不容易被后续版本忽略)。
保存后重启 MySQL:
systemctl restart mysql重启后验证:
SHOW PLUGINS;输出里应该能看到mysql_native_password这一行,状态是ACTIVE。到这一步,实例已经重新认识这个老插件了,再连接老账号应该不会报Plugin is not loaded。
4.3 如果配置文件里加了还是不生效
这种情况我遇到过两次,原因各不相同。第一次是配置文件优先级问题:系统里存在多个配置文件,我改的那个根本不会被读取。排查方法是执行:
mysqld --verbose --help | grep -A 1 "Default options"它会打印出按顺序读取的配置文件路径列表,你就能确认自己改对文件没有。
第二次是插件.so文件不存在。MySQL 8.0 的插件目录一般叫plugin_dir,执行:
SHOW VARIABLES LIKE 'plugin_dir';然后在系统里看看这个目录下有没有mysql_native_password.so:
ls -la /usr/lib/mysql/plugin/ | grep mysql_native如果文件不存在,说明你的发行版或镜像把插件裁剪掉了。解决方法是安装对应版本的mysql-community-server-compat或者从相同版本源码中提取插件文件,复制到插件目录后设置权限再重启。如果你用的是 Docker 镜像,更简单的办法是换一个更完整的镜像标签。
4.4 启用插件后的安全权衡
启用mysql_native_password相当于把旧的安全机制放回来了。它本身没有致命的已知漏洞,但如果你的数据库直接暴露在公网,使用旧插件的账号更容易被暴力破解或撞库攻击。我的建议是:
- 只让确实无法升级的旧客户端使用
mysql_native_password账号; - 新账号一律使用
caching_sha2_password; - 设置数据库防火墙或安全组,限制旧客户端来源 IP;
- 定期刷新账号密码,不要长期使用同一个静态密码。
5. 解决方案三:修改客户端连接参数,绕开插件缺失限制
5.1 适用场景
有些情况下,你既不想改服务端配置,也暂时不想动账号插件,只想让某个特定客户端尽快连上数据库跑起来。这时候可以考虑从客户端角度做调整。
这个方案只适用于客户端本身就支持caching_sha2_password的场景,只是默认配置里强制指定了mysql_native_password,或者驱动版本太老导致协商失败。
5.2 JDBC 连接串调整
Java 应用最常见的问题就是连接串里的authenticationPlugins或defaultAuthenticationPlugin被设置成了com.mysql.jdbc.authentication.MysqlNativePasswordPlugin。如果你用的是 MySQL Connector/J 8.0.x,去掉这个参数,让驱动自动和服务端协商即可。推荐的连接串:
jdbc:mysql://host:3306/dbname?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC其中allowPublicKeyRetrieval=true很关键。caching_sha2_password在非 SSL 连接下,需要从服务器获取公钥做 RSA 加密传输密码,这个参数默认是关闭的,不开会报Public Key Retrieval is not allowed。
如果你的驱动版本较老,建议升级到mysql-connector-java8.0.30 以上,这个版本对caching_sha2_password支持已经很成熟。
5.3 Python 连接适配
PyMySQL从 1.0.0 版本开始完全支持caching_sha2_password,但有一个前提,需要在连接时加入:
import pymysql conn = pymysql.connect( host='127.0.0.1', user='appuser', password='App@2024Pass', database='appdb', charset='utf8mb4', # 关键参数:允许从服务器获取公钥,用于 RSA 密码加密传输 program_name='MyApp' )实际上PyMySQL里没有直接的allowPublicKeyRetrieval参数命名,它默认就能处理公钥获取。如果你现在用的还是MySQLdb(mysqlclient),需要确认版本支持情况,老版本在连接新认证账号时会直接卡住。
5.4 PHP 与其它语言
PHP 的mysqli扩展在 PHP 7.4 之后基本兼容新认证插件,但如果你用的是mysql扩展(PHP 5.x 时代产物),那对不起,这个方案救不了你。PHP 5.6 及以下连接 MySQL 8.0 的新认证账号几乎必失败,唯一的出路就是升级运行时环境或走服务端插件方案。
5.5 客户端方案的局限
需要明确一点,客户端方案解决的是单一连接方的问题。如果报错的服务端本身因为插件缺失导致ALTER USER都无法执行,那客户端方案根本无从谈起。所以这个方案更适合作为服务端修复完成后的配套工作,而不是独立的主方案。
6. 实战排错记录:我踩过的坑与最终处理过程
6.1 案例背景
某模拟项目 X 的测试环境,原先跑在 MySQL 5.7 上,因为磁盘迁移误操作,数据目录被直接拷贝到一台新装的 MySQL 8.0 实例上。启动正常,但所有业务账号连接全部报Plugin 'mysql_native_password' is not loaded。
这台 MySQL 是用某 Linux 发行版自带包管理工具安装的极简版,插件目录里果然没有mysql_native_password.so。
6.2 排查步骤拆解
第一步,确认实例版本:
SELECT VERSION(); -- 8.0.32第二步,查用户表:
SELECT user, host, plugin FROM mysql.user;输出结果里几乎所有应用账号的plugin都是mysql_native_password,只有 root 是auth_socket和caching_sha2_password。
第三步,查插件表:
SHOW PLUGINS;确实没有mysql_native_password的行。
第四步,查插件目录:
SHOW VARIABLES LIKE 'plugin_dir';目录下文件列表里找不到mysql_native_password.so。
6.3 处理过程与选择逻辑
一开始我按直觉直接改了my.cnf加mysql_native_password=ON,重启后SHOW PLUGINS还是没有。后来确认是插件文件缺失,于是从一个相同大版本(8.0.32)的完整安装包里提取了mysql_native_password.so,复制进插件目录,设置root:root和755权限,再改配置重启,插件状态变为ACTIVE。
到这一步,老账号能正常连接了。但事情没完——我顺带用第二个方案把所有应用账号批量迁移到了caching_sha2_password,因为测试环境的客户端都能升级,没必要一直背着老插件带来的安全包袱。
6.4 这次排错沉淀的经验
- 版本号要精确匹配:插件
.so文件的 ABI 兼容性在大版本内部通常没问题,但如果你用 8.0.33 的文件放在 8.0.21 的实例里,轻则加载失败,重则导致 mysqld 启动异常,所以复制插件文件时一定看准版本。 - 改配置别忘验证生效:
my.cnf加参数后不是百分百生效,特别是存在多个配置文件或者使用include机制时,参数可能被覆盖。每次改完配置,至少执行一次SHOW PLUGINS或SHOW VARIABLES验证。 - 密码和插件同时改,避免二次故障:如果你打算走
caching_sha2_password,最好在业务低峰期操作,因为切换瞬间旧连接全部失效,应用侧需要重连。
7. 快速排查表:一条命令判断该走哪条路
| 检查项目 | 命令/操作 | 结果判断 |
|---|---|---|
| 当前版本 | SELECT VERSION(); | 8.0+ 才需要考虑此问题 |
| 插件是否已加载 | SHOW PLUGINS; | 无记录说明未加载 |
| 插件是否注册 | SELECT * FROM mysql.plugin WHERE PLUGIN_NAME='mysql_native_password'; | 无记录说明未注册 |
| 插件文件是否存在 | ls plugin_dir | 无.so文件说明被裁剪 |
| 哪些账号依赖旧插件 | SELECT user, host, plugin FROM mysql.user; | 关注plugin='mysql_native_password'的行 |
| 错误日志 | log_error变量指向的文件 | 看有无插件加载失败记录 |
判断逻辑很简单:
- 插件文件存在,且已注册→ 直接走方案一,把账号改到
caching_sha2_password即可,不需要动服务端配置。 - 插件文件存在,未注册→ 走方案二,在
my.cnf加参数并重启。 - 插件文件不存在→ 要么补文件(方案二变体),要么所有客户端升级后走方案一。
- 客户端无法升级→ 只能走方案二,把老插件加载回来。
8. 最后想说的几点实操心得
处理这个报错,最关键的是先分清楚是账号的问题还是插件本身的问题。很多人一看到Plugin is not loaded就急着去改账号,结果插件文件根本不存在,改来改去还是报错。反过来,也有人先花半天时间找插件文件,却不知道其实只要把账号换到新插件,问题当场就解决了。
如果你想避免以后再被这个问题困扰,我建议在初始化 MySQL 8.0 实例时,就统一规划认证插件策略:
- 新部署的实例,一律使用
caching_sha2_password,不要为了省事给所有账号都指定mysql_native_password; - 如果业务线里有无法升级的老客户端,单独建一个库、单独分配账号,只给这些账号指定旧插件,减小影响面;
- 做好监控,对使用
mysql_native_password的账号做定期审计,一旦客户端完成升级,就立刻把账号迁回新插件。
迁移数据目录这种事,能不用文件拷贝就别用文件拷贝,优先走逻辑备份(mysqldump或mydumper),能少踩一大堆兼容性坑。这次如果你也碰到了类似的插件报错,对照上面几个方案按顺序排查,基本都能把问题收拾干净。