Nacos 2.3 接入达梦 DM8 实战:数据源、建表脚本与方言避坑

发布时间:2026/10/26 18:28:38
Nacos 2.3 接入达梦 DM8 实战:数据源、建表脚本与方言避坑

简介:面向需要在国产数据库环境中落地注册配置中心的开发与运维人员,这份 nacos 适配达梦数据源整合包提供可直接运行的 Windows、Linux 版本,并附带可二次修改、自行编译的源码,用于解决 Nacos 默认数据源与达梦不兼容时的部署和改造问题。压缩包共 92 个文件,约 297.51MB,涵盖配置、日志、集群元数据、启动脚本、SQL 初始化文件及构建产物等,既有已编译服务端目录,也保留可导入 IDEA 调整的工程内容,便于按需替换配置或重新打包。已有 895 人学习,说明该方案在国产化迁移场景中具有一定参考价值。读者可快速搭建验证环境,导入达梦初始化脚本,并结合配置项与脚本排查数据源连接、集群启动和配置推送等问题,减少从零改造的时间。

1. 从一次换库翻车说起:Nacos 2.3 接达梦 DM8 到底卡在哪

某次信创改造,要把一套微服务的注册配置中心从 MySQL 挪到达梦 DM8。当时判断很朴素:换驱动、改 URL、导脚本,半小时的事。结果 Nacos 起来了,控制台能登录,配置也能发,一周后才发现数据全写进了内嵌 Derby,达梦那边一张表都没动。这就是 nacos 达梦 组合最坑的地方——它不报错,它只是静默地用另一套存储。中间还接连踩了建表脚本里ON UPDATE CURRENT_TIMESTAMP、分页LIMIT ?,?、实例大小写敏感三个雷。下面按实际落地顺序走一遍:版本怎么挑、达梦侧参数和脚本怎么改、Nacos 侧驱动和方言怎么收口、报错怎么查,最后怎么确认数据真的落进达梦。

2. 版本盘点与环境准备:Nacos、DM8 与 DmJdbcDriver18 的搭配清单

动手之前先把三条链路分清楚:Nacos 服务端自身的 JDK、达梦实例的 JDBC 驱动、以及 Nacos 访问数据库的那层数据源实现。这三者版本错位,表现出来往往是“启动成功但行为不对”,排查起来非常耗时间。我一般会先在纸上把版本写死再开工,避免一边装一边试。

2.1 三条链路分别对齐什么版本

Nacos 2.x 的服务端编译目标是 JDK 8,运行在 JDK 8 和 JDK 11 上都稳;JDK 17 不是不能跑,但一些依赖的反射行为会变,不是这次换库要解决的问题,别在这上面加变量。达梦这边,DM8 的 JDBC 驱动有好几个文件名,DmJdbcDriver18.jar对应 JDK 1.8 及以上,驱动类是dm.jdbc.driver.DmDriver,这个类名必须和配置文件里写的一字不差,写错就是No suitable driver。达梦服务端版本用 8.1.x 系列即可,重点不在小版本号,而在实例初始化时的那几个参数,第 3 章会展开。

Nacos 版本是这套组合里最需要做取舍的一项。2.0.x 和 2.1.x 用spring.datasource.platform声明数据源平台;2.2.0 起改成spring.sql.init.platform,同时把 MySQL 驱动从主包里摘出去,改由plugins/mysql/目录外挂。这个变化对达梦适配是利好——驱动外置意味着我们塞达梦的 jar 进去就能被加载,不用重新打 fat jar。所以只要条件允许,我建议直接上 2.2.x 或 2.3.x,别在 2.1 上折腾。

组件建议版本关键说明
Nacos2.2.3 / 2.3.x2.2 起数据源插件化、JDBC 驱动外置
JDK8 或 11与 Nacos 编译目标一致,别引入 17
DM88.1.x需要 MySQL 兼容模式
JDBC 驱动DmJdbcDriver18.jar驱动类dm.jdbc.driver.DmDriver
客户端工具disql灌 schema、核对数据都靠它

另外要留意一个容易被忽略的差异:Nacos 2.2 之后nacos-datasource-plugin这一层把 SQL 方言抽成了 SPI,默认实现只有 MySQL 一套。也就是说,Nacos 发出的 SQL 天然是按 MySQL 语法写的,达梦能不能直接接住,取决于兼容模式覆盖到什么程度。这个判断直接决定后面要不要写插件代码。

