一、问题现场

用下面这个脚本启动 MySQL 容器:

docker run -d \
  --name mysql8 \
  --restart always \
  -p 3306:3306 \
  -v /Users/anchao/develop/mysql8/data:/var/lib/mysql \
  -v /Users/anchao/develop/mysql8/logs:/var/log/mysql \
  -v /Users/anchao/develop/mysql8/conf.d:/etc/mysql/conf.d \
  -e MYSQL_ROOT_PASSWORD=Root@123456 \
  -e TZ=Asia/Shanghai \
  mysql:8.4.11

挂载的 my.cnf 内容:

[mysqld]
port=3306
bind-address=0.0.0.0
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
max_connections=500
default-authentication-plugin=mysql_native_password
lower_case_table_names=1

容器起来了,但客户端(DataGrip / IDEA Database)连接直接报错:

DBMS: MySQL (版本 8.0.46)
驱动程序: MySQL Connector/J (版本 mysql-connector-j-9.7.0,JDBC4.2)

[HY000][1524] Plugin 'mysql_native_password' is not loaded.

二、根因分析

这个报错的核心是 MySQL 认证插件的版本演进

MySQL 版本mysql_native_password 状态default-authentication-plugin
5.7默认认证插件支持
8.0仍内置,但已标记 deprecated支持(默认 caching_sha2_password
8.4不再内置,退化为需手动加载的动态插件,且默认 OFF参数已被移除,改用 authentication_policy
9.x彻底删除已移除

所以问题链条是:

  1. 镜像用的是 mysql:8.4.11,该版本 mysql_native_password 默认没有加载。
  2. my.cnf 里的 default-authentication-plugin=mysql_native_password 在 8.4 中已经是无效参数(严格模式下甚至会导致启动失败)。
  3. 客户端握手阶段服务端尝试用 native 插件校验,插件不存在 → 抛 ERROR 1524 (HY000)

顺带一提:报错信息里显示的是 MySQL 8.0.46,说明连接的可能是另一个残留容器或旧数据目录初始化出来的实例。但无论 8.0 还是 8.4,根因都指向 native 认证插件的兼容性问题,排查时值得先确认真实版本。

三、三种解法

方案一(推荐):改用 caching_sha2_password

mysql-connector-j 8.0.9+ 起就已经完整支持 caching_sha2_password,9.7.0 更是没有任何问题。所以最简单的做法就是删掉那行历史遗留配置

[mysqld]
port=3306
bind-address=0.0.0.0
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
max_connections=500
lower_case_table_names=1

重建容器:

docker rm -f mysql8 && bash mysql8/mysql.sh

如果 root 账号已经被初始化成 native 认证,进容器改回来:

docker exec -it mysql8 mysql -uroot -pRoot@123456 \
  -e "ALTER USER 'root'@'%' IDENTIFIED WITH caching_sha2_password BY 'Root@123456'; FLUSH PRIVILEGES;"

⚠️ 坑点提醒lower_case_table_names 属于初始化时才能确定的参数。如果 /var/lib/mysql 数据目录已经存在,且当前配置值与初始化时不一致,MySQL 会直接拒绝启动。遇到这种情况需要清空数据目录重新初始化(记得先备份)。

方案二:在 8.4 上强行启用 native 插件

如果老系统确实依赖 mysql_native_password(比如某些老版本 ORM、连接池或运维工具),8.4 里插件本体还在包中,只是需要显式加载:

[mysqld]
port=3306
bind-address=0.0.0.0
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
max_connections=500
lower_case_table_names=1
mysql_native_password=ON
authentication_policy=mysql_native_password

注意两点:

  • 8.4 中不能再用 default-authentication-plugin,必须换成 authentication_policy
  • MySQL 9.x 已经彻底删除该插件,此方案是死路一条,只是给你争取迁移时间。

方案三:降级镜像到 8.0

对认证方式没得选、又不想动配置的场景,直接把镜像换成 8.0 系列,原 my.cnf 可以原样使用:

docker run -d \
  --name mysql8 \
  --restart always \
  -p 3306:3306 \
  -v /Users/anchao/develop/mysql8/data:/var/lib/mysql \
  -v /Users/anchao/develop/mysql8/logs:/var/log/mysql \
  -v /Users/anchao/develop/mysql8/conf.d:/etc/mysql/conf.d \
  -e MYSQL_ROOT_PASSWORD=Root@123456 \
  -e TZ=Asia/Shanghai \
  mysql:8.0.42

四、排查命令清单

遇到类似问题,先用这几条命令把现场摸清楚:

# 1. 确认真实版本 + 各账号使用的认证插件 + 插件加载状态
docker exec -it mysql8 mysql -uroot -pRoot@123456 \
  -e "SELECT VERSION(); SELECT user,host,plugin FROM mysql.user; SHOW PLUGINS;"

# 2. 看启动日志,确认配置是否被拒绝
docker logs mysql8 --tail 50

# 3. 确认没有多个容器抢占 3306
docker ps -a --filter "publish=3306"

# 4. 确认配置文件确实被加载
docker exec -it mysql8 cat /etc/mysql/conf.d/my.cnf

其中 SELECT user,host,plugin FROM mysql.user; 是最关键的一条 —— 它能直接告诉你哪个账号卡在了 native 插件上。

五、经验总结

  1. 别再抄 5.7 时代的配置模板了default-authentication-plugin=mysql_native_password 这行配置在网上流传了近十年,但从 8.4 开始它已经是错误配置。
  2. 镜像 tag 一定要写死小版本mysql:8mysql:latest 会在某次 docker pull 后悄悄从 8.0 跳到 8.4,然后你的服务在半夜挂掉。
  3. 认证插件迁移宜早不宜迟。9.x 已经删掉 native 插件,现在花十分钟把账号改成 caching_sha2_password,比将来大版本升级时被迫加班要划算。
  4. 初始化型参数要提前定好lower_case_table_namescharacter-set-server(部分场景)这类参数写进数据目录,改了就得重建。

更多推荐