Nacos 用 MySQL 持久化:五个必备条件,缺一个就起不来
Nacos 默认用内嵌 Derby,容器一重建,配置与注册信息全丢,所有服务都起不来。本篇梳理 MySQL 持久化的五个必备条件,附可直接抄的 compose 片段。
一、为什么必须做持久化
Nacos 默认使用内嵌 Derby 数据库。容器一旦被 docker compose down 或重建,所有配置与注册信息全部丢失。
在"配置中心"这个角色上,这个后果很严重:应用启动时要 spring.config.import 从 Nacos 拉配置(而且是非 optional 的强依赖),配置没了 → Nacos 里的 dataId 全没了 → 所有服务启动失败。
所以 compose 里要做这件事:
nacos:
image: nacos/nacos-server:v3.2.4
environment:
MODE: standalone
SPRING_DATASOURCE_PLATFORM: mysql # ← 切到外部 MySQL
MYSQL_SERVICE_HOST: mysql
MYSQL_SERVICE_PORT: 3306
MYSQL_SERVICE_DB_NAME: ${NACOS_DB_NAME:-nacos_config}
MYSQL_SERVICE_USER: ${MYSQL_USER:-api_cloud}
MYSQL_SERVICE_PASSWORD: ${MYSQL_PASSWORD}
MYSQL_SERVICE_DB_PARAM: >-
characterEncoding=utf8&connectTimeout=10000&socketTimeout=30000&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
注意:SPRING_DATASOURCE_PLATFORM=mysql 只是"声明意图"。 声明之后,下面五个条件必须同时满足,缺任何一个都会启动失败——而且失败方式各不相同,报错也不指向真正的原因。
二、五个必备条件
条件 1:库名必须与建库语句完全一致
-- mysql/init/01-init-nacos.sql
CREATE DATABASE nacos_config DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE nacos_config;
-- ... 建表语句
对应环境变量:
MYSQL_SERVICE_DB_NAME: nacos_config
两边名字必须逐字相同。 不一致的表现是 Unknown database 'nacos_config'——这个还算直白,但要注意:mysql/init/*.sql 只在数据卷为空时执行(详见 D1 篇),如果数据库是你手动建的、名字打错了,改 SQL 文件不会生效。
条件 2:表结构必须用官方 schema,且版本要对
Nacos 3.x 的 schema 与 2.x 不兼容。3.x 共 10 张表,其中必须包含:
config_info_gray ← 3.x 新增,灰度发布用
而 2.x 时代的这三张表已经废弃:
config_info_aggr
config_info_beta
config_info_tag
用错版本 schema 的表现:Nacos 能启动,但访问配置时抛 SQL 异常(表不存在 / 列不存在)。比直接起不来更麻烦。
官方 schema 的正确获取方式
https://raw.githubusercontent.com/alibaba/nacos/master/distribution/conf/mysql-schema.sql
只有 master 分支能取到。 实测这两条都 404:
https://raw.githubusercontent.com/alibaba/nacos/3.2.4/distribution/conf/mysql-schema.sql ❌
https://cdn.jsdelivr.net/gh/alibaba/nacos@3.2.4/distribution/conf/mysql-schema.sql ❌
另外注意:官方 schema 里不含
CREATE DATABASE和USE语句,需要自己补在文件开头。
条件 3:必须显式授权(最容易漏的一条)
MySQL 官方镜像的初始化逻辑是这样的:
-- 镜像内部大致做的事
CREATE DATABASE IF NOT EXISTS ${MYSQL_DATABASE};
GRANT ALL ON `${MYSQL_DATABASE}`.* TO '${MYSQL_USER}'@'%'; -- ← 只授权这一个库
它只把 MYSQL_USER 授权给 MYSQL_DATABASE 指定的那一个库。
而本项目的 compose 里,MySQL 服务创建的是业务库:
mysql:
environment:
MYSQL_DATABASE: ${MYSQL_DATABASE:-api_lfzx_wang} # 业务库
MYSQL_USER: ${MYSQL_USER:-api_cloud}
Nacos 用的是另一个库 nacos_config,同一个用户 api_cloud 去连它就会被拒绝:
java.sql.SQLException: Access denied for user 'api_cloud'@'%' to database 'nacos_config'
必须在 init SQL 里补一条显式授权:
-- 01-init-nacos.sql 末尾
GRANT ALL PRIVILEGES ON nacos_config.* TO 'api_cloud'@'%';
FLUSH PRIVILEGES;
这条之所以容易漏:Nacos 的网络、密码、库名全都对,错误信息却是 "Access denied"——容易往"密码错了"的方向查,而实际上是权限范围问题。
条件 4:JDBC 参数必须补 allowPublicKeyRetrieval
MySQL 8 默认认证插件是 caching_sha2_password。首次握手时,客户端需要向服务端索取公钥来加密密码——而这个动作默认是被禁止的:
java.sql.SQLException: Public Key Retrieval is not allowed
Nacos 官方镜像的 MYSQL_SERVICE_DB_PARAM 默认值不包含这个参数,必须自己补:
allowPublicKeyRetrieval=true
本项目的完整参数:
characterEncoding=utf8
&connectTimeout=10000
&socketTimeout=30000
&autoReconnect=true
&useSSL=false
&allowPublicKeyRetrieval=true
&serverTimezone=Asia/Shanghai
useSSL=false与超时参数也不是可选的美化项:容器网络下 TLS 协商会明显拖慢启动,而 Nacos 的 healthcheck 时间窗本来就紧。
条件 5:start_period 不能小于 180 秒
Nacos 3.x 首次启动实测需要 150–240 秒(JVM 预热 + gRPC + 插件加载)。
healthcheck:
test: ["CMD-SHELL", "curl -sf http://127.0.0.1:8848/nacos/ >/dev/null || exit 1"]
interval: 10s
timeout: 5s
retries: 20
start_period: 180s # ← 关键
start_period 过短的后果是连锁的:
start_period 设成 30s
→ Nacos 还在启动,healthcheck 判定失败
→ 容器被标记 unhealthy
→ 依赖它的四个应用服务:depends_on: condition: service_healthy 永远不满足
→ 四个业务服务全部起不来
表象是"四个业务服务启动失败",根因在 Nacos 的 healthcheck 参数上。
探针路径用
GET /nacos/而不是/nacos/v3/console/health/readiness—— 后者在 3.2.4 上实测 404(详见 A2 篇)。
三、诊断口诀:日志里的"启动中"可能是假的
调试 Nacos 启动时,日志里会看到一堆 banner 和初始化信息,看起来像"正在启动"。但有一行必须认识:
ThreadPoolManager Start destroying ThreadPool
这行的含义是"上一次启动失败、进程正在退出"。
如果你看到它,说明你看到的不是"正在启动",而是一次失败的启动 + 被 restart: always 拉起来的新进程。此时不要看当前这段日志,要往前翻,找那一次失败的真实异常。
restart: always 会让这个判断变得很困难——日志里会交替出现"销毁线程池"和"启动 banner",看起来像正常启动的抖动。
排查建议:
# 看完整日志,找第一次失败的位置
docker logs api-cloud-nacos 2>&1 | head -100
# 或者临时把 restart 改成 no,让失败停下来
docker update --restart=no api-cloud-nacos
四、一个容易忽略的收尾:首次登录强制改密码
Nacos 3.x 首次登录控制台会强制修改默认密码。 改完之后必须同步更新 .env:
NACOS_PASSWORD=<新密码>
否则会出现这个循环:
控制台登录 → 改了密码 → Nacos 容器重启(比如 docker compose down)
→ Nacos 又用 .env 里的旧密码初始化
→ 你新改的密码失效
这与"D1 密码唯一真源"是同一类问题:密码存在两处(Nacos 内部 + .env),哪一处改了就漂移了。
五、验证清单
# 1. 库与表都在(应为 10 张 nacos 相关表)
docker exec -i api-cloud-mysql mysql -uapi_cloud -p"$MYSQL_PASSWORD" \
-e "USE nacos_config; SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='nacos_config';"
# 2. 关键表存在
docker exec -i api-cloud-mysql mysql -uapi_cloud -p"$MYSQL_PASSWORD" \
-e "USE nacos_config; SHOW TABLES LIKE 'config_info%';"
# 必须包含 config_info_gray
# 3. Nacos 健康
docker compose ps nacos # 应为 Up (healthy)
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8848/nacos/ # 非 000
# 4. 持久化真的生效(关键验证)
docker compose restart nacos
# 等 2-3 分钟,登录控制台 → 配置列表应该还在
第 4 步是最有说服力的验证。只看"启动成功"不能证明持久化生效——Nacos 完全可能用内嵌 Derby 也能启动成功,然后在你 down 的时候把数据一起带走。
六、复盘
| 条件 | 失败表现 | 误导方向 |
|---|---|---|
| 库名不一致 | Unknown database |
较直白 |
| schema 版本错 | 启动成功但访问配置报 SQL 异常 | 容易当成 Nacos bug |
| 未显式授权 | Access denied |
容易误判为密码错 |
缺 allowPublicKeyRetrieval |
Public Key Retrieval is not allowed |
较直白,但报错在 JDBC 层 |
start_period 过短 |
四个业务服务起不来 | 根因被藏在另一个容器里 |
两条可复用的经验:
Nacos 起不来时,先看它自己的日志,别急着看依赖它的服务。 本项目里"四个业务服务全部启动失败"这个现象,最终指向的是 Nacos 的 healthcheck 参数——一个跟业务服务毫无关系的地方。
docker compose restart不等于重建。 如果你改的是environment,必须up -d。这条在本项目里踩了不止一次,后面 D1 篇还会再遇到。
原创文章,作者:ECHO陈文,如若转载,请注明出处:https://www.luweipai.cn/ops/1789977059/