[20151028]linux下删除数据文件的恢复细节4
--前几天一直在做删除数据文件的恢复测试,中间遇到许多问题自己无法解决,从我个人讲我不主张使用句柄的方式来恢复,而更愿意
--使用rman的方式,这种情况仅仅适合非归档模式。
--前几天的测试非常混乱,我自己都不知道为什么在删除数据文件的情况下有时候执行alter system checkpoint数据库会直接crash,有
--时候为什么有不会。我再把整个恢复过程做一个总结:
1.测试环境:
SCOTT@test> @ &r/ver1
PORT_STRING VERSION BANNER
------------------------------ -------------- ----------------------------------------------------------------
x86_64/Linux 2.4.xx 10.2.0.4.0 Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi
RMAN> report schema;
using target database control file instead of recovery catalog
Report of database schema
List of Permanent Datafiles
===========================
File Size(MB) Tablespace RB segs Datafile Name
---- -------- -------------------- ------- ------------------------
1 480 SYSTEM *** /mnt/ramdisk/test/system01.dbf
2 35 UNDOTBS1 *** /mnt/ramdisk/test/undotbs01.dbf
3 250 SYSAUX *** /mnt/ramdisk/test/sysaux01.dbf
4 5 USERS *** /mnt/ramdisk/test/users01.dbf
5 100 EXAMPLE *** /mnt/ramdisk/test/example01.dbf
List of Temporary Files
=======================
File Size(MB) Tablespace Maxsize(MB) Tempfile Name
---- -------- -------------------- ----------- --------------------
1 20 TEMP 32767 /mnt/ramdisk/test/temp01.dbf
SCOTT@test> create table tj (id number,name varchar2(20)) tablespace example;
Table created.
SCOTT@test> insert into tj values (1,'aaaa');
1 row created.
SCOTT@test> commit ;
Commit complete.
--测试前我做了1个冷备份。
2.测试:
--session 1:
SCOTT@test> col spid new_value v_spid
SCOTT@test> @ &r/spid
SID SERIAL# SPID C50
---------- ---------- ------ --------------------------------------------------
149 3 3210 alter system kill session '149,3' immediate;
SCOTT@test> select rowid,tj.* from tj ;
ROWID ID NAME
------------------ ---------- --------------------
AAAM7JAAFAAAAP2AAA 1 aaaa
SCOTT@test> host ls -l /proc/&v_spid/fd | grep example
--无输出。因为数据已经在buffer cache,无需打开对应的文件句柄。
SCOTT@test> alter system flush buffer_cache ;
System altered.
SCOTT@test> select rowid,tj.* from tj ;
ROWID ID NAME
------------------ ---------- --------------------
AAAM7JAAFAAAAP2AAA 1 aaaa
SCOTT@test> host ls -l /proc/&v_spid/fd | grep example
lrwx------ 1 oracle oinstall 64 Oct 28 09:08 10 -> /mnt/ramdisk/test/example01.dbf
3.开始删除数据文件:
$ rm /mnt/ramdisk/test/example01.dbf
/bin/rm: remove regular file `/mnt/ramdisk/test/example01.dbf'? y
--session 1:
SCOTT@test> host ls -l /proc/&v_spid/fd | grep example
lrwx------ 1 oracle oinstall 64 Oct 28 09:08 10 -> /mnt/ramdisk/test/example01.dbf (deleted)
--可以发现句柄依旧存在,也就是session 1可以访问对应的数据文件。甚至可以dml操作数据库.
SCOTT@test> select rowid,tj.* from tj ;
ROWID ID NAME
------------------ ---------- --------------------
AAAM7JAAFAAAAP2AAA 1 aaaa
SCOTT@test> insert into tj values (2,'bbbb');
1 row created.
SCOTT@test> commit ;
Commit complete.
--网络上介绍的许多方法就是这个使用通过linux删除文件一些进程文件句柄没有释放的特性来恢复,但是这个时候一些已经打开句柄的
--进程读写文件完全没有没有影响,如果使用这种方式拷贝文件,实际上相当于要热备份文件而没有把数据文件表空间设置为热备份模式
--理论讲这种方式存在一些问题,虽然测试没有出错,但是并不表示适合真实的情况.
--我以前的提到必须先讲表空间设置为只读模式,然后在拷贝,这样才能获得一致的数据文件备份。但是我忽略一点,这是网友指出的问
--题,新进入的会话无法执行alter tablespace example read only的,会出现如下错误:
--session 2:
SCOTT@test> alter tablespace example read only ;
alter tablespace example read only
*
ERROR at line 1:
ORA-01116: error in opening database file 5
ORA-01110: data file 5: '/mnt/ramdisk/test/example01.dbf'
ORA-27041: unable to open file
Linux-x86_64 Error: 2: No such file or directory
Additional information: 3
--因为要执行上面的命令,必须先获得该文件的句柄,而这个适合文件已经删除,另外在session 1,执行上面的命令没有问题,因为句柄已经
--打开.这就面临一个问题,我想到的解决方式通过链接欺骗oracle这个文件是存在的.步骤如下:
$ ps -ef | grep ora_dbw[0-9]_test
oracle 3168 1 0 09:05 ? 00:00:00 ora_dbw0_test
oracle 3170 1 0 09:05 ? 00:00:00 ora_dbw1_test
oracle 3172 1 0 09:05 ? 00:00:00 ora_dbw2_test
$ ls -l /proc/3168/fd | grep example
lrwx------ 1 oracle oinstall 64 2015-10-28 09:28:54 22 -> /mnt/ramdisk/test/example01.dbf (deleted)
--注意最好选择ora_dbw之类的后台进程,因为前台的进程退出,会释放文件句柄.
$ ln -s /proc/3168/fd/22 /mnt/ramdisk/test/example01.dbf
--session 2:
SCOTT@test> alter tablespace example read only ;
Tablespace altered.
--这样执行就ok了.然后再删除链接,使用拷贝命令代替原来的链接.
$ rm /mnt/ramdisk/test/example01.dbf
/bin/rm: remove symbolic link `/mnt/ramdisk/test/example01.dbf'? y
$ cp /proc/3168/fd/22 /mnt/ramdisk/test/example01.dbf
--这个时候不能直接执行alter tablespace example read write ;,因为恢复的文件i节点与原来已经不同,也就是原来删除的句柄没有释
--放,我这里又犯了一个低级错误.而且在这里困惑很久.回到session 1:
--session 1:
SCOTT@test> host ls -l /proc/&v_spid/fd | grep example
lrwx------ 1 oracle oinstall 64 Oct 28 09:08 10 -> /mnt/ramdisk/test/example01.dbf (deleted)
--实际上要释放这个句柄必须将数据文件offline,如果我在session 2执行alter datafile datafile 5 offline,我回到session 1执行
--host ls -l /proc/&v_spid/fd | grep example,发现句柄一直没释放,这个是我当时测试昏惑的地方,我以为是某个后台进程擦操作释
--放句柄,我等很久根本不会释放,实际上你只要执行一些sql命令就会释放句柄(大家可以自行测试). 正确的恢复是:
alter database datafile 5 offline; -- 说明:这里加入 offline drop 与offline 是一样的,除非数据库是非归档模式,必须加drop参数.
recover datafile 5 -- 这一部实际上是多余的,因为前面已经将表空间设置为只读,
alter database datafile 5 online;
alter tablespace example read write ;
--这样就完成了恢复.
4.下面演示错误的恢复方式:
--如果跳过offline ,online这些步骤,直接设置表空间读写,就出现奇怪的问题,旧的会话打开的文件句柄指向删除的文件,而新打开的会
--话文件句柄指向新建立的数据文件.实际上还有一种方式在设置read write前.删除已经打开文件删除文件句柄的经常,注意不能杀后台
--进程,这样有点粗暴,不提倡使用.
# lsof | grep -i example01.dbf | grep deleted
oracle 3168 oracle 22uR REG 0,18 104865792 602277 /mnt/ramdisk/test/example01.dbf (deleted)
oracle 3174 oracle 25u REG 0,18 104865792 602277 /mnt/ramdisk/test/example01.dbf (deleted)
oracle 3178 oracle 19u REG 0,18 104865792 602277 /mnt/ramdisk/test/example01.dbf (deleted)
oracle 3210 oracle 10u REG 0,18 104865792 602277 /mnt/ramdisk/test/example01.dbf (deleted)
oracle 3301 oracle 15u REG 0,18 104865792 602277 /mnt/ramdisk/test/example01.dbf (deleted)
--下面演示错误的恢复方式:
--session 2:
SCOTT@test> alter tablespace example read write ;
Tablespace altered.
--session 1:
SCOTT@test> host ls -l /proc/&v_spid/fd | grep example
lrwx------ 1 oracle oinstall 64 Oct 28 09:08 10 -> /mnt/ramdisk/test/example01.dbf (deleted)
--可以发现删除的文件句柄没有释放.
SCOTT@test> update tj set name='AAAA' where id=1;
1 row updated.
SCOTT@test> commit ;
Commit complete.
SCOTT@test> host ls -l /proc/&v_spid/fd | grep example
lrwx------ 1 oracle oinstall 64 Oct 28 09:08 10 -> /mnt/ramdisk/test/example01.dbf (deleted)
--可以发现操作的文件是删除的文件.
--session 2:
SCOTT@test> select rowid,tj.* from tj ;
ROWID ID NAME
------------------ ---------- --------------------
AAAM7JAAFAAAAP0AAA 2 bbbb
AAAM7JAAFAAAAP2AAA 1 AAAA
--我们也可以看到修改结果.如果我们执行刷新buffer cache,再看就出现问题了.
SCOTT@test> alter system flush buffer_cache ;
System altered.
SCOTT@test> select rowid,tj.* from tj ;
ROWID ID NAME
------------------ ---------- --------------------
AAAM7JAAFAAAAP0AAA 2 bbbb
AAAM7JAAFAAAAP2AAA 1 aaaa
--注意看ID=1的记录变成了小写,这样整个数据库就乱套了.切记这种恢复必须要offline+online这个步骤.
5.这样的数据库无法正常关闭.
SYS@test> shutdown immediate ;
ORA-01122: database file 5 failed verification check
ORA-01110: data file 5: '/mnt/ramdisk/test/example01.dbf'
ORA-01208: data file is an old version - not accessing current version
SYS@test> alter database datafile 5 offline ;
alter database datafile 5 offline
*
ERROR at line 1:
ORA-01145: offline immediate disallowed unless media recovery enabled
--不能offline.
SYS@test> select open_mode from v$database ;
OPEN_MODE
----------
READ WRITE
SYS@test> alter system checkpoint ;
alter system checkpoint
*
ERROR at line 1:
ORA-03113: end-of-file on communication channel
--直接crash,运气好的可以恢复,不好就出现我前面的情况,出现如下错误:
ORA-00283: recovery session canceled due to errors
ORA-12801: error signaled in parallel query server P005
ORA-00600: internal error code, arguments: [3020], [6], [837], [1], [93], [2614], [16], []
ORA-10567: Redo is inconsistent with data block (file#)
--补充恢复过程:
SYS@test> startup
ORACLE instance started.
Total System Global Area 486539264 bytes
Fixed Size 2084872 bytes
Variable Size 138416120 bytes
Database Buffers 335544320 bytes
Redo Buffers 10493952 bytes
Database mounted.
ORA-01113: file 5 needs media recovery
ORA-01110: data file 5: '/mnt/ramdisk/test/example01.dbf'
SYS@test> recover datafile 5;
Media recovery complete.
SYS@test> alter database open ;
Database altered.
SYS@test> select rowid,tj.* from scott.tj ;
ROWID ID NAME
------------------ ---------- ------
AAAM7JAAFAAAAP0AAA 2 bbbb
AAAM7JAAFAAAAP2AAA 1 AAAA
--总结:
1.这种方式仅仅适合非归档模式.
2.首先先确定删除文件句柄:
$ ps -ef | grep ora_dbw[0-9]_test
oracle 3168 1 0 09:05 ? 00:00:00 ora_dbw0_test
oracle 3170 1 0 09:05 ? 00:00:00 ora_dbw1_test
oracle 3172 1 0 09:05 ? 00:00:00 ora_dbw2_test
$ ls -l /proc/NNNN/fd | grep example
lrwx------ 1 oracle oinstall 64 2015-10-28 09:28:54 22 -> /mnt/ramdisk/test/example01.dbf (deleted)
--确定删除文件句柄.
3.使用ln 命令欺骗oracle,假设文件存在:
$ ln -s /proc/NNNN/fd/MMMM /mnt/ramdisk/test/example01.dbf
4.执行如下:
alter tablespace example read only ;
$ rm /where/path/ln_datafile.dbf
$ cp /proc/NNNN/fd/MMMM /mnt/ramdisk/test/example01.dbf
alter database datafile N offline; -- 说明:这里加入 offline drop 与offline 是一样的,除非数据库是非归档模式,必须加drop参数.
recover datafile N; -- 这一部实际上是多余的,因为前面已经将表空间设置为只读,
alter database datafile N online;
==在这里可以加入检查:# lsof | grep -i example01.dbf | grep deleted
alter tablespace example read write ;
5.检查恢复情况,我建议最好关闭数据库看看.