FGODU (全称:FGEDU Oracle DUL) 是一个 Oracle 数据库数据恢复与抽取工具,用于在数据库无法正常启动的情况下,不依赖 Oracle 实例,直接从数据文件中读取并提取数据。支持字典模式与非字典模式,支持 ASM/裸设备/UDEV/LVM 等多种存储类型,跨平台支持 Linux 与 Windows。

目录


关于作者

联系方式 信息
作者 风哥
官方网站 http://www.fgedu.net.cn , http://www.itpux.com

如果您在使用过程中遇到问题、需要商业技术支持、或希望参与贡献,欢迎通过上方联系方式与作者取得联系。

1. 程序介绍

1.1 概述

FGODU 是一个离线 Oracle 数据抽取工具(DUL = Data UnLoader),它直接读取 Oracle 数据文件(.dbf)的二进制内容,解析数据块格式,提取表结构和行数据,而不需要启动 Oracle 数据库实例

这意味着即使数据库因以下原因无法启动,FGODU 仍然可以恢复数据:

  • 控制文件损坏或丢失
  • SYSTEM 表空间损坏
  • 参数文件(spfile/pfile)损坏
  • 联机日志文件丢失
  • 数据库软件损坏
  • 操作系统无法启动 Oracle 进程
  • 服务器硬件报废但磁盘仍可读
  • 误删库、错删表后没有可用备份

FGODU 的核心设计目标就是 “成为 DBA 的最后一根救命稻草”:当备份失效、归档缺失、数据库完全无法 MOUNT 时,只要还有数据文件(或 ASM 磁盘)可读,FGODU 就能直接从磁盘层面把表数据读出来,恢复为 Oracle 原生兼容的 DMP / SQL*Loader / SQL 等格式,导入到任何 Oracle 目标库中。FGODU 具有以下独特优势:

  1. 完全开源,白盒可控:源代码完全公开,无任何隐藏逻辑与后门,政企客户可自行审计安全
  2. 部署极简,单文件:一个 fgodu 二进制即可运行,不依赖 Oracle 客户端,不安装 JRE/.NET/任何库
  3. 跨平台 + 低 GLIBC 依赖:Linux 版仅依赖 GLIBC_2.2.5 / 2.3,可直接拷贝到 RHEL 5~10、国产麒麟/UOS/欧拉 等任意系统
  4. 自动双格式 DMP 输出:根据目标 Oracle 版本(9i → 26ai)自动切换传统 exp/imp 与 Data Pump expdp/impdp 格式
  5. 智能坏块 + 空行过滤:自动跳过磁盘坏块与 DELETE 残留的无效行,内置主键去重,输出干净可直接入库

1.2 与 Oracle 原生工具的区别

特性 FGODU exp/expdp RMAN
需要 Oracle 实例运行
需要 Oracle 软件安装
数据库无法启动时可用 部分
SYSTEM 损坏时可用 是(非字典模式)
恢复已删除数据
跨平台部署 是(单二进制)
读取 ASM 磁盘 通过实例

1.3 适用场景

  1. 数据库无法启动:控制文件、SYSTEM 表空间、参数文件损坏
  2. 误删除数据恢复:DELETE/DROP/TRUNCATE 后的数据恢复
  3. 数据迁移:将数据从损坏的数据库迁移到新环境
  4. 数据审计:从数据文件中提取历史数据
  5. 坏块处理:跳过坏块,最大程度恢复可用数据
  6. ** ASM 磁盘恢复**:ASM 磁盘组无法 mount 时的数据恢复

2. 功能特性

2.1 核心功能

FGODU 的核心功能围绕 “在 Oracle 实例无法运行的情况下,直接从磁盘把数据捞回来” 这一本质目标展开,每一项功能都是作者在数百次真实故障恢复场景中反复打磨出来的,而非纸面设计:

功能 说明
数据文件直读 直接以只读方式打开 Oracle 数据文件,绕过 Oracle 实例。即使数据库 MOUNT 失败、控制文件损坏,只要 .dbf 文件本身可读,FGODU 就能从文件头、数据块中提取信息。全程使用 O_RDONLY 打开,绝对不会对原始数据文件造成二次写入。
字典模式 利用 Oracle 数据字典(SYSTEM 表空间中的 tab 、 c o l 、col col、seg 、 u s e r 、user user、ts 、 u e t 、uet uet 等核心表)自动发现全部用户表、列结构和区映射。这是最推荐的模式,只要 SYSTEM 完好,FGODU 能自动把数据库里所有 schema、所有表都枚举出来,省去人工定义的繁琐。
非字典模式 SYSTEM 表空间严重损坏、无法引导字典时的 “兜底” 模式。用户通过 define table 语句手动定义表结构和列类型,FGODU 根据已知的 dataobj# 或直接暴力扫描,把匹配的行数据从磁盘上挖出来。即使 SYSTEM 全坏,也能保住核心业务表。
** ASM 支持** 直接读取 ASM 磁盘组,支持 oracleasm、udev 规则命名、LVM 逻辑卷、raw 裸设备、EMC PowerPath 多路径等多种在生产环境中常见的存储方式。对存储透明,DBA 无需先手工把 ASM 拷贝成文件系统文件再恢复。
块级解析 完整实现 Oracle 数据块格式解析,包括缓存头(cache header)、ITL 事务槽、行目录、行头、列偏移表、NULL 列压缩等;同时提供块 checksum 校验、损坏检测、跨块行链接(chained rows / migrated rows)的拼接处理,确保最大限度捞回每一行。
数据类型解码 支持 NUMBER、VARCHAR2、CHAR、DATE、TIMESTAMP、RAW、LOB、ROWID、UROWID、LONG、BFILE、INTERVAL 等所有 Oracle 常用与高基数数据类型的内部二进制解码,并按 Oracle 官方格式正确输出。

2.2 高级功能

在核心抽取能力之外,FGODU 还提供了面向 “疑难杂症” 的高级恢复功能,这些功能往往是商业 DUL 才具备的能力:

功能 说明
删除数据恢复 支持 DELETE、DROP、TRUNCATE 三种常见误操作后的数据恢复。DELETE 场景利用已删除行在行目录中仍保留的 flag=0x20 标记抓取;TRUNCATE 场景通过扫描段原高水位线下的空余区间恢复;DROP 场景通过匹配 obj#/dataobj# 定位回收站与残留段。
块大小自动检测 自动识别 Oracle 所有支持的块大小 2K/4K/8K/16K/32K/64K/128K,并根据数据文件头 (V1/V2/V3 header) 智能识别。对于头损坏无法识别的极端情况,也支持通过 set db_block_size 手动指定。
多输出格式 SQL INSERT、CSV、TXT、DMP(exp/imp 与 expdp/impdp 双格式)、SQL*Loader 共 5 种主流输出格式。从可人工审核的文本到 Oracle 原生二进制,从 ETL 工具友好到 100% 批量导入,覆盖几乎所有下游数据再利用场景。
跨平台兼容 Linux (RHEL 5~10、CentOS、OEL、Ubuntu、SUSE、国产系统) 与 Windows 原生双版本,32 位 / 64 位架构均可运行。Linux 便携版在构建时对 GLIBC 版本做符号版本降级,确保一份二进制跑遍新旧系统。
** LOB 提取** 支持 BLOB/CLOB/NCLOB 三类大对象,包括 inline LOB(直接存储在行中)与 out-of-row LOB(存储在 LOB 段),以及带 CHUNK / PCTVERSION / SECUREFILE / BASICFILE 的不同格式。
UNDO 回滚 从 UNDO 表空间中读取前镜像(Before Image),把被 UPDATE 覆盖的数据、被 DELETE 删除的数据重新找回,特别适合 DELETE 未 commit 但 DBWR 已写盘、或数据库异常崩溃后事务未清理的场景。
压缩块处理 支持 Oracle 压缩特性:OLTP 压缩(Advanced Row Compression)、Query 压缩(Hybrid Columnar Compression Warehouse)、Archive 压缩,对压缩块先解压再抽取。
蛮力扫描 没有字典信息、没有 SYSTEM 表空间、甚至不知道块归属时的终极手段。对所有数据文件逐块扫描,根据行内列分布特征去匹配目标表结构,把能匹配上的行全部吐出来。
多线程 内置线程池支持并行扫描与并行写出。默认用 CPU 核数/2 作为线程数,也可 set parallel auto / set parallel 8 手动调整。大表 / 大文件场景下吞吐相比单线程提升 3~6 倍。
RAC 支持 支持 Oracle RAC 集群环境。即使 RAC 所有节点同时宕机、CRS/GI 无法启动,只要从共享存储中取出各节点的 datafile,FGODU 就能合并读取并恢复完整数据。
坏块跳过 内置 checksum 校验 + 尾部标记校验,一旦发现磁盘介质损坏的块(corrupt block),自动标记并跳过,绝不因一块坏数据导致整张表恢复失败;同时统计坏块位置、数量,供 DBA 后续分析。
进度显示 实时显示进度条、已处理块数 / 总块数、处理速度(MB/s、rows/s)、剩余时间预估。让运维工程师在面对几十 GB / 几 TB 的数据恢复时心里有数,而不是 “黑屏等半天”。
字符集自适应 自动从 bootstrap$ / props$ 中检测出源数据库字符集 (NLS_CHARACTERSET) 与国家字符集 (NLS_NCHAR_CHARACTERSET),在输出 DMP / SQL 时保持原始编码,避免乱码。
主键去重 基于主键列值做去重。同一数据块可能保留 UPDATE 前后的多个版本,FGODU 在输出阶段会按主键分组,保留最新或完整的一条,避免导入后出现重复行。
空行过滤 DELETE / UPDATE 操作后,原有的行空间不会被物理清零,只是行头 flag 标记 delete+lock。FGODU 内置 is_empty_row 与 flag 判断,把这种"行壳子"过滤掉,输出文件里没有垃圾数据。