2.2 数据源到底走哪条路,先想清楚再配

Nacos 单机启动时,如果没有配外置数据源,会退回内嵌 Derby,数据落在${NACOS_HOME}/data/derby-data下。这个兜底逻辑是好事,也是最大的坑:配置写错它不会启动失败,只是默默用 Derby。判断依据只有一个——db.num是否大于 0 且db.url.0是否存在,两个条件同时满足才会构建外置数据源。

所以配置阶段的核心不是“把 URL 写对”,而是“让 Nacos 相信你要用外置库”。常见做法是把这几行一次性写全,不要留注释状态的分号试探:

# 平台声明:2.2.0 起用 spring.sql.init.platform,老版本是 spring.datasource.platform spring.sql.init.platform=mysql # 库数量,必须 >=1,否则直接走内嵌 Derby db.num=1 # 第 0 个库的连接串,schema 指定到达梦用户 db.url.0=jdbc:dm://10.0.0.21:5236?schema=NACOS&useUnicode=true&characterEncoding=UTF-8 db.user.0=NACOS db.password.0=Nacos_2024#dm # 驱动类必须显式改,默认值还是 com.mysql.cj.jdbc.Driver db.pool.config.driverClassName=dm.jdbc.driver.DmDriver

db.num这一行看着多余,实际是最常被删掉的一行。有人在精简配置时觉得“只有一个库写它干嘛”,删完重启,日志一切正常,Derby 目录又冒出来了。db.pool.config.前缀是 Nacos 对连接池属性的透传,后面的名字按连接池本身的属性名写,driverClassName、maximumPoolSize、connectionTimeout都在这个命名空间下。

2.3 开工前的资源核对

数据库连接数是最容易在上线后炸的一项。达梦实例的MAX_SESSIONS默认值不大,而 Nacos 集群每个节点都会起一个连接池,再加上运维工具、监控采集的会话,很容易顶到上限。我一般按“节点数 × 池上限 + 50”估一个值出来,再回头调实例参数。内存方面,单机验证给 2G 就够,startup.sh -m standalone默认堆不大;但达梦侧如果和 Nacos 同机,注意别把两边堆加起来把机器吃满。

还有一项准备是备份。改application.properties之前先复制一份带日期后缀的副本,这是整套流程里唯一的后悔药。后面任何一步走不通,把副本盖回去、重启,至少能回到一个已知可用的状态,不用在几十行配置里靠记忆还原。

3. 达梦侧的准备工作:实例参数、表空间与建表脚本改造

这一章是整套适配里最花时间、也最容易返工的部分。Nacos 的建表脚本是给 MySQL 写的,直接扔进达梦能执行一半,然后在中途某条语句上报语法错误,前面的表已经建了,后面的没建,最后得到半个 schema。所以脚本必须先改后灌,不要边灌边改。

3.1 两个初始化参数决定了后面所有事

达梦实例有两个参数,装完再改代价很大,必须在建库那一步就定下来。

第一个是COMPATIBLE_MODE,取值 3 表示 MySQL 兼容模式。开了之后,一部分 MySQL 语法和函数能被识别,Nacos 发出的 SQL 命中率会高很多。这个参数可以通过系统过程修改并重启生效:

-- scope 传 2 表示同时写进 dm.ini,重启实例后生效 SP_SET_PARA_VALUE(2, 'COMPATIBLE_MODE', 3); -- 顺手把会话上限提上去,避免后面连接池打满 SP_SET_PARA_VALUE(2, 'MAX_SESSIONS', 500);

第二个是CASE_SENSITIVE,控制标识符大小写是否敏感,默认是敏感的。达梦在敏感模式下会把不加引号的表名、列名统一按大写处理,而 Nacos 的 SQL 全是小写裸名,结果就是一堆“无效的列名”。这个参数不能在实例建好后修改,只能在初始化实例时指定为不敏感。我见过最惨的一种情况是实例已经跑着别的业务,只能新建一个实例专门给 Nacos 用。

修改完参数,用下面这条语句确认落地情况,别只信命令没报错:

SELECT PARA_NAME, PARA_VALUE FROM V$DM_INI WHERE PARA_NAME IN ('COMPATIBLE_MODE', 'CASE_SENSITIVE', 'MAX_SESSIONS');

