闪回搞不定时怎么办?Oracle数据恢复的终极武器

Oracle数据恢复进阶实战:LogMiner日志挖掘与TRUNCATE恢复全攻略

大家好,我是睿。

上一期我们聊了Oracle闪回技术全家桶,从闪回查询到闪回数据库,把6种闪回方式全部实操了一遍。文章最后我留了个预告------当闪回技术搞不定的时候,该怎么办?

现实中,DBA经常遇到这些棘手场景:

• DML误操作后,redo日志已经被归档,在线日志里找不到

• 需要精确审计"谁在什么时间改了什么数据"

• TRUNCATE误删了核心业务表,闪回技术无能为力

• 没有开归档,闪回区也过期了

今天这篇文章,我们就来掌握几把终极武器:LogMiner日志挖掘和TRUNCATE后的数据恢复方案。这些是闪回之外的最后防线,每一个DBA都应该烂熟于心。

一、LogMiner日志挖掘

1.1 LogMiner能做什么

LogMiner是Oracle自带的redo日志分析工具,它能读取redo log和archive log,把二进制日志翻译成可读的SQL语句。核心能力:

• 查看DML操作的SQL_REDO(重放)和SQL_UNDO(回滚)

• 精确审计:谁、什么时间、对哪张表、做了什么操作

• 追踪数据变更历史,辅助故障定位

• 不需要额外license,标准版即可使用

1.2 前置条件与配置

开启补充日志(必须):

-- 开启最小补充日志(LogMiner的前提)

ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;

-- 验证

SELECT supplemental_log_data_min FROM v$database;

-- SUPPLEMENTAL_LOG_DATA_MIN


-- YES

全库级别补充日志(可选,获取完整SQL_UNDO):

-- 全列补充日志(生成的SQL_UNDO更完整)

ALTER DATABASE ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;

-- 或者针对特定表

ALTER TABLE mary.test_logminer ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;

1.3 实战:挖掘在线redo日志

下面用一个完整的实验来演示整个流程。

Step 1:准备测试数据

-- 1.1 创建mary用户并授权(如已有可跳过)

CREATE USER mary IDENTIFIED BY mary DEFAULT TABLESPACE mary;

GRANT CONNECT, RESOURCE TO mary;

GRANT EXECUTE_CATALOG_ROLE TO mary; -- LogMiner需要此权限

-- 1.2 以mary用户创建测试表

CONN mary/mary

CREATE TABLE mary.test_logminer (

id NUMBER PRIMARY KEY,

name VARCHAR2(50),

salary NUMBER

);

-- 1.3 插入测试数据

INSERT INTO mary.test_logminer VALUES (1, 'Alice', 15000);

INSERT INTO mary.test_logminer VALUES (2, 'Bob', 18000);

INSERT INTO mary.test_logminer VALUES (3, 'Charlie', 20000);

COMMIT;

-- 1.4 验证数据

SELECT * FROM mary.test_logminer;

ID NAME SALARY


1 Alice 15000

2 Bob 18000

3 Charlie 20000

Step 2:执行DML操作(模拟误操作)

-- 2.1 模拟误操作:UPDATE + DELETE

UPDATE mary.test_logminer SET salary = 99999 WHERE id = 1;

1 row updated.

DELETE FROM mary.test_logminer WHERE id = 2;

1 row deleted.

COMMIT;

-- 2.2 查看误操作后的数据

SELECT * FROM mary.test_logminer;

ID NAME SALARY


1 Alice 99999 -- 被改了

-- Bob被删了

3 Charlie 20000

Step 3:添加日志文件到LogMiner

-- 查看当前在线redo日志

set linesize 500

set pagesize 100

col member for a40

col status for a10

SELECT a.THREAD#, a.GROUP#, b.MEMBER, a.BYTES / 1024 / 1024 AS SIZE_MB ,a.status

FROM VLOG a JOIN VLOGFILE b

ON a.GROUP# = b.GROUP# ORDER BY a.THREAD#, a.GROUP#;

THREAD# GROUP# MEMBER SIZE_MB STATUS


1 1 /u01/app/oracle/oradata/NOCDB/onlinelog/ 200 INACTIVE

o1_mf_1_nkf4jk6f_.log

1 1 /u01/app/oracle/fast_recovery_area/NOCDB 200 INACTIVE