2.3 输出格式详解

FGODU 支持多种输出格式,每种格式适用于不同的导入场景:

2.3.1 SQL INSERT 格式 (set output_format sql)

生成标准 INSERT 语句,可直接在目标数据库执行:

INSERT INTO SCOTT.EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
VALUES (7369, 'SMITH', 'CLERK', 7902, '1980-12-17', 800, NULL, 20);

适用场景:小数据量恢复、需要人工审核数据内容

2.3.2 CSV 格式 (set output_format csv)

逗号分隔值格式,通用兼容性:

EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO
7369,SMITH,CLERK,7902,1980-12-17,800,,20

适用场景:导入到 Excel、其他数据库、ETL 工具

2.3.3 TXT 格式 (set output_format txt)

制表符分隔格式,适合人工查看:

EMPNO   ENAME   JOB     MGR     HIREDATE        SAL     COMM    DEPTNO
7369    SMITH   CLERK   7902    1980-12-17      800             20
2.3.4 SQL*Loader 格式 (set output_format sqlldr)

生成 SQL*Loader 兼容的 CTL + 数据文件:

LOAD DATA
INFILE *
INTO TABLE SCOTT.EMP
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
TRAILING NULLCOLS
(
  EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO
)
BEGINDATA
7369,"SMITH","CLERK",7902,"1980-12-17",800,,20

适用场景:大规模数据导入,Oracle 9i~26ai 全版本兼容

2.3.5 DMP 二进制格式 (set output_format dmp)

生成 Oracle 原生二进制 DMP 文件,根据目标 Oracle 版本自动选择格式

Oracle 版本 DMP 格式 导入工具 命令
Oracle 9i exp/imp 二进制格式 imp imp userid=user/pass@db file=X.dmp tables=T ignore=y
Oracle 10g+ Data Pump expdp/impdp 格式 impdp impdp userid=user/pass@db directory=DP_DIR dumpfile=X.dmp tables=T

版本切换命令

fgodu> .set oracle_version 9i    # 生成 exp/imp 格式(用 imp 导入)
fgodu> .set oracle_version 10g   # 生成 expdp/impdp 格式(用 impdp 导入)
fgodu> .set oracle_version 11g   # 生成 11g 兼容的 expdp/impdp 格式

DMP 文件结构(exp/imp 格式)

[文件头记录] version + charset_name + charset_id + ncharset + db_name
[DDL 记录]   CREATE TABLE 文本
[数据记录]   INSERT 文本 + 类型规范 + 二进制行 + 0xFFFF
[EOF 标记]   0x00000000

DMP 文件结构(Data Pump 格式)

[文件头块]   [4字节大小][4字节类型=1][version+charset 信息]
[XML 元数据块] [4字节大小][4字节类型=3][XML DDL]
[数据块]     [4字节大小][4字节类型=4][二进制行数据]
[EOF 块]     [4字节大小=0][4字节类型=5]

重要提示

  • Oracle Data Pump 是闭源专有格式
  • 如果 impdp 导入报 ORA-39002/ORA-39070 错误,请改用以下方案之一:
    1. 使用 imp 工具(Oracle 10g 仍支持):.set oracle_version 9i 重新生成
    2. 使用 SQL*Loader 格式(100% 标准兼容):set output_format sqlldr

3. 支持环境

3.1 支持的 Oracle 版本

从 9i 一直到最新的 26ai,FGODU 兼容 Oracle 所有主流版本。不同版本在块格式、压缩算法、数据字典结构上存在差异,FGODU 均已在内核层面适配;尤其值得注意的是 DMP 输出格式会根据目标版本自动切换——9i 输出传统 exp/imp 格式,10g 及以上自动输出 Data Pump expdp/impdp 格式:

版本 发布年份 支持状态 DMP 导出格式 导入工具 核心备注
Oracle 9i R2 (9.2.0.x) 2002 完全支持 exp/imp 二进制 imp 传统格式,所有 Oracle 版本的 imp 工具均可导入
Oracle 10g R2 (10.2.0.x) 2005 完全支持 Data Pump expdp/impdp impdp 首次引入 Data Pump
Oracle 11g R2 (11.2.0.x) 2009 完全支持 Data Pump expdp/impdp impdp 目前存量最大的版本
Oracle 12c R1/R2 (12.1/12.2) 2013/2016 完全支持 Data Pump expdp/impdp impdp 多租户 CDB/PDB,12cR2 引入 Identity 列
Oracle 18c (18.3/18.4) 2018 完全支持 Data Pump expdp/impdp impdp 年度发布模型首版
Oracle 19c (19.3+) 2019 完全支持 Data Pump expdp/impdp impdp Oracle 长期支持版,当前企业主流
Oracle 21c (21.3) 2021 完全支持 Data Pump expdp/impdp impdp 创新发行版
Oracle 23c (23.2+) 2023 完全支持 Data Pump expdp/impdp impdp 免费开发者版 + JSON 关系融合
Oracle 26ai 2026 完全支持 Data Pump expdp/impdp impdp 带 AI 向量检索的最新版

3.2 支持的操作系统

FGODU 使用标准 C11 编写,依赖最少的系统库(libc + pthread),所以能在非常广泛的 Linux 发行版和 Windows 上运行。Linux 便携版只依赖 GLIBC_2.2.5 / GLIBC_2.3 / GLIBC_2.3.2,这意味着哪怕是十多年前的 RHEL 5,也能直接拷贝二进制运行,无需重新编译:

家族 发行版 版本 架构 备注
RHEL / OEL Red Hat Enterprise Linux 5 / 6 / 7 / 8 / 9 / 10 x86_64 / aarch64 完全支持
Oracle Linux 5 / 6 / 7 / 8 / 9 / 10 x86_64 / aarch64 完全支持,含 UEK 内核
CentOS / Rocky / Alma CentOS 5 / 6 / 7 / 8 / 9 x86_64 完全支持
Rocky Linux 8 / 9 x86_64 / aarch64 完全支持
AlmaLinux 8 / 9 x86_64 / aarch64 完全支持
国产系统 银河麒麟 Kylin V10/V11 V10 SP1/SP2 / V11 x86_64 / 飞腾 / 鲲鹏 完全支持,已在多个政企项目实测
统信 UOS 服务器版 20 / 23 x86_64 / 鲲鹏 / 飞腾 完全支持
欧拉 openEuler 20.03 LTS SP3 / 22.03 LTS x86_64 / aarch64 / riscv64 完全支持
龙蜥 Anolis OS 7 / 8 / 9 x86_64 / aarch64 完全支持
Debian / Ubuntu Ubuntu Server LTS 16.04 / 18.04 / 20.04 / 22.04 / 24.04 x86_64 / aarch64 完全支持
Debian 8 (Jessie) ~ 12 (Bookworm) x86_64 / aarch64 完全支持
SUSE SUSE Linux Enterprise Server 11 SP4 / 12 SP5 / 15 SP5+ x86_64 / aarch64 完全支持
Windows 桌面 Windows XP / Vista / 7 / 8 / 10 / 11 x86 / x86_64 完全支持
Windows Server Windows Server 2003 / 2008 / 2012 / 2016 / 2019 / 2022 x86 / x86_64 完全支持

3.3 支持的存储类型

对于数据文件在磁盘上如何呈现给 FGODU,工具对所有常见的存储形态都做了适配。对用户而言,多数情况下直接写路径即可,FGODU 会根据路径模式自动判断存储类型:

存储类型 示例路径 自动识别模式 说明
文件系统(FILE) /oradata/fgedudb/system01.dbf 最常见;标准文件 IO,预读 + mmap 加速
ASM (oracleasm) /dev/oracleasm/disks/DATA_0001 Oracle ASMLib 驱动的字符设备接口
UDEV 命名的 ASM /dev/asm-disk1/dev/disk/by-id/scsi-3xxx udev 规则持久化命名,替代 oracleasm
LVM 逻辑卷 /dev/mapper/vg_data-lv_oradata Linux LVM2 逻辑卷,可跨过多个 PV
Linux 裸设备 /dev/raw/raw1 老系统使用 raw 设备绑定的裸盘
通用块设备 /dev/sda1/dev/vdb KVM / Xen / 物理服务器的标准块设备
EMC PowerPath /dev/emcpowera1 EMC 存储的多路径驱动
其他多路径(device-mapper) /dev/mapper/mpath0p1 否,手动指定 lvmraw DM-Multipath 生成的聚合设备
AIX LVM / HP-UX VG /dev/rvg01/rlv_oradata 否,手动指定 raw 小型机场景,使用字符设备模式读取

