Oracle 迁移金仓,50 个存储过程到底要改多少?聊聊实际改造中的几个问题

Oracle 迁移金仓,50 个存储过程到底要改多少?聊聊实际改造中的几个问题

去年参与过一个 Oracle 到金仓的迁移评估项目。

客户是政务行业,系统已经运行了很多年,数据库里除了业务表,还有不少存储过程、函数和定时任务。

项目刚开始的时候,大家对数据迁移倒不是特别担心。

真正没底的是存储过程。

Oracle 的 PL/SQL 用了这么多年,很多业务逻辑直接写在数据库里。如果目标数据库不兼容,后面就得安排开发人员逐个改造,再重新做业务测试。

客户一开始也按比较保守的方式估算了工作量,预留了不少开发和测试时间。

后来我们用 KDMS 对数据库对象做了一轮评估。

其中 50 个存储过程的初步评估结果是:

| 评估结果

|

数量

| | --- | --- | |

无需修改即可编译

|

35 个

| |

需要少量调整

|

10 个

| |

涉及较复杂的兼容性问题

|

5 个

|

看到这个结果,大家才发现,实际需要处理的存储过程比原先想象的少。

不过,评估报告出来以后,事情还没有结束。

35 个存储过程能直接编译,并不意味着这 35 个已经完成迁移。

后面还得检查执行结果、事务行为、异常处理,以及应用程序调用是否正常。

这也是我后来做数据库迁移时比较在意的一点:评估报告可以帮我们估算改造量,但不能代替实际验证。

一、为什么存储过程比普通 SQL 更难迁?

普通业务 SQL 相对好处理。

例如:

SQL

ini 复制代码
SELECT *
FROM orders
WHERE order_id = 10001;

这种查询只要表结构、数据类型和 SQL 语法兼容,通常不会有太大的改造工作量。

但存储过程不一样。

一个运行了很多年的 Oracle 系统,可能会把大量业务逻辑放在 PL/SQL 里。

例如:

SQL

sql 复制代码
CREATE OR REPLACE PROCEDURE update_order_status (
    p_order_id IN NUMBER
)
IS
BEGIN
    UPDATE orders
    SET status = 'COMPLETED'
    WHERE order_id = p_order_id;

    COMMIT;
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;
        RAISE;
END;
/

这还算简单的。

复杂一点的存储过程,里面可能同时包含游标、动态 SQL、异常处理、临时表、事务控制,还会调用其他存储过程或 Oracle 系统包。

迁移的时候,不能只看这些语句能不能编译。

比如原来的过程里有一个异常处理逻辑:

SQL

less 复制代码
EXCEPTION
    WHEN NO_DATA_FOUND THEN
        ...

目标数据库能识别这个异常,并不代表所有查询场景下都会以相同方式触发它。

又比如事务。

原来的过程内部执行了 COMMIT,但应用程序外层可能还存在事务管理。

数据库换了以后,如果事务边界发生变化,问题就不只是存储过程本身了,还可能影响 Java 业务代码。

所以我现在看存储过程迁移,通常会分成两个问题:

第一,原来的代码能不能继续执行?第二,执行以后的业务行为有没有变化?

后一个问题往往更费时间。

二、50 个存储过程,为什么只有 15 个需要调整?

回到前面那个项目。

50 个存储过程里,有 35 个在初步评估阶段没有发现需要修改的语法问题。

这部分过程主要使用常见的 PL/SQL 结构,例如:

SQL

sql 复制代码
BEGIN
    ...
END;

以及游标循环、条件判断、异常处理和普通 DML 操作。

例如:

SQL

sql 复制代码
CREATE OR REPLACE PROCEDURE update_order_amount
IS
BEGIN
    FOR rec IN (
        SELECT order_id, amount
        FROM orders
        WHERE status = 'PENDING'
    ) LOOP

        UPDATE orders
        SET amount = rec.amount * 1.05
        WHERE order_id = rec.order_id;

    END LOOP;
END;
/

这类存储过程的业务逻辑比较简单,也没有调用太多 Oracle 特有的系统功能。

在金仓相应的 Oracle 兼容环境下,迁移工作通常集中在编译检查和业务验证上。

但剩下的 15 个就不太一样了。

其中一部分涉及 SQL 写法差异,另外一些调用了 Oracle 特有的系统包,或者依赖原有数据库的调度、文件访问等功能。

这两类问题,处理方式完全不同。

三、语法不兼容,通常不是最麻烦的

先说比较容易处理的部分。

比如 Oracle 里的 ROWNUM

原系统可能有这样的查询:

SQL

sql 复制代码
SELECT *
FROM orders
WHERE ROWNUM <= 10;

如果目标环境不支持相同的写法,可以根据业务需求改成:

SQL

sql 复制代码
SELECT *
FROM orders
LIMIT 10;

但这里有个容易忽略的问题。

如果原来的 SQL 是:

SQL

