场景:在 Oracle数据库使用过程中,误删对象或误删记录的情况时有发生;这种情况下通过技术手段可以将对象的状态和数据恢复到删除前的某个时刻;
建议:技术手段的恢复毕竟是有限的,为了降低类似的误操作还是要通过规范的流程和定期的备份来规避,就像是车技再好的车手也不该经常在悬崖边漂移,否则可能都来不及说再见!
Oracle数据库提供了多种方式可以将数据库或对象或记录恢复到某个时间节点;
1)undo文件
说明:undo文件的数据有空间大小限制,数据库按照循环写的方式类使用,可恢复的数据也限于短时间内的操作;
2)redo日志
说明:redo日志因为有空间大小限制,和 undo文件数据类似,是循环写数据,所以可以恢复的数据时间跨度不会很大,一般只限于很短的时间内的操作;
3)archived redo(归档日志)
说明:归档日志是将online redo日志固化为文件存储下来,会占用不小的服务器空间;
4)rman备份
5)recyclebin(回收站)
从ORACLE 10g开始,引入了一个叫回收站(RecycleBin)的概念,全称为Tablespace Recycle Bin。回收站实际是一个逻辑区域,它以表空间中现有已经分配的空间为基础,而不是从表空间上物理划出一个固定区域用作回收站。这意味着回收站和表空间中的对象共用存储区域、系统没有给回收站预留空间。
当表被DROP后,如果可用空间充足,并且没有对回收站进行清理,那么被DROP掉的对象会一直存在回收站中,仍然会占用空间。但是如果可用空间紧张的情况下,数据库会根据先进先出的顺序覆盖Recycle Bin中的对象。所以回收站机制也不是百分百的保险机制。另外从原理上来说它就是一个数据字典表,放置用户Drop掉的数据库对象信息。
SQL>
SQL> show parameter recyclebin;
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
recyclebin string on
SQL>
或者
select t.NAME, t.VALUE, t.DISPLAY_VALUE
from v$parameter t
where t.NAME = 'recyclebin';
---输出
NAME VALUE DISPLAY_VALUE
1 recyclebin on on
1)会话级别
---session
alter session set recyclebin=on/off;
2)系统级别
---system
alter system set recyclebin=on/off;
---query_sql
select owner,
object_name,
original_name,
operation,
type,
ts_name,
createtime,
droptime,
dropscn,
partition_name,
can_undrop,
can_purge,
related,
base_object,
purge_object,
space
from DBA_RECYCLEBIN t
where t.owner = '&username'
order by t.createtime desc;
select name,log_mode from v$database t;
log_mode:NOARCHIVELOG 为非归档状态,current为归档状态;
或者在服务器的以sys用户登录的sqlplus 模式下执行:
SQL>
SQL> archive log list
Database log mode No Archive Mode
Automatic archival Disabled
Archive destination USE_DB_RECOVERY_FILE_DEST
Oldest online log sequence 134
Current log sequence 138
SQL>
Oracle补充日志(附加日志)是基于常规日志的基础上,额外记录的日志信息,用于恢复数据库的逻辑状态;
补充日志(supplemental log)并非一定要再开启归档日志的情况下才可以开启;
补充日志的开启有2种粒度,一种是数据库级别,一种是表级别;补充日志可以有2种,即最小补充日志、标识关键字段补充日志两大类;
select name,
t.SUPPLEMENTAL_LOG_DATA_MIN as log_min,
t.SUPPLEMENTAL_LOG_DATA_PK as log_pk,
t.SUPPLEMENTAL_LOG_DATA_UI as log_ui,
t.SUPPLEMENTAL_LOG_DATA_FK as log_pk,
t.SUPPLEMENTAL_LOG_DATA_ALL as log_all
from v$database t;
开启关键字段补充日志的前提是要开启最小补充日志;如果需要开启某种标识关键字段的补充日志,会自动开启最小补充日志,无需提前开启最小补充日志;
标识关键字段的补充日志有分为基于主键、唯一索引、外键、全字段、自定义5类;
alter database add supplemental log data columns;
alter database drop supplemental log data columns;
---open key
alter database add supplemental log data(primary key) columns;
alter database add supplemental log data(primary key,unique,foreign key,all) columns;
---stop key
alter database drop supplemental log data(primary key) columns;
alter database drop supplemental log data(primary key,unique,foreign key,all) columns;
调整状态
SQL>
SQL> select name,
2 t.SUPPLEMENTAL_LOG_DATA_MIN as log_min,
3 t.SUPPLEMENTAL_LOG_DATA_PK as log_pk,
4 t.SUPPLEMENTAL_LOG_DATA_UI as log_ui,
5 t.SUPPLEMENTAL_LOG_DATA_FK as log_pk,
6 t.SUPPLEMENTAL_LOG_DATA_ALL as log_all
7 from v$database t;
NAME LOG_MIN LOG_PK LOG_UI LOG_PK LOG_ALL
--------- -------- ------ ------ ------ -------
DEVDB YES YES NO NO NO
SQL>
---open key
alter table ${schema_name.tabel_name} add supplemental log data(primary key) columns;
alter table ${schema_name.tabel_name} add supplemental log data(primary key,unique,foreign key,all) columns;
---stop key
alter table ${schema_name.tabel_name} drop supplemental log data(primary key) columns;
alter table ${schema_name.tabel_name} drop supplemental log data(primary key,unique,foreign key,all) columns;
强调:表级关键字段补充日志在开启前,需要提前开启数据库级最小补充日志;
表级关键字段补充日志相比于数据库级,额外多一种日志粒度,为自定义关键字段日志;自定义关键字段补充日志分为有条件式和无条件式(加 always关键字);
alter table ${schema_name.tabel_name} add supplemental log group ${group_name} (keycol_1,keycol_2,keycol_3);
alter table ${schema_name.tabel_name} add supplemental log group ${group_name} (keycol_1,keycol_2,keycol_3) <always>;
---stop
alter table ${schema_name.tabel_name} drop supplemental log group ${group_name};
select t.owner,
t.table_name,
t.log_group_name,
t.always,
t.generated,
cl.column_name,
cl.position,
cl.logging_property
from dba_log_groups t
inner join dba_log_group_columns cl
on (t.owner = cl.owner and t.table_name = cl.table_name and
t.log_group_name = cl.log_group_name)
where 1 = 1
and t.owner='SJZBQ_SSTB'
and t.table_name='DJ_NSRXX'
order by t.owner, t.table_name, t.log_group_name, cl.position;
对于对象被误删除定义的场景,只能依靠回收站来恢复;需要确定回收站处于开启状态,且在 dba_recyclebin 里可以查到相应记录;
执行恢复操作
flashback table ${schema_name.object_name} to before drop;
对于被恢复定义的对象,索引和主键等名称处于乱码状态,需要重建;
对象数据被误删除在没有开启归档且没有 RMAN 备份的情况下,可以依靠 Undo 数据或者 Redo 数据来恢复;
通过闪回查询确定删除前的记录:
select * from ${schema_name.object_name} as of timestamp to_timestamp('2020-07-26 17:00:50','yyyy-mm-dd hh24:mi:ss') where 1=1 and ${query_condition};
闪回操作:
flashback table ${schema_name.object_name} to timestamp to_timestamp('2020-07-26 17:00:50','yyyy-mm-dd hh24:mi:ss');
说明:这类操作需要确保 undo被删除前的数据没有被覆盖,如果执行flashback操作报错 ora-01555,则相应的回滚段数据已经被覆盖;此时这种方式已经无法恢复数据,或者只能恢复一部分;
该类恢复方式需要依赖到redo日志文件,需要确保redo文件内容没有被覆盖;
step_1:select * from v$logfile t;
step_2:登录数据库服务器
sqlplus '/as sysdba'
exec dbms_logmnr.add_logfile('/data/zxcp/redo05.log',options=>dbms_logmnr.new);
exec dbms_logmnr.add_logfile('/data/zxcp/redo06.log', dbms_logmnr.ADDFILE);
exec dbms_logmnr.start_logmnr(options=>dbms_logmnr.dict_from_online_catalog);
create table log_recover as select * from v$logmnr_contents;
select to_char(timestamp,'yyyy-mm-dd hh24:mi:ss'),sql_redo,sql_undo from log_recover where username='xxxx' and table_name='xxxx';
---最后执行
exec dbms_logmnr.end_logmnr();
此处 sql_redo里的dml语句是当时执行的删除语句,sql_undo里的语句是对应恢复的dml语句;可以将 sql_undo 里的语句存储为动态语句循环执行;
注意:此种方式对于 lob 字段,值内容很长的,可能最终恢复的数据 lob 列的值部分记录为空;因为对于 lob 字段,数据不直接存储在表里;要想Lob 列也可以闪回,需要在数据更新前开启数据库级最小附加日志;
dbms_logmnr.start_logmnr
begin
-- Call the procedure
sys.dbms_logmnr.start_logmnr(startScn => :startScn,
endScn => :endScn,
startTime => :startTime,
endTime => :endTime,
DictFileName => :DictFileName,
Options => :Options);
end;
说明:该方法有如上几个参数,在进行redo日志文件分析时,可以加 scn号的限制或时间戳的限制;
============================================== over ================================================