1. 为什么要给 Nacos 换掉默认数据库
先说个实际场景:你维护一个微服务集群,注册中心和配置中心用的 Nacos,跑了一段时间发现一个问题——节点一多,配置老是莫名其妙丢,或者服务列表看着不对劲。查了一圈,十有八九是内置的 Derby 在搞鬼。
Derby 是 Java 写的嵌入式数据库,Nacos 单机模式默认就用它,开箱即用确实方便,部署起来零依赖。但它的定位是“嵌入式”,不是给生产环境做多节点共享用的。一旦你从单机切到集群模式,每个节点各存一份 Derby 数据,节点之间靠 Nacos 自己的 Distro 协议做同步。这套机制在节点少、网络稳定的内网环境勉强能跑,但只要遇到网络抖动、节点重启、数据量上来,就特别容易出现各节点数据不一致的问题。我踩过最典型的一次坑:两个节点互相认为自己是 Leader,配置管理页面显示的数据和客户端实际拉到的数据完全对不上,整个发布流程差点失控。
所以把 Nacos 的存储层从 Derby 切到独立数据库,是几乎所有生产环境都要做的一步。数据库选型上,MySQL 是绝大多数团队的第一选择,网上资料也最多。但现实是,这些年 PostgreSQL 的使用率涨得很快,很多新项目从一开始就选了 PG,甚至有些团队因为许可证、信创要求,需要把存储切到达梦、人大金仓这类国产数据库上。Nacos 2.3.0 这个版本有一个很关键的变化——数据库接入变成了插件化架构,不光是 PostgreSQL,连达梦、Oracle 这类数据库都能接进去。这也正是这篇文章想聊透的东西。
2. Nacos 2.3.0 的插件化数据库架构,解决了什么问题
2.1 从源码硬编码到 SPI 插件机制
在 2.2.x 及更早的版本里,Nacos 的数据源层写得很“死”。你查看源码会发现,它内部对数据库的操作基本上就是围绕 Derby 和 MySQL 两套方言去写的,想接一个别的数据库,最直接的办法就是改源码——把 JDBC 驱动换掉、改 SQL 方言、改建表脚本,然后重新编译打包。不是说不能跑,但维护成本极高。你每次升级 Nacos 版本,都要把这些改动重新合一遍,中间出个兼容性问题,排查起来非常痛苦。
2.3.0 把这块彻底重构了。它引入了标准的数据源插件机制,基于 SPI(Service Provider Interface)动态加载外部数据源实现。也就是说,数据库接入逻辑不再写死在 Nacos 主程序里,而是通过一个独立的插件 jar 包来提供。你需要接什么数据库,就下载对应的插件,扔到指定目录,改一下配置文件,重启即生效。Nacos 主程序的代码一行都不用动。
这个设计思路其实和很多中间件是相通的。比如日志框架的 SLF4J,比如 MyBatis 的数据库方言支持,都是通过 SPI 把“核心框架”和“具体实现”解耦。Nacos 从这里切入,等于把数据库适配这个最麻烦的扩展点做了一个标准化的口子,后面官方加新数据库支持也好,社区贡献新插件也好,都有了一条固定路径。
2.2 官方扩展插件项目
Nacos 官方把数据源插件单独放到了 GitHub 上的一个仓库里,项目名叫nacos-datasource-plugin-ext。这个仓库目前维护了几个主流数据库的扩展实现,包括 PostgreSQL、达梦、Oracle、人大金仓等。每一个插件对应一个子模块,结构非常清晰。
比如要接 PostgreSQL,用到的插件包是nacos-datasource-plugin-postgresql;要接达梦,就是nacos-datasource-plugin-dameng。你只需要下载对应版本的插件 jar 包,放到 Nacos 安装目录下的plugins目录里,然后在application.properties里把数据源类型改成postgresql或者dameng,再把连接地址、账号密码配上,重启就完事了。
整个接入过程相比之前改源码的方式,工作量可以说是断崖式下降。我自己第一次接 PG 的时候,从下载插件到 Nacos 页面正常显示数据和发布配置,总共也就花了二十来分钟,大部分时间还花在建库建表和配权限上。
2.3 同库多表模式
接 PG 之前有一个概念需要先理清楚:Nacos 在 2.3.0 之后,同一个数据库实例里会用到两张逻辑上的表空间,也就是nacos_config和nacos_rs。前者存配置数据,后者存服务注册、健康检查这类数据。
这个模式在之前的 MySQL 接入里就已经存在了,只是在插件化重构之后强调得更加明确。你在建库的时候,不管是 PG 还是达梦,都需要把这两套表结构都初始化进去,不能只建配置相关的表。很多人第一次接的时候只导入了nacos_config的建表脚本,结果服务启动正常,但用 Distro 协议同步服务实例数据的时候一直报错,查了半天才发现nacos_rs的表根本不存在。
3. 接入 PostgreSQL 的完整实操
3.1 环境准备与版本确认
我这次实操用的环境是 Alibaba Cloud Linux 3,JDK 用的 1.8,Nacos 版本是 2.3.0,PostgreSQL 装的是 14.x。这个组合是当前比较主流的搭配,如果你的生产环境用的是 PG 12 或者 PG 15,理论上也没问题,Nacos 官方在 PostgreSQL 插件里兼容了常用版本。
先确认几件事。
第一,Nacos 2.3.0 依赖 JDK,推荐 1.8 以上,我建议直接用 JDK 8 的小版本 202 以上,或者 JDK 11,都行。第二,PostgreSQL 的 JDBC 驱动版本,插件本身会通过 Maven 依赖自动带上来,你不用手动去下载驱动 jar。第三,确认你的 Nacos 是完整包部署方式,不要用源码直接跑,否则插件目录的定位路径会不一样。
服务器上安装 PostgreSQL 的过程我就不展开了,不同操作系统、不同安装方式差异不小。我这里只说几个关键点:PG 安装好之后,记得用 systemctl 或类似方式确认服务在正常运行;默认的postgres超级用户建议不要直接给 Nacos 用,单独建一个账号并分配最小权限,这在任何数据库接入场景里都是正确习惯。
3.2 初始化 PostgreSQL 数据库与导入建表脚本
登录到 PostgreSQL 里,先创建 Nacos 要用的独立数据库和账号。我这边用的命令大致是这样:
sudo -u postgres psql然后执行:
CREATE USER nacos WITH PASSWORD 'nacos@123'; CREATE DATABASE nacos OWNER nacos; GRANT ALL PRIVILEGES ON DATABASE nacos TO nacos;这里建议把数据库名字就叫nacos,和你配置里的库名保持一致,避免混淆。创建完之后,用\q退出,再用nacos账号登录验证一下能不能正常连上:
psql -h 127.0.0.1 -U nacos -d nacos -W能进到这个库的 psql 交互界面,说明账号和权限没问题。接着就是导入建表脚本。Nacos 的官方发布包里自带了一套 PostgreSQL 的建表 SQL,路径在conf/目录下,文件名是nacos-pgsql.sql。这个文件同时包含了nacos_config和nacos_rs两张表空间的建表语句,你直接一条命令导进去就行:
psql -h 127.0.0.1 -U nacos -d nacos -f /opt/nacos/conf/nacos-pgsql.sql导入之后,建议检查一下关键表是否创建成功。最核心的是config_info、config_info_beta、config_info_tag、config_public_info以及服务注册相关的service_info和instance_info,数据同步相关的nacos_rs也要确认。
\dt这条命令会列出当前库里的所有表,你扫一眼就能看出来有没有问题。
注意:如果 Nacos 是 2.3.0 之前的版本,官方包里可能没有现成的 PG 建表脚本,你需要手动从 GitHub 的
nacos-datasource-plugin-ext仓库里找对应版本的 SQL 文件。2.3.0 开始已经把 PG 脚本放进了主发布包。
3.3 插件 jar 放置与配置修改
建库建表搞定之后,进入接入的关键环节。
先从 GitHub 的nacos-datasource-plugin-ext仓库找到 PostgreSQL 插件模块,下载对应 2.3.0 版本的 jar 包。如果你本地有 Maven 环境,也可以直接把仓库 clone 下来,切到对应的 tag,然后单独构建 PostgreSQL 插件子模块:
git clone https://github.com/nacos-group/nacos-datasource-plugin-ext.git cd nacos-datasource-plugin-ext mvn -pl nacos-datasource-plugin-postgresql -am package -DskipTests构建完成后,在nacos-datasource-plugin-postgresql/target/目录下会生成一个带依赖的 jar 包,通常文件名类似nacos-datasource-plugin-postgresql-2.3.0.jar。把这个 jar 包复制到 Nacos 安装目录下的plugins/目录里:
cp nacos-datasource-plugin-postgresql-2.3.0.jar /opt/nacos/plugins/插件放好之后,修改 Nacos 的配置文件,位置在/opt/nacos/conf/application.properties。需要改动的内容如下:
spring.datasource.platform=postgresql db.num=1 db.url.0=jdbc:postgresql://127.0.0.1:5432/nacos?tcpKeepAlive=true&reWriteBatchedInserts=true&ApplicationName=nacos_java db.user.0=nacos db.password.0=nacos@123 db.pool.config.connectionTimeout=3000 db.pool.config.validationTimeout=10000逐个解释一下关键项。
spring.datasource.platform这一项必须改成postgresql,这是告诉 Nacos 主程序去找 PostgreSQL 对应的数据源插件。db.url.0是连接串,注意reWriteBatchedInserts=true这个参数,它允许 PG 驱动把多条插入语句重写为批量插入,对 Nacos 这种频繁写入元数据的场景性能提升明显。db.pool.config.connectionTimeout和validationTimeout是根据我这边网络情况调的,如果你的 PG 和 Nacos 不在同一台机器,建议把超时时间适当调大一些,避免网络抖动导致连接池里的连接被判死。
3.4 注册插件与启动验证
配置改完之后,执行启动命令:
cd /opt/nacos/bin sh startup.sh -m standalone启动日志里有几个关键信息需要盯一下。正常情况,日志会打印出数据源初始化的信息,并且明确显示使用的是 PostgreSQL。你还可以通过日志里是否有异常堆栈来判断插件是否加载成功,常见的错误是NoSuchDataSourceTypeException或者ClassNotFoundException,出现这些基本就是插件没放对位置或者版本不匹配。
启动完成后,用浏览器打开 Nacos 控制台,默认地址是http://服务器IP:8848/nacos。如果你能正常登录控制台,并且在配置管理里新建一条配置、发布成功、再拉取到这条配置,说明读写链路已经通了。
为了更严谨一点,我习惯在 PG 里直接查一下配置数据是否真的落库了:
SELECT data_id, group_id, content, gmt_modified FROM config_info ORDER BY gmt_modified DESC;能看到你刚发布的配置记录,并且gmt_modified时间正确更新,整个接入过程就完成了。
4. 接达梦、人大金仓等国产数据库的思路
4.1 国产数据库兼容性分析
热词里出现了不少关于达梦、人大金仓的搜索,这说明现在很多项目都有信创适配的需求。Nacos 2.3.0 的插件化架构在这个场景下确实是雪中送炭。
先说达梦(DM)。达梦数据库从语法层面和 Oracle 有很高的兼容性,但它并不是完全等价于任何一个主流数据库,很多细节方言还是有差异的。Nacos 官方针对达梦单独提供了一个插件模块nacos-datasource-plugin-dameng,这个插件内部处理了达梦特有的 SQL 方言问题,比如分页写法、序列的使用、时间函数等。
再说人大金仓(KingbaseES)。这个数据库有意思的地方在于,它的 PostgreSQL 兼容模式做得很到位,某种程度上你可以把它当成“PG 协议的另一种实现”。所以如果你用的是 KingbaseES 的 PG 兼容模式,理论上可以直接尝试用 PostgreSQL 插件去接,连接串里指定金仓的驱动和端口即可。我见过有团队在生产环境这么干过,运行一段时间后没有发现明显异常。不过稳妥起见,如果你对接的是金仓的 Oracle 兼容模式,就必须按照 Oracle 或者专用插件的方式去处理,不能沿用 PG 插件。
4.2 具体接入步骤
以达梦为例,整体流程和 PostgreSQL 几乎一致,只有几个地方不同。
第一,下载对应版本的达梦插件 jar 包,放到plugins/目录。第二,修改application.properties,spring.datasource.platform改为dameng,连接地址前缀是jdbc:dm://:
spring.datasource.platform=dameng db.num=1 db.url.0=jdbc:dm://127.0.0.1:5236/NACOS db.user.0=nacos db.password.0=nacos@123第三,初始化建表脚本。达梦插件的源码仓库里带了对应的 SQL 文件,你需要用达梦的客户端工具或者命令行执行。需要注意的是,达梦的建表语句里有大量和 Oracle 相似的写法,比如VARCHAR2、CLOB、SEQUENCE这些,直接在 PG 或 MySQL 里执行是肯定不行的。
人大金仓的接入路径和达梦基本一致,区别在于它可以选择两种兼容模式。如果金仓实例创建的时候选的是 PG 兼容模式,你还可以先尝试直接用 PG 插件接入,这是最快的路子;如果不行,再切换成专门适配的插件。
5. 遇到过的坑与排查技巧实录
5.1 插件没有被加载
这是接入 PG 最常见的报错。启动日志里出现类似No datasource type found或者直接抛异常说找不到数据源类型,不用想,就是插件 jar 没有被加载进来。
排查路径很固定:第一,确认 jar 包确实在plugins/目录下,注意不是lib/目录,很多人会搞混,lib/是 Nacos 主程序的依赖目录,插件必须放在plugins/。第二,确认 jar 包名字没有被修改过,尤其是从 Maven 构建出来的 jar 一般会是nacos-datasource-plugin-postgresql-2.3.0.jar,如果你为了图方便改成了postgresql.jar,SPI 的配置文件路径可能就被破坏了,导致加载失败。第三,确认 jar 包里的META-INF/services目录下的 SPI 配置文件是否完整,用jar tf命令可以查看。
5.2 时区问题
Nacos 默认的时区设置是东八区,如果你的 PG 服务器时区设置不对,或者 JDBC 连接串里没指定时区,会出现一个问题:控制台上显示的配置发布时间和数据库里记录的时间差了 8 个小时。
解决办法是在连接串里显式加上时区参数,PG 的写法是:
db.url.0=jdbc:postgresql://127.0.0.1:5432/nacos?serverTimezone=Asia/Shanghai同时记得确认 PG 服务器的timezone参数,可以在 PG 里执行SHOW timezone;查看,如果不是预期的时区,通过ALTER DATABASE nacos SET timezone TO 'Asia/Shanghai';修改。
5.3 建表脚本执行失败
如果你用的 Nacos 版本比较老,或者是从其他渠道拿到的建表脚本,可能会遇到脚本执行到一半报错的情况。最典型的是 PG 脚本里含有DROP TABLE IF EXISTS语句,如果表不存在,按说不会报错,但某些脚本里带了DROP SEQUENCE IF EXISTS且某些版本对序列的处理有差异,会报语法错误。
遇到这种情况,不要急着改脚本。优先去官方仓库找你对应 Nacos 版本的原版 SQL 文件,用 diff 工具对比一下差异,通常就能定位问题。
5.4 字符集与编码问题
如果你的配置内容里有中文,发布到 Nacos 之后,查数据库发现中文变成了乱码,或者控制台显示正常但客户端拉取到的是乱码,大概率是数据库字符集设置不对。
PostgreSQL 在创建数据库的时候如果没有显式指定编码,会继承模板库的默认设置。建议建库的时候直接指定 UTF8:
CREATE DATABASE nacos WITH OWNER nacos ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C' TEMPLATE template0;这里的TEMPLATE template0很关键,它避免继承 template1 里面可能存在的自定义东西,保证数据库环境是干净的。
5.5 适配国产数据库时窗口函数不兼容
搜索热词里反复出现“pgsql窗口函数”、“数据库增删改查”、“数据库面试题”,这让我想到另一个实际问题:不管你是做 Nacos 接入,还是纯粹在做 PG 的数据开发,窗口函数都是绕不开的东西。但窗口函数在达梦、金仓这些数据库里的支持情况是有差异的。
如果你之前习惯写ROW_NUMBER() OVER (PARTITION BY ...)这种窗口函数,在 PG 里跑得很顺,迁移到达梦的时候,某些版本可能不支持或者写法有细微差别。解决方案是把窗口函数改写为关联子查询或者用分析函数替代。这个问题虽然不是 Nacos 接入本身的报错,但你在做数据库适配和测试的时候大概率会碰到。
5.6 连接池被打满
Nacos 默认的连接池参数是按照 MySQL 场景调的,切换到 PG 之后,连接池的maximumPoolSize可能偏小,尤其是在配置中心频繁推流、服务实例频繁上下线的场景下。如果你在 PG 端看到too many connections的报错,或者 Nacos 日志里频繁出现获取连接超时异常,建议把连接池上限调大,同时检查一下 PG 服务端max_connections的配置,留足余量。
5.7 集群模式下节点间数据不同步
这是最隐蔽的问题,往往出现在接入 PG 之后的集群环境中。Nacos 集群模式下,服务注册和配置发布的数据其实不是直接写数据库的,而是先通过节点间的 Distro 协议同步,再由某一节点落地到数据库。如果集群里某个节点的 PG 插件没有正确加载,或者该节点的数据库连接串配置有误,就会出现“部分节点数据正常、部分节点数据缺失”的诡异现象。
排查这个问题的思路是:逐个节点查看它的nacos.log,确认每个节点都成功初始化了 PG 数据源,然后再看nacos_rs表里的心跳数据是不是来自所有节点。如果发现某个节点没有写入痕迹,大概率是它的插件加载或配置有问题。
6. 从实操视角聊聊我个人的选型体会
前前后后折腾过 MySQL、PostgreSQL、达梦、人大金仓的接入,我最大的感受是:Nacos 从 2.3.0 开始做数据库插件化,这个方向走对了,而且对运维和研发都是实打实的减负。
如果你团队的新项目默认选了 PostgreSQL,那么直接上 2.3.0 加 PG 插件就行,不用再为了 Nacos 单独部署一套 MySQL,少维护一个数据库实例,这对很多中小团队来说意义很大。如果你所在单位有信创要求,必须把存储换到达梦或者金仓,Nacos 2.3.0 也让这个适配过程从“改源码”降级成了“换插件”。
最近一次版本升级,我把 Nacos 从 2.2.3 平滑升级到了 2.3.0,数据库从 MySQL 切到了 PostgreSQL,整个过程在灰度环境跑了将近一个月,没有出现一次数据不一致的问题。如果非要说有什么要留意的,就是升级前一定要把nacos_rs这套表结构初始化好,并且在切换数据库之后,先做一次全量配置的导出备份。毕竟所有中间件的存储层切换,最怕的不是切换的那一下,而是切换之后才发现数据对不上,那时候再回头就麻烦了。