sql 复制代码
SELECT *
FROM orders
WHERE ROWNUM <= 10
ORDER BY order_date DESC;

就不能简单改成:

SQL

sql 复制代码
SELECT *
FROM orders
ORDER BY order_date DESC
LIMIT 10;

因为两条 SQL 的含义不同。

第一条是在筛选出最多 10 条记录后,对这部分结果排序。

第二条是先排序,再取前 10 条。

如果原来的业务真正想查的是最新 10 条订单,那么第二种写法可能更符合需求。

但这属于业务逻辑确认,而不是简单的语法替换。

数据库迁移时,不能因为新 SQL 看起来更合理,就默认可以改变原来的查询结果。

类似的问题还有 DECODENVL、序列调用、外连接等。

这些功能在金仓不同版本和兼容模式下的支持情况可能不同。评估工具如果标记出差异,就需要结合实际 SQL 逐条确认。

不过,相比复杂业务逻辑,这类问题一般比较容易定位。

真正花时间的,往往是那些依赖 Oracle 特有功能的存储过程。

四、碰到 DBMS_JOB、UTL_FILE,先别急着重写

前面提到的 5 个复杂存储过程,主要涉及 Oracle 特有的系统包。

例如:

SQL

复制代码
DBMS_JOB

用于数据库内部的定时任务。

还有:

SQL

复制代码
UTL_FILE

用于文件读写。

这类功能迁移时,需要考虑的不只是函数名能不能对上。

比如一个定时任务,原来每天凌晨执行:

SQL

ini 复制代码
BEGIN
    DBMS_JOB.SUBMIT(
        job       => v_job,
        what      => 'sync_order_data;',
        next_date => SYSDATE,
        interval  => 'SYSDATE + 1'
    );
END;
/

如果迁移到金仓以后,原有调度接口不能按相同方式工作,就得考虑其他实现。

可以先检查目标版本是否提供兼容接口或对应的任务调度能力。

如果没有满足需求的数据库内部调度方案,也可以考虑将任务交给应用层。

例如:

markdown 复制代码
Spring Boot
    ↓
定时任务
    ↓
调用存储过程
    ↓
执行数据同步

但这种调整不能只改一个调用入口。

还需要确认任务失败以后怎么重试、多个应用实例会不会重复执行、执行记录在哪里保存,以及任务是否允许并发。

如果原来的数据库任务已经运行了很多年,这些行为可能早就被业务系统默认依赖了。

因此,我一般不会看到 DBMS_JOB 就直接判断必须重构。

先确认目标版本的兼容能力,再决定是保留原实现、调整调用方式,还是把调度逻辑迁到应用层。

这样更容易控制改造范围。

五、自治事务是我会单独检查的一项

Oracle 里还有一个比较特殊的功能:

SQL

ini 复制代码
PRAGMA AUTONOMOUS_TRANSACTION;

典型场景是记录日志。

比如业务事务执行失败,需要回滚,但希望错误日志仍然保留下来。

原来的代码可能类似:

SQL

sql 复制代码
CREATE OR REPLACE PROCEDURE write_error_log (
    p_message IN VARCHAR2
)
IS
    PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
    INSERT INTO error_log(message, create_time)
    VALUES (p_message, SYSDATE);

    COMMIT;
END;
/

迁移时,需要检查金仓目标版本及兼容模式对自治事务的支持情况。

即使相关语法可以正常编译,我也会安排单独测试。

例如:

markdown 复制代码
开启业务事务
    ↓
执行数据修改
    ↓
调用日志存储过程
    ↓
主动触发业务异常
    ↓
回滚外层事务
    ↓
检查日志是否保留

如果日志跟着外层事务一起回滚了,就说明迁移后的行为没有满足原来的业务要求。

这种问题只靠编译检查是发现不了的。

所以涉及事务的存储过程,我一般都会额外关注:

  • 提交和回滚的边界

  • 异常发生后的数据状态

  • 锁等待和事务隔离行为

  • 与 Java 事务管理的配合

尤其是核心业务系统,事务行为一定要在正式切换前验证清楚。

六、KDMS 的价值,不只是告诉你兼容率有多高

以前评估数据库迁移工作量,最麻烦的是不知道从哪里开始。

假设数据库里有几百个存储过程。

让开发人员一个个打开看,先不说能不能看懂业务,光把所有对象梳理一遍就需要不少时间。

所以这次项目我们先用 KDMS 做评估。

我比较关注的不是报告首页的兼容率,而是具体对象的问题清单。

例如:

| 对象

|

初步评估结果

|

后续处理

| | --- | --- | --- | |

普通订单处理过程

|

无明显语法问题

|

编译及业务验证

| |

报表统计过程

|

存在 SQL 差异

|

修改并核对统计结果

| |

定时同步过程

|

涉及特定系统包

|

验证兼容接口及调度行为

| |

文件导出过程

|

涉及文件操作

|

检查权限、路径及接口

|

有了这个清单,后面的工作就容易安排了。