/onlinelog/o1_mf_1_nkf4jmq9_.log

1 2 /u01/app/oracle/oradata/NOCDB/onlinelog/ 200 INACTIVE

o1_mf_2_nkf4jk8k_.log

1 2 /u01/app/oracle/fast_recovery_area/NOCDB 200 INACTIVE

/onlinelog/o1_mf_2_nkf4jsps_.log

1 3 /u01/app/oracle/oradata/NOCDB/onlinelog/ 200 CURRENT

o1_mf_3_nkf4jk99_.log

1 3 /u01/app/oracle/fast_recovery_area/NOCDB 200 CURRENT

/onlinelog/o1_mf_3_nkf4jz1z_.log

6 rows selected.

-- 添加要分析的日志文件

EXEC DBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME => '/u01/app/oracle/fast_recovery_area/NOCDB/onlinelog/o1_mf_3_nkf4jz1z_.log',OPTIONS => DBMS_LOGMNR.NEW);

Step 4:启动LogMiner(使用在线数据字典)

EXEC DBMS_LOGMNR.START_LOGMNR(Options => DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG);

Step 5:查询分析结果

-- 查看所有DML操作

SELECT timestamp, sql_redo, sql_undo

FROM v$logmnr_contents

WHERE seg_name = 'TEST_LOGMINER'

ORDER BY timestamp;

TIMESTAMP SQL_REDO SQL_UNDO


01-DEC-22

insert into "MARY"."TEST_LOGMINER"("ID","NAME","SALARY") values ('1','Alice','15000');

delete from "MARY"."TEST_LOGMINER" where "ID" = '1' and "NAME" = 'Alice' and "SALARY" = '15000' and ROWID = 'AAAbZOAAFAAAAA7AAA';

01-DEC-22

insert into "MARY"."TEST_LOGMINER"("ID","NAME","SALARY") values ('2','Bob','18000');

delete from "MARY"."TEST_LOGMINER" where "ID" = '2' and "NAME" = 'Bob' and "SALARY" = '18000' and ROWID = 'AAAbZOAAFAAAAA7AAB';

01-DEC-22

insert into "MARY"."TEST_LOGMINER"("ID","NAME","SALARY") values ('3','Charlie','20000');

delete from "MARY"."TEST_LOGMINER" where "ID" = '3' and "NAME" = 'Charlie' and "SALARY" = '20000' and ROWID = 'AAAbZOAAFAAAAA7AAC';

01-DEC-22

update "MARY"."TEST_LOGMINER" set "SALARY" = '99999' where "SALARY" = '15000' and ROWID = 'AAAbZOAAFAAAAA7AAA';

update "MARY"."TEST_LOGMINER" set "SALARY" = '15000' where "SALARY" = '99999' and ROWID = 'AAAbZOAAFAAAAA7AAA';

01-DEC-22

delete from "MARY"."TEST_LOGMINER" where "ID" = '2' and "NAME" = 'Bob' and "SALARY" = '18000' and ROWID = 'AAAbZOAAFAAAAA7AAB';

insert into "MARY"."TEST_LOGMINER"("ID","NAME","SALARY") values ('2','Bob','18000');

看到了吗?SQL_REDO是实际执行的操作,SQL_UNDO是回滚操作------这就是恢复数据的关键。

Step 6:结束分析

EXEC DBMS_LOGMNR.END_LOGMNR;

1.4 实战技巧:精准定位目标操作

生产环境的日志量动辄几十上百个G,直接全量查询等于大海捞针。分享几个我常用的精准过滤技巧:

-- 按时间范围过滤

SELECT timestamp, sql_redo, sql_undo

FROM v$logmnr_contents

WHERE timestamp BETWEEN

TO_DATE('2026-06-30 10:00:00', 'YYYY-MM-DD HH24:MI:SS')

AND TO_DATE('2026-06-30 11:00:00', 'YYYY-MM-DD HH24:MI:SS')

AND seg_owner = 'MARY'

AND operation = 'DELETE';

-- 按SCN范围过滤(更精确)

SELECT timestamp, sql_redo, sql_undo

FROM v$logmnr_contents

WHERE scn BETWEEN 59447386 AND 59449766

AND seg_name LIKE 'TEST%';

