从Oracle到金仓:一次让人又爱又恨的迁移体验录

50 阅读14分钟

@[toc]


兼容 是对前人努力的尊重 是确保业务平稳过渡的基石 然而 这仅仅是故事的起点

说实话,我在刚听到有人说金仓能“无缝替代Oracle”那会儿,跟大部分老DBA一样,心里都在犯嘀咕。你知道最让我头疼的是啥不?不是迁移工具咋用,也不是数据咋搬,而是那些藏在代码深处的坑!就像你表面上看着一条SQL在两个库里都能跑,但结果就是不对,你说气不气人。

兼容性这玩意儿,不同场景、不同业务、不同数据量,表现完全不一样。

说到参数兼容,这里其实有个被大家忽视的盲点。在网上,有很多迁移工具号称“一键迁移”,但其实大部分只是处理了SQL层面的兼容,像这种数据库配置参数、运行时行为的差异,根本没有一丁点儿的办法来实现自动处理。

PL/SQL那些让人抓狂的细节

真正让我头大的是PL/SQL,特別是那些动态SQL:

v_sql := 'UPDATE accounts SET balance = balance + 10 WHERE id = :1';
EXECUTE IMMEDIATE v_sql USING v_id;

这种写法在Oracle里完全没问题,但到了金仓早期版本就出问题了——绑定变量的作用域莫名其妙就没了,你说这事儿找谁说理去?后来还是打补丁才解决好的。

这个问题其实暴露了一个更深层的设计差异。Oracle的PL/SQL引擎对动态SQL的处理,绑定变量的作用域会继承外层块;但金仓早期版本因为实现机制不同,每个EXECUTE IMMEDIATE都被当成独立的执行上下文。

Oracle的NUMBER类型比较特殊,它既可以存整数,也可以存小数,而且精度是动态的;但到了PostgreSQL系数据库(金仓的底层),就分成了NUMERIC、BIGINT、INTEGER这些不同类型。迁移工具在做类型映射的时候,很容易就丢失了精度信息。后来我们的解决办法是,对那些涉及金额的字段,全部强制用NUMERIC(20,2)这种显式精度的类型,虽然麻烦,但至少不会出错了。

性能优化这事儿得重新学

迁移完了你以为就完事了?没那么简单!真正的大头在后面,就是性能调优。

同样一条SQL,在Oracle里走的是Index Range Scan + Nested Loop,但在金仓里可能就变成Seq Scan + Hash Join,你说这性能能不差吗?

EXPLAIN SELECT * FROM orders WHERE order_date BETWEEN '2025-01-01' AND '2025-01-31';

這種查詢在Oracle裡跑得飛快,但在金倉早期版本裡就是不走索引,你說為啥?因為統計信息沒更新啊!優化器以為全表掃描更便宜。

这个问题其实反映了两个数据库在优化器设计思路上的根本差异。Oracle的优化器可以说是行业标杆,它有几十年积累的统计信息、启发式规则、自适应调优;但金仓这边,虽然也有CBO,但底层的成本模型、统计策略、决策逻辑都不一样。

这个问题的本质是优化器的转换能力,Oracle这边确实更强。

并行执行调度也是个问题,Oracle那边是智能负载均衡,但金仓早期版本就是固定分片,很容易导致数据倾斜。你说这事儿吧,不是说不行,就是不够智能。

并行执行的调度策略,其实反映了两个数据库对"并行"的理解不同。Oracle的并行是动态的,会根据实际数据分布调整;但金仓这边,早期版本更多是静态的,按固定的规则分片。这两种策略各有利弊——Oracle的更智能,但计算代价也更大;金仓的简单粗暴,但实现起来容易。不过从使用者的角度来看,当然是希望越智能越好。

迁移工具其实挺好用的

说了这么多,好像金仓一无是处,其实也不是,人家的迁移工具链确实做得挺好。