普通 SQL 差异可以集中处理。

涉及系统包和复杂事务的过程,单独安排开发和测试。

如果某个过程被多个业务模块调用,还需要提前确认它的影响范围。

这比简单地按照"一个存储过程需要半天"去估算工作量靠谱得多。

不过,评估报告仍然只是起点。

有些问题需要运行以后才会暴露,尤其是动态 SQL、外部依赖和特定业务数据触发的异常分支。

七、编译通过以后,我会再做一轮业务验证

这是我觉得迁移项目里最不能省的步骤。

一个存储过程能正常编译,至少说明它在当前环境下没有明显的编译错误。

但上线需要的不是"能编译"。

而是原来的业务还能正常工作。

例如一个订单结算过程:

markdown 复制代码
读取订单
    ↓
计算金额
    ↓
更新订单状态
    ↓
写入结算记录
    ↓
更新账户余额

迁移以后,我会重点检查正常执行、重复执行、异常回滚和并发执行等情况。

如果原来的过程还涉及金额计算、日期转换、字符串处理,就需要核对迁移前后的结果。

比较稳妥的方式是准备一组固定测试数据,在 Oracle 和金仓环境中分别执行,然后对比关键业务表的最终状态。

对于核心过程,还需要结合实际数据量测试执行时间。

毕竟同一段 SQL,换了数据库以后,执行计划可能发生变化。

原来 Oracle 上使用的索引和统计信息,也不代表在金仓上一定能得到相同的执行效果。

迁移成功的标准不是存储过程全部变绿,而是业务结果、事务行为和性能都满足上线要求。

八、存储过程的改造量,到底应该怎么算?

回到文章开头的那组数据。

50 个存储过程里,35 个无需修改即可编译,10 个需要少量调整,5 个存在较复杂的兼容性问题。

如果只看数量,会觉得:

70% 不用改,20% 小改,10% 大改。

但如果要估算实际工期,我不会直接按照这个比例分配人力。

因为存储过程之间的复杂度差异太大了。

一个几十行的普通查询过程,可能半小时就能完成改造和测试。

但一个上千行、调用多个系统包、涉及跨表事务的核心结算过程,可能需要几天甚至更长时间。

所以我更倾向于把工作量拆成三个部分:

01

兼容性改造

需要修改多少 SQL、存储过程、函数和外部依赖。

02

业务回归测试

需要覆盖多少业务分支、异常场景和事务逻辑。

03

性能与上线验证

需要优化多少核心 SQL,是否涉及应用代码修改、数据校验和正式割接。

只有把这三部分都考虑进去,估算出来的工期才比较接近实际情况。

尤其对于老系统,很多存储过程虽然代码不复杂,但调用关系不清楚,反而需要花更多时间做回归测试。

最后说几句

这次 Oracle 迁移金仓的评估项目,让我印象比较深的不是 70% 这个数字。

而是评估前后,大家对工作量的认识发生了变化。

最开始觉得存储过程很多,可能要改很久。

后来逐个梳理才发现,大部分过程使用的都是比较常见的 PL/SQL 写法,真正需要重点处理的对象并没有想象中那么多。

但反过来,也不能因为大部分过程可以直接编译,就认为迁移工作已经完成了。

有些问题恰恰藏在那些能正常执行的代码里。

例如事务边界、异常处理、排序结果和隐式类型转换。

这些东西不一定会报错,但可能影响实际业务。

所以如果现在再接一个 Oracle 到金仓的项目,我还是会先做兼容性评估,把存储过程、函数、系统包依赖和调用关系梳理出来。

然后根据实际问题安排改造和测试。

先搞清楚哪些代码需要改,再确认哪些业务行为不能变。

这比一开始就讨论"金仓对 Oracle 的兼容率到底有多高",更能帮助开发团队把迁移项目真正落地。

相关推荐
大白801 小时前
iOS 内存深度解析:ARC 到底是怎么管理对象生命周期的
后端
焦玉全1 小时前
Redis Lua 脚本实战:从入门到精通(Java 版)
后端
知识的搬运工旺仔1 小时前
唯一索引与 NULL 值:PostgreSQL 主键约束与 NULLS NOT DISTINCT
数据库·后端·sql·postgresql
大勇前进2 小时前
从 OC 到 Swift,老 iOS 开发者踩过的 10 个语法大坑
后端
我的xiaodoujiao2 小时前
Django 基础知识详细图文教程 9-Django 模板引擎 2
开发语言·数据库·后端·django
烈风逍遥2 小时前
第五篇:通用 LLM 流式对话:前后端联接的完整实现
前端·后端·架构
Ticnix2 小时前
别再手动上线了:一条命令带备份、健康检查和自动回滚
后端·python·ci/cd
ArkPppp3 小时前
如何从零上线一个耐造的Redis缓存系统——最直接最不绕弯子的方式
后端·ai编程
Kyrie_kk3 小时前
Java--ProcessBuilder操作系统进程
java·后端