1.5 分析归档日志

实际场景中,redo日志往往已经被归档。这时候同样可以分析归档日志:

-- 查看归档日志列表

SELECT name, sequence#, first_time, next_time

FROM v$archived_log

WHERE dest_id = 1

ORDER BY sequence#;

-- 添加多个归档日志

EXEC DBMS_LOGMNR.ADD_LOGFILE( LogFileName =>'/u01/app/oracle/fast_recovery_area/NOCDB/archivelog/2026_07_01/o1_mf_1_1222_krh55h1c_.arc',Options => DBMS_LOGMNR.NEW);

EXEC DBMS_LOGMNR.ADD_LOGFILE( LogFileName =>'/u01/app/oracle/fast_recovery_area/NOCDB/archivelog/2026_07_01/o1_mf_1_1221_krh3gm1w_.arc',Options => DBMS_LOGMNR.NEW);

-- 跨库分析时,在源库(被分析的库)上执行

BEGIN DBMS_LOGMNR_D.BUILD( dictionary_filename => 'dict.ora', dictionary_location => '/u01/logmnr/', options => DBMS_LOGMNR_D.STORE_IN_FLAT_FILE ); END; /

-- 在第三方数据库做日志分析,使用字典文件启动

EXEC DBMS_LOGMNR.START_LOGMNR(DictFileName => '/u01/logmnr/dict.ora', Options => DBMS_LOGMNR.DICT_FROM_REDO_LOGS);

1.6 LogMiner的局限性与注意事项

  1. TRUNCATE无法恢复:TRUNCATE是DDL操作,只修改segment header,

不产生逐行的redo/undo日志。LogMiner只能看到"TRUNCATE TABLE xxx"

的记录,但没有SQL_UNDO,无法通过重放来恢复数据。

恢复TRUNCATE需要其他方案,见第二章。

  1. 日志保留时间:在线redo日志会被循环覆盖,归档日志也被手动或自动清理。不能依赖"总能找到日志"。

  2. 性能影响:LogMiner分析大量日志时消耗CPU和内存,

建议在测试环境执行。

  1. LOB和LONG类型:对大字段的支持有限,SQL_UNDO可能不完整。

二、TRUNCATE后的数据恢复

在动手恢复之前,先搞清楚TRUNCATE的本质:

• TRUNCATE是DDL操作,会修改segment header中的data_object_id

• 重置High Water Mark(HWM)和extent map

• 不会逐行删除数据块中的内容

• 不产生数据级别的redo/undo日志

• 默认情况下会释放存储空间

关键原则:只要数据块没有被覆盖,就有机会恢复!

公共测试环境准备:

先创建测试表并批量插入1000条数据,后续各方案的演示均基于这张表。

-- 1. 创建测试表

sqlplus mary/mary

CREATE TABLE mary.employees (

emp_id NUMBER PRIMARY KEY,

emp_name VARCHAR2(50),

dept VARCHAR2(30),

salary NUMBER

);

-- 2. 批量插入1000条测试数据

BEGIN

FOR i IN 1..1000 LOOP

INSERT INTO mary.employees VALUES (

i,

'EMP_' || LPAD(i, 4, '0'),

CASE MOD(i, 4)

WHEN 0 THEN 'IT'

WHEN 1 THEN 'Sales'

WHEN 2 THEN 'HR'

WHEN 3 THEN 'Finance'

END,

10000 + MOD(i, 500) * 30

);

END LOOP;

COMMIT;

END;

/

-- 3. 验证数据量

SELECT COUNT(*) FROM mary.employees;

COUNT(*)


1000

-- 4. 查看前几条数据样例

SELECT * FROM mary.employees WHERE ROWNUM <= 5 ORDER BY emp_id;

EMP_ID EMP_NAME DEPT SALARY


1 EMP_0001 Sales 10030

2 EMP_0002 HR 10060

3 EMP_0003 Finance 10090

4 EMP_0004 IT 10120

5 EMP_0005 Sales 10150

2.1 方案一:容灾库闪回数据库恢复

闪回数据库是恢复TRUNCATE数据最快的方案。但在生产库上直接做FLASHBACK DATABASE影响面过大,绝对不可行。正确的做法是:在容灾库上做闪回,以read only模式打开,再通过dblink把数据反向插回生产库。

