国产数据库迁移实战:从 Oracle 到达梦的三步法指南 📅 发布时间:2026/9/4 20:39:05 👁 浏览次数: 先说一个经历。几年前我第一次听到“国产数据库迁移”第一反应是“又要改一堆SQL、又要处理字段类型映射、又要担心数据丢不丢”心里多少有点打鼓。后来实际走完一套从 Oracle 到达梦数据库的迁移流程发现只要把工作拆成“评估、迁移、校验”三个阶段并且把每一阶段要做的事情提前列清楚整个过程并没有想象中那么可怕反而是一套可以复制、可以量化进度的方法。本文就把这套思路完整分享出来重点围绕“Oracle 迁移到达梦数据库”的场景展开也适合其他主流数据库向国产数据库迁移的参考。阅读本文你可以掌握国产数据库迁移前需要盘点哪些信息如何选择全量迁移还是增量同步三步迁移法的具体执行细节如何用 DBeaver、官方迁移工具、手写脚本做迁移和校验以及迁移过程中最常见的问题和最实用的工程建议。如果你是后端开发、DBA 或者项目技术负责人正在评估国产数据库落地这篇文章可以帮你少走不少弯路。1. 背景与核心概念为什么很多人觉得国产数据库迁移难1.1 国产数据库迁移到底在迁移什么数据库迁移不只是“把数据从一个库复制到另一个库”它至少包含三部分内容结构对象迁移表、视图、序列、索引、主外键、约束、存储过程、触发器、函数、包。数据迁移存量数据按目标库支持的方式写入并保证数据一致。应用适配修改连接配置、SQL 方言、事务控制、分页语句、日期函数等。从结构对象到数据再到业务调用任何一环没跟上都会让系统在新库上跑不起来或者跑起来结果不对。这也是“国产数据库迁移难”说法的主要来源很多人只做了数据复制忽略了语法差异和业务 SQL 兼容性最后在测试阶段被各种报错淹没。常见的国产数据库包括达梦 DM、人大金仓 KingbaseES、openGauss 系、OceanBase、TiDB 等。网上常说的“国产数据库排名前十名”在不同报告里口径差异很大有些看社区活跃度有些看信创落地案例有些看商业营收。选型时不必把排名当作唯一依据重点看团队熟悉度、与原库的兼容性、运维生态和许可证成本。1.2 数据库迁移的常见误区误区一认为“能连上目标库、能把数据导过去”就算迁移完成。实际上如果目标库表结构设计不合理或者应用里仍存在大量源库私有语法上线后随时可能炸出问题。误区二认为一次大版本或跨品牌迁移可以完全依赖某个工具一键完成。工具能处理常规表结构和数据但对于特殊类型、正则函数、存储过程、递归查询等复杂对象往往仍然需要人工介入。误区三忽视字符集和大小写敏感性。源库和目标库如果字符集不一致中文乱码几乎是必然事件如果目标库开启了大小写敏感配置未加引号的表名、字段名可能会产生“表或视图不存在”的经典报错。1.3 不同迁移场景的区分在实际规划中需要先区分“同构迁移”和“异构迁移”Oracle 11g 到 Oracle 12c/19c属于同构迁移常见做法有物理 DataGuard、RMAN 恢复、expdp/impdp全程相对平滑。Oracle 到达梦数据库属于异构迁移需要把 Oracle 兼容的 DDL/DML 转换为目标库能接受的写法。MySQL 到国产数据库同理要处理反引号、自增列、engineInnoDB后缀、ON DUPLICATE KEY UPDATE等语法差异。对应到迁移窗口又可以分为“停机全量迁移”和“增量同步迁移”。很多同学在搜索引擎里找“Oracle 11g 数据库怎么冷迁移”。如果你问的是 Oracle 到 Oracle冷迁移通常指停库后做文件级复制或逻辑导出再恢复如果目标是达梦这类国产库那本质上已经不是冷迁移而是“离线逻辑迁移”也就是把源库停掉或让业务暂停写入然后通过逻辑导出/迁移工具把结构、数据搬过去。对于业务允许停机、数据量不大、并发要求不高的系统这种离线全量迁移是性价比很高的方案如果业务不允许长时间停机则需要同步工具先做全量再拉取归档日志或使用 CDC 机制做增量追平。2. 迁移前的准备先盘点存量再谈方案2.1 盘点源库基本信息我曾经看过一些团队迁移方案都写好了到了现场才发现源库字符集是ZHS16GBK业务表有几百张 CLOB还有大量含START WITH ... CONNECT BY的递归查询。这些都是影响工作量的关键因素。因此第一步永远是盘点存量信息。以 Oracle 为例至少需要收集盘点项说明数据库版本10g、11g、19c 等不同版本导出的 SQL 风格有差异字符集关系到目标库字符集设计和导入导出参数实例大小与表数量总大小、最大表、行数分布对象类型清单表、索引、约束、视图、序列、触发器等特殊对象物化视图、DBLink、定时任务、AUTHID 存储过程应用连接信息JDBC 驱动、连接池、访问用户、Schema 名称查询 Oracle 当前用户下的对象数量可以用类似下面的 SQLSELECT object_type, COUNT(*) FROM user_objects GROUP BY object_type ORDER BY COUNT(*) DESC;这里补充一句目标国产库通常也提供兼容 Oracle 的字典视图但不建议把所有细节都寄托在单一工具上。很多数据字典查询SQL虽然两边都有但具体字段和返回格式会有差异。最好在源库查完之后导出一份对象清单作为后续核对目标库的依据。2.2 确定目标库和兼容模式每种国产数据库对 Oracle 的兼容程度不同。以达梦数据库为例它提供了兼容模式相关配置在 Oracle 兼容模式下很多 Oracle 数据类型如VARCHAR2、NUMBER以及SYSDATE、DECODE等函数都可以直接识别。这能大幅减少脚本改写量。需要留意的是兼容模式并不是银弹。VARCHAR2能建出来不代表内部行为完全一致SYSDATE能用不代表复杂的 PL/SQL 匿名块都能无缝执行。把目标库安装在测试环境之后一定要用真实的源库 DDL 做一轮试跑以实际报错为准。2.3 工具选型官方迁移工具、DBeaver、脚本先说一个比较反直觉的观点工具不在多而在于知道什么场景用什么工具。工具适用场景定位官方迁移工具大表多、数据类型多、存储过程多主力批量迁移DBeaver小表、临时排查、可视化查询辅助查看与手动作业自写 Python/Shell 脚本特殊表、复杂转换、接口调用补漏与定制手工 SQL少量结构调整、序列设置收尾修正这里特别说一下 DBeaver。网上经常有人问“DBeaver 如何进行数据库迁移”因为 DBeaver 是一款很流行的通用数据库客户端能同时连接 Oracle、达梦、MySQL 等多种数据源。它的定位更像“数据库工作台”适合做以下事情在一个界面里分别连接源库和目标库直观查看表、视图、序列等对象。查询小表数据对比表行数。将查询结果导出为 CSV、SQL 等格式。手工执行建表语句和修正 SQL。但要注意不要把 DBeaver 当作专业的异构迁移工具。跨品牌迁移时它的 DDL 转换能力有限也不会像专业迁移工具那样自动做对象依赖排序和错误重试。表少、字段简单时可以“连库拖数据”表多、关系复杂时还是优先用目标厂商自带的迁移工具。如果是 Java 项目从 MySQL/Oracle 迁移到达梦还需要提前确认 JDBC 驱动包和连接参数。达梦 JDBC 的驱动类和连接 URL 会随版本变化典型的写法形如spring.datasource.driver-class-namedm.jdbc.driver.DmDriver spring.datasource.urljdbc:dm://192.168.1.20:5236?schemaTEST spring.datasource.usernameTEST_USER spring.datasource.passwordyour_password注意不同版本驱动类名可能不变但 URL 参数格式以官方文档为准。生产环境不要直接用系统管理员账号应在目标库中创建业务专用账号并授予最小权限。2.4 迁移窗口与回退预案迁移方案里最容易被忽略的是回退预案。这里的回退不是说“万一失败了再导一次”而是明确回答三个问题迁移过程中源库是否还允许写入如果目标库数据校验不通过源库要不要继续对外服务回退时是否需要恢复源库到某个时间点生产环境切换前建议给源库做一次完整备份或至少导出最近可用的逻辑备份并且把备份文件存放在独立位置。涉及生产环境变更必须提前申请维护窗口并且先在小数据量的测试环境演练完整流程不要在正式环境边试边改。3. 三步迁移法评估、迁移、校验把迁移过程压缩成可执行的三个步骤并不是为了简化问题而是给整个团队一个统一的工作节奏先摸清家底再做迁移动作最后用数据和业务验证结果。下面分别展开。3.1 第一步对象与 SQL 兼容性评估评估阶段的核心产出是一份“风险清单”而不是迁移脚本。风险清单至少包括对象数量清单需要迁移哪些表、字段、约束、索引。DDL 兼容性清单哪些建表语句可以直接在目标库执行哪些需要改写。SQL 语法风险点业务代码中的NVL、DECODE、ROWNUM、CONNECT BY、SYSDATE、TO_DATE等使用情况。数据风险点大表数量、CLOB/BLOB 字段、字符集差异、可能引起乱码的对象。实际操作中可以先让官方迁移工具或 DBeaver 分别读取源库和目标库把源库的表结构脚本导出。例如下面的 Oracle 建表脚本-- Oracle 源表结构示例 CREATE TABLE t_order ( order_id NUMBER(12) NOT NULL, order_no VARCHAR2(32) NOT NULL, amount NUMBER(10,2) DEFAULT 0 NOT NULL, status NUMBER(1) DEFAULT 1, create_time DATE DEFAULT SYSDATE, remark VARCHAR2(500), CONSTRAINT pk_t_order PRIMARY KEY (order_id) );如果目标库处于 Oracle 兼容模式这段脚本可能直接创建成功如果目标库是更严格的通用模式可能需要改写成-- 目标库建表脚本通用模式改写示例 CREATE TABLE t_order ( order_id NUMERIC(12) NOT NULL, order_no VARCHAR(32) NOT NULL, amount NUMERIC(10,2) DEFAULT 0 NOT NULL, status NUMERIC(1) DEFAULT 1, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, remark VARCHAR(500), CONSTRAINT pk_t_order PRIMARY KEY (order_id) );这个例子只是想说明一个原则不要拿着一份源库 DDL 无脑执行要先在测试库试跑。官方迁移工具能生成目标库可执行的初步脚本人工只需要集中精力处理失败项这样效率更高。3.2 第二步结构迁移与数据迁移评估完之后进入正式迁移。为了避免“先导数据后建约束”导致外键校验失败一般推荐顺序是先创建用户、表空间等基础环境。迁移表结构先建父表再建子表。迁移序列和自增设置确保新插入主键不会与已有数据冲突。迁移索引但大表上的索引可以等数据导入完成后再重建。迁移数据子表数据在父表数据之后导入。迁移视图、函数、存储过程、触发器并逐项编译。最后创建外键约束和补充检查。如果是大表建议按主键范围或时间条件分批读取。下面是一个 Python 思路的骨架核心是“源库游标读一批目标库批量写一批”避免一次性加载全部数据导致内存溢出。示例代码不代表现成可跑的生产脚本需要按实际表和驱动调整。# 引入源库和目标库的 Python 驱动 # 例如pip install oracledb dmPython import oracledb import dmPython # 连接参数需要替换成实际环境 src oracledb.connect(userscott, passwordpassword, dsn192.168.1.10:1521/orcl) dst dmPython.connect(userTEST_USER, passwordpassword, server192.168.1.20, port5236) table_name t_order start_id 0 batch_size 10000 with src.cursor() as cur_src, dst.cursor() as cur_dst: while True: # 每次按主键范围取一批 cur_src.execute( SELECT * FROM t_order WHERE order_id :a AND order_id :b ORDER BY order_id, astart_id, bstart_id batch_size ) rows cur_src.fetchall() if not rows: break # 列名和占位符需要根据表结构调整 cols [ORDER_ID, ORDER_NO, AMOUNT, STATUS, CREATE_TIME, REMARK] placeholders ,.join([?] * len(cols)) insert_sql ( fINSERT INTO {table_name} f({,.join(cols)}) VALUES ({placeholders}) ) cur_dst.executemany(insert_sql, rows) dst.commit() start_id batch_size print(f已完成到 order_id{start_id}) src.close() dst.close()这里有几个需要注意的点NUMBER在 Python 读取后可能被转成Decimal或float写入目标库时要注意精度。目标库如果开启了大小写敏感不带引号的表名、列名经常被统一转为大写示例中列名写成大写可以省掉很多引号麻烦。如果某张表没有主键或唯一键分页式读取就不是好主意可以改用ROWID分片或游标批量提取。对于数据量特别大的表官方迁移工具通常比自写脚本更稳妥因为它内置并行、断点续传、日志记录等能力。脚本方案更适合用来补偿工具覆盖不到的特殊表。3.3 第三步应用适配与一致性校验数据导完后最重要的工作是应用适配与校验。这个阶段往往占整个迁移周期的一半以上。应用适配主要分两类第一类是连接层适配。比如 Spring Boot 项目要替换驱动、连接 URL、数据库方言等。很多使用若依这类快速开发平台的项目在国产化适配时会遇到初始化脚本直接执行报错的问题。原因通常是 SQL 脚本本身含有源数据库特有的语法例如若依默认初始化脚本里常见的datetime默认值、sysdate、MySQL 反引号等。正确做法不是直接在业务库执行原始脚本而是先梳理脚本中使用的方言特性再做兼容改写。若依平台本身的核心业务表结构并不复杂真正麻烦的是把菜单 SQL、初始管理员数据、部门数据一次执行成功所以建议在测试库先跑初始化脚本哪里报错改哪里不要在生产环境试错。第二类是 SQL 方言适配。举个例子很多老项目会把 Oracle 的DECODE写得满天飞。Oracle 兼容模式下达梦能识别该函数但如果团队希望长期维护低成本建议逐步改成标准的CASE WHEN。再比如分页写法Oracle 经典分页依赖ROWNUM兼容模式下仍可以识别但更推荐改成标准窗口函数-- 改写前的 Oracle 风格 SELECT * FROM ( SELECT t.*, ROWNUM rn FROM t_order t WHERE status 1 ) WHERE rn 0 AND rn 20; -- 可跨库的窗口函数风格 SELECT * FROM ( SELECT t.*, ROW_NUMBER() OVER (ORDER BY order_id) rn FROM t_order t WHERE status 1 ) WHERE rn 0 AND rn 20;数据一致性校验不是只比一张表的行数而是至少做三层比对行数比对每张表执行COUNT(*)源库和目标库结果一致。关键字段比对抽样比对主键、金额、状态、时间等核心字段。业务逻辑比对跑几组核心业务接口验证查询结果、报表统计、批量任务是否和源库一致。源库侧可以先构造一张“各表行数清单”-- Oracle 源库行数统计示例单表少时可以手工执行 SELECT t_order AS table_name, COUNT(*) AS cnt FROM t_order UNION ALL SELECT t_user, COUNT(*) FROM t_user;目标库执行同样的 SQL然后用文本对比工具比对两份输出能快速发现差异表。4. 一次 Oracle 到达梦的迁移实战拆解为了让上面的三步法更有体感下面用一个模拟场景演示整体流程。假设某内部系统使用 Oracle 11g核心表有t_order、t_user、t_order_item业务应用是 Spring Boot MyBatis当前数据库连接配置指向 Oracle。现在计划迁移到达梦数据库业务允许停机 4 小时。4.1 迁移前整理对象清单在源库执行-- 查看当前用户下所有表名 SELECT table_name FROM user_tables ORDER BY table_name; -- 查看表字段信息这里只取常用信息 SELECT table_name, column_name, data_type, data_length, nullable FROM user_tab_columns WHERE table_name IN (T_ORDER, T_USER, T_ORDER_ITEM) ORDER BY table_name, column_id;把表结构导出后进行改写并提前确定以下映射关系Oracle 类型达梦类型参考说明VARCHAR2(n)VARCHAR(n)兼容模式下可直接保留NUMBER(p,s)NUMERIC(p,s)常规数值映射DATETIMESTAMP更通用CLOBCLOB两边均有注意功能函数差异BLOBBLOB同上4.2 用官方迁移工具做存量数据迁移这一阶段不做人工 INSERT而是使用目标库官方迁移工具连接 Oracle 源库和达梦目标库选择要迁移的 Schema 或表执行“结构 数据”的迁移任务。过程中建议先只迁移 3 张核心表观察数据量、速度、日志。确认无误后再迁移其余表。迁移完成后让工具生成一份报告重点看失败对象和跳过对象。如果你的环境没有官方迁移工具也可以先使用 DBeaver 对表结构做可视化检查。用 DBeaver 分别建立 Oracle 和达梦连接后可以查看每张表的字段、索引、约束并导出部分数据做抽样。需要注意DBeaver 导出的 SQL 脚本在不同数据库间仍然存在方言问题不能保证在目标库直接执行成功。4.3 MyBatis Mapper 兼容改写数据库切换后最常见的问题不在建表而在 MyBatis 的 XML Mapper 里。比如下面这个查询本身是跨库友好的!-- 文件路径src/main/resources/mapper/TOrderMapper.xml -- select idselectValidOrders resultTypemap SELECT order_id, order_no, amount, create_time FROM t_order WHERE status #{status} ORDER BY order_id /select这段 SQL 没有NVL、没有ROWNUM、没有TO_DATE简写Oracle 和达梦都认识。但很多老项目会写出这样的句子select idselectByCondition resultTypemap SELECT * FROM ( SELECT t.*, ROWNUM rn FROM t_order t WHERE t.status #{status} ORDER BY t.create_time DESC ) WHERE rn gt; #{offset} AND rn lt; #{limit} /select这段 SQL 使用ROWNUM做分页如果目标库兼容模式能运行可以暂时保留但从可维护性角度建议改成通用窗口函数写法。改造时尤其要注意 XML 转义要写成gt;写成lt;否则 XML 解析阶段就会报错。4.4 切换数据源并验证应用侧把application.properties中的连接配置改为目标库spring.datasource.driver-class-namedm.jdbc.driver.DmDriver spring.datasource.urljdbc:dm://127.0.0.1:5236?schemaTEST spring.datasource.usernameTEST_USER spring.datasource.passwordxxxxxx然后启动应用按下面的顺序验证登录是否成功。基础查询接口是否返回数据。带分页、带排序的列表接口是否正常。事务写入接口是否成功包括插入、更新、删除。定时任务和后台批处理是否正常。报表统计 SQL 是否和源库结果一致。不建议直接切生产库验证先切到影子环境或预发环境把功能用例和对比 SQL 全部跑一遍。5. 常见问题与排查思路以下问题来自国产数据库迁移项目的高频踩坑点按“现象、原因、思路”整理成表。问题现象可能原因解决思路建表报“无效的数据类型”目标库未启用兼容模式或源端 DDL 包含 Oracle 特殊类型配置兼容模式把VARCHAR2换成VARCHARNUMBER换成NUMERIC导入数据后中文乱码源库、导出文件、目标库字符集不一致确认源库字符集导出导入参数显式指定字符集查看目标库会话字符集编译存储过程/触发器报语法错误使用 Oracle 私有语法或包行为差异逐个对象编译记录报错改写为规范 SQL参考目标库 PL/SQL 兼容说明插入主键冲突只迁移了表数据没有迁移序列或自增初始值重建对应序列把next value调整到超过当前最大主键大表迁移慢且内存占用高一次性读取全表或逐条提交按主键/时间分批读取使用批量提交先删约束索引导入后重建应用启动报“驱动类找不到”缺少目标库 JDBC 驱动包将官方驱动 jar 安装到本地依赖或用mvn install:install-file引入SQL 查询时报表名或列名不存在目标库大小写规则与源端不一致统一使用大写表名/列名避免在 SQL 中给标识符加双引号部分分页查询结果不准ROWNUM在子查询中语义有差异改用ROW_NUMBER() OVER (ORDER BY ...)标准写法针对乱码问题最理想的处理是从源头统一字符集。Oracle 侧常见字符集有ZHS16GBK、AL32UTF8达梦创建实例时也会设置字符集。如果条件允许尽量让目标库使用与源库一致的字符集或者在导出时统一转换为 UTF-8并保持应用连接参数也使用 UTF-8。在排查问题上还有一点值得提醒不要对着一张报错日志原地硬猜。遇到任何报错先记录发生的场景、执行的 SQL、目标库版本、兼容模式配置然后在测试环境用最小 SQL 复现。能复现的问题基本都能定位。6. 最佳实践与工程建议6.1 先跑通一个边缘系统再推广到核心系统公司内部如果有很多套系统不建议一上来就迁最核心的账务系统。先选一个边界清晰、数据量不大、业务影响范围有限的系统完整走一遍流程。这样团队可以积累一套自己的经验清单哪些 DDL 会报错、哪些工具需要参数调整、哪些 SQL 容易踩坑、目标库参数怎么设置。等流程跑顺了再迁移核心系统时有据可查。6.2 建立“数据字典映射文档”迁移过程中产生的类型映射、函数改写、SQL 替换规则不要只存在个人笔记里。建议维护一份类似下面结构的映射文档源库对象/写法目标库适配写法涉及文件是否必要VARCHAR2(32)VARCHAR(32)建表脚本按兼容模式决定NUMBER(10,2)NUMERIC(10,2)建表脚本建议SYSDATECURRENT_TIMESTAMPSQL/MyBatis建议NVL(a, b)COALESCE(a, b)SQL/MyBatis建议DECODE(expr, v1, r1, r2)CASE WHEN expr v1 THEN r1 ELSE r2 ENDSQL/MyBatis建议ROWNUM 分页ROW_NUMBER() OVER () 分页MyBatis XML建议这份文档既是迁移进度的跟踪表也是后续其他系统迁移的培训材料。看见风险点提前处理远比在测试阶段被报错推着走要高效。6.3 坚持最小权限和账号隔离迁移数据时不要使用目标库的系统管理员账号执行业务 SQL。DBA 可以先用管理员账号创建业务用户、表空间和基础环境然后把导入和校验工作交给业务专用账号。业务账号只应拥有操作业务 Schema 的权限。数据库账号和密码要纳入配置管理不能硬编码在代码仓库里。6.4 大表操作注意锁和回滚段如果目标库导入期间无法停业务可能出现锁等待或回滚段膨胀。建议分批提交避免一个超长事务运行几个小时。导入大表前可以和外键约束、索引的创建顺序结合考虑先导数据再建索引和外键能显著减少每行插入时的额外校验成本。分批提交也要考虑异常中断后的恢复点最好记录每个分片的完成状态这样失败后不需要从头开始。6.5 校验脚本应纳入自动化只用眼睛看几条数据是不够的。建议把行数比对和关键字段抽样比对写成脚本至少能在迁移完成后自动跑一遍并输出差异报告。如果团队有 Jenkins 或定时任务平台可以把校验脚本做成一个可重复执行的任务后续在并行运行阶段持续监控两边数据差异。6.6 切换前准备回退方案生产切换前必须和业务方确认回退条件和联系人。目标库数据导入完成后仍然保留源库对外只读或备份状态直到核心业务在目标库运行稳定。万一发生不可恢复的数据问题优先选择回退到源库而不是在目标库继续修补。回退不是“士气低落”的表现而是一个正式工程应有的安全阀。7. 总结与后续实践回到最初那句话国产数据库迁移并非想象中难但它确实不是“复制粘贴”就能完成的轻松任务。把整个工作拆成评估、迁移、校验三步并且每一步都有清晰的交付物项目的可掌控程度就会高很多。本文分享的关键经验可以浓缩为以下几条适合在项目启动前贴在团队共享文档里迁移方案要包含结构、数据、应用三层不能只盯着 INSERT。优先使用目标库官方迁移工具DBeaver 和自写脚本定位是辅助与补漏。先建结构、再导数据、后建约束索引可以降低大量报错。字符集、大小写、序列、ROWNUM、NVL、DECODE是异构迁移的高频风险点。数据一致性必须用行数、抽样字段和业务功能三层校验。任何生产变更前要备份源库准备好回退方案并在测试环境反复演练。如果你们团队接下来刚好有国产数据库选型或数据库迁移需求建议先拿一个非核心系统跑一次完整流程把遇到的每一个报错都记录到兼容性清单里。第一批问题解决完以后你会发现后续系统的迁移速度会快很多因为大部分坑已经在测试环境中提前探明真正困难的部分往往不是数据库本身而是业务代码里那些年久失修的“方言”写法。希望这篇文章能成为你们迁移工作的一份实用参考。