ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

国产数据库迁移全流程:三步搞定结构、数据与应用切换

2026/9/4 5:20:56 拓冰建站 浏览量
国产数据库迁移全流程:三步搞定结构、数据与应用切换 国产数据库迁移这几年已经不是“要不要迁”的问题而是“怎么迁、按什么节奏迁、迁完如何保证业务不倒退”的问题。真正劝退很多团队的不是迁移工具本身而是迁移前不知道从哪里下手迁移中报错不知道去查哪一层上线后跑慢了又分不清是应用问题、SQL 问题还是数据库问题。把对象迁移、全量数据、增量同步、应用改造全部混在一个停机窗口里处理失败率自然很高。国产数据库迁移并不是把数据从 A 库倒到 B 库那么简单。它实际上是一条完整的工程链路先盘点数据字典和对象依赖再按结构、全量、增量三个通道执行迁移最后做应用适配、切换演练和回退控制。只要把这条链路拆成三个清晰步骤每步有明确输出物和验证标准绝大多数迁移项目都可以在可控风险内完成。下面以一套常见的 Oracle 业务库迁移到目标国产数据库为例逐步说明如何操作、如何验证、如何排查。无论你是做数据库运维、应用开发还是信创项目统筹都可以把这套流程当作项目计划的基础版本再按自己的业务规模调整。1. 理解国产数据库迁移为什么难才能避免把技术问题扩大成项目问题迁移项目之所以让人头疼并不是因为数据库本身有多难而是因为“兼容”这个词在不同人口中含义完全不同。开发人员说兼容通常指 SQL 能跑运维人员说兼容通常指备份恢复、监控、高可用能力要能对齐架构师说兼容还要看并发模型、锁机制、事务隔离级别、异常行为是否一致。1.1 兼容性不是零改造而是兼容面有多宽一次完整的数据库迁移至少会涉及四个层面的兼容性层面主要检查内容常见影响对象兼容表、视图、索引、约束、序列、触发器、存储过程、包、物化视图、同义词、DBLINK对象建不起来后续数据导入直接失败语法兼容SELECT、INSERT、UPDATE、MERGE、分页、字符串拼接、日期函数、分析函数SQL 执行报错或返回结果不一致行为兼容空字符串与 NULL、隐式类型转换、事务隔离级别、锁等待与死锁检测、序列缓存应用逻辑时序变化可能出现并发问题工具生态兼容JDBC 驱动、连接池、ORM 方言、备份恢复、监控采集、数据同步应用接入困难运维体系需要重做很多迁移项目失败不是因为表和数据导不过去而是因为团队只处理了第一层和第二层忽略了行为兼容与工具生态兼容。比如 Oracle 中空字符串会被当作 NULL但部分 PostgreSQL 内核的国产库并不会这样处理。同样一句WHERE name 在源库可能查不到任何数据到目标库却能查出大量为空的记录。这类问题非常隐蔽不是靠“导出导入”能发现的。1.2 迁移失败通常发生在边界特性和依赖顺序上看一个迁移工具执行记录的报错会发现大量问题并不在普通表上而在这些位置外键引用的表还没有创建导致建表阶段报“表或视图不存在”。序列没有设置起始值应用插入数据时主键与历史数据冲突。触发器中引用了远程表或 DBLINK目标库没有等价对象。物化视图的刷新任务无法迁移进入生产后数据不再刷新。包里的动态 SQL 使用了源库特有的系统视图比如ALL_TAB_COLUMNS或V$SESSION。定时任务直接依赖源库的调度器迁移后目标库任务管理方式不同。应用使用SELECT sequence_name.NEXTVAL FROM DUAL目标库序列用法不一致。这些问题会分散在迁移的不同阶段出现。如果把所有表和对象一次性搬过去再集中处理报错排查成本会非常高。正确做法是先通过系统视图把对象清单、依赖关系、数据量、增量速度全部摸清再决定用什么样的迁移顺序和并行策略。1.3 一条主线用三个步骤覆盖迁移全流程迁移项目可以收敛成三条主通道步骤核心工作输出物验证标准步骤一盘点与评估对象清单、数据量清单、兼容性评估表、迁移窗口计划知道要迁什么、哪些需要手改、大概要多久步骤二对象与数据迁移目标库目录结构、迁移日志、数据校验报告目标库对象完整全量数据一致增量可追平步骤三应用适配与切换上线改造后的数据源配置、切换记录、回退方案、上线监控报告应用新库运行稳定异常可回退后面的章节会按这三个步骤展开。你不需要一次性把整套体系做完但每完成一个步骤必须让团队确认输出物是否完整否则不要进入下一步。2. 步骤一迁移前盘点数据库家底先知道风险在哪“直接开导遇到问题再说”是迁移项目最大的坑。数据库对象之间是有依赖关系的表、外键、序列、存储过程和定时任务互相引用不盘点清楚就动手往往会在导入中段才发现某个核心表被漏掉或者某个包无法在目标库编译。2.1 用系统视图盘点对象类型和数据量在源库先查清业务 schema 下的对象分布。以 Oracle 为例可以执行类似下面的 SQL-- 确认业务账号下有哪些对象类型以及各类对象数量 SELECT object_type, COUNT(*) FROM dba_objects WHERE owner BUSI_USER GROUP BY object_type ORDER BY COUNT(*) DESC; -- 查看业务账号下各表的数据量估算值 SELECT table_name, num_rows, blocks * 8 / 1024 AS size_mb FROM dba_tables WHERE owner BUSI_USER ORDER BY num_rows DESC;这段 SQL 的价值在于快速建立“体量概念”。如果某个用户下有 300 张表、500 个存储过程、30 个物化视图那么迁移计划就不能只考虑表数据还要为对象转换预留足够时间。注意num_rows来自统计信息不一定是精确值。对于影响核心业务的大表后期还需要用COUNT(*)确认。但盘点阶段用统计信息已经足够发现哪些表需要重点设计策略。还要继续检查那些容易被漏掉的对象-- 查看数据库定时任务 SELECT owner, job_name, enabled, state FROM dba_scheduler_jobs WHERE owner BUSI_USER; -- 查看物化视图和最近刷新时间 SELECT owner, mview_name, refresh_method, last_refresh_date FROM dba_mviews WHERE owner BUSI_USER; -- 查看数据库链接 SELECT owner, db_link, host FROM dba_db_links WHERE owner BUSI_USER;DBLINK 和定时任务往往不被迁移工具处理。如果源库的报表应用通过 DBLINK 查询其他库目标库必须重新建立等价链路否则上线后报表接口会直接失败。定时任务也是一样迁移对象时不会把调度器配置自动搬过去需要业务侧重新确认执行频次和执行逻辑。2.2 盘点数据增量速度和可停写窗口要确定到底需不需要增量同步必须先知道在正常业务时段里源库每天会产生多少新数据。可以在核心业务表上观察时间字段的增长SELECT MAX(create_time) AS max_create_time, COUNT(*) AS recent_count FROM orders WHERE create_time SYSDATE - 1;也可以观察数据库归档日志的增长速度、增量备份的大小或主库的 Redo 生成速率。目的只有一个估算从全量迁移开始到切换上线为止源库会新增多少数据。如果总的业务停写窗口非常短比如只有 2 小时而全量导入需要 6 小时就必须采用增量同步方案。如果业务可以接受一个周末停写则可考虑用“全量迁移 切换前最后一次短停写”的方式完成。这里没有标准答案取决于业务容忍度、数据量和网络带宽。2.3 盘点应用侧 SQL 方言形成改造字典数据库迁移不只是 DBA 的任务应用团队必须参与。应用侧需要整理出典型 SQL 的清单。最简单的方法是在测试环境打开 JDBC 层的 SQL 日志抓取一段时间内的真实 SQL然后按关键字搜索NVL、NVL2、DECODESYSDATE、TO_DATE、TO_CHARROWNUM、ROW_NUMBERLISTAGG、CONNECT BY、START WITHMERGE INTOSELECT ... FROM DUAL把这些关键字出现的次数统计出来就知道应用改造工作量主要集中在哪里。也可以得到一张映射表Oracle 常见写法目标库常见等价写法注意事项NVL(a, b)COALESCE(a, b)或等价函数注意目标库函数是否支持两个以上参数SYSDATECURRENT_TIMESTAMP或NOW()如果目标库兼容 Oracle 模式也可能仍支持ROWNUM 20FETCH FIRST 20 ROWS ONLY或LIMIT 20取决于目标库内核语法DECODE(a, x, y, z)CASE WHEN改写不建议依赖各库私有函数SELECT seq.NEXTVAL FROM DUAL目标库序列调用方式必须按目标库语法处理TO_CHAR(dt, YYYY-MM-DD)日期格式化函数需要检查格式模型是否一致空字符串目标库空字符串语义如果目标库不把空字符串当 NULL需要逐条确认这张表要随盘点持续更新最终会成为应用改造阶段最直接的执行清单。2.4 输出迁移评估表盘点后应该形成一张评估表至少包含下面这些列对象类型数量自动迁移程度需要人工处理的部分表约 200高分区、大字段、字符集普通索引约 400高命名冲突表空间映射约束约 150中外键导入顺序序列约 50高起始值需要重置存储过程/函数/包约 200中动态 SQL、系统视图、包变量触发器约 30低业务触发逻辑是否保留物化视图约 15低刷新策略需要重建定时任务约 10低目标库任务计划需重新配置DBLINK若干低网络链路改造同义词约 80中目标库对象命名调整评估表的核心作用是让迁移工作“被看见”。自动迁移不代表零风险人工处理并不一定特别复杂。关键是提前知道哪些对象会占用大量排错时间。3. 步骤二结构迁移、全量迁移、增量同步三段式推进整个迁移阶段可以分成三条执行通道。先处理结构再处理数据最后处理增量。不要试图在一个任务里把所有事情做完。3.1 先做结构迁移再进入数据通道源端结构迁移可以借助数据库厂商提供的迁移工具比如达梦的 DTS、人大金仓的 KDTS或者其他生态工具。不同版本工具界面和菜单有差异但整体流程通常都包含新建迁移工程、选择源库、选择目标库、选择对象、执行迁移、查看日志。结构迁移建议遵循以下顺序在目标库创建业务用户和表空间。迁移基础表定义。迁移序列、同义词、视图。迁移存储过程、函数、包。迁移索引和约束。最后处理触发器和定时任务。先建表结构可以避免外键引用对象不存在的问题索引和约束后建可以减少导入数据时的额外校验开销触发器后建可以避免迁移工具导入历史数据时触发不必要逻辑定时任务最后建可以避免上线前在目标库提前执行任务。迁移完成后不要直接开始导数据先在目标库执行一次对象数量对比-- 目标库执行 SELECT object_type, COUNT(*) FROM all_objects WHERE owner BUSI_USER GROUP BY object_type ORDER BY COUNT(*) DESC;再回到源库执行同一条 SQL对比两端的对象数量确认没有整类缺失。3.2 全量数据迁移要分表分批不要一把梭数据迁移的常见误区是“所有表建立一个任务一次性跑完”。遇到小表多、字段少的库这种方式也许能跑通一旦遇到千万行以上、还包含大字段的大表任务很容易因为内存不足、网络超时或单个事务过大而失败。更稳妥的做法是按表拆分任务再对大表做批次切分。伪代码思路如下FOR EACH table IN 迁移清单: IF 表数据量 批次阈值: 单任务全量迁移该表 ELSE: 按主键范围或时间范围切分多个连续范围 FOR EACH 范围: 只迁移当前范围的数据 每批提交后记录断点 由下一批任务继续处理实际执行时批次阈值要根据迁移工具所在服务器内存、源库与目标库网络带宽、表行宽来调整。不要照搬其他项目的参数。导入大表时还建议暂时停用目标库相关表上的触发器或者确认迁移工具是否会在导入时触发触发器。否则每导入一行都可能执行业务侧逻辑极大地拖慢导入速度也可能把状态字段改乱。建议迁移工具只负责数据落地触发器在数据追平后再启用。3.3 存储过程、函数和包是改写工作量最大的部分普通表和索引大多能由迁移工具自动转换存储过程、函数、包则是最容易出问题的地方。工具能做词法级转换但很难保证语义完全一致。一个简单函数从 Oracle 迁到兼容 Oracle 模式的国产库可能直接编译通过-- 源端示例 CREATE OR REPLACE FUNCTION fn_order_count(p_customer_id IN NUMBER) RETURN NUMBER AS v_count NUMBER : 0; BEGIN SELECT COUNT(*) INTO v_count FROM orders WHERE customer_id p_customer_id; RETURN v_count; END;但如果包内引用了源库特有的数据字典视图运行了动态 SQL或使用了PRAGMA AUTONOMOUS_TRANSACTION这类事务控制迁移工具转换后很可能出现编译错误或行为偏差。存储过程改造不能只看“能不能编译”还要看“结果是否一致”。推荐的步骤是先由迁移工具自动转换。列出所有编译失败的对象。逐个对象打开源码记录目标库缺少哪些函数、视图或语法。修改后单独编译。使用源库中已有的测试数据构造输入参数分别执行并比对结果。在所有存储过程编译通过之前不要进入上线倒计时。数据库迁移中存储过程问题最容易被低估因为导入阶段表数据可能已经一致了如果没有人逐条调用这些函数问题会推迟到生产环境被真实业务触发。3.4 增量同步要设计追平机制和停写点如果业务只允许短时间停写迁移方案必须包含增量同步。增量同步的实现有多种方式使用数据同步工具、使用日志解析产品或者在应用侧维护变更流水表。具体选型要结合源数据库版本、目标库版本、网络和安全要求不能简单套用。如果线上确实没有成熟的增量同步工具可以采用“小停写窗口”的常见化简方案在业务低谷开始全量迁移。全量迁移完成后记录源库当前业务表最大主键或最大时间字段。停止应用写入或让应用进入只读模式。执行最后一次增量补录也就是把全量迁移期间源库新增的数据再次导入。执行数据一致性校验。确认一致后切换应用数据源。“停写窗口”长短取决于从停止写入到切换完成的时间通常可以控制在几小时以内。这个方案不需要额外的日志解析组件适合源库表结构具备主键或时间字段的项目。无论采用哪种增量方式切换前都必须在源端记录一个明确的业务水位点。比如切换前最后一笔订单的编号、最后一条日志的时间切换后到目标库中查询这些记录必须存在。3.5 数据一致性校验要做三层不能只数行数数据导入完成后最容易犯的错误是只对比两端表数量或者只对每张表执行COUNT(*)。行数相同不代表数据内容一致常见情况是整行漏了又补了两行其他数据或者某条长文本字段被截断。校验至少做三层第一层总数校验。-- 源端 SELECT orders, COUNT(*) FROM orders UNION ALL SELECT order_items, COUNT(*) FROM order_items;目标端执行同样的 SQL然后逐表对比。这个步骤能为每张表提供基础信任。第二层关键汇总校验。拿订单业务来说可以对比订单总金额、订单总条数、客户总数等维度的汇总结果。-- 源端 SELECT COUNT(DISTINCT customer_id) AS customer_cnt, SUM(total_amount) AS total_amount FROM orders;目标端执行同样 SQL对比结果。这样即使两张表行数相同也能发现某些行被替换或金额字段被改写的风险。第三层抽样字段级校验。从每一类表中抽取若干关键字段按主键排序导出成统一格式文件再用工具或脚本做文本差异对比。如果是普通业务表可以把主键、金额、时间、状态字段拼成一行文本在源端和目标端分别生成文件再用diff或md5sum比较。三层校验力度递增但耗时也递增。小项目至少做到第二层涉及金额、合同、订单等核心数据时建议做到第三层。3.6 迁移任务中途失败应该按断点续传而不是盲目重导很多迁移工具在任务失败后会提供断点信息。比如从 MySQL 迁往 KingbaseES 的 KDTS 任务执行到某张表失败时日志中通常可以看到任务名称、表名、失败行号和具体错误。排查顺序可以按下面来看任务状态和错误日志确认是写入超时、网络中断、约束冲突还是字符集问题。如果失败在单表导入阶段确认目标库该表此前是否已经导入了部分数据。如果有断点续传能力调整参数之后从失败点续跑。如果没有断点续传能力先把目标库该表清理干净再重跑这个表的迁移任务避免重复导入造成唯一键冲突。不要一遇到失败就重新创建整个迁移工程并全量重跑那样会浪费大量时间还可能把已经修改好的存储过程覆盖掉。迁移任务失败不可怕可怕的是失败原因没有记录下次跑到同一个位置再次失败。每失败一次都应该把原因补充到兼容性评估表中。4. 步骤三应用适配、切换上线与回退风险控制数据和对象迁移完成并不代表项目已经结束。应用还连接着旧库连接池配置、ORM 方言、SQL 语句都需要同步调整。上线切换也不是改一下配置中心的数据源 URL 就结束而是要有先后顺序、验证点和回退条件。4.1 连接层改造驱动、URL、连接池参数必须一起改应用连接层首先要替换 JDBC 驱动和连接 URL。常见国产数据库在驱动和 URL 上会有自己的约定例如采用类似下面的格式# 原 Oracle 数据源配置示例 jdbc.driveroracle.jdbc.OracleDriver jdbc.urljdbc:oracle:thin:127.0.0.1:1521/orcl jdbc.usernameapp_user jdbc.passwordxxxxx# 切换为目标库后的常见配置示例 jdbc.driverdm.jdbc.driver.DmDriver jdbc.urljdbc:dm://127.0.0.1:5236/APP_DB jdbc.usernameapp_user jdbc.passwordxxxxx注意不同版本的驱动类名和 URL 格式可能不同。在正式切换前一定要以目标库当前版本附带的驱动文档为准不能用旧版本记忆硬套。连接 URL 中是否要带 schema、字符集参数、事务参数都需要在测试环境验证。连接池配置也需要同步调整。原先为 Oracle 设计的maxActive、minIdle、maxWait参数不一定适合新库尤其当目标库对连接数和会话的占用机制与源库不同时可能出现连接获取缓慢或连接数打满的问题。4.2 SQL 方言改造要形成统一对照表应用层最耗时的工作是处理 SQL 方言。以下是一张常见对照思路但实际是否兼容必须拿着真实 SQL 到目标库验证场景原 Oracle 常用写法改造方向分页ROWNUM ?优先使用目标库支持的标准分页或改写为FETCH FIRST ? ROWS ONLY当前时间SYSDATECURRENT_TIMESTAMP或目标库等价函数空值处理NVL(column, 0)COALESCE(column, 0)条件取值DECODE(status, 1, 有效, 无效)CASE WHEN字符串聚合LISTAGG(name, ,)目标库同类函数或应用层聚合序列SELECT seq.NEXTVAL FROM DUAL目标库序列调用方式日期转换TO_DATE(2024-01-01, YYYY-MM-DD)保持标准 SQL避免格式模型依赖模糊追级查询CONNECT BY如果目标库兼容则保留否则需要应用递归查询替换空字符串Oracle 将当作 NULL应用层统一传 NULL或改写条件改造过程中不要只靠人工替换关键字应该把改动纳入代码仓库。每次修改对应一笔提交提交信息写明“为 XX 数据库适配把 ROWNUM 分页改为 FETCH FIRST 分页”。这样可以形成改造历史一旦上线后出现问题能快速定位是哪次修改引起的。4.3 切换前先做演练把正式上线变成一次可重复操作正式切换前至少要完成一次完整的切换演练演练环境和生产环境保持同一套迁移流程和脚本。演练目的不是证明“能切过去”而是为了测出时间节点。一次完整的切换建议按以下顺序操作停止应用的新增写入或将应用切为只读模式。等待增量数据追平并记录追平时间点。在目标库执行最后一次数据校验重点核对核心业务表。对目标库做基线备份保证后续可回退到一致点。修改数据源配置指向目标库。启动关键应用模块执行冒烟测试。观察数据库连接、慢查询、锁等待和错误日志。存量应用全部启动后观察 30 分钟到 1 小时。冒烟测试用例必须提前准备覆盖登录、查询列表、分页、详情、新增订单、修改状态等核心流程。不要上线后再临时想测试点。4.4 回退方案要具体到“什么现象触发回退”回退不只是“把 URL 点回去”这么简单。如果回退时目标库已经接受了部分新增数据旧库中并不存在这些数据回退后这部分业务数据会丢失。所以切换前必须明确回退条件和数据补偿方案。建议在切换方案中写清楚什么现象必须立刻回退例如核心交易接口失败率超过阈值、目标库发生大面积锁死、金额汇总不一致。哪些问题可以灰度修复例如某个非核心报表查询慢、某些字段显示格式不对。回退时目标库是否需要做导出备份以避免新数据丢失。回退窗口保留多久例如 48 小时内一旦发现严重问题可以整体回退超过 48 小时则按新问题处理。切换上线不是终局。要保留旧库一段时间的只读访问能力不要在上线当天立刻删除源库数据。4.5 上线后重点观察的指标上线不是终点而是新数据库运行状态的起点。建议前三天重点观察应用慢查询列表是否出现新 SQL。数据库连接数是否比旧库更早打满。锁等待次数和死锁日志。关键业务表的写入延迟。数据备份任务是否按计划执行。目标库统计信息是否准确是否存在执行计划中途变差。如果上线后某个核心页面明显变慢一定不要只调数据库参数。先从应用抓取这条 SQL到目标库执行EXPLAIN看执行计划和索引使用情况。很多性能问题不是数据库引擎不行而是统计信息缺失、索引没有迁移或者 SQL 写法没有为优化器提供足够信息。5. 迁移和上线过程中最常见的报错与排查路径下面这些报错在国产数据库迁移项目中出现频率很高。看到类似问题时不要先怀疑数据库不稳定按照“数据源、对象、约束、字符集、SQL 语义”的顺序逐层排查。5.1 导入数据时违反唯一约束或主键冲突现象迁移任务执行到某一张表时报唯一约束冲突进度停止。可能原因该表之前已经迁移过部分数据未清理干净。序列起始值不对业务新插入数据与历史数据主键重叠。源表存在重复数据但源库因约束未启用而允许写入。检查方式查看迁移日志中冲突的是哪张表、哪条约束。在源表和目标表上分别查询冲突的主键值是否存在。查看源库表的索引约束状态确认源库是否有重复数据。处理方式如果目标表已有残留数据按迁移断点清理后重跑该表。如果序列起始值低于表中最大 ID重建序列并设置正确起始值。如果源表本身存在重复数据需要业务侧先确认保留规则再做数据清洗不能让重复数据流入目标库。预防方式迁移前对所有含唯一约束的表执行一次重复性检查。5.2 存储过程或函数编译失败现象目标库中存储过程状态为 INVALID或应用调用时报“无效对象”。可能原因包中引用了目标库不存在的系统视图或函数。迁移工具只转换了外层语法没有转换内部动态 SQL。自定义类型、对象类型的依赖顺序不对。检查方式查看数据库错误日志中第一个编译错误的位置。在目标库手动执行编译命令获得明确报错ALTER PROCEDURE app_proc COMPILE;如果报错来自动态 SQL需要把动态 SQL 拼接后的实际内容打印出来单独到目标库执行。处理方式按语法错误逐条改写不要为了快速上线关闭编译校验。把不能确认语义差异的存储过程放到单独变更单中由应用开发团队和 DBA 共同确认。5.3 字符集不一致导致中文乱码或长度报错现象导入后中文显示为乱码或者字段内容比源库短甚至报“字符串长度超出限制”。可能原因源库字符集与目标库字符集不一致。迁移客户端使用错误的字符集提交数据。源库 Varchar2 字段以字节为长度单位目标库部分类型以字符为长度单位导致长度计算逻辑变化。检查方式在源端查看字符集SELECT VALUE FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER NLS_CHARACTERSET;在目标库查看对应字符集参数。从源库和目标库分别查询含中文、全角符号、特殊字符的数据做肉眼或程序对比。处理方式统一将目标库字符集设置为 UTF-8 体系。迁移任务参数中明确指定客户端编码。对迁移后 Varchar2、Char 字段的长度做一次专项评估确认是否需要按字符长度扩容。预防方式正式迁移前先导一张含中文特殊字符的表测试不要用全英文测试数据验证编码问题。5.4