前提条件:容灾库(Physical Standby)长期开启Flashback Database。

操作步骤:

-- ===== 以下步骤在容灾库(备库)执行 =====

-- 1. 暂停容灾库的redo apply

ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL;

-- 2. 在容灾库执行闪回到TRUNCATE之前的时间点

FLASHBACK DATABASE TO TIMESTAMP

TO_TIMESTAMP('2026-06-30 10:00:00', 'YYYY-MM-DD HH24:MI:SS');

-- 3. 以read only方式打开容灾库

ALTER DATABASE OPEN READ ONLY;

-- 4. 验证数据已恢复

SELECT COUNT(*) FROM mary.employees;

COUNT(*)


1000

通过dblink将数据反向回传到生产库:

-- ===== 在生产库执行 =====

-- 5. 在生产库创建到容灾库的dblink

CREATE DATABASE LINK to_standby

CONNECT TO mary IDENTIFIED BY oracle

USING '(DESCRIPTION =

(ADDRESS_LIST =

(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.110.101)(PORT = 1521))

)

(CONNECT_DATA =

(SERVICE_NAME = nocdb)

)

)';

-- 6. 从容灾库拉回数据

INSERT INTO mary.employees

SELECT * FROM mary.employees@to_standby;

commit;

-- 7. 验证

SELECT COUNT(*) FROM mary.employees;

COUNT(*)


1000

恢复完成后,容灾库重新同步:

-- ===== 在容灾库执行 =====

-- 8. 容灾库重新flashback到最新状态,恢复同步

ALTER DATABASE RECOVER MANAGED STANDBY DATABASE

USING CURRENT LOGFILE DISCONNECT;

这个方案的核心优势在于:不影响生产库正常运行,不需要RESETLOGS,数据通过dblink精准回传。唯一前提是容灾库必须提前开启Flashback Database------这也是我一直强调容灾库要开flashback的根本原因。

2.2 方案二:RMAN备份异机恢复

如果容灾库没有开Flashback,或者手头有RMAN备份,可以通过异机恢复来抢救数据。核心原则不变:所有操作在异机(辅助实例)上完成,绝不动生产库。

方法A:异机全库恢复(最有效,但最耗时、耗资源)

-- 在异机(辅助实例)上执行

-- 1. 在异机准备参数文件、创建目录结构

-- 2. 启动辅助实例到nomount

STARTUP NOMOUNT PFILE='/tmp/pfile.ora';

-- 3. 全库恢复到TRUNCATE之前的SCN

RMAN> RUN {

SET UNTIL SCN 59464891;

RESTORE DATABASE;

RECOVER DATABASE;

ALTER DATABASE OPEN RESETLOGS;

}

-- 4. 验证数据存在

SELECT COUNT(*) FROM mary.employees;

-- 5. 在生产库创建dblink连接到异机

CREATE DATABASE LINK to_aux

CONNECT TO mary IDENTIFIED BY mary_password

USING 'aux_tns';

-- 6. 从异机拉回数据

INSERT INTO mary.employees

SELECT * FROM mary.employees@to_aux;

COMMIT;

-- 7. 恢复完成后关闭异机

SHUTDOWN IMMEDIATE;

方法A的优势是数据完整性100%有保证,缺点是耗时长(需要全库restore+recover),且需要一台空闲的辅助服务器,适合大型灾难场景。

方法B:异机表空间级恢复

-- 在异机执行

-- 1. 只恢复目标表空间(节省时间和空间)

准备可用的参数文件

准备创建控制文件的脚本,只保留必要的数据文件

alter database backup controlfile to trace as '/soft/controlfile_reset.sql' resetlogs;

vi controlfile_reset.sql (只保留system、sysaux、undo、temp、目标表空间)

-- 目标环境执行:

STARTUP NOMOUNT

CREATE CONTROLFILE REUSE DATABASE "LOCALDB" RESETLOGS FORCE LOGGING ARCHIVELOG

MAXLOGFILES 16

MAXLOGMEMBERS 3

MAXDATAFILES 100

MAXINSTANCES 8

MAXLOGHISTORY 292

LOGFILE

GROUP 1 '/oradata/localdb/redo01.log' SIZE 50M BLOCKSIZE 512,

