简介:面向使用 Nacos 做配置与注册中心、同时需要适配达梦数据库的 Java 开发者与运维人员,这套源码与可运行包组合解决了 Nacos 默认多依赖 MySQL 等数据源、迁移到达梦时需改动持久层与初始化脚本的问题。包内同时提供 Windows 与 Linux 环境可直接启动的版本,也保留完整源码,既能导入 IDEA 按需修改后重新编译,也可直接取用已编译产物,并附带达梦数据库初始化 SQL 脚本,便于快速搭起可验证的运行环境。压缩包共 92 个文件,约 297.51MB,涵盖日志、Raft 元数据与快照、配置样例、Shell 启动脚本、SQL 脚本、properties/yaml 配置及 jar 等类型,分别对应运行记录、集群状态、启动部署与数据库初始化等用途,目录按 Nacos 发行结构组织,检索和替换较直观。目前已有 895 人学习,适合需要完成国产数据库适配、排查数据源配置或对照源码理解 Nacos 持久化改造的读者参考。
1. 为什么 Nacos 接达梦不是换个 URL 就完事:先看三个硬门槛
把 Nacos 的数据源从 MySQL 切到达梦,最容易出现的误判是:改完db.url、db.user、db.password就能启动。实际做 Nacos 达梦 适配时,连接打通只是第一关,后面还有 DDL 类型、分页 SQL、幂等写入、时间函数和连接池参数。这个适配包解决的是“已有 Nacos 配置中心和注册中心,要把底层存储换成达梦”的落地问题,适合正在做数据库替换的后端、中间件运维和测试同学。
先按路线走,别一上来就盲改 XML。Nacos 的持久化层不是黑匣子,它把配置、租户、命名实例等数据交给 MyBatis Mapper 执行 SQL;MySQL 能跑的语句,在达梦里未必能跑,原因通常不是 JDBC 连不上,而是方言差异。
2. 从 MySQL 方言到达梦:先定适配路线再动 Mapper
Nacos 的持久化层有一套固定的表结构和 Mapper XML,配置发布、配置查询、租户信息、命名实例注册都会走这些 SQL。达梦默认更接近 Oracle 风格的标识符和函数体系,所以“只换驱动”只能解决连接问题,解决不了执行问题。我的习惯是先把路线定清楚,再决定改多少代码。
2.1 两条路线:兼容模式快验 vs 方言改造长期维护
现场常见的做法有两条,别混着用。
| 路线 | 改动范围 | 优点 | 风险 | 适用场景 |
|---|---|---|---|---|
| A:达梦兼容模式 | 保留 MySQL schema 和大部分 Mapper,只改驱动、URL、账号 | 上手快,适合先验证网络、驱动、权限 | 兼容模式能过启动,不代表分页、并发发布、命名心跳都正常 | 临时验证、POC |
| B:方言改造 | 改 DDL、分页、upsert、时间函数,按达梦默认兼容风格跑 | 行为可控,适合长期维护 | 工作量大,需要逐条回归 | 生产交付 |
我一般先用 A 打通连接,确认驱动、账号、schema、网络没问题;再用 B 做交付。血泪经验是:兼容模式能过启动,不代表配置列表翻到第二页不重复,也不代表命名实例下线后列表能及时消失。生产环境不要把 A 的结果直接当最终方案。
2.2 驱动、连接串与连接池参数怎么落
第一步是把达梦 JDBC 驱动放进 Nacos 运行时 classpath。常见做法是先把厂商给的 jar 安装到本地 Maven 仓库,版本号用local占位,避免把不存在的版本写进依赖。groupId和artifactId可以按团队规范改,但必须和pom.xml保持一致。
mvn install:install-file \ -Dfile=./DmJdbcDriver.jar \ -DgroupId=com.dameng \ -DartifactId=DmJdbcDriver \ -Dversion=local \ -Dpackaging=jar这段命令只做一件事:把本地 jar 注册到 Maven 仓库,让后面的模块能按坐标引用。-Dfile指向你手里的驱动文件;-Dversion=local是本地占位版本,不是达梦的官方版本号。装完后在持久化相关模块加依赖:
<dependency> <groupId>com.dameng</groupId> <artifactId>DmJdbcDriver</artifactId> <version>local</version> </dependency>如果不在 Maven 模块里加依赖,也可以把驱动放进 Nacos 发行包的运行时lib目录,二选一,别只丢在conf目录。驱动类名用dm.jdbc.driver.DmDriver,连接串里的 schema 建议全大写,例如NACOS,这样能避开达梦默认大写标识符带来的“表不存在”问题。
spring.datasource.platform=mysql db.num=1 db.url.0=jdbc:dm://127.0.0.1:5236/NACOS?useUnicode=true&characterEncoding=UTF-8&useSSL=false db.user.0=NACOS db.password.0=change_me db.pool.config.driverClassName=dm.jdbc.driver.DmDriver db.pool.config.connectionTimeout=30000 db.pool.config.validationTimeout=10000 db.pool.config.maximumPoolSize=20 db.pool.config.minimumIdle=5这里的spring.datasource.platform=mysql是 Nacos 外置数据源的加载入口,不是数据库类型声明;真正连哪个库由driverClassName和db.url.0决定。如果你的 Nacos 分支对platform做了枚举校验,就在数据源工厂里补一个dm分支,别只改字符串。db.num=1表示单数据源;maximumPoolSize=20是起步值,不要照搬 MySQL 场景的 100;connectionTimeout=30000和validationTimeout=10000先把超时压住,后面再按压测调。连接测试如果显式配置,达梦通常用SELECT 1 FROM DUAL,不要写裸的SELECT 1。
改完配置后重新打包并启动:
mvn -Prelease-nacos -DskipTests clean package sh bin/startup.sh -m standalone tail -f logs/start.out-m standalone是单机模式,适合先验证适配链路;生产按你的集群部署方式启动。logs/start.out里重点看数据源初始化、Mapper 加载和建表语句报错,不要只看“启动成功”四个字。
3. 建库建表:把 Nacos 的 MySQL DDL 翻成达梦能认的结构
Nacos 发行包里的 MySQL schema 是适配的底稿,但不能原样执行。常见差异集中在自增主键、时间类型、大文本、ON UPDATE CURRENT_TIMESTAMP、反引号和ENGINE子句。先把这些规则定下来,再批量改,比在报错里一条条试要快。
3.1 数据类型与自增主键的替换规则
下面这张表是我常用的第一轮替换规则,具体字段还要以你手上版本的 schema 为准。
| MySQL 写法 | 达梦建议写法 | 注意点 |
|---|---|---|
BIGINT AUTO_INCREMENT | BIGINT IDENTITY(1,1) | 插入时不要显式给 id 赋值 |
INT AUTO_INCREMENT | INT IDENTITY(1,1) | 老代码若显式插 id,要改成序列或兼容写法 |
DATETIME | TIMESTAMP | 默认值统一用CURRENT_TIMESTAMP |
TEXT/LONGTEXT | CLOB | MyBatis 参数要带jdbcType=CLOB |
TINYINT | SMALLINT或现场支持的TINYINT | 不要假设无符号 |
UNSIGNED | 去掉 | 达梦不按 MySQL 的无符号语义走 |
| 反引号 | 去掉 | 保留反引号容易变成带引号的标识符 |
ENGINE=InnoDB | 去掉 | 达梦不认这个子句 |
ON UPDATE CURRENT_TIMESTAMP | 应用层赋值或触发器 | 我倾向应用层显式赋值,触发器批量更新时容易埋雷 |
config_info是配置中心的核心表,可以先拿它做样板。下面这段 DDL 只保留关键字段,实际以你版本里的 schema 为准:
CREATE TABLE config_info ( id BIGINT IDENTITY(1,1) NOT NULL, data_id VARCHAR(255) NOT NULL, group_id VARCHAR(128), content CLOB NOT NULL, md5 VARCHAR(32), gmt_create TIMESTAMP DEFAULT CURRENT_TIMESTAMP, gmt_modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP, src_user CLOB, src_ip VARCHAR(50), app_name VARCHAR(128), tenant_id VARCHAR(128), c_desc VARCHAR(256), c_use VARCHAR(64), effect VARCHAR(64), type VARCHAR(64), c_schema CLOB, encrypted_data_key VARCHAR(1024), PRIMARY KEY (id) );这段 DDL 的重点不是字段名,而是三个行为:id用IDENTITY自增,插入后要能被 MyBatis 回填;content、src_user、c_schema用CLOB,不能拿=直接比较;gmt_modified用TIMESTAMP,更新时由应用层或触发器赋值。CLOB字段不要建普通唯一索引,唯一约束尽量落在data_id、group_id、tenant_id这种短字段上。
3.2 从 schema 到索引:一次可复现的初始化流程
第一步,从 Nacos 发行包的conf目录找到 MySQL schema 脚本,复制一份到临时目录,不要直接改原文件。第二步,用脚本做第一轮机械替换,生成草稿,再人工审查。下面这个 Python 脚本只处理最常见的几类写法,输出的是草稿,不是最终可上线脚本。
import re from pathlib import Path src = Path("mysql-schema.sql").read_text(encoding="utf-8") sql = src.replace("`", "") # 自增主键:bigint(20) / int(11) -> IDENTITY sql = re.sub( r"(?i)\b(bigint|int)\(\d+\)\s+NOT NULL\s+AUTO_INCREMENT", lambda m: f"{m.group(1).upper()} IDENTITY(1,1) NOT NULL", sql, ) # 时间与大文本 sql = re.sub(r"(?i)\bdatetime\b", "TIMESTAMP", sql) sql = re.sub(r"(?i)\b(longtext|mediumtext|text)\b", "CLOB", sql) # 去掉 MySQL 表选项 sql = re.sub(r"(?i)\s+ENGINE=\w+[^;]*", "", sql) sql = re.sub(r"(?i)\s+ON UPDATE CURRENT_TIMESTAMP", "", sql) sql = re.sub(r"(?i)\s+DEFAULT CHARSET=\w+", "", sql) Path("dm-schema-draft.sql").write_text(sql, encoding="utf-8")脚本里最关键的是自增替换和TEXT -> CLOB,这两类不改,后面大概率会在插入和查询阶段翻车。ENGINE、DEFAULT CHARSET这类表选项直接删掉;ON UPDATE CURRENT_TIMESTAMP不要指望达梦按 MySQL 的行为自动更新,改成应用层赋值更稳。脚本跑完必须人工搜一遍UNSIGNED、TINYINT、JSON、ENUM、KEY这些残留写法,别把草稿直接灌进生产库。
第三步,建用户和 schema。不要让应用直接用高权限账号,按现场规范来。下面只是最小示例:
CREATE USER NACOS IDENTIFIED BY "change_me"; GRANT RESOURCE TO NACOS; CREATE SCHEMA NACOS AUTHORIZATION NACOS;CREATE USER和CREATE SCHEMA的顺序按现场规范调整;GRANT RESOURCE是给基础对象权限,生产还要按最小权限补角色。schema 名和连接串里的NACOS保持一致,大小写也保持一致,后面能省很多排查时间。
第四步,用达梦客户端执行草稿:
disql NACOS/change_me@127.0.0.1:5236 SQL> start /opt/nacos/dm-schema-draft.sqldisql是达梦常用命令行客户端;start后面跟脚本绝对路径。执行时不要只看最后一句“成功”,要往上翻有没有单条语句失败。第五步,把原 schema 里的索引补齐,尤其是唯一索引:
CREATE UNIQUE INDEX uk_configinfo_datagrouptenant ON config_info(data_id, group_id, tenant_id); CREATE INDEX idx_configinfo_gmt_modified ON config_info(gmt_modified);唯一索引决定了后面MERGE的匹配行为,不能省。config_info的data_id + group_id + tenant_id是常见唯一组合,具体以你版本为准;gmt_modified上的普通索引用于列表排序和分页。索引名可以按团队规范改,但唯一键的字段顺序要和 Mapper 里的查询条件对得上。
4. Mapper 改造:分页、幂等写入和函数替换的四个落点
DDL 跑通后,下一关是 Mapper XML。Nacos 的持久化 SQL 里有一批 MySQL 方言写法,最典型的是LIMIT、ON DUPLICATE KEY UPDATE、INSERT IGNORE、NOW()、IFNULL、DATE_FORMAT。不要靠启动报错一条条撞,直接全局搜。
4.1 分页 SQL 不要照搬 LIMIT
MySQL 的LIMIT #{start}, #{pageSize}在达梦里不要原样保留。常见改法是OFFSET ... FETCH,但要注意start是偏移量,不是页码。下面是一个 Mapper 片段的前后对比。
改之前:
<select id="findConfigInfo4Page" resultMap="configInfoResultMap"> SELECT * FROM config_info WHERE tenant_id = #{tenantId} ORDER BY gmt_modified DESC LIMIT #{start}, #{pageSize} </select>改之后:
<select id="findConfigInfo4Page" resultMap="configInfoResultMap"> SELECT * FROM config_info WHERE tenant_id = #{tenantId} ORDER BY gmt_modified DESC, id DESC OFFSET #{start} ROWS FETCH NEXT #{pageSize} ROWS ONLY </select>OFFSET #{start} ROWS表示跳过多少行,FETCH NEXT #{pageSize} ROWS ONLY表示取多少行。排序里加id DESC是为了让gmt_modified相同的数据也有稳定顺序,否则翻页时容易出现重复或丢行。如果你的达梦版本不支持OFFSET/FETCH,就用ROWNUM包一层,但不要混用两套分页写法。改完后至少造 200 条配置,从第一页翻到最后一页,确认没有重复 id。
4.2 upsert 与 INSERT IGNORE 改成 MERGE
Nacos 里租户信息、部分注册数据会用“插入或更新”的写法。MySQL 常见是ON DUPLICATE KEY UPDATE,达梦用MERGE INTO更直接。改之前:
INSERT INTO tenant_info (id, kp, tenant_id, tenant_name, create_time, update_time) VALUES (#{id}, #{kp}, #{tenantId}, #{tenantName}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) ON DUPLICATE KEY UPDATE tenant_name = #{tenantName}, update_time = CURRENT_TIMESTAMP;改之后:
MERGE INTO tenant_info t USING (SELECT #{kp} AS kp, #{tenantId} AS tenant_id FROM DUAL) s ON (t.kp = s.kp AND t.tenant_id = s.tenant_id) WHEN MATCHED THEN UPDATE SET t.tenant_name = #{tenantName}, t.update_time = CURRENT_TIMESTAMP WHEN NOT MATCHED THEN INSERT (kp, tenant_id, tenant_name, <p> <a href="https://download.csdn.net/download/qq_39177884/87474330" style="color:#ec7500;font-size:14px;"> 本文还有配套的精品资源,点击获取 </a> <img alt="menu-r.4af5f7ec.gif" src="https://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif" style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;"> </p>