
前段时间做信创适配手头有个微服务项目要从MySQL整体迁到国产关系型数据库注册中心和配置中心用的是Nacos。一开始以为把连接串改一改就能跑结果真动起手来才发现Nacos从2.x到3.x变化不小3.1.0适配国产数据库这件事远不是换个驱动那么简单。这篇文章就围绕Nacos 3.1.0适配国产数据库这件事把我在实际项目中踩过的坑、验证过的方案、改过的配置、编译过的插件完整梳理一遍。整个过程以达梦、人大金仓、openGauss三个国产关系型数据库为主要适配目标顺带聊聊TiDB、OceanBase这类兼容MySQL协议的数据库怎么做。如果你是做基础软件国产化替换、微服务架构改造的尤其是负责注册中心、配置中心迁移的这篇文章应该能帮你省不少时间。1. 项目背景与整体适配思路1.1 为什么3.1.0这个版本值得单独适配先说个容易忽视的点Nacos 3.1.0和2.x在存储层上的差异非常大。很多人以为Nacos只是个配置中心底层数据库无非是存几行配置、服务实例注册信息随便换个库都行。但Nacos 3.x引入了更完善的控制台、更复杂的元数据管理、新的服务发现模型表结构、SQL语句、事务处理方式都和2.x不一样了。我在适配时翻了3.1.0的源码发现它默认的数据库脚本、Mapper XML里的SQL方言、分页写法等很多地方是面向MySQL写的。比如配置发布时的历史版本记录、灰度配置的存储、服务实例的心跳续约数据这些操作在3.x里更频繁对数据库的事务隔离级别、自增主键处理、SQL语法兼容性要求也更高。所以做3.1.0的国产数据库适配不只是把驱动JAR换掉、把URL改一下而是要解决三类问题驱动层兼容国产数据库的JDBC驱动类名、URL格式、连接参数和MySQL不同。SQL方言兼容Nacos内置的SQL语句在MySQL下没问题但换到达梦、金仓这类不完全兼容MySQL语法的数据库上就会报错。构建与插件机制适配Nacos从2.2之后支持数据源插件扩展适配国产数据库需要利用或扩展这个插件机制。1.2 适配方案选型协议兼容与源码改造两条路关于适配方案我当时列了三个选项挨个分析后选了第二个。这三条路分别是第一直接用MySQL协议兼容的数据库。像TiDB、OceanBase这类数据库因为兼容MySQL协议Nacos基本不用改驱动用MySQL的连接串改一改就能跑。这个方案成本最低但如果项目要求必须用达梦、金仓这类自研数据库就走不通。第二基于Nacos的数据源插件机制自定义实现国产数据库的方言适配。Nacos本身提供了一组SPI接口比如DataSourcePlugin允许接入自定义数据源。在这条路里需要自己编译插件、打包驱动、改配置。代价是工作量大一些但适配完之后不依赖MySQL协议是真真正正接入了国产库。第三直接改Nacos源码把内置SQL全部改成目标数据库方言。这个方案最彻底但维护成本极高Nacos每次升级都要重新合并代码不建议采用。我实际采用的是第二条同时结合第一条的思路用MySQL协议的库先跑通功能再逐步切换到达梦、金仓上做完整验证。这篇文章后面讲的内容主要也是围绕这个方案展开的。2. 适配前的环境准备与依赖替换2.1 版本清单与环境确认动手之前先把版本对齐这件事做好。我在项目里用的是一套比较新的组合建议你也按这个思路去确认自己的版本不要想当然。我本机环境是这样的Nacos版本3.1.0源码包和发行包我都用到了编译插件用源码部署用发行包JDK版本17Nacos 3.x要求JDK 8以上但官方实际上推荐JDK 17控制台编译、插件编译都更稳Maven版本3.8目标数据库达梦8试用版、人大金仓V8R6、openGauss 5.0后面还用了TiDB 6.5做对照Nacos部署方式单机模式先验证后续再上集群这里特别提醒一下Nacos 3.x的默认鉴权、控制台登录这些功能适配数据库时不要关闭。有些文档让你先把鉴权关掉方便调试但3.x下某些操作在鉴权关闭时可能走不同的逻辑分支反而掩盖了数据库兼容问题。我踩过一次关掉鉴权后配置发布正常一打开鉴权就报查询权限表失败折腾了半天才发现是SQL大小写的问题。2.2 数据库初始化从MySQL脚本到国产库方言Nacos发行包里自带的数据库脚本比如mysql-schema.sql默认放在distribution/conf目录下。这个脚本是MySQL语法写的你不能直接拿到达梦、金仓上执行得先做方言转换。我第一步是建库和建用户。以达梦为例建议创建独立用户和表空间不要把Nacos的数据放到SYSDBA用户下。因为达梦对权限管理比较严格Nacos连接数据库时如果使用SYSDBA后续初始化表结构可能因为模式名不对而失败。具体操作大概是-- 达梦创建用户并指定表空间 CREATE TABLESPACE NACOS_TS DATAFILE NACOS_TS.DBF SIZE 256; CREATE USER NACOS IDENTIFIED BY nacos123 DEFAULT TABLESPACE NACOS_TS; GRANT DBA TO NACOS;这里要注意达梦默认的DBA角色权限比较大如果你们公司的DBA觉得风险高可以只授予Nacos需要的增删改查、创建表、创建索引等权限但那样需要额外排查权限点比较费时间。我在实验环境直接用了DBA角色生产环境建议让DBA评估最小权限。接下来的重点是表结构脚本的转换。Nacos 3.1.0的表数量比2.x多了不少涉及配置、服务、命名空间、用户、角色、权限、灰度、健康检查等模块。直接用工具转换的话容易漏掉自增列、默认值、索引名这些细节。我建议用DBeaver或者DataGrip先把MySQL脚本里的ENGINEInnoDB DEFAULT CHARSETutf8mb4这种MySQL专属语法全局去掉再逐段执行。达梦方面比较坑的一点是它对字段注释的处理。MySQL脚本里COMMENT xxx写在字段定义后面达梦8也能支持但如果语句顺序不对或者字段类型是LONGTEXT达梦不认要改成TEXT或CLOB。Nacos的config_info表里content字段就是LONGTEXT我当时改成了TEXT实测达梦下完全够用配置内容几MB以内的都没问题。2.3 构建期替换数据源插件与驱动JAR的处理Nacos官方在设计上已经考虑到了多数据源扩展但默认发行包只带了MySQL和Derby的插件。要接达梦、金仓这类库需要自己构建插件并放到Nacos的plugins目录下。我用的是源码构建方式。从GitHub拉取nacos-3.1.0源码后在plugin/datasource目录下能看到几个子模块其中nacos-datasource-plugin-ext就是给扩展数据源用的。我的做法是这样# 拉取源码并切换到3.1.0标签 git clone https://github.com/alibaba/nacos.git cd nacos git checkout 3.1.0 # 进入数据源扩展插件目录 cd plugin/datasource然后修改nacos-datasource-plugin-ext的pom.xml加入对应国产数据库的JDBC驱动依赖。以达梦为例达梦官方驱动坐标不在中央仓库得先手动安装到本地Maven仓库mvn install:install-file \ -DfileDmJdbcDriver18.jar \ -DgroupIdcom.dameng \ -DartifactIdDmJdbcDriver \ -Dversion8.1.2.192 \ -Dpackagingjar这里有个细节不同达梦版本驱动包名会有差异比如DmJdbcDriver18表示JDK1.8及以上版本编译的如果你的Nacos跑在JDK17上也要选对应的驱动。装完依赖后在插件源码里新增一个数据源实现类继承Nacos的AbstractDataSourcePlugin或者直接DataSourcePlugin接口。我当时写了一个DmDataSourcePlugin核心就是返回达梦的DataSource设置好驱动类、URL、用户名、密码。这个过程比想象的简单主要工作量集中在SQL方言适配和后续的调试上。插件构建完之后把产物JAR和对应驱动JAR一起复制到Nacos发行包的plugins目录修改application.properties指定数据源类型再启动验证。这一步说起来轻松实际操作中因为依赖冲突、SPI配置没生效导致的问题我后面在踩坑章节会细讲。3. 核心配置与SQL方言的适配细节3.1 application.properties中的关键配置项Nacos 3.1.0的数据库配置在conf/application.properties里。默认情况下如果你不配置数据库Nacos会使用内嵌Derby。改成国产数据库后核心配置项是这样的spring.datasource.platformmysql db.num1 db.url.0jdbc:dm://127.0.0.1:5236/NACOS?zeroDateTimeBehaviorconvertToNulluseUnicodetruecharacterEncodingutf8 db.user.0NACOS db.password.0nacos123咦为什么数据库是达梦spring.datasource.platform却写的是mysql这是一个很容易误导人的地方。原因在于Nacos内部会按照platform的值去加载对应的SQL语句集合。Nacos源码里默认内置了mysql和derby两套SQL映射插件机制还没有强大到把Mapper XML也完全外部化。所以我接达梦的时候采取的思路是数据源用达梦驱动但SQL执行时依然走Nacos内置的MySQL方言集合。这意味着什么呢意味着达梦数据库要开启MySQL兼容模式或者在SQL层面尽量兼容MySQL语法。达梦8在这方面做得还可以特别是COMPATIBLE_MODEMYSQL这个参数建议在达梦的dm.ini里把兼容模式打开。不然的话Nacos的Mapper XML里大量使用的LIMIT分页语法、反引号包裹的表名字段名、NOW()函数达梦都会识别不了。如果你用的是金仓或openGauss同样走这个思路。金仓在kingbase.conf里有一个dbcompatibility参数可以设为MySQLopenGauss也支持SET dbcompatibility B这样的兼容性设置。但要注意即使开了兼容模式某些查询仍然可能出现问题这个我下面会重点讲。3.2 达梦、金仓、openGauss的驱动差异对照我把三个数据库的驱动信息整理成了一个对照表方便你直接抄数据库JDBC驱动类URL格式默认端口注意事项达梦8dm.jdbc.driver.DmDriverjdbc:dm://127.0.0.1:5236/NACOS5236需要开启MySQL兼容模式驱动注意JDK版本人大金仓V8R6com.kingbase8.Driverjdbc:kingbase8://127.0.0.1:54321/nacos54321基于PostgreSQL内核部分PG函数可用openGauss 5.0org.opengauss.Driverjdbc:opengauss://127.0.0.1:5432/nacos5432也是PG内核但驱动需要下载对应版本TiDB 6.5com.mysql.cj.jdbc.Driverjdbc:mysql://127.0.0.1:4000/nacos4000完全兼容MySQL协议适配成本最低OceanBase 4.xcom.mysql.cj.jdbc.Driverjdbc:mysql://127.0.0.1:2883/nacos2883MySQL模式基本兼容需注意OB的分布式事务限制驱动这一层最容易踩的坑是版本不匹配。比如达梦驱动有DmJdbcDriver16、DmJdbcDriver17、DmJdbcDriver18的区别对应不同JDK版本。金仓的驱动包版本如果和数据库服务端版本不一致可能能连上但执行复杂SQL时会出现奇怪的报错比如ERROR: invalid input syntax for type oid。openGauss这边由于Nacos 3.1.0运行在JDK17上而openGauss官方驱动下载页提供的JDBC包有时候是JDK8编译的理论上JDK17能跑但如果你遇到IllegalAccessError就要检查是不是驱动和JDK版本兼容性问题。3.3 SQL方言兼容分页、自增主键、保留字这一节是全文核心中的核心因为驱动和连接只是入场券真正让Nacos跑起来的是那一堆SQL语句。我在适配过程中发现Nacos 3.1.0内置SQL在国产数据库上主要暴露在三类问题上。第一类是分页语法。Nacos有很多查询列表的SQL比如配置列表、服务列表、历史版本列表全部用了MySQL的LIMIT offset, size写法。达梦在非兼容模式下分页要写成LIMIT size OFFSET offset或者用TOP加嵌套查询。金仓呢因为内核是PostgreSQL如果设置了dbcompatibilityMySQLLIMIT offset, size这个写法能识别但openGauss不一定有时候必须写成LIMIT size OFFSET offset。所以我在验证时专门把Nacos源码中所有的LIMIT ? , ?找出来逐个在目标数据库里执行测试确保数据库能接受这种写法。第二类是自增主键。Nacos 3.x的表比如config_info的主键id用的是BIGINT AUTO_INCREMENT。达梦的IDENTITY列和MySQL的AUTO_INCREMENT行为不完全一致。在达梦里即使你向表中显式插入自增列的值后续自动增长也可能不会跳到最大值1这会导致配置ID冲突。我当时采取的办法是把达梦表的自增列改成IDENTITY(1,1)并且在初始化脚本里显式重建序列Nacos插入时不指定主键ID让数据库自己分配。第三类是保留字和大小写问题。Nacos的SQL里大量使用反引号包裹表名、字段名比如SELECT data_id, group_id, tenant_id FROM config_infoMySQL下这个写法没问题但达梦、金仓默认会把不带引号的对象名转成大写存储带反引号的内容又会按小写处理两种模式混在一起就会导致表或视图不存在。解决思路是在数据库初始化阶段就把表建成小写名称且在兼容模式下运行。如果表已经建成大写那只能回炉重新建表。这里提醒一句建表脚本执行前先确认目标库的标识符大小写策略免得做到一半推倒重来。4. 踩坑实录与问题排查4.1 Derby残留数据导致的启动失败这是一个非常典型的坑。Nacos如果之前用默认配置启动过会在用户目录下生成nacos-derby-data目录里面是Derby的数据库文件。你切换到国产数据库后如果配置没改干净或者配置文件里还有一个spring.sql.init.platform之类的配置没覆盖Nacos启动时还是会尝试初始化Derby结果可能出现端口占用、Derby锁文件冲突、或者控制台里能看到旧数据的问题。我排查这个问题时看到一个现象明明application.properties里已经配置了达梦的数据源启动日志刚开始也显示连接了达梦但控制台首页的旧配置还在。后来发现是用户目录下的Derby缓存没有清理Nacos 3.1.0在启动流程里对Derby有额外的初始化动作。解决办法很粗暴每次切换数据库前把nacos-derby-data、nacos-derby.log这些缓存文件删干净。具体路径一般在用户目录下的nacos文件夹里。如果你用的是集群模式还要注意每个节点的缓存都要清理只清一台没用。4.2 控制台中文乱码与字符集设置配置中心存中文内容很常见Nacos控制台展示配置时对字符集很敏感。MySQL下默认utf8mb4就没什么问题但到达梦上如果表字符集是默认的GBK或者驱动连接参数没加characterEncodingutf8控制台会出现中文乱码甚至保存带中文的配置时会报错。我最终在连接串里加了一串参数才解决jdbc:dm://127.0.0.1:5236/NACOS?useUnicodetruecharacterEncodingutf8zeroDateTimeBehaviorconvertToNull这里zeroDateTimeBehaviorconvertToNull也是必要的因为Nacos里有些表的创建时间字段可能为0000-00-00 00:00:00MySQL驱动默认会报错达梦驱动虽然不报错但加上这个参数可以避免一些边界情况。另外Nacos 3.1.0控制台本身的静态资源是UTF-8编码但如果你给JVM传了-Dfile.encodingGBK日志和控制台接口返回的中文会有问题。建议启动参数里显式加上-Dfile.encodingUTF-8。4.3 健康检查与心跳任务异常服务注册之后Nacos会对实例做健康检查。3.1.0的健康检查逻辑里涉及到数据库的读写操作比如记录实例心跳时间、更新实例状态。我在达梦上遇到一个诡异的情况服务能注册控制台能看到实例但过一会儿实例就被标记为不健康日志里报数据库连接异常。排查过程比较曲折后来发现是达梦的连接空闲超时时间太短Nacos的连接池里有些连接被数据库服务端断掉了但客户端不知道拿出来继续用就报错。解决办法是在达梦的服务端参数里把空闲连接回收时间调大或者在连接串里开启驱动层面的自动重连。但自动重连这个功能不同数据库驱动支持程度不一样达梦驱动我实测是不太可靠的。所以更稳妥的办法是调大连接池的validation-query检测并配合test-while-idle参数。Nacos的数据源配置里你可以在application.properties中增加db.pool.config.connectionTimeout30000 db.pool.config.validationTimeout10000这样连接池在取连接时会先验证连接是否可用能有效降低“连接已死但池里还在用”的概率。4.4 常见问题速查表我整理了一个速查表都是我在这次适配中真实遇到或验证过的问题场景你可以收藏一下等真遇到的时候对照排查现象可能原因排查/解决思路启动报table not found建表脚本没执行或表名大小写不一致重新执行转换后的建表脚本确认表名小写启动报driver not found驱动JAR没放到plugins目录或坐标没打入插件检查plugins目录确认插件JAR包含驱动类控制台打开配置报SQLSyntaxErrorExceptionSQL方言不兼容比如分页写法开启数据库MySQL兼容模式或替换SQL为对应方言服务注册成功但心跳失败数据库连接被服务端断开调大空闲超时开启连接池连接检测配置发布成功但查询列表为空事务隔离级别或配置信息读取SQL有问题查看Nacos日志的SQL执行详情检查事务是否能正常提交控制台登录后权限接口报错用户表、角色表数据没初始化检查users、roles、permissions三张表数据字符集乱码表字符集或连接参数问题统一使用UTF8字符集连接串加characterEncodingutf8集群模式下数据不同步插件未在集群所有节点生效集群每个节点都要放置插件和驱动且版本保持一致5. 回归验证与适配清单总结5.1 功能回归测试怎么做才算合格涉及注册中心和配置中心的迁移回归验证不能随便点点就完事。我在完成达梦适配后专门列了一个验证清单覆盖Nacos 3.1.0最核心的几个使用场景配置管理新建配置、编辑配置、删除配置、导入导出配置、历史版本回滚、灰度配置发布。服务发现服务注册、服务注销、临时实例心跳续约、持久实例注册、健康检查状态变化。权限控制用户创建、角色绑定、权限配置、登录鉴权、控制台操作审计。集群一致性多节点Nacos通过数据库共享数据验证同一个配置在不同节点上的读取一致性。异常场景杀掉一个Nacos节点、断掉数据库连接、重启Nacos进程后数据能否恢复正常。回归过程中我发现国产数据库下最薄弱的环节是“灰度配置发布”和“配置导入导出”。灰度配置在Nacos 3.x里会操作灰度标签相关的表表结构里有些字段长度比较长在达梦下如果字段类型转换时没有对应好存储时会报“字符串超出长度限制”。配置导入导出则涉及到复杂的批量SQL和临时表操作达梦对临时表的处理方式和MySQL不同容易在执行时报错。这两个功能一定要重点测。5.2 这套适配方案的适用边界很多朋友可能会问这套适配方案能不能直接搬到生产环境我的看法是要看你的生产环境数据库内核是什么。如果你的国产数据库是严格自研的比如达梦、金仓那么你大概率和我一样需要走源码插件扩展这条路并且要对Nacos内置SQL做充分的方言验证。如果你的国产数据库是开源内核衍生出来的比如openGauss、某些基于PostgreSQL的国产化产品那其实可以尝试用PostgreSQL方言来跑而不是继续用MySQL兼容模式但那样改动量会更大因为Nacos官方没有内置PostgreSQL的Mapper XML你需要把整套SQL都改掉工作量不小。我这里给一个实用建议在选型阶段优先和各数据库厂商的售前工程师确认他们对Nacos 3.x的支持情况。有些厂商已经提供了现成的适配包或部署方案比自己从头搞要省力得多。比如我们后来用openGauss时就发现社区里已经有了相关讨论和测试结果直接站在别人的肩膀上把坑跳过去了。5.3 插件化改造的长期维护体验最后聊聊长期维护的问题。Nacos升级是大版本演进中绕不开的事今天适配了3.1.0明天出了3.2.0怎么办我这次的实践体会是插件化改造本身就是为了降低升级维护成本。数据源插件、驱动JAR都放在plugins目录下只要Nacos官方没有大幅改动数据源接口新版本升级时大概率可以直接复用。但Nacos 3.x阶段接口还在演化每次升级前一定要对比官方release notes里是否有数据源相关的breaking change。如果只是SQL语句层面的调整那直接把新版发行包的conf目录覆盖再替换插件基本一分钟搞定。我自己维护这个适配时会做一个小的变更记录每次改了什么、为什么改、验证结果如何都记下来。坚持这么做后续排障和升级会轻松很多。最后分享一个我实际操作中的体会适配国产数据库这件事真正的难点不在于驱动和配置而在于你愿不愿意把Nacos的SQL一行一行地跑一遍、把控制台的每个功能点都点一遍。数据库兼容是个体力活但只要环境准备充分、测试覆盖到位3.1.0完全可以在国产关系型数据库上稳定运行。希望这篇文章能帮你少走几步弯路。