Nacos连接MySQL异常?DataGrip竟成救星

Nacos 报 "No DataSource set",为什么用 DataGrip 连一次 MySQL 就好了?

一次 Docker 环境下的 Nacos 2.1.1 与 MySQL 8 连接问题排查。

这次排查里,最反常的现象不是 Nacos 启动失败,而是它的"恢复方式":

  • MySQL 和 Nacos 都通过 Docker 部署;
  • MySQL 启动后,Nacos 报 No DataSource set
  • 用 DataGrip 连接 MySQL,执行一次查询;
  • 再启动 Nacos,它就能正常连接数据库;
  • MySQL 重启后,同样的问题还会再次出现。

乍看之下,像是 MySQL 需要先执行一条 SQL 才会真正工作。但数据库并不存在"必须由客户端先查询一次才能接受其他连接"的正常机制。DataGrip 更像是无意中替 Nacos 完成了某个前置步骤。

沿着这个线索继续查,问题逐渐指向了 MySQL 8 的 caching_sha2_password 认证机制,以及 Nacos JDBC 连接参数中缺少的 allowPublicKeyRetrieval=true

那串异常真正说了什么

Nacos 的日志很长,最外层异常是:

text 复制代码
UnsatisfiedDependencyException: Error creating bean with name 'memoryMonitor'

继续向下追,依次经过 asyncNotifyServiceexternalDumpService,最后才看到真正关键的内容:

text 复制代码
Caused by: java.lang.IllegalStateException: No DataSource set

调用链最终落在:

text 复制代码
ExternalStoragePersistServiceImpl.findConfigMaxId

这说明 Nacos 已经选择了外部数据库存储路径,却没有成功创建可用的数据源。memoryMonitor 并不是根因,只是 Spring 容器初始化失败后的连锁反应。

同样的异常在 Nacos 官方仓库中出现过。有人反馈数据库从外部可以连接、表也已经初始化,但 Nacos 仍然报 No DataSource set。其中一种原因是 Nacos 启动时 MySQL 尚未 ready,等待数据库就绪后重启 Nacos 即可。

不过,这只能解释普通的启动时序问题,还不足以解释"DataGrip 连接一次后立刻恢复"。真正与这个现象高度吻合的,是 MySQL 8 的首次认证流程。

DataGrip 实际上做了什么

MySQL 8 默认使用 caching_sha2_password 作为认证插件。

它会在服务端维护一份内存认证缓存:某个用户第一次成功完成完整认证后,MySQL 会把对应信息放入缓存。这个用户后续再次连接时,可以走更快的认证流程。

问题在于,这份缓存不会跨 MySQL 重启保留。

MySQL 官方文档说明,当认证缓存中没有对应记录时,客户端的第一次连接需要满足下面至少一个条件:

  • 使用 TLS 等安全连接;
  • 通过 RSA 公钥完成密码交换。

如果 JDBC 使用的是非 TLS 连接,又没有事先配置服务器 RSA 公钥,就需要允许客户端从服务器获取公钥。MySQL Connector/J 对应的参数就是:

text 复制代码
allowPublicKeyRetrieval=true

而 Connector/J 中这个参数的默认值是 false。Nacos Docker 2.1.1 自带的默认连接参数也没有包含它。

于是,整个过程就可能变成这样:

text 复制代码
MySQL 重启
    ↓
caching_sha2_password 认证缓存被清空
    ↓
Nacos 发起第一次非 TLS 连接
    ↓
首次完整认证需要 RSA 公钥交换
    ↓
Connector/J 不允许动态获取服务器公钥
    ↓
数据源初始化失败
    ↓
Nacos 最终只显示 No DataSource set

DataGrip 通常能够完成这次完整认证。登录成功后,MySQL 的认证缓存里有了该用户的记录,随后 Nacos 再连接时走的是快速认证流程,因此看起来像是"一条查询把数据库激活了"。

实际上,查询内容并不重要,真正产生影响的是 DataGrip 已经成功登录。

先别急着改配置,做两个确认

这套解释与现象高度吻合,但是否属于同一个问题,最好先用数据库和日志确认。

1. 检查 Nacos 用户的认证插件

在 MySQL 中执行:

sql 复制代码
SELECT user, host, plugin
FROM mysql.user
WHERE user = 'nacos';

如果结果中的 plugin 是:

text 复制代码
caching_sha2_password

说明具备触发上述问题的前提。

2. 查看 DataGrip 介入前的 Nacos 日志

不要等 DataGrip 连接成功后再取日志。在第一次失败后,直接复制容器里的日志:

bash 复制代码
docker cp nacos:/home/nacos/logs/nacos.log ./nacos-before-datagrip.log

搜索认证和公钥相关错误:

bash 复制代码
grep -Ei \
'Public Key Retrieval|caching_sha2|RSA|Authentication|Access denied|Communications' \
./nacos-before-datagrip.log

如果出现下面这类信息,基本就能确认方向:

text 复制代码
Public Key Retrieval is not allowed

Nacos 输出到控制台的 No DataSource set 只是外层结果,更具体的 JDBC 异常往往藏在 /home/nacos/logs/nacos.log 中。

修复:补上 allowPublicKeyRetrieval

使用 Docker Compose 时,可以在 Nacos 环境变量中配置完整的连接参数:

yaml 复制代码
services:
  nacos:
    image: nacos/nacos-server:v2.1.1
    environment:
      MODE: standalone
      SPRING_DATASOURCE_PLATFORM: mysql
      MYSQL_SERVICE_HOST: mysql
      MYSQL_SERVICE_PORT: "3306"
      MYSQL_SERVICE_DB_NAME: nacos_config
      MYSQL_SERVICE_USER: nacos
      MYSQL_SERVICE_PASSWORD: "你的密码"
      MYSQL_DATABASE_NUM: "1"
      MYSQL_SERVICE_DB_PARAM: "characterEncoding=utf8&connectTimeout=10000&socketTimeout=30000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"

如果使用的是自定义 application.properties,则需要把这个参数追加到 db.url.0 的 JDBC URL 中:

properties 复制代码
db.url.0=jdbc:mysql://mysql:3306/nacos_config?characterEncoding=utf8&connectTimeout=10000&socketTimeout=30000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true

配置完成后重新创建 Nacos 容器:

bash 复制代码
docker compose up -d --force-recreate nacos

验证时要刻意保留"冷启动"条件:

  1. 重启 MySQL;
  2. 不打开 DataGrip;
  3. 等待 MySQL 就绪;
  4. 直接启动或重建 Nacos;
  5. 重复两到三次。

如果不再需要 DataGrip 预先连接,就能确认这个参数解决了首次认证问题。

别漏掉另一个问题:MySQL 可能还没 ready

allowPublicKeyRetrieval=true 解决的是认证公钥交换问题,不负责保证 MySQL 已完成启动。

Docker Compose 的普通 depends_on 只保证 MySQL 容器先启动,并不保证它已经可以执行 SQL。为了避免 Nacos 在数据库初始化阶段抢跑,最好再增加真实查询级别的健康检查:

yaml 复制代码
services:
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: nacos_config
      MYSQL_USER: nacos
      MYSQL_PASSWORD: "你的密码"
      MYSQL_ROOT_PASSWORD: "root密码"
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "MYSQL_PWD=\"$${MYSQL_PASSWORD}\" mysql --protocol=TCP -h127.0.0.1 -u\"$${MYSQL_USER}\" \"$${MYSQL_DATABASE}\" -Nse 'SELECT 1 FROM config_info LIMIT 1' >/dev/null 2>&1"
        ]
      interval: 5s
      timeout: 5s
      retries: 30
      start_period: 30s

  nacos:
    depends_on:
      mysql:
        condition: service_healthy

这个检查会同时验证数据库服务、账号密码、目标数据库和 Nacos 表结构,比只检测 3306 端口更接近 Nacos 的真实依赖条件。

如果 MySQL 和 Nacos 都在 Docker 中,还应让它们加入同一个网络,并把 MYSQL_SERVICE_HOST 设置为 MySQL 的服务名,例如 mysql。不要让容器绕到宿主机映射端口,也不要在 Nacos 容器中填写 localhost127.0.0.1

关于安全:这个参数不是万能开关

allowPublicKeyRetrieval=true 允许客户端在连接过程中向服务器获取 RSA 公钥。在可信的本机或 Docker 内部网络里,这是一种常见的开发和部署配置。

如果数据库连接会经过不可信网络,生产环境更稳妥的做法是:

  • 为 MySQL 配置 TLS,并校验证书;
  • 或把可信的服务器 RSA 公钥提供给客户端,通过 serverRSAPublicKeyFile 指定;
  • 不要为了省事,把数据库账号随意改成更旧的认证方式。

换句话说,allowPublicKeyRetrieval=true 解决的是"首次认证拿不到公钥",不是所有数据库连接异常的通用答案。

最后

这次问题最容易误判的地方,是 No DataSource set 看起来像一条普通的数据源配置错误,而 DataGrip 的介入又让问题显得像某种 MySQL"预热"行为。

把两个现象放在一起看,线索其实很清楚:MySQL 重启会清空 caching_sha2_password 的内存认证缓存;DataGrip 完成了第一次完整认证;Nacos 随后的快速认证才得以成功。

正确的解决方式,不是把"先用 DataGrip 查一次"写进启动流程,而是让 Nacos 自己具备完成首次认证的条件,并在 MySQL 真正就绪后再启动。

参考资料

相关推荐
G.O.G.O.G1 小时前
LeetCode SQL 从入门到精通(MySQL)06(上)
数据库·sql·mysql·leetcode
千桐科技1 小时前
qData 数据中台开源版从 MySQL 到 Doris完成一次数据同步
大数据·mysql·开源·doris·数据中台·qdata
研究员子楚1 小时前
GEO行业发展标准体系白皮书V2.0-第10卷 · 全球篇:跨国标准协同与全球品牌语义治理框架
数据库·人工智能·microsoft·架构·geo
Omics Pro2 小时前
深度学习多组学互作:组内+组间
数据库·人工智能·深度学习·mysql·搜索引擎·自然语言处理
残*影4 小时前
如何优雅地保存MySQL数据变更历史?
数据库·mysql
乐观的Terry4 小时前
3、数据库设计与领域实体
java·数据库·spring boot·spring cloud·ai编程
舞影天上4 小时前
RuoYi-Vue-Plus Docker 部署踩坑:MySQL 中文双重编码的根因与修复
数据库
Database_Cool_4 小时前
数据仓库弹性扩缩容怎么实现?AnalyticDB MySQL 在线扩容 0 中断实战
数据库·数据仓库·mysql·阿里云
Wzx1980125 小时前
Redis&ES——Retriever的抽象实现
数据库·人工智能·redis·elasticsearch