KDMS这个迁移评估工具用起来还挺顺手的,它能自动扫描源库对象,生成兼容性报告,告诉你哪些地方需要改,哪些地方不用改:

kdms -source oracle://user:pass@host:1521/db -target kingbase://user:pass@newhost:54321/db --auto-convert

然后是KDTS这个数据传输工具,支持结构迁移、全量同步、增量捕获,还支持断点续传,这个在大数据量迁移的时候特别有用。

最有意思的是KFS这个異構數據同步工具,它能在Oracle和金倉之間建立實時的數據同步鏈路,而且延遲很低,基本上是秒級的。

我們當時就是用KFS做了雙軌並行,Oracle還是主庫,金倉作為備庫實時同步,兩邊一起跑了幾個星期,確認沒問題了再正式切換。最有意思的是,不管你歷史數據有多大,最終割接停機時間基本上都在5分鐘左右,這比傳統的導出導入方式強太多了!

关于双轨并行这个方案,其实有个被大家忽视的好处——它给了你充足的验证时间。传统的"停机迁移"模式,你可能只有几小时的验证窗口,一旦发现问题,回退都来不及;但双轨并行可以让你两边一起跑几周,慢慢比对数据、验证业务,这样风险就大大降低了。当然,这个方案也有代价——你需要两套系统并行运行,成本会增加。但从风险控制的角度来看,这个代价是值得的。

成本这事儿得算总账

说到底,迁移这事儿成本控制是关键。金仓这边主打的就是"三低一平"——低难度、低成本、低风险、平滑迁移。

我们那个项目算下来,如果用传统方式迁移,可能需要200个人天,但用金仓这套工具,60个人天就搞定了,节省了差不多70%。

还有停机损失,传统方式可能要停好幾個小時,那損失就大了去了,金倉這邊5分鐘就能搞定,你說這省下來的錢可不是小數目。

软件授权成本也降了不少,Oracle那边那个license费用,一年下来可不是小数目,金仓这边就实惠多了,基本上能降个60%以上。

还有就是硬件要求,Oracle那边往往需要专有设备,金仓这边通用服务器就能跑,你说这硬件成本能不降吗?

这里我想说个有点反直觉的观点:成本这个东西,不能只看显性成本。很多人算迁移成本,只算了人力、硬件、软件授权这些,但忽略了隐性成本——比如学习成本、磨合成本、风险成本。你从Oracle迁到金仓,团队得重新学习新数据库的特性和调优方法,这个成本其实不小。还有,万一迁移出了问题,业务中断的损失,这个更是没法准确估算。所以说,在做迁移决策的时候,得把显性成本和隐性成本都考虑进去,不能只看表面。

数据类型那些事儿

迁移过程中最容易出问题的就是数据类型。

Oracle那边有个NUMBER类型特别常用:

CREATE TABLE products (
  product_id NUMBER(10),
  price NUMBER(10,2)
);

到金倉這邊就變成NUMERIC或者BIGINT了,大部分情況下沒問題,但有些精度要求特別高的場景就得小心了,特別是金融系統,小數點後兩位丟了可不是小事!

还有DATE和TIMESTAMP这两个类型,Oracle那边行为和金仓这边有点不一样,特别是涉及时区的时候,最好统一用TIMESTAMP WITH TIME ZONE避免出问题:

CREATE TABLE events (
  event_id SERIAL,
  event_time TIMESTAMP WITH TIME ZONE,
  description TEXT
);

字符集这个也得注意,Oracle那边常用ZHS16GBK,但金仓这边默认UTF8,迁移的时候不处理好容易出乱码:

SELECT userenv('language') FROM dual;

这个查询能告诉你Oracle那边用的是啥字符集,然后你在金仓这边设置一下:

client_encoding='GBK'

