前阵子帮一个朋友团队收尾 Nacos 集群升级他们全栈都在用 PostgreSQL结果 Nacos 2.x 时代只能被 MySQL 绑死要么放弃统一数据库栈要么自己改代码打补丁升级一次崩一次。后来切到 Nacos 3.x 的 PostgreSQL 插件方案才算是把这块石头搬开。这篇文章就把我这次从单机验证到三节点集群落地的完整过程写下来包括插件机制的原理、配置细节、初始化数据库时容易被坑的地方以及生产环境里连接池、迁移、备份这些实际关注点。无论你是刚接触 Nacos 的新手还是已经在 2.x 集群里被数据库选型卡住的运维/开发这篇文章照着做基本都能复现出来。1. 别再为 Nacos 的存储纠结了PostgreSQL 支持是怎么来的1.1 老方案里的两个选择到底卡在哪Nacos 一直到 2.x官方默认的存储其实只有两条路内嵌的 Derby或者 MySQL。Derby 是 Java 自带的内嵌数据库单机体验一下很轻便但一到集群模式就露馅了每个节点各自维护一套 Derby 数据节点之间不会自动同步。你往节点 A 写一个配置从节点 B 的控制台根本看不到服务注册也一样各写各的完全谈不上一致性。所以生产集群基本上只能上 MySQL。但问题在于很多公司的基础设施里根本没有 MySQL 的运维体系PostgreSQL 反而是标准配置。为一个 Nacos 单独引入一套 MySQL要配主从、做备份、管理账号对团队来说就是平白多养一套数据库。Nacos 3.x 把数据源接成插件机制之后PostgreSQL 才成了官方支持的选项这套“多养一套库”的历史包袱才算卸掉。1.2 3.x 拆出来的数据源插件层Nacos 3.x 做的不是简单地加一个 PostgreSQL 驱动而是把底层数据访问这一层重新做成了 SPI 可插拔结构。Nacos 内核只负责定义好“数据源需要提供什么能力”比如配置存储、服务实例存储、用户权限存储然后由不同的数据库实现去填充这些能力。MySQL、PostgreSQL 这些都是通过插件 jar 的形式存在启动时被 SPI 扫描到才会加载。这个设计的直接好处是数据库支持不再打进 Nacos 主发行包里团队想接什么数据库就下载对应的插件放进去不用等到官方主版本迭代才能获得新存储支持。社区开发者也可以在没有上游合并压力的情况下自己维护适配某类数据库的插件。对使用者来说升级 Nacos 主版本的时候只需要同步确认插件版本是否兼容整体升级路径比“自己改源码”要干净得多。1.3 选 PostgreSQL 的实际收益如果团队本身的数据库底座就是 PostgreSQL那收益最直接的就是不用再维护第二套存储。此外 PostgreSQL 在 SQL 能力上的底子也确实适合这类元数据存储场景配置表、服务实例表、用户权限表虽然单表数据量不大但都涉及频繁的按主键更新、范围查询、历史记录写入PostgreSQL 的索引机制和 MVCC 并发控制在这种负载下表现稳定运维工具链也比 MySQL 在某些场景下更顺手。对我个人来说还有一个软性收益是团队心智统一。开发、DBA、测试都只维护一套数据库所有数据备份、监控、账号权限体系完全复用之前已有的 PostgreSQL 规范而不是为了 Nacos 再学一套 MySQL 运维。这种隐性成本往往比机器成本更值得先算清楚。2. 插件集成的原理SPI 替换的是“数据源实现”不是换了一个数据库2.1 从数据访问层看插件的位置很多人第一次接触插件机制时容易把它想复杂以为 Nacos 改了底层存储引擎。其实不是Nacos 上层所有的一致性算法、服务发现流程、配置管理接口完全不需要关心底下是什么数据库。它们只面向一层数据访问接口这层接口负责把所有数据库操作翻译成对应的 SQL 方言再由具体的数据源插件去执行。SPI 机制相当于在这层接口背后留出了一个“实现加载点”。Nacos 内核在启动时会去 classpath 里扫描数据源插件找到匹配的 SPI 实现类然后初始化数据库连接池并绑定到上层模块。换句话说PostgreSQL 插件的工作不是教 Nacos 怎么认识 PostgreSQL而是替 Nacos 把 PostgreSQL 的连接串、SQL 方言、类型映射、初始化脚本都准备好让数据访问层在完全不改代码的情况下换一套底层实现。2.2 PostgreSQL 插件包里都有什么从拿到的插件 jar 文件内容来看通常包含三部分数据源连接相关的工厂类负责根据jdbc:postgresql://开头的连接串建立连接池SQL 方言适配层处理分页 SQL、批量插入返回主键、锁语句这些数据库差异还有数据库初始化脚本也就是 PostgreSQL 版本的建表 SQL一般在 jar 内部的 schema 目录里发行版包的conf目录下也会放一份独立文件。需要留意的是插件包通常会把 PostgreSQL JDBC 驱动也一起打进去不需要你另外下载驱动 jar。但你最好检查一下 Nacos 安装目录里有没有重复的驱动如果既有插件内置的驱动又手动放了另一个版本的驱动启动时可能因为版本冲突出现连接参数失效这类怪问题。2.3 相比 MySQL 内置支持PostgreSQL 方案差在哪好在哪用表格对比一下会更直观。对比维度MySQL 内置支持PostgreSQL 插件支持维护路径跟随 Nacos 主版本长期维护插件独立迭代需同步版本初始化 SQL发行包自带 mysql 版 schema发行包自带 postgresql 版 schema连接池HikariCP 统一管理HikariCP 统一管理体验一致数据库能力支持只覆盖 MySQL 特性只覆盖 PostgreSQL 特性团队已有 PG 资产无法复用完全复用社区问题反馈渠道官方主仓库处理插件仓库或主仓库对应 issue说实话插件方案在“开箱即用”程度上不如内置支持因为你要多下载一个 jar、多确认版本匹配但只要把这一步做好后面的配置、启动、运维体验和 MySQL 是持平的甚至由于不用为 Nacos 单独部署 MySQL整体系统更简单了。3. 单机先跑通从下载插件到初始化 PostgreSQL 的完整步骤3.1 版本匹配先确认 Nacos 版本和插件版本的对应关系我的建议是第一步不要急着建库先去确定版本。下载 Nacos 3.x 发行包之后在 Maven 中央仓库或 GitHub Releases 里搜nacos-datasource-plugin-postgresql找到和你的 Nacos server 主版本一致的插件版本。比如 Nacos server 是 3.2.x插件最好也选同主版本段。版本匹配这件事最容易踩的坑是“看着名字差不多就拿来用”。插件和主版本之间有兼容性约定如果插件版本过旧可能没有实现新版数据访问接口启动时 SPI 扫描不到实现类版本过新又可能引用了主版本里还没有的类。稳妥做法是先在本地或者测试环境把插件 jar 放进去启动一次看日志里能不能识别到 PostgreSQL 数据源再继续往下配。3.2 建库建用户注意 PG15 的 schema 权限变化PostgreSQL 准备阶段很简单找一个用来跑 Nacos 的库和账号。建议单独建一个数据库不要和其他业务表混在同一个库里因为 Nacos 的表结构有自己的索引和约束混用容易互相干扰。sudo -u postgres psql CREATE USER nacos WITH PASSWORD nacos2024; CREATE DATABASE nacos_config OWNER nacos ENCODING UTF8; \q这里有个关键点如果你用的是 PostgreSQL 15 及以上版本建库时最好直接把 owner 设为 nacos千万不要用超级用户建完库再授权给普通账号。PG15 开始普通用户不再默认拥有 public schema 的 CREATE 权限如果你用 postgres 建库再把库授权给 nacos 用户后续执行 schema 脚本时会直接报permission denied for schema public。处理办法我在后面踩坑部分会给出。3.3 执行官方 schema 脚本拿到发行包后conf目录下会有一份 PostgreSQL 专用的建表脚本文件名通常类似postgresql-schema.sql。执行前先确认脚本存在不要用 MySQL 版脚本去 PostgreSQL 里硬跑语法差异会导致一堆报错。cd /opt/nacos/conf ls -la | grep -i postgres PGPASSWORDnacos2024 psql -h 127.0.0.1 -U nacos -d nacos_config -f postgresql-schema.sql执行完之后进 PostgreSQL 确认表已经建出来\dt看到 config_info、service、users 这些核心表就算成功。如果中途报错多半是用户权限问题或脚本被截断把报错信息贴出来对比脚本行号排查即可。3.4 application.properties 里的 5 个关键配置Nacos 的 datasource 配置都集中在发行包的conf/application.properties文件里。不同小版本的配置项名称可能有调整比如有的版本用spring.datasource.platform有的版本改成spring.sql.init.platform我写配置时会先定位到 datasource 相关段落以发行包注释为准。核心配置项大概长这样spring.datasource.platformpostgresql db.num1 db.url.0jdbc:postgresql://127.0.0.1:5432/nacos_config?ApplicationNamenacosconnectTimeout5000socketTimeout15000tcpKeepAlivetruereWriteBatchedInsertstrue db.user.0nacos db.password.0nacos2024逐个解释一下spring.datasource.platform告诉 Nacos 使用哪种数据源实现。改成postgresql后启动时才会去加载 PostgreSQL 插件写mysql就会加载 MySQL 实现。想用插件但这项没改是最常见的“配置了插件却不生效”原因。db.num数据源数量通常情况下是 1也就是共用一套数据库连接。集群模式下不要以为节点多就要配多个数据源这里指的是每个节点内部的数据源实例数量不是数据库实例数。db.url.0PostgreSQL 连接串。ApplicationNamenacos方便你在 PostgreSQL 侧查 active 连接时区分出 Nacos 发的请求connectTimeout和socketTimeout控制建连和读超时默认值太大会让健康检查变得迟钝tcpKeepAlivetrue避免数据库侧断开空闲连接后客户端不知道reWriteBatchedInsertstrue是 PostgreSQL JDBC 驱动里很实用的优化参数能让批量插入合并成更高效的语句。db.user.0和db.password.0数据库账号密码如果密码里有特殊字符注意在 properties 文件里做对应转义。3.5 启动日志怎么判断有没有真的连上 PostgreSQL配置完成后启动单机sh bin/startup.sh启动后不要只看控制台输出关键是看日志。找到logs/nacos.log搜索类似数据源初始化的内容确认里面出现的是 PostgreSQL 相关信息而不是 Derby 或者 MySQL。最稳妥的验证方式是启动完成后去 PostgreSQL 里查一下连接和表变化SELECT datname, usename, application_name, client_addr FROM pg_stat_activity WHERE application_name nacos;然后打开 Nacos 控制台随便创建一个配置回到 PostgreSQL 查询config_info表能看到刚写入的记录就说明这条链路已经通了。4. 上集群三个节点共享同一个 PostgreSQL 的配置与验证4.1 集群拓扑怎么设计单机跑通之后集群部署其实就是把同样的数据源配置复制到多台机器上让所有节点读写同一个 PostgreSQL。我的建议拓扑是Nacos 节点三台PostgreSQL 单独部署在主从高可用架构上不要让 PostgreSQL 和 Nacos 挤在同一台机器。如果资源紧张至少也要用云数据库的托管实例独立备份。三个 Nacos 节点只是“计算节点”包括配置缓存、服务注册状态和一致性协议通信都跑在这三台上PostgreSQL 只承担最终存储的角色。这种结构下哪怕某一台 Nacos 节点宕机剩余节点仍然可以通过共享数据库提供完整的配置读写能力。节点角色示例 IP对外开放端口node-aconfig / naming192.168.10.118848、9848、9849、7848node-bconfig / naming192.168.10.128848、9848、9849、7848node-cconfig / naming192.168.10.138848、9848、9849、7848pg-primary数据存储192.168.10.205432pg-standby数据存储备份192.168.10.2154324.2 cluster.conf、端口、环境变量的正确姿势Nacos 集群节点列表通过conf/cluster.conf文件配置一行一个节点地址格式是 IP:端口三台机器内容保持一致192.168.10.11:8848 192.168.10.12:8848 192.168.10.13:8848如果不想改文件也可以用环境变量NACOS_SERVERS传入同样的列表容器化部署时会更方便。启动时指定集群模式sh bin/startup.sh -m cluster端口方面除了控制台 HTTP 的 8848还要注意 9848 是客户端 gRPC 端口9849 是服务端之间 gRPC 通信端口7848 是配置管理一致性协议端口。安全组只开 8848 的话客户端连接会发现 9848 连不上只开 9848 没开 7848节点之间数据同步就会出问题。这三类端口在节点之间必须全部互通。4.3 三个节点全部指向 PostgreSQL 后该验证什么集群配置完成后分几步验证打开任意节点控制台进入集群管理页面确认三个节点全部显示 UP。在节点 A 创建一个配置然后切到节点 B、节点 C 的控制台查看应该都能看到同一份配置因为数据来自同一个 PostgreSQL。注册一个临时服务实例分别在三个节点的服务列表里检查实例信息应该一致。停掉节点 A用 Nacos 客户端重新注册或读取配置客户端应该自动切换到剩余节点不会出现大面积失败。再说个容易被忽略的点验证集群状态时不要只看控制台页面页面走的是你当前连接节点的 admin 接口它显示的节点状态是节点间通过协议互相上报的结果。更真实的验证方式是用 Nacos 客户端 SDK 连接集群地址列表反复读写配置和注册服务看行为是否符合预期。4.4 节点宕机和数据库抖动会有什么表现Nacos 节点宕机时配置读取会由客户端自动切换节点已有配置因为每个节点内存里都有缓存所以读不受影响新建配置或更新配置会走到还活着的节点。瞬时出现少量超时是正常的因为客户端节点列表刷新和服务发现有一个重试过程。数据库抖动的情况则表现不一样。PostgreSQL 短暂不可用时Nacos 节点不会立刻宕机因为配置和服务信息在内存里有副本但所有需要落库的操作会失败比如新建配置、修改配置、持久化服务实例。数据库恢复后HikariCP 连接池会自动重连不用重启 Nacos。我在实际测试中遇到过连接池重连卡住的现象表现为数据库恢复后 Nacos 仍然报连接失败。后来排查是 HikariCP 的 maxLifetime 值和数据库侧空闲连接回收机制不匹配旧连接被数据库断掉后连接池没有及时淘汰。把连接池参数理清楚这个问题是可以避免的后面一节会展开讲。5. 生产环境踩过的坑连接超时、schema 权限、孤儿节点5.1 连接串参数时区、批量写、TCP 保活连接串里的参数看起来不起眼但影响很大。reWriteBatchedInsertstrue没有这个参数PostgreSQL 驱动批量插入还是逐条发送性能差一截。Nacos 批量写入服务实例、批量记录配置历史时都有明显收益。tcpKeepAlivetrue如果连接空闲超过数据库侧超时时间数据库会把连接断掉但 Java 端感知不到。TCP 保活能让客户端主动发现连接失效触发 Hikari 回收。connectTimeout和socketTimeout不要设成 0。0 表示无限等待数据库卡住时 Nacos 的数据库操作线程会全部堆积最终拖垮整个节点的配置读写。建议连接超时控制在 3-5 秒读超时 10-15 秒。时区相关参数视情况配置。如果 PostgreSQL 和 Nacos 部署在不同时区建议在连接串里显式指定时区避免配置时间记录、心跳时间出现偏差。5.2 PG15 的 public schema 权限问题这个坑我前面提过实际遇到时很值得单独说。PostgreSQL 15 开始出于安全考虑普通用户失去了对 public schema 的 CREATE 权限。很多人习惯用 postgres 超级用户先建库然后再把库 grant 给业务账号但 schema 权限没跟上执行 Nacos 的 schema 脚本时就会卡在permission denied for schema public。如果已经建了库但 owner 给错了用下面两条语句修复ALTER DATABASE nacos_config OWNER TO nacos; ALTER SCHEMA public OWNER TO nacos;建议直接在建库时就把 owner 设对省得后面处理。云厂商的托管 PostgreSQL 一般也会遇到同样的限制如果控制台建库时没法设置 owner就去 PostgreSQL 里手动执行 ALTER。5.3 集群里出现“孤儿节点”的排查链路集群模式下出现“节点 A 写的配置节点 B 看不到”是经典故障案例。我在测试环境复现过一次完全按文档配置结果还是数据不一致。当时梳理的排查链路是这样第一步去节点 B 的日志里搜数据源初始化记录确认它加载的确实是 PostgreSQL 插件而不是某种后备逻辑。如果节点 B 因为配置项名称不对或插件缺失可能悄悄退回 Derby那它的数据自然和 A 完全隔离。第二步在 PostgreSQL 上查config_info表如果 A 写入的数据确实已经落库说明存储层没问题问题出在 B 从数据库读到数据之后没有同步到自己的缓存。第三步检查节点之间的 7848 端口连通性和 cluster.conf 内容。配置管理在一致性协议上的数据传播依赖节点间能正常交换日志如果 B 加入集群失败即使共享数据库B 也不会认为 A 是最新的数据来源。第四步看日志里有没有一致性协议相关的错误比如对端地址不可达、日志复制超时。最后通过重启出问题的节点让它重新参与协议同步问题解决。这里有个认知要纠正共享同一个 PostgreSQL不等于所有节点自动一致。PostgreSQL 是最终存储但 Nacos 节点内存里的状态是通过一致性协议对齐的数据库只是共识结果的落地点。节点间协议不通数据库再统一也是各看各的。5.4 从 MySQL 迁移配置别直接导 SQL走 OpenAPI 导出如果你从 MySQL 版本的 Nacos 迁移到 PostgreSQL不要试图直接把 MySQL 的config_info表数据导出再导入 PostgreSQL。两张表结构虽有相似处但自增主键、时间类型、唯一索引都有差异直接导数据很容易造成 ID 冲突或外键错乱。最稳妥的方式是走 Nacos 控制台或 OpenAPI 的配置导出功能。控制台里的配置管理支持把配置数据导出成压缩包里面是标准的配置文件和元数据。迁移到新集群后再通过导入功能恢复到 PostgreSQL 存储上。服务实例一般不需要迁移客户端重启后会重新注册临时实例也会自动重新上报心跳。这种做法的好处是迁移的是“配置内容”而不是“数据库记录”绕开了存储层差异是跨数据库迁移最安全的手段。6. 性能、备份和后续演进6.1 连接池参数与写入量评估Nacos 对数据库的要求其实不像业务系统那么高配置变更、服务注册心跳都不是密集写库场景每次写入的数据量也很小。核心是控制好连接池避免异常情况下连接占满。我这边生产环境用的参数可以作为起点db.pool.config.connectionTimeout30000 db.pool.config.validationTimeout5000 db.pool.config.idleTimeout600000 db.pool.config.maximumPoolSize50 db.pool.config.minimumIdle10 db.pool.config.maxLifetime1800000maximumPoolSize不要直接照着大业务系统的经验设到几百Nacos 本身并发写库能力有限池子过大会增加 PostgreSQL 侧的连接数压力。50 这个级别在中等规模集群下已经足够。连接池参数改完要重启 Nacos 才会生效线上变更时先选一个非核心节点验证确认连接池起来后再滚动更新。6.2 PostgreSQL 侧的备份与历史表清理Nacos 的his_config_info表记录每一次配置变更历史刚开始没人在意跑几个月后可能涨到几百万行。配置变更越频繁这张表膨胀越快。建议在 PostgreSQL 侧做两层动作。第一层是例行的备份用 PostgreSQL 自带的逻辑备份即可PGPASSWORDnacos2024 pg_dump -h 192.168.10.20 -U nacos -d nacos_config -F custom -f /backup/nacos_config.dump第二层是历史数据清理。在确认团队不需要长期审计所有配置历史后可以按时间清理DELETE FROM his_config_info WHERE gmt_create now() - interval 90 days; VACUUM ANALYZE his_config_info;清理前建议先确认业务审计要求有的团队配置历史是要留底的。如果要保留更合理的做法是把历史表归档到独立数据库或对象存储而不是长期堆在主库里。6.3 插件化之后还能接什么数据库数据源插件化之后理论上 Nacos 内核不再绑定任何一种数据库官方提供 MySQL、PostgreSQL 的适配社区也可以基于 SPI 接口贡献其他数据库实现。像 Oracle、达梦、OceanBase 这类国内政企常用的数据库只要有人按接口实现 SQL 方言和数据源工厂就可以作为插件接入。这个方向的意义在于Nacos 不再抢占公司已有的数据库技术栈而是主动去适配现有的基础设施。我接触过的不少团队数据库选型不是开发者定的而是公司基础设施部门统一的。插件化让 Nacos 在选型上少了很多阻碍。6.4 一点个人的使用体会整套方案跑下来我最直接的感受是“存储选型的自由度回来了”。以前 Nacos 集群想上生产MySQL 几乎是硬性条件现在 PostgreSQL 作为一等公民意味着我们可以在一个技术栈内解决配置中心、服务发现、数据库存储的所有问题。如果你正要规划 Nacos 3.x 集群我建议在测试环境多花一点时间验证插件版本匹配和 PG 权限因为这两个点出问题时的报错信息不够直观容易让人误以为部署步骤写错了。把单机链路完全跑通再上集群能省掉很多排查时间。另外别低估 PostgreSQL 本身的高可用地位。Nacos 再高可用底下的数据存储层挂了配置读写依然会受影响。数据源插件的引入解决的是兼容性问题但数据库本身的备份、监控、故障切换该做的功夫一样不能少。