最近手头做了一次跨平台数据迁移,整个项目下来最有价值的东西就是这套组合:XTTS-V3配合DBMS_FILE_TRANSFER。如果你面临的任务是“几十TB的数据要从老平台迁到新平台,停机窗口只能按小时甚至按分钟算”,那这篇文章应该能帮你把方案骨架搭起来,同时避掉我踩过的那些坑。
这套东西适合谁看?主要是DBA、数据库迁移的负责人,以及做灾备和平台升级的运维人员。我会从原理讲到具体步骤,再给一段可以直接抄的PL/SQL传输模板和排错清单。不是照着官方文档念,是按真实项目里的操作顺序来讲。
1. XTTS-V3到底省在哪里
1.1 先看传统方案卡在哪
数据迁移最让人头疼的不是“能迁”,而是“怎么在有限窗口里迁完”。常规思路无非是逻辑导出导入、物理备库、或者传统表空间传输。
数据泵(Data Pump)这类逻辑迁移,胜在跨平台、跨版本灵活,但缺点是导出导入过程是单线程或者少量并行,数据量一上TB,时间就是灾难。而且逻辑迁移还有一个隐性成本:重建索引、重编译存储过程、收集统计信息,这些动作在目标端全都得重来一遍,最后算下来经常比想象中多花两倍时间。
物理备库(Data Guard)跨平台复制需要同字节序、同平台类型,很多老库迁移根本不满足条件。再说了,备库搭建完还要做角色切换,切换后仍要处理一堆应用连接的重新指向,对异构环境的支持就更是聊胜于无。
传统表空间传输(TTS)倒是快,但它的痛点是:要把表空间置为只读后才能传输,也就是说,从置只读那一刻起,业务就断了。断多久?取决于你全量数据传完需要多久。如果只有几百GB还行,到了几十TB,光拷文件就可能一夜,业务能停一夜吗?不能。
所以核心矛盾就是:TTS的只读时间 = 全量传输时间,这个时间在数据量变大后根本不可接受。
XTTS-V3就是把这个问题拆开了。
1.2 V3把停机时间压到分钟级的核心思路
XTTS-V3的全称是 Cross-Platform Transportable Tablespace,版本V3意味着它支持增量。它的思路可以类比成搬家:不是把所有东西先打包封存再一次性搬走,而是先把衣柜、床、沙发这些大件搬到新家,日常用品继续用着,等真要搬的那一天,只需要把最近几天新增的零碎东西打包带走就行。
换成数据库语言就是:
- 第一次把表空间置为只读,做一个全量的跨平台备份,传到目标端并完成恢复。目标端此时已经能“看到”这套数据文件了,但暂时不开放给业务。
- 源端表空间恢复读写,业务继续跑。这段时间产生的变化,通过周期性的增量备份滚动传到目标端,在目标端反复应用。
- 到了真正割接的窗口,源端表空间最后一次置为只读,做一个最终增量备份,传过去应用掉。此时目标端数据就追上源端了,整个只读时间只有“最后一次增量备份 + 传输 + 应用”这几步。
这样一来,全量传输被挪到业务可接受的提前量里,窗口内的只读时间被压缩到了一个增量的规模。实践下来,把原本需要停6到8小时的任务压到40到60分钟,不是夸张的说法。
1.3 DBMS_FILE_TRANSFER在这个方案里的定位
XTTS流程里有个绕不开的环节:文件怎么从源端到目标端。很多人第一反应是NFS挂载、rsync、或者直接让RMAN走网络。但每个方案都有前提条件,比如NFS要两边都挂共享目录,rsync要操作系统层级的账号权限。
DBMS_FILE_TRANSFER的价值在于,它是Oracle内置的包,只依赖数据库间的数据库链接(dblink),就可以把服务器本地文件系统的文件直接传输到远端数据库的服务器文件系统里。不需要共享存储、不需要FTP、不需要操作系统账号,只要你有数据库权限,就能完成文件搬运。
放在XTTS场景里,它的角色就是“搬用工”。全量备份出来的数据文件、增量备份产生的备份集,都可以用它从源端直接推到目标端。对DBA来说,这种方式权限边界清晰,网络链路单一,出了问题也好查。
2. 上项目前,先做这几项硬核检查
2.1 场景适配与兼容性评估
XTTS-V3不是所有迁移场景都对应,它适合的是高数据量、跨平台、停机窗口紧张的项目。如果数据量不到几百GB,老老实实用数据泵或者停机时间可接受的TTS就够了,没必要把流程搞复杂。
确认用XTTS-V3之前,至少要评估这么几项:
- 源端和目标端数据库版本,官方要求是11.2.0.4以上的版本才有完整的V3增量支持,19c这种新版本更是完全覆盖。
- 源端和目标端操作系统是否跨平台、跨字节序。小端Linux迁到大端AIX这种场景,XTTS也能做,但处理方式有差异。最稳的还是同字节序、不同OS,比如Solaris到Linux的x86-64。
- 目标端是否已经有一套正常的数据库环境,包括SYSTEM、SYSAUX这些系统表空间。XTTS只搬业务表空间,不搬系统表空间。
- 字符集一致性。源端和目标端字符集最好完全一致,至少目标端必须是源端的超集。字符集不匹配会导致乱码,而且这种问题往往要等业务跑起来才发现,回滚成本极高。
这里有个经验:项目评估阶段就先把源端所有业务表空间的自包含检查跑一遍,发现问题早解决,别等到割接前才来处理。
2.2 三件绝对不能马虎的准备事项
第一件是表空间自包含检查。一个表空间集合里,如果存在跨表空间的对象引用(比如A表空间的表索引建在B表空间,或者A表空间的表有LOB段在B表空间),直接传输是会报错的。检查脚本很简单:
EXECUTE DBMS_TTS.TRANSPORT_SET_CHECK('TS_APP,TS_INDEX,TS_LOB', TRUE); SELECT * FROM TRANSPORT_SET_VIOLATIONS;第二件是平台转换确认。源端平台名和目标端平台名要拿准,RMAN命令里要写对,否则后面备份集生成出来目标端根本认不了。可以用这个SQL查平台名称:
SELECT PLATFORM_ID, PLATFORM_NAME, ENDIAN_FORMAT FROM V$TRANSPORTABLE_PLATFORM ORDER BY PLATFORM_ID;第三件是备份目录的规划。DBMS_FILE_TRANSFER只负责传文件,文件从哪来、传到哪去,目录对象要提前建好,两边服务器上的操作系统目录也要有对应权限。特别是源端备份目录,全量备份加增量备份可能占掉很大的磁盘空间,至少预留表空间总大小1.2倍的空间,我见过有人因为备份目录爆掉导致整个迁移重启。
2.3 文件传输通道怎么选
DBMS_FILE_TRANSFER不是唯一的传输选择,但它在XTTS场景里是最省事的。我把常见的几种方式拉一个对比:
| 传输方式 | 是否需要共享存储 | 是否需要操作系统账号 | 断点续传 | 适用场景 |
|---|---|---|---|---|
| DBMS_FILE_TRANSFER | 否 | 否 | 无,失败需要重传整文件 | 纯数据库环境,DBA全权掌控 |
| NFS共享目录 | 是 | 是 | 依托文件系统能力 | 两边机房网络稳定,有共享存储基础设施 |
| rsync/scp | 否 | 是 | rsync支持,scp不支持 | 有操作系统层权限,追求速度 |
| RMAN直接连接目标端备份 | 否 | 部分 | RMAN管道机制 | 需要目标端实例打开,且RMAN网络备份流程成熟 |
我最终选DBMS_FILE_TRANSFER,主要是两点原因:第一,整个传输过程不经过操作系统中间层,源库和目标库之间走数据库链接,链路日志可以从数据库层排查;第二,环境里没有现成共享存储,临时搭NFS又多一层故障点。代价是没有断点续传,大文件传一半失败了只能从头再传,所以后面对网络稳定性要求很高,我会在排错部分写这个问题。
3. 全量传输阶段实操记录
3.1 源端表空间自包含检查与只读切换
全量阶段的第一步,是把要迁移的表空间全部置为只读。这一步大部分DBA会觉得简单,但有个细节:置只读的顺序实际上无所谓,关键是置只读前一定要把自包含检查做完,否则后面很容易出现“一个表空间置只读了,另一个还没置,结果业务刚好在跨表空间做了DML”这种半生不熟的状态。
假设待迁移表空间是TS_APP、TS_INDEX、TS_LOB,自包含检查通过后,执行:
ALTER TABLESPACE TS_APP READ ONLY; ALTER TABLESPACE TS_INDEX READ ONLY; ALTER TABLESPACE TS_LOB READ ONLY;置只读的过程,如果表空间里有正在跑的长事务,会被阻塞或者延迟。所以这个动作本身要选在业务低峰期。我曾经在白天高峰期做这一步,等了40分钟才全部置成功,因为总有事务没结束。从那之后,我都会提前一天看Undo里最长运行的事务,确认没有超长事务才动手。
置为只读后,建议马上执行一次检查:
SELECT TABLESPACE_NAME, STATUS, CONTENTS FROM DBA_TABLESPACES WHERE TABLESPACE_NAME IN ('TS_APP','TS_INDEX','TS_LOB');确认状态都是READ ONLY,再进行备份。
3.2 生成跨平台传输的备份文件
现代Oracle版本里,RMAN可以直接生成跨平台可传输的备份集。我用的是19c环境,直接执行:
RMAN> BACKUP FOR TRANSPORTABLE TABLESPACE TS_APP,TS_INDEX,TS_LOB FORMAT '/backup/xtts/full_%U';这个命令生成的目标平台类型默认会按照目标端来判断,前提是你已经配置好目标端的平台信息。也可以用TO PLATFORM子句显式指定。比如:
RMAN> BACKUP FOR TRANSPORTABLE TABLESPACE TS_APP,TS_INDEX,TS_LOB TO PLATFORM 'Linux x86 64-bit' FORMAT '/backup/xtts/full_%U';这里生成的文件是备份集(backup piece)还是镜像拷贝(image copy),取决于你的写法。备份集的好处是自动压缩、体积小,但目标端恢复时需要一个restore动作。镜像拷贝的好处是目标端可以直接用,但体积跟原库一样大。
实际操作里,全量阶段我优先用备份集,因为网络传输量更小。但增量阶段,如果用LEVEL 1 INCREMENTAL FOR TRANSPORTABLE,这个备份集本身就会包含被修改的块,目标端可以基于这个增量备份集做恢复,所以增量备份集反而是自然的选择。
全量备份生成后,检查一下备份集文件列表:
RMAN> LIST BACKUP OF TABLESPACE TS_APP,TS_INDEX,TS_LOB;同时确认源端OS目录里文件都齐了,再进入传输环节。
3.3 DBMS_FILE_TRANSFER上机实录
文件准备好之后,传输就是整个项目里最机械化却也最容易翻车的环节。
我提前在源端和目标端各建好目录对象。源端目录指向备份文件所在路径:
CREATE OR REPLACE DIRECTORY SRC_BACKUP_DIR AS '/backup/xtts';目标端目录指向数据文件最终要落盘的路径,或者一个中转路径都可以。目标端目录对象必须建在目标端数据库里,而且对应的操作系统目录必须存在并且对Oracle用户可写:
CREATE OR REPLACE DIRECTORY DST_DATA_DIR AS '/u01/app/oracle/oradata/PROD/ts_app';还需要在源端创建一个指向目标端的数据库链接。这个链接的连通性直接决定PUT_FILE能不能成功:
CREATE DATABASE LINK TARGET_LINK CONNECT TO system IDENTIFIED BY 你的密码 USING '目标端TNS别名';然后授权执行权限:
GRANT EXECUTE ON SYS.DBMS_FILE_TRANSFER TO SYSTEM; GRANT CREATE ANY DIRECTORY TO SYSTEM;传输命令的模板是这样的,以单个备份集为例:
BEGIN DBMS_FILE_TRANSFER.PUT_FILE( source_directory_object => 'SRC_BACKUP_DIR', source_file_name => 'full_abc123.bus', destination_directory_object => 'DST_DATA_DIR', destination_file_name => 'full_abc123.bus', destination_database => 'TARGET_LINK' ); END; /这是我实际执行过的写法。参数一共五个,前四个都很直接,最后一个destination_database填写数据库链接名。如果不填这个参数,数据库会认为目标目录也在本地,直接在本地文件系统之间复制文件,那就完全不涉及网络了。
全量备份集可能有几十个文件,我一个一个手写太累了,直接写了个PL/SQL块,用一个游标把所有备份集文件名取出来循环调PUT_FILE:
DECLARE v_src_dir VARCHAR2(100) := 'SRC_BACKUP_DIR'; v_dst_dir VARCHAR2(100) := 'DST_DATA_DIR'; v_dblink VARCHAR2(100) := 'TARGET_LINK'; BEGIN FOR f IN (SELECT FILENAME FROM TABLE(RDBMS_SERVER_DN.FILE_LIST(v_src_dir))) LOOP DBMS_FILE_TRANSFER.PUT_FILE(v_src_dir, f.FILENAME, v_dst_dir, f.FILENAME, v_dblink); END LOOP; END; /注意,上面的TABLE(RDBMS_SERVER_DN.FILE_LIST)写法适合把OS目录文件列出来,如果你用的版本没有这个包,就直接写死文件名列表。我更喜欢用一个临时表把文件名灌进去,然后循环更直观。
大文件传输时要注意一个细节:PUT_FILE是同步调用,单个文件传输期间会话会一直占住。如果几十个文件串行传输,时间会很长。所以我会开4到6个SQL*Plus会话,每个会话负责传不同文件,用并发把近千兆网络利用起来。实测下来,1GE网络跑满到800Mbps左右是可以做到的,单会话传输只能跑到300Mbps左右。
另外,传输期间不要动源端备份文件,也别让任何进程扫描文件加锁,否则PUT_FILE会报文件被占用相关错误。
3.4 目标端恢复与接入
文件传到目标端后,RMAN这边需要识别这些外来文件。做法是把备份集加入目录,然后恢复。
目标端RMAN执行:
RMAN> CATALOG START WITH '/u01/app/oracle/oradata/PROD/ts_app'; RMAN> RESTORE FOREIGN TABLESPACE TS_APP,TS_INDEX,TS_LOB;这里RESTORE FOREIGN TABLESPACE的意思是,从外来平台备份集中提取数据文件。Oracle会自动做平台转换,因为备份集里已经带了目标平台的格式信息。恢复完成后,再把数据文件切换到最终位置:
RMAN> SWITCH DATAFILE ALL;最后应用全量阶段的信息:
RMAN> RECOVER TABLESPACE TS_APP,TS_INDEX,TS_LOB;到了这一步,目标端已经持有了一套完整的业务表空间。我习惯马上做个完整性检查,直接登录目标库查每个表空间的段数量,跟源端对比。这个动作特别重要,因为如果这里漏了,后续增量循环会发现对不上,排查起来非常痛苦。
至此,全量阶段结束。源端表空间可以恢复读写,业务继续跑。
ALTER TABLESPACE TS_APP READ WRITE; ALTER TABLESPACE TS_INDEX READ WRITE; ALTER TABLESPACE TS_LOB READ WRITE;4. 增量同步与割接窗口控制
4.1 增量备份循环怎么设计
全量阶段完成之后,XTTS-V3的核心价值才开始体现,就是增量同步。
增量备份的命令是:
RMAN> BACKUP INCREMENTAL LEVEL 1 FOR TRANSPORTABLE TABLESPACE TS_APP,TS_INDEX,TS_LOB FORMAT '/backup/xtts/incr_%U';FOR TRANSPORTABLE这个关键字决定了增量备份集可以被RMAN从一个平台恢复外来表空间。注意,源端生成的增量备份集如果大小很大,也要用DBMS_FILE_TRANSFER传到目标端。
增量备份的频率怎么定?主要看源端每天的数据变化量。如果一天只变10GB以内,每天一次备份完全够了。如果业务很重,一天变化上百GB,那增量备份间隔就得缩短到6小时甚至2小时,否则割接时最后一次增量的备份时间会很长,窗口压不下来。
我实际用的方案是每天凌晨跑一次增量,遇到割接日前一天,再额外在早高峰前加跑一次。原因是凌晨备份完到早上业务高峰之间的数据变化需要被捕获,不然第二天割接时,最后一次增量就会包含一整个白天的变化量,备份时间直接翻倍。
每个备份周期结束后,立刻把增量备份集传过去、在目标端应用掉,不要让增量积压。积压的增量越多,目标端RECOVER的时间越长,而且一旦中间某个增量包坏了,整个链条都要重来。
传输增量和应用增量,我记得最普通的做法是:
- 源端执行BACKUP INCREMENTAL LEVEL 1 FOR TRANSPORTABLE。
- 用DBMS_FILE_TRANSFER把生成的备份集全部传到目标端。
- 目标端RMAN里CATALOG这些备份集。
- 目标端执行RECOVER TABLESPACE。
4.2 增量应用与最终切换
目标端应用增量非常简单,看命令:
RMAN> CATALOG START WITH '/u01/app/oracle/oradata/PROD/ts_app/incr'; RMAN> RECOVER TABLESPACE TS_APP,TS_INDEX,TS_LOB;RMAN会自动识别增量备份集中包含的已修改块,把它们应用到对应的数据文件上。这个过程不需要目标端表空间是只读或者脱机,数据文件处于恢复状态即可,实际上RECOVER本身就会管理数据文件状态。
到了割接日,流程是固定的:
- 源端业务表空间最后一次置只读:
ALTER TABLESPACE TS_APP READ ONLY; ALTER TABLESPACE TS_INDEX READ ONLY; ALTER TABLESPACE TS_LOB READ ONLY;- 源端做最后一次增量备份:
RMAN> BACKUP INCREMENTAL LEVEL 1 FOR TRANSPORTABLE TABLESPACE TS_APP,TS_INDEX,TS_LOB FORMAT '/backup/xtts/final_%U';- 把最终增量备份集传输到目标端。
- 目标端CATALOG并RECOVER。
- 目标端把表空间置为读写(非CDB环境):
ALTER TABLESPACE TS_APP READ WRITE; ALTER TABLESPACE TS_INDEX READ WRITE; ALTER TABLESPACE TS_LOB READ WRITE;如果目标端是12c以上的CDB架构,最后这步会变成把数据文件与某个PDB关联起来的操作,本质是一样的,就是把表空间接入数据库。
切换完成之后,我还会做一轮数据核对,比如查几个关键大表的MAX(更新字段)、统计信息,跟源端做对比,确认没有差异再通知业务验证。
4.3 停机时间估算方法论
整个项目走到这里,大家最关心的“停机多久”终于能算了。核心公式是:
停机窗口 = 最终增量备份时间 + 传输时间 + 目标端RECOVER时间 + 校验时间
最终增量备份时间取决于当时数据变化量。比如业务高峰期每分钟产生2GB变化的库,最后窗口前的业务如果停得早,那备份量就小;如果一直跑到最后一刻才置只读,那备份量可能就大了。所以实际操作里,我会要求业务在割接之前提前清退夜间批处理任务,让数据变化量降下来。
传输时间取决于最终增量备份集的大小和带宽。100GB增量在1GE网络上,理论极限约14分钟,实际考虑协议开销和磁盘IO,20分钟左右合理。
目标端RECOVER时间取决于增量中要应用的块数量,通常跟增量备份集大小正相关。100GB的增量实际RECOVER可能需要15到30分钟。
这三步加起来,就是整个窗口的大头。如果要压缩,可以从两个方向下手:一个是提高源端增量备份的I/O能力,比如备份到SSD临时目录;另一个是提高目标端RECOVER期间的并发,比如临时调整恢复相关参数。
我用一个案例来算:某项目全量数据约15TB,平时每日变化量约40GB。最终窗口内,置只读到最终增量备份完成耗时12分钟,最终增量包大小约38GB,1GE网络传输耗时约8分钟,目标端RECOVER耗时约22分钟,加上数据校验和应用验证约15分钟,整个窗口约1小时。相比原来预估值4小时,压缩了4倍。
5. 常见问题与排查技巧实录
5.1 DBMS_FILE_TRANSFER传输失败怎么查
PUT_FILE最经典的报错是ORA-29283,碰到这个错误先不要慌,问题基本出在三个方面:
- 源端目录对象指向的OS目录不存在或Oracle进程无读权限。确认方法是去源端服务器上ls那个目录,并用Oracle用户测试读取其中文件。
- 目标端目录对象指向的OS目录不存在或Oracle进程无写权限。这个比源端更容易漏,因为目标端的目录对象是建在目标库里的,目录对应的OS目录必须提前创建好。
- 数据库链接不通。可以先在源端执行SELECT 1 FROM DUAL@TARGET_LINK来验证链接是否正常。
还有一个高频坑:DBMS_FILE_TRANSFER没有断点续传。大文件传到一半,网络抖动断了,就只能从零开始。我后来想了个办法,传输前先把所有文件都做好目录对象侧的清单,传输后用目标端文件列表和源端对比,大小不一致的重传。脚本里可以这样对比:
SELECT FILE_NAME, SIZE_BYTES FROM TABLE(RDBMS_SERVER_DN.FILE_LIST('DST_DATA_DIR')) ORDER BY FILE_NAME;如果目标端目录里文件数量没到预期,就针对缺失的文件重新调用PUT_FILE。
5.2 增量应用报错的典型场景
增量恢复最常见的报错是ORA-01189或者RMAN错误,大意是找不到某个归档日志或者备份集里的SCN不连续。
这类问题的根源通常是增量备份集传输漏了文件。我遇到过两次,都是因为文件名里带时间戳,传输脚本里用通配符匹配时漏掉了部分文件。解决方案很简单:每次增量备份结束后,把LIST BACKUP的输出保存下来,和传输清单做一次人工比对。
另一个报错场景是目标端数据文件状态不对。增量恢复要求在目标端先完成全量恢复并把数据文件置于可恢复状态,如果直接跳过全量恢复就开始应用增量,RMAN会拒绝执行。所以流程里我一直强调,全量阶段的恢复动作必须完整跑完。
还有一种情况:增量备份集的SCN早于目标端数据文件的当前SCN。这种一般是因为你把旧的增量包又重新CATALOG了一次,导致RMAN状态混乱。排错方法:查V$RECOVERY_PROGRESS或V$RECOVER_FILE,确认数据文件需要的SCN范围,然后检查增量备份集的CREATION_TIME,确保按时间顺序应用。
5.3 性能与空间踩坑记录
全量阶段最容易忽略的是源端备份目录空间。备份集虽然是压缩的,但压缩需要额外CPU,压缩率又取决于数据特征。我曾经遇到一个表空间里面大量已删除数据的碎片,备份集占了原空间的一半,目录直接爆掉。所以备份前用RMAN的VALIDATE和空间预估(ALLOCATE CHANNEL后自动显示所需空间)提前确认。
DBMS_FILE_TRANSFER并发传输也会有一个问题:它本质上是数据库服务器进程在做文件I/O和网络I/O,并发太高的时候会占满数据库自身的I/O。我踩过一坑,并发开8个会话传文件,导致源库的正常业务查询响应变慢。后来把并发压到4个,同时把网络流控走专用VLAN,业务影响就完全消失了。
还有个小点:PUT_FILE传大文件时,源端数据库的LGWR和归档进程不会受直接影响,但如果磁盘I/O被占满,就会牵连联机日志写入。所以传输期间监控源端AWR里的I/O指标,IO负载超过80%就降低并发,这是实用经验。
6. 个人经验与一点补充
每次做完这种迁移,我都会把整个过程的日志、备份清单、传输记录归档成一份文档,目的不是给领导汇报,而是为了方便回滚和后续复盘。这次做完XTTS-V3迁移,最大的体会是:方案本身不难,难的是把每一步的验证动作做到位。少一次自包含检查、漏一次增量包的传输入库,都可能在临门一脚时把项目拖进延期的泥潭。
最后分享一个我自己设计的小习惯,可以少很多麻烦。在所有DBMS_FILE_TRANSFER调用脚本里,文件传输成功后我在目标端加一个简单的校验动作:把传过去的文件大小落进一张日志表,每条传输记录包含源文件名、目标文件名、字节数和完成时间。这样整个迁移过程的可审计性和可追溯性非常清晰,后面万一要排查“某个文件到底传没传对”,一张表就能查明白,省去在目录里翻来覆去找的功夫。