V$DM_INI是达梦的实例参数视图,PARA_VALUE是字符串类型,看到3和N才算真的生效。如果CASE_SENSITIVE还是Y,后面的建表和查询都会反复出问题,不如现在就停下来重建实例。

3.2 建表空间与业务用户

不建议用 SYSDBA 跑业务表,权限太大,出了问题也不好定位。单独建一个表空间和用户,把这套 Nacos 的表全部收在里面。

-- 表空间:初始 512M,按 64M 递增,上限 10G CREATE TABLESPACE "NACOS" DATAFILE '/dm/data/DAMENG/NACOS01.DBF' SIZE 512 AUTOEXTEND ON NEXT 64 MAXSIZE 10240; -- 业务用户,默认表空间指向 NACOS CREATE USER "NACOS" IDENTIFIED BY "Nacos_2024#dm" DEFAULT TABLESPACE "NACOS"; -- 验证环境直接给 DBA,生产按需授予 CREATE TABLE / CREATE SESSION 等权限 GRANT "DBA" TO "NACOS";

这里给DBA是为了验证阶段省事,能跑通之后再逐条收权限。表空间的数据文件路径按实际安装目录改,AUTOEXTEND ON一定要开,Nacos 的历史配置表his_config_info增长比想象中快,磁盘不够时表空间写满会直接导致发布失败。

3.3 mysql-schema.sql 的逐条改法

原始脚本在 Nacos 发行包的conf/mysql-schema.sql,先解压取出来:

# 从发行包里单独取出建表脚本,避免整包解压 unzip -o nacos-server-2.3.2.zip 'nacos/conf/mysql-schema.sql' -d /tmp/nacos-src cp /tmp/nacos-src/nacos/conf/mysql-schema.sql /tmp/dm-schema.sql # 先把所有 MySQL 专有写法列出来,逐条确认改没改 grep -nE 'ENGINE=|AUTO_INCREMENT|ON UPDATE CURRENT_TIMESTAMP|UNIQUE KEY| KEY |bigint\(|tinyint\(|longtext' /tmp/dm-schema.sql

改造对照关系如下,这张表我一般存成便签,每换一次库都要过一遍:

MySQL 写法达梦写法说明
bigint(20)/tinyint(4)BIGINT/TINYINT去掉显示宽度,达梦不认括号里的数字
AUTO_INCREMENTIDENTITY(1,1)放在列类型后面,自增起点和步长
longtext/textCLOB配置内容字段必须用大对象类型
datetimeTIMESTAMP时间精度按需加(6)
ON UPDATE CURRENT_TIMESTAMP直接删掉达梦不支持,靠应用层显式更新时间
ENGINE=InnoDB DEFAULT CHARSET=utf8直接删掉表尾整段去掉
UNIQUE KEY uk_x (a,b)CONSTRAINT uk_x UNIQUE (a,b)内联索引改成表级约束
KEY idx_x (a)单独CREATE INDEX idx_x ON t(a);普通索引从建表语句里拆出来

ON UPDATE CURRENT_TIMESTAMP删掉之后要确认一件事:Nacos 更新配置时是否自己写了gmt_modified。大部分更新语句是显式传时间参数的,但也有少数依赖数据库自动更新。稳妥的做法是先跑通,然后连续发布两次配置,用 disql 看gmt_modified有没有变化,没变就说明这条链路依赖了数据库特性,得补一个触发器或者改 SQL。

列名关键字的问题也要单独过一遍。type、level、role、comment这几个词在达梦里属于关键字,直接当列名用会报语法错误。两种处理:建表时给列名统一加双引号,配合实例的大小写不敏感能对上 Nacos 发出的裸名;如果加引号后仍然报无效列名,就说明必须改名,改完把对应的 Mapper 字段名一起改。

改完的脚本用 disql 灌进去,用 heredoc 的方式避免 shell 把反引号当命令替换:

disql NACOS/'Nacos_2024#dm'@10.0.0.21:5236 <<'EOF' set echo off start /tmp/dm-schema.sql exit EOF

灌完立刻核对表数量,别等 Nacos 启动时才报错:

-- 业务用户下应该能看到十几张表 SELECT COUNT(*) FROM USER_TABLES; -- 单独确认配置主表结构完整 SELECT COLUMN_NAME, DATA_TYPE FROM USER_TAB_COLUMNS WHERE TABLE_NAME = 'CONFIG_INFO' ORDER BY COLUMN_ID;