GROUP 2 '/oradata/localdb/redo02.log' SIZE 50M BLOCKSIZE 512,

GROUP 3 '/oradata/localdb/redo03.log' SIZE 50M BLOCKSIZE 512

-- STANDBY LOGFILE

DATAFILE

'/oradata/localdb/system01.dbf',

'/oradata/localdb/sysaux01.dbf',

'/oradata/localdb/undotbs01.dbf',

'/oradata/localdb/mary01.dbf',

'/oradata/localdb/mary03.dbf',

'/oradata/localdb/mary02.dbf'

CHARACTER SET ZHS16GBK

;

RECOVER DATABASE USING BACKUP CONTROLFILE

ALTER TABLESPACE TEMP ADD TEMPFILE '/oradata/localdb/temp01.dbf' REUSE;

-- 启动到mount状态,恢复system、sysaux、undo、目标表空间

rman target /

RUN {

SET UNTIL SCN 5326681;

RESTORE TABLESPACE mary;

RESTORE TABLESPACE sysaux;

RESTORE TABLESPACE system;

RESTORE TABLESPACE undotbs1;

RECOVER TABLESPACE mary;

RECOVER TABLESPACE sysaux;

RECOVER TABLESPACE system;

RECOVER TABLESPACE undotbs1;

}

RMAN> recover database;

sqlplus / as sysdba

SQL> alter database open resetlogs;

SQL> select name from v$tablespace;

NAME


SYSTEM

SYSAUX

UNDOTBS1

MARY

TEMP

SQL> SELECT count(*) FROM mary.employees;

COUNT(*)


1000

-- 2. 同样通过dblink在生产库拉取数据

INSERT INTO mary.employees

SELECT * FROM mary.employees@to_aux;

COMMIT;

表空间级恢复的速度比全库恢复快得多,但需要注意:异机的文件路径、表空间名称等可能需要调整。可控性也不如全库恢复,实际使用要根据具体情况灵活判断。

2. 3 方案三:FY_RECOVER_DATA存储过程包

FY_RECOVER_DATA是一个专门用于TRUNCATE恢复的工具包,核心原理是构造傀儡表,将被TRUNCATE表的数据块内容嫁接到傀儡对象上,从而实现数据恢复。

支持范围:

• 普通堆表(Heap Table)

• 压缩表(Compression)

• 索引组织表(IOT)

• 分区表

• BLOB/CLOB Store in Row

• 离线恢复(Offline Recovery)

• Oracle 9i及以上,全平台支持

关于数据条数限制:

FY_RECOVER_DATA从数据块层面恢复数据,理论上没有硬性的条数限制。但实际恢复效果和性能受以下因素影响:

• 数据块是否已被覆盖:如果TRUNCATE后有大量写入操作,

原数据块可能已被重用,这部分数据无法恢复

• 表的大小:超大表(几十GB以上)恢复时间较长

• 表空间剩余空间:恢复过程需要在同表空间创建傀儡对象,

需要足够的空间

• 建议:TRUNCATE后尽快执行恢复,减少数据块被覆盖的概率

实战演示:

-- 1. 模拟TRUNCATE误操作

SELECT COUNT(*) FROM mary.employees;

COUNT(*)


1000

TRUNCATE TABLE mary.employees;

Table truncated.

-- 2. 确认数据已丢失

SELECT COUNT(*) FROM mary.employees;

COUNT(*)


0

-- 3. 加载FY_RECOVER_DATA包

SQL> @/soft/FY_Recover_Data.pck

Enter value for files: (这里直接回车即可)

old 30: -- 1. Temp Restore and Recover tablespace & files ---

new 30: -- 1. Temp Restore and Recover tablespace ---

Package created.

Package body created.

-- 4. 执行恢复

SQL> exec fy_recover_data.recover_truncated_table('MARY','EMPLOYEES');

PL/SQL procedure successfully completed.

-- 5. 查看恢复结果

SQL> SELECT COUNT(*) FROM mary.employees$$;

COUNT(*)


1000

-- 6. 将恢复的数据写回原表

INSERT INTO mary.employees SELECT * FROM mary.employees$$;

COMMIT;

-- 7. 验证

SELECT COUNT(*) FROM mary.employees;

COUNT(*)


1000