3.4 硬件与资源要求

FGODU 本身是个非常轻量的二进制(Linux 版仅 ~250KB),但面对几十 GB / 几 TB 的数据文件恢复时,对硬件资源有一定要求。以下给出一个经验参考值:

资源 最低配置 推荐配置(1TB 以内) 推荐配置(1TB~10TB)
CPU 2 核 8 核 16 核 以上
内存 1 GB 8 GB 32 GB
可用磁盘空间 数据文件总大小的 1.5×(给输出留空间) 数据文件总大小的 2× 数据文件总大小的 2×(建议 SSD)
磁盘 IO 普通 SATA SSD 或 RAID10 高速 SSD / NVMe
运行用户权限 对数据文件的读权限 root 或 oracle 用户 root 用户(避免设备权限问题)
网络 不需要(离线工具) 1 Gbps(若要远程拷贝 DMP) 10 Gbps(远程拷贝大文件)

内存说明

  • 字典模式下,FGODU 会把数据字典(OBJ$ / COL$ / SEG$ / UET$)全部缓存在内存中,通常占用 50MB ~ 500MB 就足够了
  • 但如果启用蛮力扫描、UNDO 回滚、LOB 提取,会额外使用 IO 缓存
  • 建议给 FGODU 留 4GB 以上可用内存,避免因 OS 页缓存不足导致读取变慢

磁盘说明

  • 原始数据文件只读打开,不占用写入 IO
  • 但 DMP / SQL 输出会持续写入,所以输出目录建议放在 SSD 上
  • 如果数据文件 + 输出目录在同一块磁盘,会产生随机读 + 顺序写的争抢,IOPS 会低不少

3.5 字符集支持

FGODU 会在 bootstrap 阶段自动从 sys.props$ 读取源数据库字符集 (NLS_CHARACTERSET) 与国家字符集 (NLS_NCHAR_CHARACTERSET),全程保持原始编码写入输出文件,避免乱码。导入时只需设置 NLS_LANG 与源端一致即可。下表列出全部已验证的字符集:

Oracle 字符集名 语言/地区 编码 常见场景
AL32UTF8 全球多语言 UTF-8 9i+ 默认推荐,支持所有 Unicode 字符
UTF8 全球多语言 UTF-8 (CESU-8) 8i/9i 遗留 Unicode 字符集
ZHS16GBK 简体中文 GBK / GB2312 国内最常见的中文字符集(老库)
ZHS32GB18030 简体中文 GB18030 国产合规项目指定字符集
ZHT16BIG5 繁体中文 BIG5 台湾 / 香港老项目
ZHT16HKSCS 繁体中文 HKSCS 香港增补字符集
ZHT32EUC 繁体中文 EUC-TW 台湾繁体 EUC 格式
WE8ISO8859P1 西欧 ISO-8859-1 (Latin-1) 欧洲老项目
WE8MSWIN1252 西欧 Windows-1252 西欧 Windows 平台默认
WE8DEC 西欧 DEC-MCS Digital / OpenVMS 遗留
US7ASCII 纯英文 ASCII 7-bit 极简场景
EE8MSWIN1250 中东欧 Windows-1250 东欧 Windows 平台
CL8MSWIN1251 俄语 Windows-1251 俄罗斯 / 独联体
EL8MSWIN1253 希腊语 Windows-1253 希腊
AR8MSWIN1256 阿拉伯语 Windows-1256 中东阿拉伯语
JA16SJIS 日语 Shift-JIS 日本 Windows 项目
JA16EUC 日语 EUC-JP 日本 Unix 项目
JA16UTF16 日语 NCHAR UTF-16BE 日本常用 NCHAR 字符集
KO16KSC5601 韩语 KSC5601 (EUC-KR) 韩国项目
KO16MSWIN949 韩语 Unified Hangul Code (UHC) 韩国 Windows 项目
TH8TISASCII 泰语 TIS-620 泰国项目
VN8MSWIN1258 越南语 Windows-1258 越南项目
WE8EBCDIC37 EBCDIC 大型机 IBM-037 AS/400、z/OS EBCDIC
WE8EBCDIC1047 EBCDIC OpenEdition IBM-1047 z/OS USS 常见

对于上表未列出的字符集,FGODU 会以 “二进制不做转换” 模式透传——也就是说,只要 NLS_LANG 设置正确,导入后依然能正确还原,不会出现"字符集转换错误导致整列 NULL"的情况。

3.6 数据类型支持

下表列出 FGODU 当前版本在解码、输出(SQL / CSV / TXT / sqlldr / DMP 双格式)阶段均完整支持的数据类型清单,以及部分受限支持的类型:

分类 数据类型 支持状态 说明 / 限制
字符串 CHAR(n) ✅ 完全支持 0~2000 字节
VARCHAR2(n) ✅ 完全支持 0~4000 字节(12c 可扩展至 32767)
NCHAR(n) ✅ 完全支持 NLS_NCHAR_CHARACTERSET 字符集
NVARCHAR2(n) ✅ 完全支持 NLS_NCHAR_CHARACTERSET 字符集
LONG ✅ 完全支持 最长支持 2GB,分块读出;输出时转 VARCHAR2 大字段或保留 LONG
数值 NUMBER(p,s) ✅ 完全支持 p: 0~38, s: -84~127,完整保留 Oracle 内部精度(不转浮点)
FLOAT(n) ✅ 完全支持 Oracle 内部仍以 NUMBER 存储,完整读出
BINARY_FLOAT ✅ 完全支持 IEEE-754 单精度
BINARY_DOUBLE ✅ 完全支持 IEEE-754 双精度
日期时间 DATE ✅ 完全支持 世纪/年/月/日/时/分/秒 7 字节
TIMESTAMP(n) ✅ 完全支持 精度 0~9,支持小数秒
TIMESTAMP(n) WITH TIME ZONE ✅ 完全支持 含 TZH:TZM 时区偏移
TIMESTAMP(n) WITH LOCAL TIME ZONE ✅ 完全支持 以 UTC 存储,按源数据库时区还原
INTERVAL YEAR(n) TO MONTH ✅ 完全支持 年-月 间隔
INTERVAL DAY(n) TO SECOND(m) ✅ 完全支持 日-秒 间隔
二进制 RAW(n) ✅ 完全支持 0~2000 字节,输出为十六进制字符串
LONG RAW ✅ 完全支持 最长 2GB,十六进制输出
LOB BLOB ✅ 完全支持 inline(<4000 字节行内)+ out-of-row(LOB 段独立块)均支持
CLOB ✅ 完全支持 单字节 / 多字节字符集均可,保留字符编码
NCLOB ✅ 完全支持 使用 NLS_NCHAR_CHARACTERSET 解码
BFILE ⚠️ 受限支持 仅能读取 BFILE 指针(directory alias + filename),文件本身不在数据文件中
行/地址 ROWID ✅ 完全支持 扩展 ROWID:OOOOOO.FFF.BBBBBB.RRR
UROWID ✅ 完全支持 IOT 表逻辑 ROWID
其他 XMLType (object-relational) ✅ 完全支持 底层 CLOB/XMLType 存储,按 CLOB 读出
JSON (12c+ OSON 格式) ⚠️ 受限支持 以底层 CLOB 文本方式读取,保留 JSON 原文
SDO_GEOMETRY (Oracle Spatial) ⚠️ 受限支持 对象类型按 ADT 结构解析,可展开列数据
不支持 ANYDATA / ANYTYPE ❌ 不支持 通用封装类型
用户自定义 ADT / VARRAY / Nested Table ❌ 不支持 复杂嵌套对象(普通对象部分场景可展开)

4. FGODU程序使用

4.1 安装构建

4.1.1 环境要求

Linux:

  • GCC 4.8+ 或兼容编译器
  • Make
  • glibc-static(可选,用于完全静态构建)

Windows:

  • Visual Studio 2015+ (MSVC) 或 MinGW
  • NMAKE(随 Visual Studio 附带)
4.1.2 构建命令

Linux 构建:

# 获取源码
git clone https://github.com/fgedu/fgodu.git
cd fgodu

# 默认构建(便携版,推荐)
make

# 构建 Debug 版本 (调试用)
make debug

# 构建完全静态版本(需要 glibc-static)
make static

# 安装到系统
make install

Windows 构建:

# 方式1:使用 MSVC (Visual Studio 命令行)
nmake -f Makefile.win

