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'
继续向下追,依次经过 asyncNotifyService、externalDumpService,最后才看到真正关键的内容:
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
验证时要刻意保留"冷启动"条件:
- 重启 MySQL;
- 不打开 DataGrip;
- 等待 MySQL 就绪;
- 直接启动或重建 Nacos;
- 重复两到三次。
如果不再需要 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 容器中填写 localhost 或 127.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 真正就绪后再启动。