前言
先问个扎心的问题:你们上次数据库迁移的窗口,约了多久才约下来?
我猜不少人的答案是------约了一个月,还没约下来。业务一句"周日晚上不能停",DBA 就只能接着等。好不容易窗口批下来了,新麻烦跟着来:导出俩小时、导入四个小时、建索引再俩小时,中间任何一步翻车,全部推倒重来,因为天亮了。
数据量小,熬一晚也就熬了。到了 TB 级,停机迁移这条路基本走死。
那有没有办法让业务一边跑、数据一边搬?有。这篇文章就聊金仓数据库的在线迁移:两个工具打配合,KDTS 搬存量,KFS(KingbaseFlySync)追增量。跑顺了的话,真正停业务的时间,就切换那几分钟。
@TOC
一、先想明白:这套方案到底怎么转的
打个比方。这事儿跟搬家一个道理:新房装修好了,你不可能让全家先出去住酒店,等东西全搬完再回来------等不起,也没必要。聪明的搬法是:大件儿一趟一趟往新家运,老房子里日子照过;最后剩点随身行李,找个上午一口气拎过去,半小时收工。
对应到数据库这边,"运大件"就是 KDTS 的活:把 Oracle 里已有的表结构、索引、约束、历史数据,整建制搬进金仓数据库。这个过程源端业务完全无感,该读读,该写写。
"随身行李"归 KFS 管。搬迁期间业务还在 Oracle 上写新数据,怎么办?KFS 时刻盯着 Oracle 的重做日志,谁插了一行、谁改了个字段,全记下来,回头在金仓那边按顺序重放一遍。
有个时序要点,先划出来:必须先启动 KFS,再跑 KDTS。顺序反了,存量搬迁期间的变更就没人盯着,等你发现少数据,哭都来不及。另外,存量和增量的交界处难免混进点重复,KFS 靠冲突处理策略兜住------这也是后文反复念叨"无主键表必须提前处理"的原因:没主键,连冲突都没法定位。
真正要停业务的,只有最后切换窗口里那几分钟。
二、动手之前,把 Oracle 端收拾利索
这一章没什么高深内容,但真值钱。事后追着排查数据对不上的,回头一看,十有八九是这步没做扎实。咱一条条过。
归档模式得先打开。KFS 吃的是 Oracle 重做日志,库跑在非归档模式,它连饭都吃不上,链路根本起不来。
然后是附加日志。默认 redo 里那点信息,定位不到具体某一行,增量同步得靠附加日志补齐:库级开一次,再给关键业务表加全列附加。这步别省,省下来的时间,后面数据对不上时会加倍还回去。
账号单独建。迁移一个、同步一个,分开管,出了问题好查,权限也好审计,互不背锅。
最要命的是无主键表。增量同步的 UPDATE、DELETE 全靠主键或唯一键定位行,一张没主键的表混在链路里,就是颗雷。处理办法就俩:要么补主键,要么确认它就是张纯日志表、不需要实时跟着走,直接剔出同步范围。
代码直接抄,SQL*Plus 里逐条执行:
sql
-- 1. 确认归档模式
archive log list;
-- 若显示 No Archive Mode,需要重启实例切换(挑变更窗口做,别在高峰上手抖):
shutdown immediate;
startup mount;
alter database archivelog;
alter database open;
-- 2. 库级附加日志
alter database add supplemental log data;
-- 3. 关键业务表加全列附加日志
alter table ORDERS add supplemental log data (all) columns;
alter table ORDER_ITEMS add supplemental log data (all) columns;
-- 4. 同步专用账号(这里列的是常用项,完整清单以 KFS 文档为准)
create user KFS_SYNC identified by "Sync#2026";
grant create session, execute_catalog_role, select any transaction,
select any table, select any dictionary to KFS_SYNC;
grant select on v_$database to KFS_SYNC;
grant logmining to KFS_SYNC; -- Oracle 12c 及以上
顺手把家底也摸一遍:哪些表没主键、哪些表是大户,心里得有本账。
sql
-- 无主键表清单(增量同步的雷区)
select t.table_name
from user_tables t
where not exists (
select 1 from user_constraints c
where c.table_name = t.table_name
and c.constraint_type = 'P');
-- 数据量 TOP 10,用来估存量搬迁的时长
select segment_name, round(sum(bytes) / 1024 / 1024 / 1024, 2) as size_gb
from user_segments
group by segment_name
order by size_gb desc
fetch first 10 rows only; -- 11g 及以下把最后一行换成 where rownum <= 10 的写法
摸完底,再算两笔土账。
磁盘账:目标端空间按源端数据量的 1.5 倍起步留。听着夸张?索引要地方、临时表要地方、大对象还要地方,等真不够用的时候,扩容可比预留难多了。
带宽账:千兆内网理论吞吐 118MB/s 上下,5TB 数据光在网络上传就得 12 个小时起。算完要是发现网是瓶颈,提前想办法------临时拉条万兆,或者分批、挑重点表先走。别等开搬了才干瞪眼。
三、KDTS:存量数据,整建制搬过去
KDTS 是金仓数据库配套的迁移工具,图形控制台,浏览器里点选就行:源端选 Oracle,目标端选金仓,勾库勾模式,配好线程数,开跑。表、视图、序列、约束、索引、注释,这些常规对象都包在它的能力范围内。出错行可以配"跳过并记录",也可以配"直接中断",跑完出一份迁移报告,哪些成了、哪些败了、败在哪儿,写得明明白白。
线程数给大方点。几十张表的任务,单线程和多线程跑下来时间差好几倍,没道理省这个。
类型映射估计是大家最惦记的。好消息:金仓数据库的 Oracle 兼容模式原生支持 number、varchar2、clob、blob 这一票常用类型,KDTS 默认映射基本同名直迁。少数类型要留个心眼,挑容易出事的说:
| Oracle 类型 | 金仓端常见映射 | 搬迁时要注意什么 |
|---|---|---|
| NUMBER(p,s) | number(p,s) | 不带精度的不定长 NUMBER 直接映射为 number,极端精度业务留意舍入行为 |
| NUMBER(p,0) / INTEGER | bigint | 小整数列落成整型,省空间,索引也快 |
| VARCHAR2(n) | varchar2(n) | 分清 n 是 BYTE 还是 CHAR 语义,存中文的表照抄数字,容易踩截断 |
| CHAR(n) | char(n) | 定长补空格,比对逻辑留意尾部空格差异 |
| DATE | date | Oracle 的 DATE 自带时分秒,金仓 Oracle 兼容模式下同样带,不丢时间部分 |
| TIMESTAMP(n) | timestamp(n) | 带时区的版本映射为 timestamptz,两端时区参数要一致 |
| CLOB / NCLOB | clob | 大字段多的表搬得慢,单独分线程更稳 |
| BLOB | blob | 大对象按磁盘量预估,空间留够 |
| RAW / LONG RAW | blob | LONG 系列是上古类型,能在源端先改掉就先改掉 |
剩下几个坑,提前念叨念叨。
序列是个暗坑。序列迁过去时带的是迁移那一刻的值,可存量搬完到正式切换之间,Oracle 那头序列还在往前走。等切完了,金仓这边序列落后一截,新数据一进来主键直接撞车。所以切换前必须把序列顶上去,具体放第六章清单里。
存储过程和包,是全程最吃人力的一块。KDTS 能自动转写大部分常用 PL/SQL,但 Oracle 那些特色写法------包体、自治事务、DBMS_* 一堆包调用、绕来绕去的 ROWNUM------经常得人肉改。建议对象迁完后,在 KStudio 里挨个编译一遍,报错的拎出来排期处理。工期往宽了留,这块比你想的费时间。
字符集提前对齐。源端 GBK 还是 UTF8,目标库初始化时就定死。工具有转码能力没错,可脏数据这东西平时不吭声,一到转码就集体冒头(别问我怎么知道的),提前定好规矩省得返工。
跑存量的时机也有讲究:挑业务低峰启动。大表的耗时主要耗在网络传输和目标端写盘上,高峰期跑,源端 IO 和同步链路抢资源,两头都慢,得不偿失。
四、KFS:增量追平,把延迟磨到零
KFS 干的事,一句话讲完:借助 Oracle LogMiner 解析重做日志,把源端的 DML 变更翻译成金仓数据库能执行的语句,按提交顺序在目标端重放。源端、目标端各装一套同步服务,一头管抓、一头管放,管理界面里链路状态和延迟都看得见。
存量搬完、KFS 开始回放积压增量之后,你盯两个指标就够了。
一个是延迟。正常剧本是断崖式下跌,然后慢慢趴到零附近。要是延迟死活降不下去,八成有无主键表在作妖------没主键的 UPDATE 到了目标端只能全列匹配,性能差好几个量级,一张表就能拖垮整条链路。
另一个是回放速率。增量产生的速度长期压过回放速度,说明目标端写不动了,去查目标库 IO、查同步服务的资源配置,别干等。
另外三件事,提前打预防针。
大事务,能拆就拆。迁移期间别一口气 update 几千万行,日志解析端处理大事务极吃内存,同步服务被撑挂,链路重来,前面等的时间全白费。真有大批量需求,拆成每批几万行,细水长流。
DDL,冻住。从 KFS 启动到正式切换,源端结构变更原则上别碰。中途加字段、改类型,链路接不接得住得看版本支持,没到切换那天,别在刀尖上玩花样。
归档日志,留够。增量追平那几天,清理策略的手别紧,KFS 要用的日志段一旦被删,链路就得重起,前面的等待全打水漂。等切完了再收紧,不迟。
五、校验这步,千万别偷懒
数据搬完、延迟归零,先别急着切。校验做扎实,切换那晚才睡得踏实------不然躺到一半,突然想起有张表没对数,爬起来开电脑的滋味,不好受。
实用的是三层,由粗到细。
先对总行数。两端各跑个 count,几秒钟的事。粗是粗,大问题先筛出来。
总行数对上了,再分桶比聚合值。按时间或主键范围切桶,比行数、比金额合计,差异能很快圈进某个时间段里。
最后抽明细。从差异桶里拎几十行,逐字段人肉比,定位到根因为止。
分桶比对的两端 SQL 长这样,注意口径必须一模一样:
sql
-- Oracle 端
select to_char(trunc(create_time), 'yyyy-mm-dd') as day,
count(*) as cnt,
nvl(sum(amount), 0) as amt
from orders
where create_time >= date '2026-09-01'
group by trunc(create_time)
order by 1;
-- 金仓端
select to_char(trunc(create_time), 'yyyy-mm-dd') as day,
count(*) as cnt,
coalesce(sum(amount), 0) as amt
from orders
where create_time >= date '2026-09-01'
group by trunc(create_time)
order by 1;
两边数字对上,说明存量加增量这场接力没掉棒。还有个容易忽略的点:校验的时候,源端还在写呢。要么挑业务低峰、增量延迟贴近零的时候做快照式比对,要么等切换窗口数据冻结了再比。拿一个动着的数去对一个静止的数,比出差异也是自己吓自己。
六、切换窗口:真正要停的,就这几分钟
前面铺这么多,就是为了把停业务的窗口压到最小。切换当天,下面这张清单建议打出来贴在桌上,一项一项打勾------别靠脑子记,凌晨两点的脑子靠不住。
| # | 动作 | 通过标准 |
|---|---|---|
| 1 | 通知业务停写,应用置为只读或短暂停服 | Oracle 端确认没有活跃的写入会话 |
| 2 | 等 KFS 延迟彻底归零 | 延迟为 0 并保持稳定,不再波动 |
| 3 | 终验:行数 + 聚合值快速复核 | 与切换前校验口径一致 |
| 4 | 推进金仓端序列到安全水位 | 所有序列当前值 ≥ Oracle 端对应值 |
| 5 | 应用连接串切到金仓,确认驱动和连接池 | 连通性测试通过,无驱动类报错 |
| 6 | 恢复业务,重点盯错误日志和核心交易 | 核心链路正常,无批量异常 |
| 7 | Oracle 转只读保留,约定观察期 | 观察期内无回退需求,再安排下线 |
第 4 步序列推进的 SQL 贴在这儿,到时候直接用:
sql
-- 金仓端:把序列顶到安全水位,避免切换后主键冲突
select setval('seq_orders_id', (select max(id) from orders) + 1000);
回退也得提前想好。切完别急着下 Oracle,转只读,至少留一周;应用的连接配置双份保留,金仓真出大事,一把就能切回去。讲究点的团队,会用 KFS 再搭一条金仓回 Oracle 的反向链路,切完两端继续互备,回退就是分钟级。代价是多养一套链路------划不划算,看业务分量。核心交易系统,我建议搭。
最后啰嗦两句
这套方案说穿了就一句话:搬家拆成"大件先运、随身最后拎",最耗时的部分业务无感,人工操作全压缩到最后几分钟的窗口里。
工具确实把体力活都干了,可迁移项目真正磨人的,从来不是点按钮------是那些没主键的表、不敢跑的大事务、存储过程里绕来绕去的 Oracle 写法,还有切换当晚一条条过清单的耐心。摸底做得越细,切换那晚就越好过。
祝各位一次切过,窗口十分钟,回去补觉。