国产数据库迁移,尤其是 Oracle 向达梦数据库迁移,很多人第一反应是“工作量巨大、语法不兼容、风险不可控”。实际跑过一轮常规业务库的迁移之后,结论并没有那么复杂:只要把过程强行切分成“结构迁移、数据迁移、应用适配”三个独立步骤,每一步设置明确验收标准,绝大多数 Oracle 老系统都能平滑落到达梦上运行。
这次以 Oracle 11g 迁往达梦数据库的典型场景为例,给出一步到位的三步迁移流程。这套流程不绑定单一工具,换到人大金仓、openGauss、GaussDB 时同样适用,只要把迁移工具、JDBC 驱动和 SQL 方言对应替换。如果担心线上割接窗口太长,文末还会给一个“全量迁移 + 最后增量补齐”的思路,把停机时间压缩到最低。
博文覆盖四块内容:迁移前要看什么数据、哪些对象最容易踩兼容性坑、三步迁移法每一步怎么操作和验收、以及常见报错的排查表和回退预案。适合正在做信创适配、国产数据库选型或者老系统改造的 DBA、运维和 Java 后端开发直接参考。
1. 核心能力速览
| 能力项 | 说明 |
|---|---|
| 技术主题 | Oracle/MySQL/SQL Server 等向国产数据库迁移 |
| 典型目标库 | 达梦 DM、人大金仓 KingbaseES、openGauss、GaussDB |
| 推荐迁移方式 | 官方迁移工具(如 DM DTS、KDTS)+ 必要脚本修正 |
| 迁移核心步骤 | 结构迁移、数据迁移、应用适配与割接 |
| 是否支持批量 | 支持,按表分批、按任务队列执行 |
| 是否需要停机 | 常规冷迁移需要停机窗口,可配合增量同步缩短窗口 |
| 典型难点 | 存储过程、触发器、序列、分页 SQL、日期函数兼容 |
| 推荐验证方式 | DBeaver/客户端连接后行数比对 + 应用回归测试 |
| 适合读者 | DBA、后端开发、运维、信创项目交付人员 |
| 不适合场景 | 无源库访问权限、无备份回退方案的生产系统直连迁移 |
从实际交付角度看,迁移不等于“把表和数据倒过去”。真正大量消耗时间的是三类工作:数据对象不完整导致的补迁移、SQL 方言不兼容导致的应用报错、以及割接后数据不一致后的返工。三步迁移法从一开始就把这些问题拆到不同阶段处理,每完成一步就可以自测,避免问题全部堆到最后集中爆发。
2. 迁移前评估:先认清源库再动手
这里讲一个稍微反直觉的结论:迁移项目的前 30% 时间,应该花在“盘点”而不是“迁移”上。源库情况摸不透,后面所有步骤都会返工。
2.1 盘点源库对象与规模
一个 Oracle 业务库通常包含几十张业务表,外加上百个视图、存储过程、函数、序列、触发器、同义词、物化视图、DBMS_JOB 定时任务。如果只迁移表和索引,应用一启动就会因为找不到存储过程或序列而报错。
盘点对象数量可以先用下面这类查询评估总量:
SELECT object_type, COUNT(*) FROM dba_objects WHERE owner = 'APP_USER' GROUP BY object_type ORDER BY COUNT(*) DESC;另外还要统计每张核心表的行数分布和占用空间。全库数据量不大时,可以全量导出,数据量较大时则需要按大表、中表、小表分开设计迁移批次。大表建议按主键范围分批搬,而不是一次性 SELECT 全表插入目标库,否则内存和回滚段压力都会很高。
在盘点阶段还包括收集数据库版本、字符集、归档模式、权限账号,并确认源库是否允许在迁移期间保持只读。这些信息直接决定后面使用冷迁移还是增量迁移方案。
2.2 兼容性预判
国产数据库通常提供一套兼容 Oracle 的模式,例如达梦的“Oracle 兼容模式”,人大金仓也有对应的 Oracle 模式。开启兼容模式能降低 70% 左右的 SQL 改写工作量,但并不能做到百分百一致。
以下是几个需要提前排查的高频兼容性风险点:
| 风险对象 | Oracle 原写法 | 迁移时的常见处理 |
|---|---|---|
| 分页查询 | ROWNUM | 改写为 LIMIT/OFFSET 或目标库方言 |
| 字符串拼接 | ` | |
| 空值排序 | NULLS FIRST/LAST | 部分国产库默认行为不一致,需测试 |
| 日期函数 | TO_DATE, SYSDATE | 注意兼容模式下日期格式默认值 |
| 序列 | SEQ.NEXTVAL | 目标库建同名序列,应用侧调整获取方式 |
| 存储过程 | 包、重载、集合类型 | 建议单测,包内复杂逻辑可能需要人工改写 |
| 误用 Oracle 特有语法 | CONNECT BY 递归 | 部分国产库支持,版本不同差异大 |
建议在正式迁移之前,先抽取 3 到 5 个逻辑最复杂的存储过程和最典型的分页查询,在目标库的兼容模式下跑一遍,形成一份“兼容性差异清单”。这份清单会直接影响后面的改造工时预估。
3. 环境准备:源库、目标库与迁移工具
迁移不是从执行导出脚本那一刻才开始,而是从环境准备就开始了。这里建议先把源库连接、目标库实例、迁移工具三件事准备干净,再进入正式步骤。
3.1 目标库实例规划
以达梦数据库为例,规划阶段至少确定以下信息:
- 实例名与端口(达梦默认示例端口常为 5236)
- 字符集与页大小,需要与源库匹配或保证兼容
- 表空间大小与数据文件路径
- 是否开启 Oracle 兼容模式
- 迁移账号权限,建议单独建一个迁移专用用户
如果是在自己的物理机或测试服务器上验证,可以下载达梦开发版或试用版后依照官方文档完成安装。正式环境建议由 DBA 和厂商技术支持共同确认版本与授权。
3.2 安装迁移工具
常见迁移工具有这么几类:
- 官方图形化迁移工具:达梦 DTS、人大金仓 KDTS
- 通用 ETL 工具:Kettle、DataX
- 自研脚本:Python + 数据库驱动,适合规则特殊的增量同步或字段清洗
达梦 DTS 通常是安装达梦数据库后自带的图形化工具,也可以通过命令行方式调用。启动后新建迁移工程,选择源库类型为 Oracle,再选择目标库类型为 DM,分别填入连接串、账号、密码即可。不同版本菜单名称会略有差别,但整体流程一致。
3.3 可验证连接的客户端
准备一个 DBeaver 或官方客户端,用来在迁移后连接源库和目标库做比对。DBeaver 支持 Oracle、达梦、人大金仓等不同数据源类型,适合在一个界面里同时打开源库和目标库,快速核对对象列表、行数和索引信息。
4. 三步迁移法总体设计
这里说清楚“三步”的边界。三步之间的依赖关系是:
- 第一步“结构迁移”,解决表结构、索引、约束、序列、视图、存储过程等对象是否完整的问题。
- 第二步“数据迁移”,解决存量数据是否准确搬过去的问题。
- 第三步“应用适配与割接”,解决新系统连上目标库后业务是否跑得通的问题。
每步都需要对应验收,不能跳步。实际项目中最常见的失败原因是结构还没校验完就直接跑数据,结果数据搬完才发现目标库缺少外键或索引。对象结构没稳定之前,先不要启动大数据量迁移。
5. 步骤一:结构迁移与 DDL 改写
结构迁移是整个迁移的基础。表面上只是“在目标库执行建表语句”,但由于 Oracle 的 DDL 语法和国产数据库存在差异,直接用 PL/SQL Developer 导出的 DDL 到目标库执行,大概率会报错。
5.1 使用迁移工具生成目标端 DDL
以达梦 DTS 为例,新建迁移任务后,选择需要迁移的用户对象,迁移工具会读取 Oracle 端的 DDL 定义,再转换为达梦语法,并自动生成目标端的建表、建索引、建序列脚本。
实际使用中需要注意:工具自动转换不等于百分百正确。尤其是下面这几种情况,转换后必须人工 review:
- 字段默认值,例如
SYS_GUID()、SYSTIMESTAMP等函数默认值 - 注释与字段 COMMENT 是否完整保留
- 主键、唯一约束、外键约束是否生成
- 分区表语法是否被正确映射
- 索引类型是否从 Oracle 的位图索引等类型正确转换
5.2 存储过程、包与触发器迁移
存储过程和触发器是迁移中修改量最大的部分。Oracle 的存储过程大量使用PLS_INTEGER、TYPE ... IS TABLE OF、SYS_REFCURSOR、DBMS_OUTPUT、DBMS_SQL等内置能力,国产数据库的兼容度直接决定这些代码能否原样运行。
建议迁移策略如下:
- 先用迁移工具把存储过程包体自动转换到目标库。
- 执行编译,记录编译错误清单。
- 对编译失败的对象逐个人工改写,优先参考目标库的 Oracle 兼容文档。
- 写简单的存储过程调用脚本,验证入参、出参、异常分支是否符合预期。
如果一个存储过程本身就存在复杂业务逻辑,迁移后必须准备对应的单元测试数据,不能只编译成功就算完成。实际项目中很多问题是运行时报错,而不是编译时报错。
5.3 结构迁移验收清单
结构迁移完成的标准不是“没有报错”,而是满足下面这些条件:
- 对象数量比对一致:表、视图、索引、序列、存储过程、触发器的数量与源库相同
- 目标库能成功编译所有存储过程、函数和触发器等对象
- 字段类型与源库语义一致,日期、金额、字符长度没有发生精度损失
- 主键、外键、唯一约束、非空约束都已生效
- 序列的当前值大于源库最大值,避免插入数据时主键冲突
- 字符集查看结果与源库保持一致或满足数据存储要求
比对时可以通过客户端分别查询两个库的ALL_OBJECTS或用户对象列表,也可以直接用工具生成差异报告。
6. 步骤二:数据迁移与多维度校验
结构迁移验收通过后,开始搬数据。建议在正式批量迁移前先抽 1 到 2 张小表做通路验证,确认工具连接和字段映射没问题,再放开全量任务。
6.1 全量数据迁移
全量迁移有多种实现方式:
- 官方 DTS 直接同步
- DataX 配置 JSON 后执行
- 使用自定义 Python/PowerShell 脚本批量读取写入
针对 DTS 方式,通常需要选择要迁移的表,设置批量提交大小,并开启日志观察迁移速度。针对数据量大的大表,建议分批执行,例如按照主键范围或者时间字段切分。
下面是 DataX 风格的一个 JSON 配置模板,实际使用时要替换源库和目标库的 IP、端口、账号与表名:
{ "job": { "content": [ { "reader": { "name": "oraclereader", "parameter": { "username": "source_user", "password": "source_password", "column": ["*"], "connection": [ { "jdbcUrl": ["jdbc:oracle:thin:@//192.168.1.100:1521/orcl"], "table": ["APP_USER.ORDERS"] } ] } }, "writer": { "name": "dmwriter", "parameter": { "username": "target_user", "password": "target_password", "writeMode": "insert", "column": ["*"], "preSql": [], "connection": [ { "jdbcUrl": "jdbc:dm://192.168.1.101:5236/DMSERVER", "table": ["ORDERS"] } ] } } } ], "setting": { "speed": { "channel": 4 } } } }DataX 插件版本不同,reader/writer 名称也会不同,如果没有dmwriter插件,可以直接使用rdbmswriter、postgresqlwriter或者目标库官方提供的插件,具体以实际安装的数据源插件为准。批量任务建议按表目录组织,一个表一个任务文件,方便失败后单独重跑。
6.2 数据校验
数据迁移后的校验不能只查表数量。建议至少分成三层校验:
- 行数校验:所有迁移表的总行数与源库一致,大表可按条件分段比对。
- 关键业务表抽样校验:抽样对比主键值、金额字段、时间字段。
- 聚合结果校验:对订单金额、记录总数等做 SUM/COUNT/MAX/MIN 对比,确认没有精度和取整问题。
可以使用下面的通用 SQL 思路,分别在源库和目标库执行并对比结果:
SELECT COUNT(*) AS total_rows, COUNT(DISTINCT id) AS distinct_id, SUM(amount) AS total_amount FROM ORDERS;如果发现行数相等但某个唯一主键对不上,很可能存在字符集或精度问题。此时建议把两张表按主键全量拉出来做差集比对,定位具体不一致的行。
6.3 冷迁移窗口与增量补齐
Oracle 11g 这类老系统如果不要求实时同步,通常会选择停机冷迁移。但为了缩短停机时间,可以采取“先全量、后增量”的思路:
- 提前一天执行全量迁移。
- 业务继续运行期间,记录源库中发生变化的数据。
- 在正式割接窗口内,只同步停机开始后产生的增量数据。
- 应用切换前再次比对行数和关键汇总。
增量数据可以依靠源库时间戳字段、主键自增字段或归档日志解析等方式获取。如果源库没有统一的时间戳字段,最简单可靠的做法是放宽割接窗口,在停机期间直接再执行一次“只导入增量分区”或全表覆盖导入。
7. 步骤三:应用适配、割接切换与回归验证
数据校验完成不代表应用能直接跑起来。应用层需要替换驱动、调整连接配置,并处理业务 SQL 中不兼容的写法。
7.1 替换 JDBC 驱动与连接串
以 Spring Boot 项目为例,原本连接 Oracle 的配置文件可能是这样:
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver spring.datasource.url=jdbc:oracle:thin:@//192.168.1.100:1521/orcl spring.datasource.username=app_user spring.datasource.password=app_password切换到达梦后,需要引入达梦的 JDBC 驱动依赖,并修改配置:
spring.datasource.driver-class-name=dm.jdbc.driver.DmDriver spring.datasource.url=jdbc:dm://192.168.1.101:5236/DMSERVER spring.datasource.username=app_user spring.datasource.password=app_password具体驱动类名和 URL 格式请以目标库版本对应的官方文档为准。注意如果你的应用原本使用数据库连接池,还需要检查连接池的validationQuery和 Oracle 专用参数是否需要调整。多数连接池对达梦会自动探测,但如果配置了SELECT 1 FROM DUAL,在达梦兼容模式下一般也可以执行。
7.2 应用内 SQL 兼容性排查
Java 项目中使用 SQL 的方式主要有 XML Mapper、注解 SQL、存储过程调用和 MyBatis-Plus 自动生成的 SQL。改造时重点排查三个方面:
- 分页 SQL。Oracle 的
ROWNUM分页写法需要修改,MyBatis-Plus 或 PageHelper 这类框架会根据方言自动改写,但手工 SQL 需要检查。 - 序列获取。如果代码中使用
SELECT SEQ_XXX.NEXTVAL FROM DUAL,需要注意目标库是否使用相同写法,或需要替换为底层序列函数。 - 函数用法。Oracle 特有函数在目标库中可能需要替换,例如
NVL、DECODE、LISTAGG、TO_CHAR的格式占位符。
以若依这类基于 Spring Boot + MyBatis 的常用后台框架为例,适配国产数据库时工作量通常集中在数据源配置、初始化 SQL 文件和个别分页查询。正式切换前要跑一遍完整的“登录、增删改查、导入导出、定时任务”回归用例。
7.3 割接检查单
割接不是只切换连接串,建议按照下面的顺序执行:
- 通知所有业务方进入维护窗口。
- 停止应用写入源库。
- 最后一次同步增量数据。
- 对比核心表行数和关键汇总。
- 修改应用连接串指向目标库。
- 启动应用,观察日志中是否出现数据库报错。
- 执行冒烟测试:登录、列表、详情、新增、编辑、删除、审批流转等。
- 观察数据库连接数、慢 SQL 和后台定时任务是否正常。
割接后一般要保留 7 到 30 天的回退窗口。如果发现严重问题,可以将连接串切回 Oracle 原库,同时整理增量日志和业务补偿数据。
8. 批量任务、自动化与持续集成建议
数据库迁移适合用脚本和队列把任务组织起来,尽量避免一个人人工盯几十张表的迁移进度。比较实用的做法是:
- 按业务域拆分迁移批次,例如订单域、用户域、财务域。
- 每个批次启动一个独立任务日志文件。
- 使用自动化脚本轮询任务状态,失败自动记录并重试。
- 迁移完成后统一生成汇总报表。
可以写一个简单的巡检脚本,定时查询各表迁移状态并输出为 Markdown 或 CSV 报表。脚本模板如下:
import datetime import psycopg2 # 如果目标是人大金仓/openGauss,可用对应驱动连接 conn = psycopg2.connect( host="192.168.1.101", port=54321, dbname="migrate_db", user="migrate_user", password="migrate_password" ) table_list = ["ORDERS", "USERS", "PAYMENT"] for table in table_list: cur = conn.cursor() cur.execute(f"SELECT COUNT(*) FROM {table}") cnt = cur.fetchone()[0] print(f"[{datetime.datetime.now()}] {table}: {cnt}") cur.close() conn.close()这个脚本只是巡检的思路,实际部署在服务器上时建议使用目标数据库的官方驱动包,并用配置文件保存连接信息,不要硬编码密码。
9. 资源占用与性能观察
迁移过程中对源库和目标库都会产生负载,尤其是大表批量写入时,目标库的磁盘 I/O、内存和日志量会成为瓶颈来源。
建议在迁移过程中通过数据库自带性能视图或监控平台重点观察下面几个指标:
- 目标库写入事务的提交频率。
- 源库查询会话是否占用了过多的 CPU 和 PGA 内存。
- 目标库的表空间增长是否异常。
- 大表迁移时是否存在磁盘队列过长问题。
如果发现目标库写入慢,可以尝试调大批量提交的 batch size、降低并发通道数、或者把目标库的归档日志临时放到独立磁盘。迁移工具默认参数并不一定适应用户的生产环境,需要通过小规模试跑找到最优并发数。
10. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 目标库建表失败 | DDL 使用了 Oracle 特有类型或语法 | 查看具体报错信息和字段定义 | 人工改写 DDL,替换为兼容数据类型 |
| 存储过程编译失败 | 包、集合类型或者内置函数不兼容 | 在目标库中重编译并抓取错误日志 | 逐个改写,排查源库中不常见的函数 |
| 数据迁移到一半报错 | 字段超长、字符集不匹配或主键冲突 | 查看任务日志定位错误行 | 修正数据源表数据或调整映射规则后重跑该批次 |
| 行数对不上 | 有字段为空导致工具跳过,或源表迁移期间有写入 | 对比两张表的分组统计结果 | 重新执行增量补齐,再跑一次差异 SQL |
| 应用启动报驱动找不到 | 驱动包没有引入或连接串写错 | 检查 pom.xml/Maven 依赖和 application 配置 | 按目标库官方文档导入驱动并检查驱动类名 |
| 新增记录时报主键冲突 | 序列没有迁移,或序列当前值比源表最大值低 | 查询序列 CURRVAL 和表 MAX(ID) | 重建序列并将起始值设置到源表最大值之后 |
| 分页查询结果错乱 | 方言包没配置或手工 SQL 仍用 Oracle 写法 | 查看执行 SQL 与分页日志 | 改用框架方言适配器或重写分页 SQL |
| 时区与日期显示不一致 | 驱动时区参数未设置 | 查询连接串中的时区配置 | 在 JDBC URL 中增加时区参数并测试 |
| 定时任务不执行 | 存储过程或 JOB 没有迁移完整 | 对比启动任务监控 | 使用目标库配置的定时器重新建任务,或改造为应用定时框架 |
| 割接后性能不如源库 | 统计信息未更新、索引失效、执行计划未收集 | 查看慢 SQL 和索引使用情况 | 收集统计信息,按业务查询重建索引或调整优化器参数 |
常见报错可以先按“日志定位 -> 最小复现 -> 单条 SQL 测试 -> 批量执行”的方式去处理,不要直接在生产目标库反复试探。遇到兼容性差异,优先查官方兼容性文档,而不是盲目套用其他数据库的写法。
11. 最佳实践与合规提醒
数据库迁移不能只关注技术动作,还需要在流程和数据安全上做足控制,尤其是在生产系统或涉及敏感数据的系统中。
建议先统一遵循下面几条基本规范:
- 迁移前必须在测试环境完整跑通至少一轮,不要直接在割接窗口做首次迁移。
- 生产源库导出数据时,要确认导出账号的权限边界,确认数据导出操作符合相关数据安全要求。
- 敏感字段在生产环境迁移、开发环境验证时要做好脱敏处理。如果迁移后需要在测试环境使用,建议用脱敏数据代替真实生产数据。
- 源库全量导出前要做一次备份,保留可回退的记录。
- 迁移工具和自研脚本中的密码不要硬编码,尽量使用环境变量或密钥管理。
- 割接前要和业务方确认可维护窗口和回退时间点,避免系统中断产生额外风险。
- 涉及财务、合同、用户隐私类数据的迁移,建议增加独立审计与抽样复核。
从工程化角度,每次迁移任务都应该有可重复执行的脚本和可恢复的记录。一个比较保险的最小实践是准备三个目录:backup存放源库备份,scripts存放迁移和校验脚本,logs存放每次执行的任务日志。这样无论哪一步出问题,都能快速定位。
12. 总结
国产数据库迁移并没有想象中那么难,关键在于不要把“搬迁”和“切换”混为一谈。用三步迁移法可以清晰切割职责:结构迁移保证对象完整,数据迁移保证数据准确,应用适配与割接保证业务可运行。每完成一步就做一次独立验收,即使中途发现问题,成本也远低于全部推倒重来。
建议第一次尝试迁移时,先选一个小而完整的测试库,里面至少包含几张表、一个序列、一个存储过程和一个视图。花一天时间把三步流程完整跑通后,再去处理生产级的大量大表,踩坑成本会低很多。
这套方法可以用在达梦、人大金仓、openGauss、GaussDB 等多种国产数据库上。后面还可以继续扩展的方向包括:接入实时增量同步工具、设计自动化回归测试用例、引入慢 SQL 对比平台、以及把迁移流程沉淀成内部 DevOps 流水线,让下一次数据库改造做到更快的交付和更可控的质量。