# 方式2:使用 MinGW (Windows 上)
mingw32-make -f Makefile.win CC=gcc

# 方式3:从 Linux 交叉编译 Windows 版本 (需要 mingw32-gcc)
make mingw
4.1.3 构建选项
命令 说明 输出文件
make 默认 - 便携版,静态链接 libgcc/libstdc++ build/portable/fgodu
make debug 调试构建,含符号 build/debug/fgodu
make portable 便携版,仅依赖 GLIBC_2.2.5/2.3 build/portable/fgodu
make release 优化构建,动态链接 build/release/fgodu
make static 完全静态链接(需要 glibc-static) build/static/fgodu
make mingw Windows 交叉编译(需要 mingw32-gcc) build/mingw/fgodu.exe
nmake -f Makefile.win Windows 原生编译(MSVC) build_win/fgodu.exe
4.1.4 跨平台部署

Linux 便携版部署(推荐):

make  # 构建便携版

# 在目标系统上直接运行
scp build/portable/fgodu user@target:/tmp/
ssh user@target "/tmp/fgodu -v"

便携版特性:

  • 静态链接 libgcc 和 libstdc++
  • 仅依赖 GLIBC_2.2.5、GLIBC_2.3 和 GLIBC_2.3.2
  • 可直接复制到任何 Linux 系统运行
  • 支持 RHEL5/6/7/8/9/10、Oracle Linux、Ubuntu、SUSE、国产操作系统等

Windows 版部署:

# 方式1: 从 Linux 交叉编译
make mingw
# 将 build/mingw/fgodu.exe 复制到 Windows 服务器

# 方式2: 在 Windows 上编译
nmake -f Makefile.win
# 将 build_win/fgodu.exe 复制到目标服务器

# 在 Windows 上运行
C:\fgodu\fgodu.exe

Windows 版特性:

  • 支持 XP/Vista/7/8/10/11、Server 2003-2022
  • 功能与 Linux 版完全一致
  • 仅支持文件系统存储模式(不支持 ASM)
  • 路径使用反斜杠 \ 分隔

4.2 启动方式

4.2.1 交互式模式
# 直接启动
./fgodu

# 启动后显示:
FGODU - FGEDU Oracle DUL v1.0.0
Running on: Oracle Linux 9 (x86_64 (AMD64))
Kernel: 5.15.0-200.131.27.el9uek.x86_64
========================================

FGODU - FGEDU Oracle DUL v1.0.0
Type 'help' for available commands.

fgodu>
4.2.2 命令行模式
# 使用配置文件
./fgodu -c control.cfg

# 执行单条命令
./fgodu -e "datafile 1 /oradata/fgedudb/system01.dbf"

# 全功能模式(需要密码)
./fgodu -all
# 然后输入密码:

# 显示版本信息
./fgodu -v

# 显示帮助
./fgodu -h

全局选项:

选项 说明
-c <config> 指定配置文件
-e <command> 执行单条命令并退出
-all 启用全功能模式(需输入密码)
-v 显示版本信息
-h 显示帮助

全功能模式说明:

默认模式下,FGODU 限制每表最多恢复 100 行数据。使用 ./fgodu -all 并输入密码 `` 可解除限制:

$ ./fgodu -all
Enter password for full access: 
Full access mode enabled.
FGODU - FGEDU Oracle DUL v1.0.0
fgodu>

4.3 命令详解

快捷别名
别名 对应命令
add / a datafile
boot / b bootstrap
ls show
desc describe
get unload
find scan
def define
rcv recover
chk check
cfg set
exit / q quit
? help
4.3.1 set - 设置参数
# 设置数据库块大小(默认自动检测)
fgodu> set db_block_size 8192

# 设置数据库名称
fgodu> set db_name ORCL

# 设置输出格式(sql/csv/txt/dmp/sqlldr)
fgodu> set output_format dmp

# 设置输出目录
fgodu> set output_dir /tmp/recovery

# 设置并行线程数
fgodu> set parallel 8

# 设置是否跳过坏块
fgodu> set skip_bad_blocks yes

# 设置目标 Oracle 版本(影响 DMP 格式)
fgodu> .set oracle_version 11g
4.3.2 datafile - 添加数据文件
# 添加文件系统数据文件(自动检测类型)
fgodu> datafile 1 /oradata/fgedudb/system01.dbf

# 添加裸设备
fgodu> datafile 2 /dev/raw/raw1 raw

# 添加 ASM 磁盘(oracleasm)
fgodu> datafile 3 /dev/oracleasm/disks/DATA01 asm

# 添加 UDEV 设备
fgodu> datafile 4 /dev/disk/by-id/scsi-xxx udev

# 添加 LVM 设备
fgodu> datafile 5 /dev/mapper/vg_data-lv_oradata lvm

存储类型自动检测规则:

路径模式 自动识别类型
/dev/oracleasm/ ASM
/dev/asm- ASM
/dev/mapper/ RAW
/dev/raw/ RAW
/dev/emcpower RAW
/dev/disk/ RAW
/dev/sd* RAW
其他文件路径 FILE
4.3.3 bootstrap - 引导数据字典
# 引导数据字典(需要先添加 SYSTEM 表空间数据文件)
fgodu> bootstrap

# 引导所有字典信息(包括 COL$ 列元数据)
fgodu> boot all

引导流程:

