1、核心业务表误drop恢复(no purge)
1.1模拟drop误操作
创建jason业务表,如下:
SQL> create table jason as select * from dba_objects; SQL> create index idx_jason_oid on jason(object_id);
用户在删除数据的时候,不小心误删jason业务表,如下:
SQL> drop table jason;
1.2通过回收站恢复误删数据
首先,检查误删业务表当前所属用户的回收站,如下:
SQL> select object_name,original_name,operation,type,ts_name,droptime from user_recyclebin;
如果运气较好,被误删的业务表还保留在回收站中,那么可以通过回收站进行还原恢复,如下:
SQL> flashback table "BIN$ggkFrmhwKEngUwjSqMDkqA==$0" to before drop rename to JASON_NEW; SQL> alter table JASON rename to JASON_OLD; SQL> alter table JASON_NEW rename to JASON;
检查回收站功能如果开启,可选择关闭,如下:
SQL> ALTER SESSION SET recyclebin = OFF; SQL> ALTER SYSTEM SET recyclebin = OFF SCOPE = SPFILE;
检查回收站功能如果关闭,可选择开启,如下:
SQL> ALTER SESSION SET recyclebin = ON; SQL> ALTER SYSTEM SET recyclebin = ON SCOPE = SPFILE;
2、核心业务表误drop恢复(purge)
2.1模拟drop误操作
创建jason业务表,如下:
SQL> create tablespace tabtest datafile '/home/oracle/test/oradata/tabtest01.dbf' size 10m autoextend on; SQL> create table tabtest tablespace tabtest as select * from dba_objects;
用户在删除数据的时候,不小心误删jason业务表,如下:
SQL> drop table tabtest purge;
2.2将误删表所在的表空间设置为只读
由于表已经误删除,因此需要和用户或业务开发等相关人员确认该业务表所在的表空间,并尽快将此业务表空间设置为只读模式,在设置之前需要创建与tabtest表拥有相同表结构的tabtest_old表,如下:
SQL> alter system set deferred_segment_creation=false; SQL> create table tabtest_old tablespace tabtest as select * from dba_objects where 1=2; SQL> alter tablespace tabtest read only;
2.3临时在SYSTEM表空间创建恢复表
由于tabtest表数据恢复,势必要涉及到IO读写操作,如果数据量不大,可临时设置恢复表的默认表空间为SYSTEM;如果数据量很大,建议为恢复表创建新的默认表空间,如下:
SQL> create table tabtest_new tablespace system as select * from dba_objects where 1=2;
2.4通过日志挖掘获取误删表data_object_id
首先,手动切换归档日志,并记录切换后时间戳,如下:
SQL> alter system switch logfile; SQL> alter system switch logfile; SQL> select sysdate from dual; SYSDATE ------------------- 2022-06-20 15:07:11
检查误操作发生至归档手动切换后系统生成的归档日志,如下:
col name for a60
set line 200
select unique NAME, THREAD#, SEQUENCE#, COMPLETION_TIME
from gv$archived_log
where thread# = 1
and COMPLETION_TIME >=
to_date('2022-05-09 13:15:03', 'yyyy-mm-dd hh24:mi:ss')
and COMPLETION_TIME <=
to_date('2022-05-09 13:24:47', 'yyyy-mm-dd hh24:mi:ss')
order by SEQUENCE#;
添加第一个要加载的归档日志文件,如下:
SQL> execute dbms_logmnr.add_logfile(logfilename=>'/home/oracle/test/arch/1_84_1106134187.dbf',options=>dbms_logmnr.new);
多次添加要加载的归档日志文件,如下:
SQL> execute dbms_logmnr.add_logfile(logfilename=>'/home/oracle/test/arch/1_85_1106134187.dbf',options=>dbms_logmnr.addfile);
执行日志挖掘分析,如下:
SQL> execute dbms_logmnr.start_logmnr(options=>dbms_logmnr.dict_from_online_catalog);
查询v$logmnr_contents动态性能视图,获取归档日志解析信息,如下:
SQL> select count(*) from v$logmnr_contents; COUNT(*) ---------- 93190 SQL> create table sys.hzmc_logmnr_contents_fordrop as select * from v$logmnr_contents;
关闭日志挖掘分析,如下:
SQL> execute dbms_logmnr.end_logmnr;
获取drop purge动作执行的时间,如下:
SQL> select scn,timestamp,sql_redo from sys.hzmc_logmnr_contents_fordrop where operation='DDL' and lower(sql_redo) like '%tabtest%' order by timestamp desc; SCN TIMESTAMP SQL_REDO ---------- ------------------- --------------------------- 266498191 2022-06-20 14:55:43 drop table tabtest purge;
获取误删业务表data_object_id,如下:
SQL> select scn,timestamp,sql_redo from sys.hzmc_logmnr_contents_fordrop where timestamp like '2022-06-20 14:55%' and sql_redo like 'delete%';