USER_TABLES和USER_TAB_COLUMNS是达梦的数据字典视图,第二条能直接看出content字段是不是变成了CLOB、自增列有没有加上IDENTITY。这一步花五分钟,比后面在启动日志里猜半小时划算。

4. Nacos 侧接入配置:驱动落位、数据源参数与方言收口

达梦那边准备好之后,Nacos 侧的活其实不多,但每一步都有个“配错了也不报错”的陷阱。顺序上我习惯先解决驱动能不能加载,再解决连接串对不对,最后才处理 SQL 方言——前面没通之前,改 SQL 是白费功夫。

4.1 驱动 jar 放在哪才生效

Nacos 2.2.0 之后 JDBC 驱动不再打进主包,而是从${NACOS_HOME}/plugins/下的子目录加载。达梦的驱动直接从安装目录拷过去就行:

# 达梦驱动默认在安装目录的 drivers/jdbc 下 ls /opt/dmdbms/drivers/jdbc/DmJdbcDriver18.jar # 放到 Nacos 约定的外挂驱动目录,注意是 plugins/mysql,不是 plugins/dm mkdir -p ${NACOS_HOME}/plugins/mysql cp /opt/dmdbms/drivers/jdbc/DmJdbcDriver18.jar ${NACOS_HOME}/plugins/mysql/ # 确认启动脚本把这个目录拼进了类加载路径 grep -n "plugins/mysql" ${NACOS_HOME}/bin/startup.sh

目录名mysql看着别扭,但这是 Nacos 发行包固定的约定,放plugins/dm不会被扫描到。如果grep没有任何输出,说明这个版本的启动脚本没有拼这段路径,手动在JAVA_OPT里补一行:-Dloader.path=${BASE_DIR}/plugins/mysql。补完用sh bin/startup.sh -m standalone启动一次,日志里出现驱动相关异常就说明类没加载到,先解决这个再看别的。

4.2 application.properties 的六行关键配置

配置文件的改动集中在数据源这一段,其余保持默认。除了 2.2 小节那几行连接信息,连接池参数也建议显式写出来,默认值在不同版本间有差异:

# 连接池上限,按达梦 MAX_SESSIONS 反推,集群节点数乘以它要留余量 db.pool.config.maximumPoolSize=20 db.pool.config.minimumIdle=5 # 拿不到连接等多久,默认 30 秒,内网可以适当缩短便于快速暴露问题 db.pool.config.connectionTimeout=30000 # 连接空闲校验,防止达梦侧把长时间空闲的会话掐掉后拿到死连接 db.pool.config.validationTimeout=10000 db.pool.config.idleTimeout=600000 db.pool.config.maxLifetime=1800000

maxLifetime比达梦侧会话超时时间短,是一套组合拳里容易被忽略的一环。数据库侧主动断开空闲连接时,连接池如果还认为连接有效,业务侧就会拿到一个已经断掉的连接,报错信息通常是“连接已关闭”之类,非常难定位。做法就是让连接池先于数据库回收连接。

还有一个判断要提前说清楚:spring.sql.init.platform=mysql这个键在 Nacos 里的作用是声明初始化脚本的平台,配合 Spring 的脚本初始化机制。生产环境不要指望它自动建表,表还是手动用 disql 灌。它的价值在于让配置和版本对齐,少一个变量。

4.3 方言收口:先跑通,再改 SQL

Nacos 的 SQL 是按 MySQL 写的,开了兼容模式之后大部分能直接执行,剩下的是少数几类。我一般按“先跑、看报错、再改”的顺序,而不是一上来就写一套方言实现。

常见的三类差异,按出现频率排:

第一类是分页。MySQL 写LIMIT offset, size,达梦兼容模式不一定接受这种带逗号的形式,报错信息通常是Syntax error near ','。改成LIMIT size OFFSET offset就能过。

第二类是INSERT ... ON DUPLICATE KEY UPDATE这类 MySQL 专有写法,达梦要用MERGE INTO改写。这类语句数量不多,但一旦命中就是启动期的功能不可用。

第三类是聚合函数,GROUP_CONCAT在达梦没有同名实现,需要换成对应的字符串聚合函数。