数据类型映射这个事儿,说起来简单,但做起来坑特别多。不只是类型对应的问题,还有精度、范围、默认值这些细节。举个例子,Oracle的DATE类型既有日期又有时间,但金仓这边,DATE只有日期,TIME只有时间,你想完整对应,得用TIMESTAMP。这种细微的差异,如果不在设计阶段就考虑到,后面出了问题再改,成本就大了。

字符串处理的坑

最讓我頭疼的是CHAR和VARCHAR2的長度語義,Oracle那邊默認是BYTE,但金倉這邊是CHAR,這就導致一些奇怪的問題:

CREATE TABLE test_str (
  name VARCHAR2(10 BYTE)
);

這種定義在Oracle裡能存10個字節,但如果是漢字的話就是5個,但金倉這邊如果不特別設置,就是10個字符,你說這能不出問題嗎?

所以迁移前最好统一设置一下:

nls_length_semantics = 'BYTE'

还有就是空字符串和NULL的处理,Oracle那边空字符串就是NULL,但金仓早期版本不是这样,后来可以通过参数控制:

ora_input_emptystr_isnull = on

这样就把行为对齐了。

字符串处理这些细节,说起来不起眼,但累积起来也挺烦人的。比如长度语义的问题,如果你的应用里有大量涉及字符串长度的判断逻辑(比如截取前10个字符显示),那这个差异可能导致界面显示异常。还有空字符串和NULL的差异,如果你的代码里有IF name = ''这种判断,在Oracle里能工作,到金仓就失效了。这些问题单个都不算大事,但累积起来,修改成本也不小。

系统视图那些事

说到视图,金仓这边做得还是挺用功的,基本上Oracle那边常用的系统视图它都支持了。

比如USER_TABLES

SELECT table_name, status FROM user_tables WHERE table_name LIKE 'T_%';

还有ALL_OBJECTSDBA_INDEXESV$SESSION这些都能用,而且还不是仅仅名字一样,字段定义、数据类型、返回值的业务语义都高度一致。

这样迁移的时候,那些依赖系统视图的代码就不用改了,直接就能跑,你说这省了多大的事!特别是那些动态生成SQL的逻辑,很多都是根据系统视图的信息来生成SQL的,如果这些视图不兼容,那改起来就太痛苦了。

包和函数的兼容性

金仓对PL/SQL的包支持还算不错,大部分情况下包的定义和实现都能直接迁移过来:

CREATE OR REPLACE PACKAGE pkg_employee AS
  FUNCTION get_salary(emp_id NUMBER) RETURN NUMBER;
  PROCEDURE update_salary(emp_id NUMBER, new_salary NUMBER);
END pkg_employee;

CREATE OR REPLACE PACKAGE BODY pkg_employee AS
  FUNCTION get_salary(emp_id NUMBER) RETURN NUMBER IS
    v_salary NUMBER;
  BEGIN
    SELECT salary INTO v_salary FROM employees WHERE employee_id = emp_id;
    RETURN v_salary;
  END;

  PROCEDURE update_salary(emp_id NUMBER, new_salary NUMBER) IS
  BEGIN
    UPDATE employees SET salary = new_salary WHERE employee_id = emp_id;
  END;
END pkg_employee;

这种简单的包基本不用改就能跑。

但是有些复杂的情况就比如包里定义了多个重载函数,Oracle里允许,但在金仓里就不行,得改名:

-- Oracle可以
FUNCTION process(data IN VARCHAR2) RETURN NUMBER;
FUNCTION process(data IN NUMBER) RETURN NUMBER;

-- 金仓得改成
FUNCTION process_str(data IN VARCHAR2) RETURN NUMBER;
FUNCTION process_num(data IN NUMBER) RETURN NUMBER;

驱动和连接那些坑

迁移完了,应用也得跟着改,主要是驱动这块。

注意这里要指定字符集,不然中文容易乱码!