恢复出来的数据会存储在以原表名加$$后缀的傀儡表中,确认数据无误后再导入原表。整个过程不需要停机,对生产环境的影响极小。

恢复完成后,别忘了清理FY_RECOVER_DATA包自动创建的临时表空间:

SQL> drop tablespace FY_REC_DATA INCLUDING CONTENTS and datafiles;

Tablespace dropped.

SQL> drop tablespace FY_RST_DATA INCLUDING CONTENTS and datafiles;

Tablespace dropped.

2.4 方案四:第三方工具(ODU/DUL/PRM)

如果以上方案都不可用------没有容灾库闪回、没有RMAN备份、也没有FY_RECOVER_DATA包------还可以借助专业的数据恢复工具。需要提醒的是:ODU、DUL、PRM均为SF软件,网上能下载到的试用版通常有数据条数限制,生产环境使用需要购买正式授权。

|----------------------------|---------------------|-----------------|
| 工具 | 特点 | 适用场景 |
| ODU (Oracle Data Unloader) | 直接读取数据文件,不依赖实例,SF工具 | 表空间损坏、数据文件损坏 |
| DUL (Database Unloader) | 类似ODU,SF工具 | 极端场景下的数据抢救 |
| PRM (Parnassus Recovery) | 恢复工具,SF工具 | 支持ASM/RAC/加密表空间 |

本文以Windows环境下的ODU为例,简单演示恢复流程。

ODU恢复流程示例:

-- 1. 将表空间offline

ALTER TABLESPACE mary OFFLINE;

-- 2. 复制数据文件到ODU所在的Windows服务器

-- 3. 进入ODU目录,修改control.txt文件,将文件路径根据实际情况修改

示例:

#ts #fno #rfno filename block_size

5 6 6 D:\workspace\test.dbf 8192

-- 4. 使用ODU加载数据字典

.\odu.exe

ODU> unload dict

-- 5. 扫描目标表空间

ODU> scan extent tablespace 5

-- 6. 导出恢复的数据

ODU> unload table mary.employees object auto

生成MARY_EMPLOYEES.ctl、MARY_EMPLOYEES.txt两个文件

-- 7. 用sqlldr将数据导入

SQL> alter tablespace mary online;

$ sqlldr userid=mary/mary control=MARY_EMPLOYEES.ctl

-- 8. 验证恢复的数据

SELECT COUNT(*) FROM mary.employees;

三、总结与实战建议

3.1 LogMiner最佳实践

  1. 日常开启最小补充日志,关键时刻不掉链子

  2. 重要表开启全列补充日志

  3. 分析时先用时间/SCN/表名精准过滤,避免大量扫描

  4. 大量日志分析建议在异机库进行,不影响生产

  5. 定期清理不需要的归档日志,释放空间

3.2 预防TRUNCATE误操作的几条铁律

  1. 做好用户的权限管理,只授予必要的使用权限

  2. 容灾库务必开启Flashback Database(关键时刻能救命)

  3. RMAN备份策略:全备+增量+归档日志尽量多的保留

  4. 重要操作前记录关键时间节点

  5. 生产环境尽量禁止直接TRUNCATE操作,审批 + 审核确认

3.3 数据恢复优先级决策树

遇到数据丢失时,按以下顺序选择恢复方案:

DML误操作(UPDATE/DELETE):

→ 闪回查询(Flashback Query)

→ 闪回表(Flashback Table)

→ LogMiner分析redo日志,获取SQL_UNDO

→ RMAN备份异机恢复

TRUNCATE误操作:

→ 容灾库闪回数据库 + dblink回传(首选)

→ RMAN异机恢复(有备份时)

→ FY_RECOVER_DATA存储过程包

→ 第三方工具(ODU/DUL/PRM)

DROP TABLE:

→ 闪回DROP(Recycle Bin)

→ RMAN备份恢复

→ 第三方工具

好了,今天的内容就到这里。LogMiner、闪回数据库、RMAN异机恢复、FY_RECOVER_DATA、第三方工具------这篇文章把TRUNCATE数据恢复从头到尾扒了个遍。如果你在生产环境中经历过数据恢复的实战,欢迎在评论区分享你的经验和踩过的坑。遇到问题也可以留言,我看到都会回。

我们下期见!

------ 睿 | Oracle性能优化老司机