Step 1: 读取 SYSTEM 表空间文件头 (#0块)
          ↓ 定位 bootstrap$ 地址
Step 2: 读取 bootstrap$ (obj#=17)
          ↓ 获取核心字典表的物理位置
Step 3: 加载核心字典表 (obj$, tab$, col$, user$, ts$, seg$, uet$, fet$)
          ↓
Step 4: 构建内存字典缓存
          ↓
Step 5: 根据 tab$+col$ 获取用户表结构
          ↓
Step 6: 根据 seg$+uet$ 获取表的物理存储位置
          ↓
Step 7: 逐个区、逐个块提取数据
4.3.4 show - 显示信息
# 显示数据库信息
fgodu> show db

# 显示表空间列表
fgodu> show tablespaces
fgodu> show ts

# 显示用户列表
fgodu> show users

# 显示指定用户的表
fgodu> show tables SCOTT

# 显示所有表
fgodu> show tables

# 显示列信息
fgodu> show columns
4.3.5 describe - 查看表结构
fgodu> describe SCOTT.EMP
fgodu> desc SCOTT.EMP
4.3.6 unload - 卸载数据
# 卸载指定表(字典模式)
fgodu> unload table SCOTT.EMP

# 卸载整个用户的数据(生成批量导入脚本 import_all.sql)
fgodu> unload user SCOTT

# 卸载整个表空间的数据
fgodu> unload tablespace USERS

# 暴力卸载(离线模式,无字典)
fgodu> unload brute 2 EMP 7369

卸载完成后输出示例:

[1/1] SCOTT.EMP  obj#=7369 dataobj#=7369 columns=8  (found 14 rows)
exp/imp DMP file created:
  Dump:     /tmp/recovery/SCOTT_EMP.dmp
  Rows:     14
  Import:   imp userid=... file=/tmp/recovery/SCOTT_EMP.dmp tables=EMP
4.3.7 recover - 恢复删除数据
# 恢复 DELETE 删除的数据(需要字典模式)
fgodu> recover deleted SCOTT.EMP

# 恢复 TRUNCATE 删除的数据
fgodu> recover truncated 0 3 7369 EMP
# 自动检测模式:
fgodu> recover truncated EMP

# 恢复 DROP 删除的数据
fgodu> recover dropped 0 EMP
# 自动检测模式:
fgodu> recover dropped EMP

# 扫描所有数据文件恢复所有可恢复数据
fgodu> recover all

# 扫描指定块范围恢复数据
fgodu> recover range 2 100 200 EMP
4.3.8 其他命令
# scan - 扫描数据
fgodu> scan extents
fgodu> scan table SCOTT.EMP
fgodu> scan all 2 EMP

# check - 检查数据文件
fgodu> check datafile 1

# dump - 转储块内容
fgodu> dump header 1 1
fgodu> dump block 1 100

# asm - ASM 管理
fgodu> asm discover
fgodu> asm mount DATA
fgodu> asm ls

# define - 非字典模式定义表
fgodu> define table EMP (EMPNO NUMBER(4), ENAME VARCHAR2(10))

# query - 查询对象信息
fgodu> query SCOTT.EMP
fgodu> query all
fgodu> query tablespace

# quit - 退出
fgodu> quit

4.4 配置文件

使用 control.cfg 配置文件启动:

# ============================================================
# FGODU 配置文件示例 (control.cfg)
# 使用方法: ./fgodu -c control.cfg
# ============================================================

[database]
db_name = ORCL
db_block_size = 8192

[output]
output_format = dmp
output_dir = /tmp/fgodu_recovery
include_columns = yes
sql_escape = yes
charset =

[performance]
num_threads = 4
progress_interval = 1000
use_compression = no

[recovery]
recover_deleted = yes
skip_bad_blocks = yes
undo_rollback = no
handle_compressed = yes
extract_lob = yes
handle_chained_rows = yes
brute_force = no

[asm]
scan_path =
au_size = 1048576

[logging]
log_level = info
log_file =
show_stats = yes

[datafiles]
datafile.1.path = /oradata/fgedudb/system01.dbf
datafile.1.type = file
datafile.2.path = /oradata/fgedudb/users01.dbf
datafile.2.type = file

4.5 设置参数详解

数据库参数
参数 值范围 默认值 说明
db_name 字符串 自动检测 数据库名称
db_block_size 2048~131072 自动检测 数据库块大小(字节)
输出参数
参数 值范围 默认值 说明
output_format sql/csv/txt/dmp/sqlldr dmp 输出格式
output_dir 路径 当前目录 输出目录
oracle_version 9i/10g/11g/12c/18c/19c/21c/23c/26ai 11g 目标 Oracle 版本(影响 DMP 格式)
charset 字符集名 自动检测 输出字符集
性能参数
参数 值范围 默认值 说明
parallel auto/1~128 CPU/2 并行线程数
skip_bad_blocks yes/no yes 是否跳过坏块

5. Oracle恢复案例场景与操作过程

案例 1: Oracle数据库无法启动 - 字典模式恢复

场景:数据库因控制文件丢失无法启动,但数据文件完好

操作步骤

# 步骤 1: 创建数据文件列表
cat > files.txt <<EOF
1 /oradata/fgedudb/system01.dbf
2 /oradata/fgedudb/sysaux01.dbf
3 /oradata/fgedudb/undotbs01.dbf
4 /oradata/fgedudb/users01.dbf
EOF

# 步骤 2: 启动 FGODU(自动加载 files.txt)
./fgodu

# 步骤 3: 引导数据字典
fgodu> bootstrap

# 步骤 4: 查看数据库信息
fgodu> show db
fgodu> show tablespaces
fgodu> show users

# 步骤 5: 设置输出参数
fgodu> set output_format dmp
fgodu> set output_dir /tmp/recovery
fgodu> .set oracle_version 11g

# 步骤 6: 提取单表数据
fgodu> unload table HR.EMPLOYEES

# 步骤 7: 按用户批量导出(自动生成 import_all.sql)
fgodu> unload user HR

# 步骤 8: 退出
fgodu> quit

导入到新数据库

# 设置字符集环境变量
export NLS_LANG=AMERICAN_AMERICA.AL32UTF8

# 方法1: 使用 impdp (10g+ DMP 格式)
sqlplus hr/hr@newdb @import_all.sql
# 或单独导入
impdp userid=hr/hr@newdb directory=DATA_PUMP_DIR \
       dumpfile=HR_EMPLOYEES.dmp tables=EMPLOYEES

# 方法2: 使用 imp (9i exp/imp 格式)
# 先设置 .set oracle_version 9i 重新生成
imp userid=hr/hr@newdb file=HR_EMPLOYEES.dmp tables=EMPLOYEES ignore=y

# 方法3: 使用 SQL*Loader (100% 兼容)
sqlldr hr/hr@newdb control=HR_EMPLOYEES.ctl log=import.log

案例 2: Oracle DELETE 误删除数据恢复

场景:用户误执行 DELETE FROM EMP 删除了大量数据,未 COMMIT 但数据已写入 UNDO

操作步骤

./fgodu
fgodu> datafile 1 /oradata/fgedudb/system01.dbf
fgodu> datafile 2 /oradata/fgedudb/undotbs01.dbf
fgodu> datafile 3 /oradata/fgedudb/users01.dbf
fgodu> bootstrap

# 恢复 DELETE 删除的数据
fgodu> recover deleted SCOTT.EMP

输出

Recovering deleted rows from SCOTT.EMP...
Recovered 100 deleted rows.

案例 3: Oracle TRUNCATE 表恢复(自动检测模式)

场景:表被意外 TRUNCATE,不知道表在哪个数据文件

操作步骤

./fgodu
fgodu> datafile 1 /oradata/fgedudb/system01.dbf
fgodu> datafile 2 /oradata/fgedudb/users01.dbf
fgodu> bootstrap

# 自动检测模式 - 扫描所有数据文件
fgodu> recover truncated EMP

输出

Auto-detection mode: searching for truncated table EMP...
Scanning all datafiles for recoverable blocks...
  Scanning datafile 1...
  Scanning datafile 2...
    Found 1000 recoverable rows
Recovered 1000 rows.

案例 4: Oracle SYSTEM 表空间损坏 - 非字典模式

场景:SYSTEM 表空间严重损坏,无法引导字典

操作步骤

./fgodu
fgodu> datafile 1 /oradata/fgedudb/users01.dbf
fgodu> set db_block_size 8192

# 手动定义表结构
fgodu> define table EMP (EMPNO NUMBER(4), ENAME VARCHAR2(10), JOB VARCHAR2(9),
                         MGR NUMBER(4), HIREDATE DATE, SAL NUMBER(7,2),
                         COMM NUMBER(7,2), DEPTNO NUMBER(2))

# 设置数据对象号(如果已知)
fgodu> set dataobj_num 7369

# 蛮力扫描
fgodu> scan all 1 EMP

# 暴力提取
fgodu> unload brute 1 EMP 7369

案例 5: Oracle ASM 磁盘组无法 mount

场景:ASM 磁盘组无法 mount,但磁盘完好

操作步骤

./fgodu
fgodu> asm discover
fgodu> datafile 1 /dev/oracleasm/disks/DATA_0001 asm
fgodu> bootstrap
fgodu> show tables SCOTT
fgodu> unload table SCOTT.EMP

案例 6: Oracle全库数据导出迁移

场景:数据库无法启动,需要迁移整个数据库到新服务器

操作步骤

# 创建导出脚本
cat > full_export.sh <<'EOF'
#!/bin/bash
OUTPUT_DIR="/tmp/fgodu_full_export_$(date +%Y%m%d_%H%M%S)"
mkdir -p $OUTPUT_DIR

./fgodu <<FGODU_EOF
bootstrap
set output_format dmp
set output_dir $OUTPUT_DIR
.set oracle_version 11g

unload user SCOTT
unload user HR
unload user OE
unload user SH

quit
FGODU_EOF
EOF

chmod +x full_export.sh
./full_export.sh

案例 7: Oracle坏块跳过恢复

场景:数据文件存在坏块,需要跳过坏块提取可用数据

操作步骤

./fgodu
fgodu> datafile 1 /oradata/fgedudb/system01.dbf
fgodu> datafile 2 /oradata/fgedudb/users01.dbf
fgodu> bootstrap
fgodu> set skip_bad_blocks yes
fgodu> unload table SCOTT.EMP

# 查看坏块统计
fgodu> check datafile 2

案例 8: Oracle按数据文件导出所有表

场景:知道数据文件路径,需要导出其中所有表

字典模式(推荐)

./fgodu
fgodu> datafile 1 /oradata/fgedudb/system01.dbf
fgodu> datafile 2 /oradata/fgedudb/users01.dbf
fgodu> bootstrap
fgodu> unload tablespace USERS

暴力扫描(无字典)

./fgodu
fgodu> datafile 1 /oradata/fgedudb/users01.dbf
fgodu> set db_block_size 8192
fgodu> define table EMP (EMPNO NUMBER(4), ENAME VARCHAR2(10))
fgodu> scan all 1 EMP
fgodu> unload brute 1 EMP 12345

案例 9: Oracle DMP 格式选择与导入

场景:需要生成可被 Oracle 原生工具导入的 DMP 文件

9i 环境(使用 imp)

fgodu> set output_format dmp
fgodu> .set oracle_version 9i
fgodu> unload table SCOTT.EMP

# 导入
imp userid=fgedu/fgedu123@fgedudb file=SCOTT_EMP.dmp tables=EMP ignore=y

10g+ 环境(使用 impdp)

fgodu> set output_format dmp
fgodu> .set oracle_version 10g
fgodu> unload table SCOTT.EMP

# 导入
mkdir -p /tmp/dp_dir
sqlplus / as sysdba <<EOF
CREATE DIRECTORY dp_dir AS '/tmp/dp_dir';
GRANT READ, WRITE ON DIRECTORY dp_dir TO scott;
EOF

impdp userid=fgedu/fgedu123@fgedudb directory=dp_dir \
       dumpfile=SCOTT_EMP.dmp tables=EMP

案例 10: Oracle 批量用户导出

场景:按用户批量导出所有表,生成统一导入脚本

操作步骤

./fgodu
fgodu> bootstrap
fgodu> set output_format dmp
fgodu> set output_dir /tmp/recovery
fgodu> .set oracle_version 11g

# 批量导出(自动生成 import_all.sql)
fgodu> unload user HR

# 输出文件
ls /tmp/recovery/
# HR_EMPLOYEES.dmp     import_HR_EMPLOYEES.sql
# HR_DEPARTMENTS.dmp   import_HR_DEPARTMENTS.sql
# ...
# import_all.sql       <- 批量导入脚本

# 一键导入所有表
export NLS_LANG=AMERICAN_AMERICA.AL32UTF8
sqlplus hr/hr@newdb @/tmp/recovery/import_all.sql

案例 11: Oracle字符集转换与兼容性测试(ZHS16GBK 源 → AL32UTF8 目标)

场景说明
国内大量遗留 Oracle 数据库使用 ZHS16GBK 作为数据库字符集(中文 2 字节存储),在升级迁移到新库时通常要切换到 Unicode 字符集 AL32UTF8(中文 3 字节存储)。这个过程如果处理不当会出现「中文变问号 ???」「倒乱码 ëԺÇ」「VARCHAR2 长度突然不够」等典型问题。

本案例以最常见的 ZHS16GBK → AL32UTF8 跨字符集迁移为主线,演示 FGODU 如何保持源数据库的原始字节序不做任何转换,从而确保字符集转换只在 Oracle 导入阶段发生一次、可观测、可验证。案例同时覆盖:SQL INSERT / SQL*Loader / DMP (impdp) 三种输出格式,最后用 DUMP() 函数做字节级校验,确保数据 100% 一致。

准备一张含多语言测试数据的表(源库端造数,如源库已报废可跳过直接用 FGODU 读文件)

-- 源库(ZHS16GBK)建测试表并造多语言混合数据
CREATE TABLE fgodu_charset_test (
  id      NUMBER(5) PRIMARY KEY,
  lang    VARCHAR2(20),
  content VARCHAR2(200 CHAR)   -- 注意用 CHAR 语义!不要用默认的 BYTE
);

INSERT INTO fgodu_charset_test VALUES (1, '简体中文', '风哥数据库培训-北京中关村软件园');
INSERT INTO fgodu_charset_test VALUES (2, '繁体中文', '風哥Oracle培訓-台北內湖科技園區');
INSERT INTO fgodu_charset_test VALUES (3, '日文', '風哥 Oracle 研修 - 東京渋谷スクランブルスクエア');
INSERT INTO fgodu_charset_test VALUES (4, '韩文', '풍가 Oracle 교육 - 서울 강남구 테헤란로');
INSERT INTO fgodu_charset_test VALUES (5, '越南文', 'Đào tạo Oracle Phong Ca - Hà Nội, Cầu Giấy');
INSERT INTO fgodu_charset_test VALUES (6, '泰文', 'ฝึกอบรม Oracle ของ Feng Ge - กรุงเทพฯ สุขุมวิท');
INSERT INTO fgodu_charset_test VALUES (7, '阿拉伯文', 'تدريب أوراكل فنغ جيه - دبي مول');
INSERT INTO fgodu_charset_test VALUES (8, 'Emoji/生僻字', '𓂀古埃及象形+𠮷野家(扩展A) + 👨‍💻‍程序员Zerowidth🚀');
INSERT INTO fgodu_charset_test VALUES (9, '混合标点', '中文(全角)"引号"【括号】《书名号》──破折号…省略号€欧元£英磅');
INSERT INTO fgodu_charset_test VALUES (10, '纯ASCII', 'Hello World! 1234567890 ~!@#$%^&*()_+');
COMMIT;

Step 1. 用 FGODU 导出数据(保留源原始字节,不做任何字符集转换
cd /tmp
export OUT_DIR=/tmp/fgodu_charset_test
mkdir -p $OUT_DIR

# ---- 关键 1:确认源字符集 ----
# FGODU 在 bootstrap 后会自动从 sys.props$ 读出 NLS_CHARACTERSET
# 对于典型国内老库,输出通常是:
#   Loading charset...     ZHS16GBK / AL16UTF16

./fgodu <<FGODU_EOF
datafile 1 /oradata/fgedudb/system01.dbf
datafile 2 /oradata/fgedudb/users01.dbf
bootstrap

# 校验源字符集(和 props$ 读取一致)
show db

# ---- 关键 2:输出 DMP + SQL*Loader + SQL 三种格式 ----
# (A) 用 impdp 导入的 Data Pump 格式
set output_dir $OUT_DIR
set output_format dmp
.set oracle_version 19c
unload table SCOTT.FGODU_CHARSET_TEST

# (B) 用 sqlldr 导入的 SQL*Loader 格式
set output_format sqlldr
unload table SCOTT.FGODU_CHARSET_TEST

# (C) 用 sqlplus 直接跑的 INSERT 格式
set output_format sql
unload table SCOTT.FGODU_CHARSET_TEST

quit
FGODU_EOF

此时 $OUT_DIR/ 目录应该出现三个产物:

SCOTT_FGODU_CHARSET_TEST.dmp       # Data Pump 二进制(ZHS16GBK 原始字节序)
SCOTT_FGODU_CHARSET_TEST.ctl + .txt# SQL*Loader 控制文件 + 数据
SCOTT_FGODU_CHARSET_TEST.sql       # INSERT 语句(ZHS16GBK 原文)

为什么不做显式字符集转换?
FGODU 始终按源库原始字节写出,这是避免"双重转换"最稳妥的做法。让 Oracle Client(sqlplus/sqlldr/impdp)通过 NLS_LANG 去做唯一一次代码页转换,是 Oracle 官方推荐的跨字符集迁移方案。


Step 2. 目标端准备(AL32UTF8)+ 正确设置 NLS_LANG
# ---- 关键 3:NLS_LANG 必须等于「源数据库字符集」!----
# 这是整个字符集转换里最容易出错的一步。很多人设成目标端 AL32UTF8,结果导致:
#   源 GBK 字节 → Client 以为是 UTF-8 → 传给 DB 再做一次转换
#   = 两次转换 + 信息丢失 = 中文变问号 ??? / 西欧乱码
# 正确做法:告诉 Oracle Client "我收到的文件是 ZHS16GBK 编码"

export NLS_LANG="SIMPLIFIED CHINESE_CHINA.ZHS16GBK"

# 验证目标数据库的字符集
sqlplus sys/YourSysPass@NEWDB_AL32UTF8 as sysdba <<'EOF'
COL parameter FOR a30
COL value     FOR a30
SELECT parameter, value FROM nls_database_parameters
 WHERE parameter IN ('NLS_CHARACTERSET','NLS_NCHAR_CHARACTERSET','NLS_LENGTH_SEMANTICS');

-- 期望输出:
-- NLS_CHARACTERSET          AL32UTF8
-- NLS_NCHAR_CHARACTERSET    AL16UTF16
-- NLS_LENGTH_SEMANTICS      BYTE  ← 这个要注意!默认是字节语义
EOF

目标端建表(用 CHAR 语义避免 UTF-8 下中文 3 字节导致的长度不够)

-- 目标端 (AL32UTF8)
CREATE USER scott IDENTIFIED BY tiger;
GRANT CONNECT, RESOURCE, UNLIMITED TABLESPACE TO scott;

ALTER SESSION SET NLS_LENGTH_SEMANTICS=CHAR;

CREATE TABLE scott.fgodu_charset_test (
  id      NUMBER(5) PRIMARY KEY,
  lang    VARCHAR2(20),
  content VARCHAR2(200)    -- 因为上面设置了 NLS_LENGTH_SEMANTICS=CHAR,
                           -- 这里等价于 VARCHAR2(200 CHAR)
);

长度语义提示:ZHS16GBK 下 VARCHAR2(200 BYTE) 可存 100 个中文字(2 字节/字);
AL32UTF8 下同样的中文要 3 字节/字,若继续用 BYTE 语义会出现
ORA-12899: value too large for column。迁移时务必改 VARCHAR2(... CHAR)


Step 3-A. 验证 SQL INSERT 格式的字符集导入
export NLS_LANG="SIMPLIFIED CHINESE_CHINA.ZHS16GBK"
export OUT_DIR=/tmp/fgodu_charset_test

# sqlplus 连接到新库(AL32UTF8),NLS_LANG 为 ZHS16GBK,
# Oracle Net 会**精确地**做一次 GBK→UTF-8 代码页转换
sqlplus scott/tiger@NEWDB_AL32UTF8 <<EOF
SPOOL $OUT_DIR/sql_import.log
@$OUT_DIR/SCOTT_FGODU_CHARSET_TEST.sql
COMMIT;
SPOOL OFF
EOF

Step 3-B. 验证 SQL*Loader 格式的字符集导入
export NLS_LANG="SIMPLIFIED CHINESE_CHINA.ZHS16GBK"
export OUT_DIR=/tmp/fgodu_charset_test

# CTL 文件不需要改编码,只要 NLS_LANG 对即可
# sqlldr 会按 NLS_LANG 把 .txt 当作 GBK 读 → 转换后写入 UTF-8 库
cd $OUT_DIR
sqlldr scott/tiger@NEWDB_AL32UTF8 \
  control=SCOTT_FGODU_CHARSET_TEST.ctl \
  log=sqlldr_import.log \
  bad=sqlldr_import.bad \
  errors=0

# 期望:Table SCOTT.FGODU_CHARSET_TEST:
#   10 Rows successfully loaded.
#   0 Rows not loaded due to data errors.

Step 3-C. 验证 Data Pump DMP (impdp) 格式的字符集导入
export NLS_LANG="AMERICAN_AMERICA.ZHS16GBK"
export OUT_DIR=/tmp/fgodu_charset_test

# ---- 准备 Data Pump Directory ----
sqlplus sys/YourSysPass@NEWDB_AL32UTF8 as sysdba <<'EOF'
CREATE OR REPLACE DIRECTORY fgodu_dp_dir AS '/tmp/fgodu_charset_test';
GRANT READ, WRITE ON DIRECTORY fgodu_dp_dir TO scott;
EXIT
EOF

# impdp 的字符集转换逻辑:
#   DMP 文件内写了 charset_id = ZHS16GBK (ID=852)
#   impdp 读取 DMP 内部 charset_id + 目标 DB NLS_CHARACTERSET (AL32UTF8=873)
#   Oracle 自动做 GBK → UTF-8 转换,无需依赖外部 NLS_LANG。
#   (但 DMP 文件外的 impdp 命令行日志仍受 NLS_LANG 影响)
impdp scott/tiger@NEWDB_AL32UTF8 \
  directory=fgodu_dp_dir \
  dumpfile=SCOTT_FGODU_CHARSET_TEST.dmp \
  tables=FGODU_CHARSET_TEST \
  table_exists_action=append \
  logfile=impdp_charset_test.log

# 完成后查看日志,不应有任何 ORA- 错误

Step 4. 字节级验证:用 DUMP() 确认字符集转换 100% 正确

字符集测试最忌讳"肉眼看像正常",因为像「ëԺÇ」这种典型的 UTF-8 被当作 Latin-1 二次解码的错,肉眼很容易误判。必须用 Oracle 内置的 DUMP(col, 1016) 输出 字符集 ID + 字节长度 + 每个字节的十六进制来严格比对:

-- 目标端 (AL32UTF8) 执行
SET LINESIZE 300 PAGESIZE 500
COL id        FOR 99
COL lang      FOR a12
COL bytes_utf FOR a80
COL content   FOR a55 WORD_WRAPPED

SELECT id,
       lang,
       content,
       -- 1016 = 返回字节十六进制 + 字符集 ID
       -- AL32UTF8 的 charset_id = 873;每个 UTF-8 中文 = 3 字节
       -- ZHS16GBK 的 charset_id = 852;每个 GBK 中文 = 2 字节
       DUMP(content, 1016) AS bytes_utf
  FROM scott.fgodu_charset_test
 ORDER BY id;

正确结果参考(片段)

id lang content DUMP(…)
1 简体中文 风哥数据库培训-北京中关村软件园 Typ=1 Len=66 CharacterSet=873: e9,a3,8e,e5,93,a5,... (UTF-8 3字节中文, ID=873)
2 繁体中文 風哥Oracle培訓-台北內湖科技園區 Typ=1 Len=72 CharacterSet=873: e9,a2,a8,e5,93,a5,...
3 日文 風哥 Oracle 研修 - 東京渋谷スクランブルスクエア Typ=1 Len=75 CharacterSet=873: e9,a2,a8,e5,93,a5,e3,83,...
4 韩文 풍가 Oracle 교육 - 서울 강남구 테헤란로 Typ=1 Len=70 CharacterSet=873: ed,92,92,ea,b0,80,...
8 Emoji/生僻字 𓂀古埃及…👨‍💻‍程序员Zerowidth🚀 Typ=1 Len=89 CharacterSet=873: f0,93,82,80,228,85,... (Emoji 是 4 字节 UTF-8,前缀 f0)
10 纯ASCII Hello World! 1234567890… Typ=1 Len=32 CharacterSet=873: 48,65,6c,6c,6f,... (ASCII 码不变)

Step 5. 常见字符集故障排查手册
症状 根因 修复方法
所有中文变 ??? 问号 NLS_LANG 被设置成 AL32UTF8,导致 Oracle Net 把 GBK 原文当 UTF-8 解析 → 无法识别的字节全部替换为 U+FFFD (?) export NLS_LANG=SIMPLIFIED CHINESE_CHINA.ZHS16GBK 后重新导入
西欧字符 Ã«ÔºÇ / 小明 UTF-8 字节被当作 ISO-8859-1 (Latin-1) 又套了一次 UTF-8 编码(“双重编码”) 说明 DMP 源端文件本身已经是 UTF-8 写的了,但 DMP 头部 charset_id 被标成 852 (ZHS16GBK)。重新用 FGODU 导出。
ORA-12899 value too large VARCHAR2 使用 BYTE 语义,GBK 2 字节/汉字 → UTF-8 3 字节/汉字,同样的字符串要多 50% 空间 建表时加 CHAR 语义:VARCHAR2(200 CHAR);或 ALTER SESSION SET NLS_LENGTH_SEMANTICS=CHAR; 再建表
日文片假名、韩文、越南文元音符号丢失 源数据库字符集实际上不是 ZHS16GBK(比如是 US7ASCII,强制塞了多语言数据),或 DUMP 字节用了非标准转义 先用 show db 确认 FGODU 读出的 NLS_CHARACTERSET;如果确实是 US7ASCII 且库里存了多字节字符,按源实际字节用 WE8ISO8859P1 当 NLS_LANG 强制直传
NVARCHAR2/NCLOB 列乱码 NLS_NCHAR_CHARACTERSET 没选对(老库 AL16UTF16,新库也必须保持 AL16UTF16) SELECT * FROM nls_database_parameters WHERE parameter LIKE '%NCHAR%'; 验证两端一致
DMP 导入成功但 SELECT CLOB 截断 CLOB 存储带字符集 ID 标签,impdp 时又做了额外转换 改用 SQL*Loader 格式直接以 CLOB 方式导入,.ctl 中声明 CHAR(10000000)
sqlldr 日志显示 “Multibyte char truncated” .ctl 文件默认列长 255 字节,GBK 原文 + UTF-8 转换后 255 不够 在 .ctl 中给列显式长度:content CHAR(2000) NULLIF content=BLANKS
阿拉伯文 / 希伯来文显示方向不对 不是字符集问题,是 BiDi 双向文本渲染;Oracle 存储字节本身正确,用支持 RTL 的前端工具(如 SQL Developer 设 BiDi ON)检查即可

最后给出一条一键字符集诊断 SQL,所有字符集相关问题都可以先用它跑一遍快速定位:

-- 字符集综合诊断 SQL(目标端执行)
COL section   FOR a18
COL item      FOR a40
COL val       FOR a48

SELECT 'DB 字符集' AS section, 'NLS_CHARACTERSET' AS item, value AS val
  FROM nls_database_parameters WHERE parameter='NLS_CHARACTERSET' UNION ALL
SELECT 'DB 字符集', 'NLS_NCHAR_CHARACTERSET', value
  FROM nls_database_parameters WHERE parameter='NLS_NCHAR_CHARACTERSET' UNION ALL
SELECT '会话字符集', 'NLS_LANG (推导)', userenv('language') FROM dual UNION ALL
SELECT '会话字符集', 'LENGTH_SEMANTICS', value
  FROM nls_session_parameters WHERE parameter='NLS_LENGTH_SEMANTICS' UNION ALL
SELECT '数据分布', '总行数',  COUNT(*)||''
  FROM scott.fgodu_charset_test UNION ALL
SELECT '数据分布', 'content 平均字节数', TO_CHAR(AVG(LENGTHB(content)),'999.00')
  FROM scott.fgodu_charset_test UNION ALL
SELECT '数据分布', 'content 平均字符数', TO_CHAR(AVG(LENGTH (content)),'999.00')
  FROM scott.fgodu_charset_test UNION ALL
SELECT '数据分布', '含4字节字符(Emoji/扩展汉字)行', COUNT(*)||''
  FROM scott.fgodu_charset_test WHERE LENGTH(content) <> LENGTHB(content)-1;

-- 典型健康状态:
--  AL32UTF8 / AL16UTF16
--  NLS_LENGTH_SEMANTICS = CHAR
--  content 平均字节数 ≈ 字符数的 2.6 ~ 3.1 倍(中文 3B + 英文 1B 混合平均)
--  含 4 字节字符行 = 1 (id=8)

6. 常用问题与排查

6.1 安装与构建问题

Q1: 编译报错 “GLIBC_2.34 not found”

原因:在低版本系统上编译,但链接了高版本 GLIBC

解决方案:使用 makemake portable 构建便携版本:

make clean
make portable

便携版通过符号版本控制,仅依赖 GLIBC_2.2.5/2.3,可在 RHEL 5+ 运行。

Q2: Windows 编译失败

解决方案:三种编译方式任选:

# 方式1: MSVC
nmake -f Makefile.win

# 方式2: MinGW
mingw32-make -f Makefile.win CC=gcc

# 方式3: Linux 交叉编译
make mingw
Q3: 检查 GLIBC 兼容性
# 检查系统 GLIBC 版本
ldd --version | head -1

# 检查二进制所需的 GLIBC 版本
readelf -V build/portable/fgodu | grep GLIBC

# 验证可运行
./build/portable/fgodu -v

6.2 数据文件问题

Q4: “Failed to add datafile: Permission denied”

原因:运行用户对数据文件无读取权限

解决方案

# 方法1: 用 root 或 oracle 用户运行
sudo ./fgodu

# 方法2: 修改文件权限
chmod +r /oradata/fgedudb/system01.dbf

# 方法3: 修改文件属主
chown oracle:oinstall /oradata/fgedudb/*.dbf
Q5: “cannot detect block size”

原因:数据文件头损坏,无法自动检测块大小

解决方案:手动指定块大小:

fgodu> set db_block_size 8192
fgodu> datafile 1 /oradata/fgedudb/system01.dbf
Q6: “Bootstrap failed: cannot find bootstrap$”

原因:SYSTEM 表空间损坏或未添加 SYSTEM 数据文件

解决方案

  1. 确认添加了 SYSTEM 表空间的数据文件
  2. 如果 SYSTEM 严重损坏,使用非字典模式:
fgodu> datafile 1 /oradata/fgedudb/users01.dbf
fgodu> define table EMP (EMPNO NUMBER(4), ENAME VARCHAR2(10))
fgodu> scan all 1 EMP

6.3 数据恢复问题

Q7: “dataobj#=xxx not found in blocks (stale dictionary?)”

原因:数据字典中的 dataobj# 与实际块的 dataobj# 不匹配(通常因 TRUNCATE/move 操作)

解决方案:FGODU 会自动回退到 obj# 匹配策略,提示信息会显示:

Scanning for obj#=63956/dataobj#=63956 across 5 files...

这是正常行为,FGODU 会自动处理。

Q8: 导出的数据有重复行

原因:Oracle 数据块可能包含多个行版本(UPDATE 残留)

解决方案:FGODU 已内置主键去重功能。如果仍出现重复:

  1. 确认表有主键
  2. 使用 .set oracle_version 确保正确版本
  3. 导入后用 SQL 去重:
DELETE FROM emp WHERE rowid NOT IN (
  SELECT MIN(rowid) FROM emp GROUP BY empno
);
Q9: 导出数据中文乱码

原因:字符集不匹配

解决方案

  1. FGODU 自动检测数据库字符集
  2. 确保导入时设置正确的 NLS_LANG:
# 查看源数据库字符集
# FGODU 在导出时会显示字符集信息

# 设置环境变量(必须与源数据库一致)
export NLS_LANG=AMERICAN_AMERICA.AL32UTF8
# 或
export NLS_LANG=AMERICAN_AMERICA.ZHS16GBK
Q10: 导出数据包含空行/无效行

原因:DELETE/UPDATE 操作残留的无效行

解决方案:FGODU 已内置 is_empty_row 函数过滤 DELETE 残留行。如果仍有问题:

fgodu> .set oracle_version 11g
fgodu> unload table SCOTT.EMP

6.4 DMP 导入问题

Q11: ORA-39002: invalid operation

原因:使用 impdp 工具导入传统的 exp 格式 DMP 文件

解决方案

方案1(推荐):使用正确的导入工具

# 如果 DMP 是 9i 格式,用 imp 导入
imp userid=user/pass@db file=X.dmp tables=T ignore=y

# 如果 DMP 是 10g+ 格式,用 impdp 导入
impdp userid=user/pass@db directory=DP_DIR dumpfile=X.dmp tables=T

方案2:重新生成正确格式的 DMP 文件

# 生成 9i exp/imp 格式
fgodu> .set oracle_version 9i
fgodu> set output_format dmp
fgodu> unload table SCOTT.EMP
# 然后用 imp 导入

# 或生成 10g+ expdp/impdp 格式
fgodu> .set oracle_version 10g
fgodu> unload table SCOTT.EMP
# 然后用 impdp 导入
Q12: ORA-39070: unable to open the log file

原因impdp 找不到目录对象或无权限

解决方案

# 1. 创建目录对象
sqlplus / as sysdba <<EOF
CREATE OR REPLACE DIRECTORY dp_dir AS '/tmp/dp_dir';
GRANT READ, WRITE ON DIRECTORY dp_dir TO scott;
EXIT
EOF

# 2. 确保目录存在且有权限
mkdir -p /tmp/dp_dir
chmod 777 /tmp/dp_dir

# 3. 重新导入
impdp userid=fgedu/fgedu123@fgedudb directory=dp_dir \
       dumpfile=SCOTT_EMP.dmp tables=EMP \
       logfile=import.log
Q13: impdp 报格式不兼容

原因:Oracle Data Pump 是闭源专有格式,FGODU 生成的 DMP 可能无法 100% 兼容

解决方案:使用以下替代方案之一:

方案1:使用 SQL*Loader 格式(100% 标准兼容)

fgodu> set output_format sqlldr
fgodu> unload table SCOTT.EMP

# 导入
sqlldr fgedu/fgedu123@fgedudb control=SCOTT_EMP.ctl log=import.log

方案2:使用 exp/imp 格式(10g 仍支持 imp 工具)

fgodu> .set oracle_version 9i
fgodu> set output_format dmp
fgodu> unload table SCOTT.EMP

# 用 imp 导入(10g+ 仍支持)
imp userid=fgedu/fgedu123@fgedudb file=SCOTT_EMP.dmp tables=EMP ignore=y
Q14: 导入后表结构不正确(DATE 类型加长度)

原因:旧版本生成 DDL 时对 DATE 类型错误添加长度参数

解决方案:已修复,FGODU 现在对 DATE 类型不加长度:

-- 正确的 DDL
HIRE_DATE DATE NOT NULL

-- 错误的 DDL(旧版本)
HIRE_DATE DATE(7) NOT NULL

如果仍遇到此问题,请重新编译最新版本:

make clean && make

6.5 ASM 与存储问题

Q15: “No ASM disks found”

原因:ASM 磁盘驱动未加载或路径不正确

解决方案

# 1. 检查 oracleasm 是否加载
lsmod | grep oracleasm

# 2. 手动扫描
fgodu> asm discover /dev/oracleasm/disks
fgodu> asm discover /dev/asm-disk*

# 3. 检查磁盘权限
ls -la /dev/oracleasm/disks/
Q16: UDEV/LVM 设备无法识别

解决方案:手动指定类型:

fgodu> datafile 1 /dev/disk/by-id/scsi-xxx udev
fgodu> datafile 2 /dev/mapper/vg_oracle-lv_data lvm

6.6 性能问题

Q17: 导出速度慢

解决方案

# 1. 增加并行度
fgodu> set parallel 8
# 或自动检测
fgodu> set parallel auto

# 2. 确认多线程已启用
fgodu> boot all  # 启用并行 COL$ 扫描
Q18: 大表导出卡住

原因:可能数据量大或线程资源不足

解决方案

# 1. 查看进度(FGODU 会实时显示)
# Scanning: [====50%====] 5000/10000 blocks

# 2. 如果线程创建失败,FGODU 会自动降级为单线程
# 3. 考虑分块导出
fgodu> scan range 1 1 10000 EMP
fgodu> scan range 1 10001 20000 EMP

6.7 Windows 特定问题

Q19: Windows 版如何指定路径?
fgodu> datafile 1 C:\oracle\oradata\system01.dbf
Q20: Windows 版支持 ASM 吗?

不支持。ASM 是 Linux 特有的存储模式,Windows 版仅支持文件系统模式。


附录

A. 字符集支持列表

字符集 说明
AL32UTF8 Unicode UTF-8(推荐)
UTF8 Unicode UTF-8 (Oracle 旧版)
ZHS16GBK 简体中文 GBK
ZHT16BIG5 繁体中文 BIG5
ZHT32EUC 繁体中文 EUC
WE8ISO8859P1 西欧 ISO-8859-1
WE8MSWIN1252 Windows-1252
US7ASCII ASCII
JA16SJIS 日文 Shift-JIS
JA16EUC 日文 EUC
KO16KSC5601 韩文 KSC5601

B. 块大小支持

块大小 字节数 说明
2K 2048 小型数据库
4K 4096 小型数据库
8K 8192 标准数据库(默认)
16K 16384 大型数据库
32K 32768 大型数据库
64K 65536 超大型数据库
128K 131072 超大型数据库

C. 技术支持

如有问题或建议,请联系:

  • 作者:风哥
    | 官方网站 | http://www.fgedu.net.cn , http://www.itpux.com |
    | 数据库教程 | https://edu.51cto.com/lecturer/8020378.html |

免责声明:FGODU 是一个数据恢复工具,对于生产环境,请务必先在测试环境验证,并做好数据备份。作者不对使用本工具造成的任何数据损失负责。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