2.5通过ROWID尝试找回误删表数据
DBMS_ROWID.ROWID_CREATE包的语法,如下:
DBMS_ROWID.ROWID_CREATE (rowid_type IN NUMBER,object_number IN NUMBER,relative_fno IN NUMBER,block_number IN NUMBER,row_number IN NUMBER) RETURN ROWID;
DBMS_ROWID.ROWID_CREATE传参解析,如下:
rowid_type Type (restricted or extended). Set the rowid_type parameter to 0 for a restricted ROWID. Set it to 1 to create an extended ROWID. If you specify rowid_type as 0, then the required object_number parameter is ignored, and ROWID_CREATE returns a restricted ROWID.
object_number Data object number (rowid_object_undefined for restricted).
relative_fno Relative file number.
block_number Block number in this file.
row_number Returns row number in this block.
将tabtest_old表的dataobj#修改为2.2.4节获取到的dataobj#,如下:
SQL> select obj#,dataobj# from obj$ where name='TABTEST_OLD'; SQL> update obj$ set DATAOBJ#=89741 where obj#=89744; SQL> commit;
获取tabtest_old表所在的数据文件号和段头块号,如下:
set linesize 200 pagesize 200 col owner for a10 col segment_name for a15 select owner, segment_name, header_file, header_block, segment_type from dba_segments where segment_name = 'TABTEST_OLD'; OWNER SEGMENT_NAME HEADER_FILE HEADER_BLOCK SEGMENT_TYPE ---------- --------------- ----------- ------------ ------------------ SYS TABTEST_OLD 8 130 TABLE
先通过dbms_rowid.ROWID_CREATE包获取第8号数据文件的第131数据块的第1行ROWID,再根据ROWID返回TABTEST表的object_id字段值,通过与dba_objects查询对比,发现一致,说明通过该方式可以尝试找回误删表的数据,如下:
alter system flush shared_pool; alter system flush shared_pool; alter system flush buffer_cache; alter system flush buffer_cache; SQL> select object_id from tabtest_old where rowid=dbms_rowid.ROWID_CREATE(1,89741,8,131,0); OBJECT_ID ---------- 20 SQL> select object_id from dba_objects where rownum<=1; OBJECT_ID ---------- 20
2.6通过PL/SQL尝试找回所有误删表数据
SET SERVEROUTPUT ON
DECLARE
nrows number;
rid rowid;
objd number;
ROWSPERBLOCK number;
BEGIN
ROWSPERBLOCK:=1000;
nrows:=0;
for i in (
select file_id,block_id,blocks from dba_free_space where tablespace_name='TABTEST'
union all
select file_id,block_id,8 blocks from dba_extents where segment_name='TABTEST_OLD'
)
loop
for fblkno in i.block_id..i.block_id+i.blocks-1 loop
for fblkrow in 1..ROWSPERBLOCK loop
begin
rid := dbms_rowid.ROWID_CREATE(1,89741,i.file_id, fblkno,fblkrow-1);
insert into sys.tabtest_new
select *
from sys.tabtest_old a
where rowid = rid;
if sql%rowcount = 1 then nrows:=nrows+1; end if;
if (mod(nrows,1000)=0) then commit; end if;
exception when others then null;
end;
end loop;
end loop;
end loop;
COMMIT;
dbms_output.put_line('Total rows: '||to_char(nrows));
END;
/
Total rows: 86726
PL/SQL procedure successfully completed.
待数据找回后,再将tabtest_new表重命名为tabtest表,在业务低峰期再对tabtest表做表空间迁移,如下;
SQL> alter table tabtest_new rename to tabtest; SQL> alter tablespace tabtest read write; SQL> alter table tabtest move tablespace tabtes