如果确实需要改 SQL,Nacos 2.2 之后提供了 SPI 扩展点,把方言实现注册进去即可。做法是在自己的工程里放一个 SPI 声明文件,内容是自定义实现类的全限定名:

# 文件路径 src/main/resources/META-INF/services/com.alibaba.nacos.plugin.datasource.MapperManager # 内容一行,指向你自己的方言实现类 com.example.nacos.dm.DmMapperManagerImpl

自定义实现的做法是继承默认的 MySQL 实现,只覆盖报错的那几个方法,其余全部沿用父类。方法签名照抄父类,不要自己发明参数顺序:

// 只覆盖分页方法,其余 SQL 全部沿用父类的 MySQL 实现 public class DmConfigInfoMapper extends ConfigInfoMapper { @Override public String findConfigInfoLike4PageFetchRows(int startRow, int pageSize) { // 达梦用 LIMIT size OFFSET offset,避免 MySQL 的 LIMIT a,b 语法 return "SELECT id, data_id, group_id, tenant_id, app_name, content, md5 " + "FROM config_info WHERE 1=1 LIMIT " + pageSize + " OFFSET " + startRow; } }

方法名以你引入的插件版本为准,编译不过就去 IDE 里看父类定义,别硬改。改完之后把插件 jar 放进${NACOS_HOME}/plugins/对应目录,重启验证。这套做法的好处是改动面极小,坏处是多一个需要维护的 jar,所以能不改尽量不改,先用兼容模式扛住。

4.4 启动与第一轮确认

启动命令本身没变化,单机模式加-m standalone:

cd ${NACOS_HOME} sh bin/startup.sh -m standalone # 启动日志,重点看有没有 use external storage 字样 tail -f logs/start.out

启动完成后第一件事不是开控制台,而是看数据目录:

# 目录里出现 derby-data 就说明还在用内嵌库,配置没生效 ls -l ${NACOS_HOME}/data

这个检查看着土,但它是整套流程里最快的是非判断。只要data/derby-data没出现,就说明 Nacos 确实走了外置数据源,后面再去核对表里的数据才有意义。

5. 排查清单:启动报错、连接超时与静默回退的五条踩坑记录

这套组合的问题有个共同点:大部分不报“致命错误”,而是表现为功能静默失效。下面五条是我在不同环境里真实遇到过的,按现象、原因、解决三段记录,遇到新问题时可以先对照一遍。

5.1 五条踩坑记录

第一,控制台能登录、配置能发布,但达梦的表是空的。现象是data/derby-data目录被创建,用 disql 查config_info只有 0 行。原因是外置数据源没构建起来,最典型的是db.num没写或者写成 0,其次是db.url.0被注释掉了,还有一种是把配置写到了cluster.conf里但启动用了单机模式。解决是把db.num=1和db.url.0两行补全,重启后看启动日志里是否出现使用外置存储的提示,再ls data/确认 Derby 目录不再生成。

第二,启动直接抛No suitable driver found for jdbc:dm://...。现象是 Nacos 起不来,日志里一大段连接异常。原因有两个:驱动 jar 没放进plugins/mysql/,或者db.pool.config.driverClassName没改,还是默认的 MySQL 驱动类。解决是确认 jar 在这个目录下、启动脚本把它拼进了loader.path,同时把驱动类显式写成dm.jdbc.driver.DmDriver,三个条件缺一不可。

第三,SQL 报“无效的列名”。现象是启动时某条查询报错,或者发布配置时报列不存在。原因是实例的大小写敏感参数是Y,达梦把裸列名当大写处理,和 Nacos 传的小写对不上;也可能是列名撞了达梦关键字。解决办法是先确认CASE_SENSITIVE为N,实例改不了就只能重建;关键字冲突就给列名加双引号,仍不行就改名并同步改 Mapper。

第四,建表脚本执行到一半中断。现象是表建了一部分,后面的语句报Syntax error near 'ON UPDATE'或ENGINE。原因是脚本没改干净,MySQL 的表尾选项漏了。解决办法是灌之前先跑一遍 grep,把ENGINE=、AUTO_INCREMENT、ON UPDATE CURRENT_TIMESTAMP、bigint(这几类全部列出来逐条确认,改完再灌。已经建了一半的表先删掉重来,不要在半成品 schema 上继续。

第五,运行一段时间后报连接池超时,Connection is not available, request timed out。现象

本文还有配套的精品资源,点击获取