凌晨两点多接到电话一套业务生产库起不来了报错刷屏控制文件能挂上但system表空间的数据文件头SCN对不上归档日志还断了档。这种场面下RMAN的常规不完全恢复基本可以宣告失败exp/expdp更无从谈起因为实例连mount都走不稳。真正能救场的只剩Oracle数据库恢复里那条不太上台面的路——非常规灾难恢复也就是AUL、MyDUL这类绕过数据库的块级卸载工具。它们不经过SGA、不走SQL引擎、不需要实例open直接按文件号加块号去啃操作系统上的数据文件把行数据一行一行抠出来再落地成文本。这篇东西写给两类人一类是手上真有打不开的库、备份又不顶用的DBA另一类是还没踩过这种坑、但想提前把底层知识补齐的同行。下面把我这些年在现场反复验证过的流程、参数、坑点全部摊开讲能抄的部分直接抄。1. 常规恢复走不通时非常规手段才是最后一道门先把话说透非常规恢复不是更高级的恢复而是最后的恢复。它出现的唯一理由是前面所有标准路径全部走死了。所以在拔刀之前必须非常清楚标准路径是怎么死的否则你会把本来能干净恢复的库硬生生做成一个数据一致性和完整性都没法保证的半成品。1.1 三条常规路径为什么会同时失效第一条路径是备份加归档做不完全恢复。它的前提太苛刻了备份集必须完整可用归档日志必须从备份终点连续到故障点中间一个gap都不能有。现场最常见的情况是备份策略看起来很完善等到真要恢复的时候才发现三个月前的全备文件被清理脚本顺手删了或者归档目录在一个独立挂载点上那块盘刚好和故障盘是同一个存储池。第二条路径是RMAN的块级恢复和Data Recovery Advisor。它能处理的是局部坏块前提是坏块数量可控、有镜像副本或备份块可用。一旦故障范围扩散到文件头、控制文件、system表空间这个工具给出的建议往往就是请从备份恢复等于把你推回第一条路。第三条路径是从一个还能open的库往外抽数据用exp/expdp或者写PL/SQL游标导出。这条路最舒服但它的门槛是库能open。现实里大量的灾难场景恰恰是open不了数据文件被误删、被同名的空文件覆盖、存储层写坏、文件头SCN与当前SCN差距超出隐含参数允许范围、控制文件全部丢失又没备份。库开不了一切依赖SQL层的工具都是摆设。我见过的死局里最典型的一种是数据文件被一个人用dd覆盖了前几个块文件头彻底废了但后面的业务数据块完好无损。这种场景下标准恢复会告诉你文件头损坏无法识别而非常规工具能直接把从第若干个块开始的业务数据全部扫出来。这就是它存在的意义——它不要求数据库是一个数据库只要求磁盘上的块还在。1.2 AUL、MyDUL这类工具的定位与边界这类工具的本质是Oracle块格式的解析器不是数据库。它们做的事情可以拆成三步第一步按操作系统层面打开数据文件第二步按Oracle的数据块结构解析每一个块识别块类型、段头、行目录、行数据第三步把解析出来的行按目标表的列定义重新拼装输出成带分隔符的文本、SQL*Loader的控制文件加数据文件或者直接生成INSERT语句。它们共同的优势有三点。一是不依赖实例目标库处于什么状态都无所谓甚至目标机上一个Oracle软件都没装也能跑部分工具需要同版本的库文件来解析字符集和类型。二是不依赖数据字典的完整性高级模式可以在数据字典表本身也损坏的情况下通过扫描段头块反推对象和区映射。三是能读坏块只要行数据区没被物理清零块头校验和错了、SCN错了都不影响它把行读出来。但边界同样清晰必须提前跟业务方讲明白否则后期扯皮。索引恢复不了也没必要恢复重建就行。PL/SQL源代码、视图定义、触发器、存储过程这些存在数据字典里的对象能尝试从source$里挖但大概率不完整实用价值有限。序列的当前值可以从seq$里读但读出来的一瞬间就已经不是当前值了。最要命的是事务一致性工具是按块读的块里可能包含未提交的事务、可能包含已经被回滚但还没来得及清理的行、可能同一个块的不同行来自不同时间点。所以恢复出来的数据必须当作可疑数据对待。注意任何一次非常规恢复在业务方眼里都是一次数据抢救不是数据恢复。提前说清楚可能丢数据、可能有脏数据、可能不一致比事后解释要省一百倍的力气。1.3 决策矩阵什么时候该动手什么时候该放弃不是所有打不开的库都值得上非常规手段。我整理过一个简单的判断表现场直接对着看。故障形态常规恢复可行性是否建议上非常规工具主要风险单个数据文件损坏有近期备份高块级恢复即可不建议无谓增加复杂度数据文件被误删无备份文件系统未覆盖低建议优先尝试文件系统级恢复块被覆盖后数据不可逆丢失数据文件头损坏业务块完好极低强烈建议需要人工确认文件号映射system表空间损坏控制文件丢失无必须上且要重建字典对象定义可能残缺存储层大面积写坏坏块率超30%无谨慎评估恢复周期极长收益不确定误TRUNCATE、误DELETE且无闪回中LogMiner可试可先用LogMiner失败再上时间窗口紧undo可能已被覆盖这张表的核心逻辑是先算收益再算成本。一个200GB的库全量扫描加解析加导入正常要两到三天。如果业务方只能接受半天停机那这事从一开始就不该接或者说接了也只能按能救多少救多少的方式做先救最核心的几张表。2. 原理拆解它们凭什么能绕过打不开的数据库很多人用这类工具是照着命令敲敲完出数据就完事一旦报错就懵。根子在于没搞清楚Oracle的块里到底存了什么。这一章我把块结构、寻址方式、类型识别三个关键点讲透搞明白这些工具报的任何错你都能自己判断是数据真没了还是参数配错了。2.1 从数据块结构说起Oracle的数据文件在操作系统看来就是一个大二进制文件逻辑上被切成一个个固定大小的块块大小由DB_BLOCK_SIZE决定常见的是8KB。每个块内部有严格的格式从前往后大致分成几段。最前面是块头大约20个字节里面有几个关键字段块地址rdba由文件号和块号编码而成块类型比如0x06是数据块、0x17是段头块、0x18是位图块、0x02是undo段头还有块格式版本和SCN的低位部分。块头后面是事务槽区每个事务槽23字节记录这个块上发生过的事务信息槽的数量由建表时的INITRANS和后续扩容决定。再往后是表目录和行目录行目录每行占2个字节存的是该行数据相对于块起始位置的偏移。行数据区在块的最后部分从块尾往前长。一行数据内部又分成行头列数、行标志、链接信息和列数据。列数据是按列顺序紧密排列的变长类型前面带长度字节定长类型直接占固定字节。这里有个特别关键的点行目录是恢复工具的生命线。只要行目录还合法工具就能定位到每一行的起始位置哪怕块头的事务信息和SCN已经乱了。我在一次现场遇到过这样的情况某个数据文件的第1号到第5号块被写坏文件头彻底没了工具直接打开会报不是有效的Oracle数据文件。但我们从第6号块开始扫每一个块的块头里都写着自己是几号文件的几号块行目录也完好。最后这张表的数据一行没少。这就是块自描述带来的好处。2.2 定位数据的三条寻址路线工具找数据走的是三条不同精度的路线理解它们的差异对选择扫描模式至关重要。字典模式最精确也最快。它先读system表空间里的基表比如obj$记录对象号和名字的对应tab$记录表和对象号的对应col$记录每一列的类型、长度、精度和列序号seg$记录段头块的位置ext$记录每一个区的文件号和起始块号。拿到这些信息后工具就知道SCOTT.EMP这张表由哪几个区组成、每个区在几号文件的几号块到几号块然后只扫这些块就行。缺点是强依赖system表空间完好。段头模式是字典损坏时的主力。它不查字典而是全文件扫描识别出所有块类型为段头的块从段头块里读出这个段的区映射。在MSSM手动段空间管理下段头里直接挂着一个区列表在ASSM自动段空间管理下段头里挂的是三级位图块的指针需要顺着L1、L2、L3逐级找到实际的数据块。这种模式能恢复出哪些块属于同一个段但段的归属对象名要靠启发式猜测或者人工比对。盲扫模式是最后的兜底。连段头都坏了、区映射彻底丢失的情况下工具会逐块尝试按行解析用一套启发式规则判断这看起来像不像一行数据行目录偏移是否在块范围内、列长度是否累加等于行长度、列类型编码是否落在合法区间、字符数据里可打印字符的比例是否够高。这种模式误判率明显上升输出的数据必须人工抽样核对。实际现场我的做法是先用字典模式跑一遍能出多少出多少对字典模式覆盖不到的表切段头模式对个别特别重要的表如果前两种都失败再单独用盲扫加人工比对。三种模式结合命中率最高。2.3 为什么坏块里的数据还能抠出来这里要破除一个误解报坏块不等于数据没了。Oracle的块校验机制检查的是块头里的校验和与SCN一旦校验不通过就抛ORA-01578。但校验和出错的原因可能是存储层的瞬时写入异常、可能是块头几个字节被覆盖块中后部的行数据区大概率完好。工具的处理方式很直接跳过块头校验直接信任行目录按行目录给的偏移去读行。如果某一行读到一半发现长度越界就跳过这一行继续读下一行。所以同一个块里可能出现前十行读出来了、第十一行丢了的情况。这也是为什么恢复出来的数据往往是完整的表少几行而不是整张表消失。还有一种情况值得提一句如果归档日志还在即使数据块彻底被覆盖理论上也能从redo里挖出这块的变更前镜像。用LogMiner解析归档把对应块的DML操作提取出来反向推导。这条路技术上是通的但操作复杂度极高适合极小范围的关键表补救不适合作为主方案。我一般把它作为最后再试一把的补充手段。2.4 字符集与类型识别最容易翻车的地方这是非常规恢复里翻车率最高的环节比扫描本身还容易出问题。原因是工具是按字节读的它不知道你原来是什么字符集、不知道这一列到底是VARCHAR2还是RAW、不知道NUMBER的二进制编码规则。参数配错出来的就是一堆问号和乱码。字符集方面如果源库是ZHS16GBK一个汉字占两个字节工具如果按AL32UTF8去解释就会把两个字节当成一个非法UTF8序列输出问号。反之源库是AL32UTF8工具按单字节读中文就会变成三个独立的乱码字符。所以源库字符集必须提前确认查不到就用老备份的控制文件、老导出文件的头信息、或者应用连接串里的配置去推断。类型识别方面几个高频类型必须记牢。NUMBER类型不是简单的整数存储它用的是十进制压缩格式第一个字节存符号和指数后面每个字节存两位十进制数字。比如数值123.45在块里是一串特定字节工具如果按字符串读就是乱码。DATE类型固定占7个字节分别是世纪、年、月、日、时、分、秒每个值都加了100的偏移量所以读出来要先减100。ROWID在10g以后是扩展格式10个字节的base64编码需要专门的解码函数。CLOB最麻烦表里那一行只存了一个大约20字节的LOB定位器真正的文本在独立的LOB段里必须单独定位LOB段再扫一遍。提示恢复前先找一张结构简单、数据量小、包含中文和数值的表做试跑。跑通了再上大表能省掉大量返工。3. 动手前的战场准备我见过太多人一拿到工具就往生产机上怼结果工具把文件锁住了、把最后的现场破坏了。非常规恢复的第一步永远不是敲命令而是保护现场和搭环境。这一章讲的全是看起来浪费时间、实际上救命的事。3.1 现场保护第一步永远是做镜像无论工具有没有只读模式都不要在原始故障盘上直接跑。原因有三工具在解析异常时可能触发意外的文件操作原始盘的坏块可能进一步扩散每多读一次就多一分风险你需要保留原始现场以便第一次解析失败后能换参数重来。镜像的做法看条件。有存储快照的直接打快照最快也最安全。没有快照的用dd做文件级镜像dd if/dev/sdb1 of/data/img/data01.img bs1M convnoerror,sync statusprogress这里两个参数的含义必须搞清楚。noerror表示遇到读错误不中断继续往下读sync表示读失败的块用零填充保证输出文件的偏移和源文件严格对齐。如果没有sync坏块被跳过后整个文件的偏移就错位了后面所有块号全对不上工具解析出来全是垃圾数据。还有一个细节Oracle数据文件在操作系统层面有一个块偏移第0个块是OS文件头某些平台有Oracle自己的文件头kfile在第1个块。做镜像后如果按原始文件解析工具一般会自动处理这个偏移但如果你是自己写脚本按块号切必须把这一块算进去否则差一个块全盘皆错。镜像完成后先算一遍校验值并记录文件大小后续每次解析失败重来都从镜像读不动原始盘。3.2 环境搭建版本、平台、字符集三对齐工具的运行环境有讲究三个维度必须对齐。版本对齐指的是工具要能识别源库的块格式。Oracle从9i到19c块结构大体稳定但数据字典表的列定义一直在变比如col$的列在不同版本有增减。用低版本工具去解析高版本库的字典会出现列数对不上的报错。所以尽量找和目标库同大版本的工具10.2的库用支持10.2的工具19c的库用支持19c的工具。平台对齐指的是字节序。x86是little-endian某些小型机平台是big-endian。跨平台解析时所有多字节整数都要做字节序转换否则读出来的文件号、块号、列长度全是天文数字。工具如果支持跨平台会有明确的参数开关不支持的话就得先在同平台机器上做转换这一步很折腾能避开就避开。字符集对齐前面讲过了这里补一句不光是源库字符集要在工具里设对目标导入库的字符集也要考虑。源库GBK、目标库UTF8的情况下工具导出的应该是源库原始字节导入时由数据库做转换如果工具提前转成了UTF8导入到GBK库又会二次转换出错。链路要理清楚别在中间环节重复转码。3.3 必须提前收集的元信息清单非常规恢复的质量很大程度上取决于你手里有多少已知信息。工具不是万能的它需要你告诉它要找什么。下面这些信息能收集到多少收集多少每多一条成功率就高一分。数据库版本、位数、平台、字符集、DB_BLOCK_SIZE这四个参数决定了解析的基础规则原始数据文件清单包括每个文件的绝对路径和对应的文件号文件号错了全盘皆错表空间清单、用户清单、表清单从旧的运维文档、监控系统、应用代码的建表脚本里翻目标表的完整列定义包括列名、类型、长度、精度、是否可为空这是拼装行数据的依据是否存在可用的归档日志和在线日志有的话可以做一致性补偿应用侧的关键业务规则比如哪些字段是主键、哪些金额字段不能为负、时间字段的合理范围其中列定义最容易被忽略也最要命。没有列定义工具只能按块里的列数盲猜猜出来的字段名是COL1、COL2值对不对全靠人工比对。我一般的做法是先从应用的建表SQL里拿到准确DDL实在拿不到就从旧dmp文件的头里提取再不行才靠工具推断。3.4 空间与耗时预估怎么算这一步是为了给业务方一个可承诺的时间窗口别拍脑袋说很快就好。输出空间的估算文本格式带分隔符和字段名体积大约是原始数据量的1.5到3倍具体看字段的空值比例和数值类型的长度。如果是生成INSERT语句体积会膨胀到5到8倍因为每条语句都有大量重复的关键字。所以磁盘预留按原始数据量的5倍准备最稳妥。扫描耗时的估算字典模式下耗时约等于数据文件总大小除以磁盘顺序读速度再乘一个1.2到1.5的解析系数。举个例子200GB的数据文件顺序读150MB/s纯读约23分钟加上解析约35分钟。段头模式和盲扫模式是CPU密集型的每个块都要做类型判断和启发式校验耗时是字典模式的3到5倍同样200GB可能要两到三个小时。导入耗时的估算用SQL*Loader的direct路径速度大约是每小时30到80GB取决于索引数量和约束复杂度。导入前先把目标表的索引和约束全部禁用导完再建能快一倍以上。把这三段加起来再乘一个1.5的缓冲系数就是可以报给业务方的时间。我一般会说最快一天保守两天半留足余量。4. MyDUL实操从扫描到落地MyDUL这类工具的命令形态各家版本有差异但操作逻辑是一致的连接、设参数、扫文件、找对象、卸数据、做校验。下面按这个顺序走一遍命令是示意形态具体以你手上工具的自带帮助为准。4.1 启动与连接工具一般是一个独立的可执行程序不依赖目标库实例所以放在一台独立的机器上跑最安全。启动后第一件事是设置全局参数这一步不能省。mydul MyDUL set charset ZHS16GBK MyDUL set block_size 8192 MyDUL set endian little MyDUL set output_dir /data/recover/out MyDUL set feedback 1000charset是源库字符集设错了中文全乱。block_size必须和源库的DB_BLOCK_SIZE一致8KB的库按16KB解析每个块都会错位。endian是字节序x86平台填little。feedback控制进度输出频率扫大文件时设1000能看到进度心里有底。连接这个动作在部分工具里是挂载数据文件不是连数据库。所以你会看到类似这样的命令MyDUL scan file /data/img/system01.img fileid 1 MyDUL scan file /data/img/users01.img fileid 4fileid必须填对这个号来自源库的v$datafile或者dba_data_files如果库开不了就从控制文件备份里读或者从文件头kfile里读工具一般能自动识别但人工指定更保险。4.2 扫描模式的选择扫描是整个流程里最耗时的一步模式选对能省几个小时。字典模式是首选命令形态大致是先扫system再扫业务文件。工具扫完system后会建立一张对象和段的映射表之后你查表、看列定义、指定卸载全靠这张表。MyDUL scan dictionary MyDUL show tables like SCOTT% MyDUL desc table SCOTT.EMPshow tables能列出识别到的表desc能列出列定义。如果这两条命令的输出符合预期说明字典模式成功后面就顺了。字典模式失败时切段头模式。工具会逐块扫描数据文件识别段头块类型从段头里读区映射。这种模式下show tables可能只显示段号和块范围没有表名需要你人工对照比如你知道SCOTT.EMP大概有多大扫出来的某个段大小吻合就试着卸出来看看内容对不对。盲扫模式一般用在这张表特别重要别的模式都失败了的场景。命令里通常要指定文件号、起始块号、结束块号工具只在这个范围内做启发式解析。MyDUL unload block file 4 from 1000 to 5000 to /data/recover/out/seg_1000.txt4.3 导出表数据与字段映射找到表之后卸载命令的核心参数是输出格式和字段映射。MyDUL unload table SCOTT.EMP to /data/recover/out/emp.txt MyDUL unload table SCOTT.EMP to /data/recover/out/emp.ctl format sqlldr第一种输出纯文本字段之间用分隔符隔开适合后续用脚本处理。第二种直接生成SQL*Loader的控制文件加数据文件导入最省事。我一般两种都出文本用来人工核对ctl用来正式导入。字段映射是这里的关键。工具从块里读出来的列顺序是建表时的物理顺序和desc看到的逻辑顺序通常一致。但如果表经历过add column、drop column、set unused物理顺序和逻辑顺序可能错位。工具一般会读col$里的COL#字段来对齐但如果字典损坏就得手动指定映射MyDUL map table SCOTT.EMP col1EMPNO col2ENAME col3JOB col4MGR这个映射表必须和对端目标表的列严格对应错一列整张表的数据就全错位了而且这种错误在数据量大的时候极难发现。4.4 LOB、RAW、时间类型的处理普通字段处理起来相对直接几个特殊类型要单独说。CLOB和BLOB必须单独配置。表里的LOB列只存定位器真正的数据在独立的LOB段里。工具需要你告诉它这个表的这个LOB列对应的LOB段在哪里或者让它自动去扫LOB段索引。命令形态大概是MyDUL unload table SCOTT.DOC to /data/recover/out/doc.txt lob DOC.CONTENT如果不指定LOB导出来的LOB列全是空或者一串十六进制定位器白忙一场。而且LOB段往往很大扫描耗时会长很多要提前规划时间。RAW类型工具一般按十六进制输出导入时目标列也是RAW的话SQL*Loader能直接识别。如果目标列改成了VARCHAR2就需要在控制文件里做转换。DATE类型前面讲过是7字节加100偏移工具会自动转换但要注意时区。如果源库用了TIMESTAMP WITH TIME ZONE块里存的是UTC时间加时区偏移工具转换后可能和原值差几个小时。这种字段导入后必须抽样比对尤其是涉及计费、结算的业务表。NUMBER类型的坑最多。工具读出来的数值如果精度超出目标列的精度定义导入会报错。我的处理办法是先在目标库把这类列临时改成VARCHAR2(50)把原始字符串导进去再用SQL在库里做清洗和转换转换失败的行单独捞出来人工处理。这样比在导入阶段批量报错要好处理得多。4.5 数据校验与导入目标库导出完成不等于任务完成校验才是决定这次恢复能不能交差的关键。第一层校验是行数。源库如果能查到历史统计信息比如旧的AWR报告、监控系统的表行数快照拿来比对。查不到就用dba_tab_statistics的旧备份或者干脆用业务侧的报表数字倒推。第二层校验是关键字段的聚合值。金额字段求和、主键的最大最小值、时间字段的最大最小值这三个指标能覆盖绝大多数数据缺失和数据错位的问题。第三层校验是抽样比对。随机抽一百行和应用侧的日志、报表、其他系统的对接记录逐字段对。这一步最有说服力也最容易发现问题。导入用SQL*Loader的direct路径sqlldr useridscott/tiger control/data/recover/out/emp.ctl \ directtrue errors100000 rows10000 \ log/data/recover/out/emp.log bad/data/recover/out/emp.baderrors要给得足够大因为非常规恢复出来的数据必然有脏行比如空值违反非空约束、数值超范围。bad文件一定要保留里面是导入失败的行后续人工清洗的依据。导入前把目标表的索引、触发器、外键约束全部禁用导完再启用速度差别很大。5. AUL实操无字典模式怎么玩AUL这一类工具的强项在字典没了也能干活它的命令体系和MyDUL接近但多了一套字典重建的流程。这一章重点讲它的差异化部分。5.1 AUL的工作模式与命令形态AUL启动后同样要先做全局设置命令形态如下AUL set charset ZHS16GBK AUL set block_size 8192 AUL set file_dir /data/img AUL scan file 1 AUL scan file 2 AUL scan file 3 AUL scan file 4它的工作模式分成有字典和无字典两种。有字典模式下命令和MyDUL基本一致扫system、列对象、卸表。无字典模式下它不依赖system表空间的字典表而是直接扫数据块用一套内置的启发式规则判断块类型、段归属、行结构。这里有个细节值得注意AUL的scan file是按文件号扫的扫完之后它会缓存每个块的类型信息。如果扫完发现某个文件的块类型分布异常比如全是数据块、一个段头都没有说明这个文件的文件头可能损坏块类型识别不准。这时候要手动指定扫描范围避开损坏的文件头区域。5.2 系统表空间损坏时的字典重建这是AUL最有价值的能力。system表空间损坏、控制文件丢失、整个库打不开的情况下它依然能通过扫描system数据文件里的基表数据块把字典重建出来。原理不复杂sys.obj$、sys.tab$、sys.col$、sys.seg$这些基表本身也是普通的堆表数据同样以块的形式存在system数据文件里。只要这些块没被物理清零工具就能按行解析出来。解析出来之后工具会把这些信息加载进自己维护的内存字典之后的所有操作都基于这份内存字典不再需要源库。AUL scan dictionary from file 1 AUL rebuild dict AUL show tables like SCOTT%rebuild dict这一步是核心它会尝试从扫出来的基表数据里重建对象、表、列、段、区的完整映射。如果obj$完整重建出来的表名、列名都准确如果obj$部分损坏重建出来的表名可能是OBJ_10023这种编号需要人工对照应用的建表脚本去还原。我的经验是字典重建的成功率取决于system表空间的损坏程度。如果只是文件头损坏、数据块完好成功率很高。如果损坏范围扩散到seg$和ext$那么哪个段在哪些块这个映射就断了只能靠段头扫描来补准确率会下降。还有一种补救手段如果手上有旧的控制文件备份里面保存了所有数据文件和表空间的对应关系能补上文件号映射这一环。控制文件的备份位置一般在$ORACLE_HOME/dbs下或者由控制文件自动备份放在闪回区。5.3 输出文件与后处理AUL的输出格式和MyDUL类似但有几个后处理的细节要注意。第一是不可见字符。导出的文本里可能夹杂着0x00、0x0D这类控制字符直接导入会报错。用sed清洗一遍sed -i s/\x00//g; s/\r//g /data/recover/out/emp.txt第二是分隔符冲突。如果数据本身包含了你设置的分隔符比如逗号会导致列错位。解决办法是换一个数据里绝对不会出现的分隔符比如|~|这种多字符组合或者干脆用定长格式。第三是空值和空串混淆。Oracle里空串就是NULL但文本导出后很难区分原本是空串和原本是NULL。如果业务对这两者有区分需要在导出时加标记或者导入后在应用层处理。第四是字符集二次转换。工具导出的是源库原始字节导入时如果SQL*Loader的连接字符集设置不对会做二次转换导致乱码。控制文件里要加上CHARACTERSET ZHS16GBK和源库字符集保持一致。LOAD DATA CHARACTERSET ZHS16GBK INFILE emp.txt APPEND INTO TABLE SCOTT.EMP FIELDS TERMINATED BY |~| (EMPNO, ENAME, JOB, MGR, HIREDATE DATE YYYY-MM-DD HH24:MI:SS, SAL, DEPTNO)6. 常见问题速查与排查思路现场遇到问题最缺的是快速定位到是哪一环出错。这一章把高频故障整理成对照表再补几个不会写在官方文档里的经验。6.1 典型故障对照表现象最可能的原因排查动作处理办法导出的中文全是问号源库字符集设置错误查源库的NLS_CHARACTERSET改charset参数重跑不用重扫报不是有效的Oracle数据文件文件头损坏或fileid填错用十六进制看第1号块的开头跳过文件头从第2号块起指定扫描范围行数明显少于预期表有链接行或迁移行检查块里的行标志位启用行片拼接参数跨块合并数值字段导入报无效数字NUMBER精度超出目标列定义看bad文件里的原始值目标列临时改VARCHAR2导入后清洗CLOB列全是空未指定LOB段确认表里是否有LOB列加LOB参数重扫只扫LOB段工具扫描到某个块就卡死该块物理损坏读操作阻塞用dd单独读该块看是否超时在扫描范围里跳过该块单独处理同一张表两次导出结果不一样未提交事务和回滚段数据被读出对比两次的差异行按业务规则去重用主键保留最新一条跨平台扫描全乱字节序不匹配确认源平台和目标平台的endian改endian参数或先做字节序转换这张表建议打印出来贴在手边八成的问题能在里面找到答案。6.2 几个不写在文档里的技巧技巧一先跑小表验证全链路。不要一上来就卸最大的表。找一张几百行的小表从扫描、解列、导出、导入全跑一遍确认字符集、分隔符、日期格式、数值精度全部正确再放大表。这个小表就是你的探针能提前暴露九成参数问题。技巧二拆片跑大表控制内存。几十GB的表一次性卸工具很可能内存溢出。按区或者按块范围拆成多个片每片单独输出一个文件最后合并。拆片的时候注意跨片的行要能正确拼接尤其是LOB数据最好按LOB段的范围拆别按表范围拆。技巧三保留中间产物别急着清理。扫描结果、块类型缓存、每一次导出的文件全部保留。非常规恢复经常需要反复重来第一次参数错了、第二次映射错了、第三次才是对的。中间产物删了重来的成本会翻倍。技巧四用主键做增量校验。大表导入后把源端如果能从旧备份或日志里拿到和目标端的主键做比对用集合运算找出差集。比全表扫要快得多而且定位准确。-- 目标端缺失的主键 SELECT empno FROM old_emp_snapshot MINUS SELECT empno FROM scott.emp;技巧五时间字段单独标记。DATE和TIMESTAMP字段在有链接行、有跨块存储的表里偶尔会读错时间部分。导入后专门跑一遍时间合理性检查找出超出业务合理范围的行比如未来时间、1970年之前的时间单独人工核对。7. 我踩过的坑和给后来人的几句实话这些年做过的非常规恢复成功率和失败率大概各占一半。失败的那一半绝大多数不是因为技术不够而是因为前期沟通和预期管理出了问题。技术上的坑踩一次就记住了沟通上的坑每次都能踩出新花样。第一个坑是承诺了完整恢复。有次一个业务负责人问能不能一行不差我含糊地说了句尽力结果恢复出来少了几百行对方拿着这个说事折腾了很久。后来我的做法是动手前先签一个字面上的东西明确恢复范围是哪几张表、哪些字段、能接受的丢失比例、交付时间。把这些白纸黑字写下来后面所有人都轻松。第二个坑是在原始故障机上跑工具。有个库的文件系统已经出现坏块扩散的迹象我为了省事直接在原盘上跑扫描结果一次扫描失败后第二次读不回原来的块了最后的现场彻底丢了。从那以后无论多急镜像这一步绝不跳过。花两个小时做镜像换回来的是可以反复重试的底气。第三个坑是忽略了事务一致性。一次恢复出来的订单表和订单明细表行数都对但两张表之间存在大量对不上的关联。原因是工具按块读订单表读到的是故障前一刻的状态明细表读到的是稍早一些的状态。这种跨表的不一致靠单表校验完全发现不了。后来我养成了一个习惯恢复完核心表之后一定要跑一遍跨表的关联完整性检查比如订单表里没有对应明细的订单数明细表里没有对应订单的明细数数量超过阈值就要警惕。第四个坑是过度依赖工具忽略了底层知识。早期我遇到报错就翻工具手册手册没写就束手无策。后来逼着自己把块结构、段头格式、类型编码这些东西啃了一遍再遇到报错一眼就能判断是数据真的没了还是参数配错了。这个转变的价值在于你能给业务方一个明确的判断而不是含糊其辞。最后一个体会是时间预期要留足。一次完整的非常规恢复从镜像、环境搭建、扫描、导出、校验到导入100GB的数据量顺利的话两到三天不顺利的话一周都有可能。我现在的做法是接到这类任务的第一个小时不是动手而是拉着业务方和运维方开个短会把时间窗口、恢复范围、交付形式全部对齐然后再开机。这一个小时花得值后面能省掉无数次的反复沟通。至于工具本身AUL也好、MyDUL也好都只是手的延伸。真正决定成败的是你对Oracle数据块的理解深度以及你在压力下能不能按流程一步步走完。我见过拿着最先进的工具、把库彻底做废的人也见过只用最朴素的方法、把数据完整捞出来的老手。差别不在工具在人对数据本身的理解。