还有连接池的配置也得调一下,驱动和连接这块,其实有个被大家忽视的问题——连接池的配置策略。Oracle的连接,通常能承受比较长的空闲时间;但金仓这边,因为底层是PostgreSQL,对空闲连接的处理不太一样。如果你直接沿用Oracle的连接池配置,可能会出现连接泄漏的问题。所以迁移后,得重新调整连接池的各种超时参数,这个工作量其实不小。

权限管理那些事

Oracle那边的权限体系挺复杂的,到了金仓这边有些权限就不一样了。

比如DBA_*这些视图,在金仓这边不一定能访问,得显式授权:

GRANT SELECT ON sys_dba_tables TO user_name;

还有SYSDBA这个权限,金仓这边也没有对应的,得用其他方式替代。

迁移前最好先把源库的用户、角色、权限都梳理清楚:

SELECT * FROM dba_role_privs WHERE grantee = 'USER_NAME';
SELECT * FROM dba_tab_privs WHERE grantee = 'USER_NAME';
SELECT * FROM dba_sys_privs WHERE grantee = 'USER_NAME';

回退机制

这是我最看重的一个功能!KFS支持双向同步,意味着如果你迁移后发现有问题,随时可以切回Oracle:

-- 割接前:Oracle为主库
SELECT * FROM orders; -- 从Oracle读

-- 割接后:金仓为主库
SELECT * FROM orders; -- 从金仓读

-- 如果有问题,切回Oracle:
-- 停止金仓写入
-- 启用Oracle为主库
-- 最后同步一次数据

这种回退机制给了我们很大的底气,敢在生产环境上试。

多模融合的优势

金仓还有个优势就是多模融合,它把关系型数据、文档数据、图数据都放在一个平台上。以前我们可能要维护Oracle、MongoDB、Neo4j好几个数据库,现在用金仓一个就够了,你说这省了多少事!

比如我们有个业务要存储用户的一些配置,用JSON格式,现在直接存在金仓里,不用再搭个MongoDB了:

CREATE TABLE user_config (
  user_id INT,
  config JSONB,
  PRIMARY KEY (user_id)
);

INSERT INTO user_config VALUES (1, '{"theme":"dark","language":"zh-CN"}');

-- 查询也很方便
SELECT config->'theme' AS theme FROM user_config WHERE user_id = 1;

AI运维的惊喜

金仓还集成了AI运维功能,能自动调参、智能告警这些。有一回系统突然慢了,AI运维自动发现了问题,还给了一个优化建议,说某个表的索引需要重建:

REINDEX TABLE orders;

我们按着建议做了,还真就好了!

写在最后

金仓在兼容性这块确实下了功夫,大部分代码都能直接跑,只需要调整一小部分。迁移工具链也比较成熟,特别是KFS这个双轨并行的方案,让我们很安心。

成本这块也确实降了不少,不只是软件授权成本,还有人力成本、硬件成本、运维成本。性能方面虽然一开始有些调优的压力,但调完之后甚至比以前还好了。

当然还有一些不足,比如有些高级特性的支持还不够完善,有些细节行为还需要适应,但这些随着版本的更新应该会越来越好。

总的来说,这次迁移还算顺利,虽然中间踩了不少坑,但最终结果还是满意的。

如果你也在考虑从Oracle迁到金仓,我给几个建议:

  1. 评估一定要做充分,用KDMS好好扫一遍,别盲目自信;
  2. 测试要做全面,不只是功能测试,性能测试也得做;
  3. 调优要有耐心,不可能一下子就完美,得慢慢磨;
  4. 监控要跟上,及时发现问题及时处理;
  5. 回退机制要准备好,给自己留条后路。

迁移这事儿不是一蹴而就的,得有耐心,有方法,最重要的是得有工具、有支撑。金仓虽然还有提升空间,但在国产数据库里算是靠谱的选择了,至少从我们这次迁移的经验来看是这样的。

如果你也在犹豫要不要迁移,我建议你可以先做个POC试试,看看实际效果再决定,毕竟实践出真知嘛!

金仓数据库官网:kingbase.com.cn