数据误删除是作为初级运维人员常常遇到的“低级错误”,一些有经验的老手有时也在疲劳、不冷静的情况下“马失前蹄”。一旦误删除数据文件,尽快采用影响最小、最迅速的手段恢复数据库是第一要务。
恢复数据的方法很多,比如冷热备份、闪回数据库等等,如果是直接从操作系统OS层面删除数据文件,在Linux/Unix环境下,有一些优选手段可以使用。其中之一就是文件描述符(File Description)。
1、聊聊File Description
不同的操作系统,在实现CPU管理、内存管理和存储文件管理的时候,采用不同的方式手段。
在Linux和Unix里面,采用文件描述符进行文件管理。一个进程要打开文件,是调用操作系统内核功能,内核返回一个文件描述符。对文件的读写操作也通过这个描述符进行操作。操作系统删除一个文件的时候,是要确定文件所有文件描述符都是释放掉之后,才会最后删除。
我们的误操作,如果是发生在正在运行的数据库系统中,文件虽然在操作系统上删除不可见了。但是数据库Oracle进程中还会保有一些存在的文件描述符,借用这些文件描述符,我们是可能找到文件信息,来恢复数据文件的。
所以,一旦发生了误删除动作,切忌三点:心态冷静、断开应用、维护现场不关库。
但是,在实际中,这种可以快速恢复的技术,并不是百分之百成功的。使用文件描述符恢复数据需要数据库不能关闭、数据库进程不能“自动剔除”数据文件等。本文从两个实验着手,介绍一下操作方法和前提。
2、实验环境搭建
我们选择Linux 2.6内核和Oracle 11g进行试验。注意:在生产环境下,绝对不能进行这样的实验。进行试验的时候,也需要完备的备份。
[oracle@bspdev ~]$ uname -a
Linux bspdev.localdomain 2.6.18-308.el5 #1 SMP Tue Feb 21 20:05:41 EST 2012 i686 i686 i386 GNU/Linux
SQL> select * from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production
PL/SQL Release 11.2.0.1.0 - Production
CORE 11.2.0.1.0 Production
TNS for Linux: Version 11.2.0.1.0 - Production
NLSRTL Version 11.2.0.1.0 – Production
创建专门的表空间、用户和数据表,用于进行试验。
SQL> create tablespace rmdtest datafile '/u01/oradata/WILSON/datafile/rmdtest01.dbf' size 1000m
2 extent management local uniform size 1m
3 segment space management auto;
Tablespace created
SQL> create user rmtest identified by rmtest default tablespace rmdtest;
User created
SQL> grant resource, connect to rmtest;
Grant succeeded
SQL> grant select any dictionary to rmtest;
Grant succeeded
SQL> create table rm_tab as select * from dba_objects;
Table created
SQL> insert into rm_tab select * from rm_tab;
72731 rows inserted
SQL> commit;
Commit complete
数据表T,在实验环境的表空间文件里面。
SQL> select tablespace_name, bytes/1024/1024 M from dba_segments where owner='RMTEST' and segment_name='RM_TAB';
TABLESPACE_NAME M
------------------------------ ----------
RMDTEST 17
确保有一份好的备份!
RMAN> list backup;
List of Backup Sets
===================
BS Key Type LV Size Device Type Elapsed Time Completion Time
------- ---- -- ---------- ----------- ------------ ---------------
135 Full 1.39G DISK 00:03:13 02-FEB-14
BP Key: 135 Status: AVAILABLE Compressed: NO Tag: TAG20140202T012300
(篇幅原因,有省略……)
Piece Name:
Piece Name: /u01/flash_recovery_area/WILSON/autobackup/2014_02_02/o1_mf_s_838430779_9gtckx4s_.bkp
SPFILE Included: Modification time: 02-FEB-14
SPFILE db_unique_name: WILSON
Control File Included: Ckp SCN: 5370719 Ckp time: 02-FEB-14
下面进行两次实验过程,模拟运行状态下数据文件被删除的场景。注意:由于操作系统差异性,Windows下是不会出现“运行打开的文件被删除”的场景的。所以,在Linux/AIX中,更容易出现误删除的情况。
3、一次“不成功”的实验
首先是一次不成功的实验。运维生产环境中,我们的原则永远是稳定。危险不确定的事情场景,一定要避免。哪怕不做、不修、不优化,也不要让业务系统的可用性去冒险。
实验环境中,我们总能够发现很多知识和现象。首先,我们尝试删除数据文件,确认文件位置。
[oracle@bspdev ~]$ cd /u01/oradata/WILSON/datafile/
[oracle@bspdev datafile]$ ls -l | grep rmdtest01.dbf
-rw-r----- 1 oracle oinstall 1048584192 Feb 2 02:14 rmdtest01.dbf
删除文件。
[oracle@bspdev datafile]$ rm rmdtest01.dbf
[oracle@bspdev datafile]$ ls -l
total 8121892
-rw-r----- 1 oracle oinstall 10493952 Feb 2 02:14 mvtbltest01.dbf
(篇幅原因,有省略……)
-rw-r--r-- 1 oracle oinstall 10493952 Feb 2 02:14 tts_simple01.dbf
注意:此时数据文件虽然从OS中删除,但是我们依然可以查询到数据!
SQL> select count(*) from rmtest.rm_tab;
COUNT(*)
----------
145462
对于这种现象,笔者认为两种可能性,一是buffer cache中的缓存数据信息,可以支持这种查询动作。另一种可能性,就是由于文件描述符的存在,让数据文件没有被真正删除,还以某种方式存在于系统中,支持dbwr查询。
证明两种猜想,对buffer cache进行清理。
SQL> alter system flush buffer_cache;
System altered
SQL> alter system flush shared_pool;
System altered
--依然可以查询结果
SQL> select count(*) from rmtest.rm_tab;
COUNT(*)
----------
145462
但是,如果数据库以某种方式,发现了文件被删除,比如check point动作,就会引起很多自动化动作出现。
SQL> alter system checkpoint;
System altered
--alert log中信息如下:
Sun Feb 02 02:27:51 2014
Beginning global checkpoint up to RBA [0x1ef.2532.10], SCN: 5374358
Errors in file /u01/diag/rdbms/wilson/wilson/trace/wilson_ckpt_4814.trc:
ORA-01171: datafile 11 going offline due to error advancing checkpoint
ORA-01116: error in opening database file 11
ORA-01110: data file 11: '/u01/oradata/WILSON/datafile/rmdtest01.dbf'
ORA-27041: unable to open file
Linux Error: 2: No such file or directory
Additional information: 3
Completed checkpoint up to RBA [0x1ef.2532.10], SCN: 5374358
Sun Feb 02 02:27:52 2014
Checker run found 1 new persistent data failures
Check Point是Oracle内部控制的一件大事。经过check point,Oracle要保证所有数据文件、控制文件SCN信息保持一致,和日志文件在恢复时间点(RBA+SCN)达成一致。这个过程就强制回去访问到数据文件,结果文件丢失被发现。
此后,查询出错。
SQL> select count(*) from rmtest.rm_tab;
select count(*) from rmtest.rm_tab
ORA-00376: 此时无法读取文件 11
ORA-01110: 数据文件 11: '/u01/oradata/WILSON/datafile/rmdtest01.dbf'
注意:如果放任不管,Oracle自动的incremental checkpoint也会有类似的效果。同时,周期性的Global Check也会发现“文件丢失了”。
这个时候,我们进行修复操作。使用文件描述符恢复文件的方法,第一步找到一个Oracle后台进程,最典型的就是dbwr。
[oracle@bspdev datafile]$ ps -ef | grep dbw
oracle 4806 1 0 02:12 ? 00:00:00 ora_dbw0_wilson
oracle 9076 4491 0 02:29 pts/0 00:00:00 grep dbw
使用lsof –p命令,找出dbwr进程对应的文件描述符信息。
[root@bspdev datafile]# lsof -p 4806
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
oracle 4806 oracle cwd DIR 253,0 4096 10574090 /u01/oracle/dbs
oracle 4806 oracle rtd DIR 253,0 4096 2 /
oracle 4806 oracle txt REG 253,0 173515991 10579756 /u01/oracle/bin/oracle
(篇幅原因,有省略……)
racle 4806 oracle 33uW REG 253,0 10493952 2978999 /u01/oradata/WILSON/datafile/mvtbltest01.dbf
oracle 4806 oracle 34uW REG 253,0 30416896 524875 /u01/oradata/WILSON/datafile/o1_mf_temp_7xt46489_.tmp
oracle 4806 oracle 35r REG 253,0 1074176 10595009 /u01/oracle/rdbms/mesg/oraus.msb
里面包括了dbwr打开的所有文件描述符,我们没有能看到删除的文件rmdtest01.dbf。文件描述符目录也没有相应的FD内容。说明:由于一些情况,Oracle进程将文件描述符删除了!
此时,我们检查文件状态。
SQL> select online_status from dba_data_files where file_id=11;
ONLINE_STATUS
-------------
RECOVER
文件已经被offline,强制剔除文件体系了。仔细想起来,这个过程是check point的一个结果。
Check Point的最终效果,是所有数据文件在文件头标注相同的SCN记录,之前脏块被写入到数据文件中。如果一个数据文件实体不存在了,这个操作一定不能完成。Oracle选择了一种方法,就是强制将这个文件“剔除”。所以我们在日志中,看到了下面一段内容。
ORA-01171: datafile 11 going offline due to error advancing checkpoint
被剔除的文件,Oracle关闭文件描述符也是可以理解的了。
这个实验失败,告诉我们一个道理:使用文件描述符进行数据恢复,并不是100%有效。如果时间很长,或者进行过很多特殊操作,这个微弱的文件描述符是会消失的!
下面我们进行一次成功的实验。
4、一次“成功”的实验
我们借助RMAN备份,恢复到实验前的状态。
[root@bspdev datafile]# ls
mvtbltest01.dbf o1_mf_temp_7xt46489_.tmp
o1_mf_example_7xt46m9x_.dbf o1_mf_undotbs1_7xt3yzl5_.dbf.bak
o1_mf_jpatest_87y6v8qc_.dbf o1_mf_undotbs1_92l5b0v4_.dbf
o1_mf_nbscommo_820frtg1_.dbf o1_mf_users_805nxydh_.dbf
o1_mf_nbscommo_820ft5y5_.dbf rmdtest01.dbf
o1_mf_sysaux_7xt3yzkb_.dbf tts_simple01.dbf
o1_mf_system_7xt3yzhj_.dbf
删除数据文件。
[root@bspdev datafile]# rm rmdtest01.dbf
rm: remove regular file `rmdtest01.dbf'? y
[root@bspdev datafile]# ls
mvtbltest01.dbf o1_mf_system_7xt3yzhj_.dbf
o1_mf_example_7xt46m9x_.dbf o1_mf_temp_7xt46489_.tmp
o1_mf_jpatest_87y6v8qc_.dbf o1_mf_undotbs1_7xt3yzl5_.dbf.bak
o1_mf_nbscommo_820frtg1_.dbf o1_mf_undotbs1_92l5b0v4_.dbf
o1_mf_nbscommo_820ft5y5_.dbf o1_mf_users_805nxydh_.dbf
o1_mf_sysaux_7xt3yzkb_.dbf tts_simple01.dbf
删除之后,借助文件描述符,我们还是可以查询的。
SQL> select count(*) from rmtest.rm_tab;
COUNT(*)
----------
145462
查找dbwr进程,确定进程编号。
[root@bspdev datafile]# ps -ef | grep dbw
oracle 9405 1 0 02:45 ? 00:00:00 ora_dbw0_wilson
root 9716 4466 0 02:56 pts/0 00:00:00 grep dbw
使用lsof –p确定文件描述符信息。
[root@bspdev datafile]# lsof -p 9405
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
oracle 9405 oracle cwd DIR 253,0 4096 10574090 /u01/oracle/dbs
(篇幅原因,有省略……)
oracle 9405 oracle 29uW REG 253,0 209723392 525165 /u01/oradata/WILSON/datafile/o1_mf_nbscommo_820frtg1_.dbf
oracle 9405 oracle 30uW REG 253,0 104865792 525166 /u01/oradata/WILSON/datafile/o1_mf_nbscommo_820ft5y5_.dbf
oracle 9405 oracle 31uW REG 253,0 104865792 525484 /u01/oradata/WILSON/datafile/o1_mf_jpatest_87y6v8qc_.dbf
oracle 9405 oracle 32uW REG 253,0 10493952 525541 /u01/oradata/WILSON/datafile/tts_simple01.dbf
oracle 9405 oracle 33uW REG 253,0 10493952 2978999 /u01/oradata/WILSON/datafile/mvtbltest01.dbf
oracle 9405 oracle 34uW REG 253,0 30416896 524875 /u01/oradata/WILSON/datafile/o1_mf_temp_7xt46489_.tmp
oracle 9405 oracle 35r REG 253,0 1074176 10595009 /u01/oracle/rdbms/mesg/oraus.msb
oracle 9405 oracle 36uW REG 253,0 1048584192 2979001 /u01/oradata/WILSON/datafile/rmdtest01.dbf (deleted)
注意:lsof是描述文件描述符最好的工具,其中包括FD列。我们从dbwr的连接中,找到了rmdtest01.dbf的信息行。其中FD=36uw。这个36就表示了连接文件的名称。
联合dbwr的进程编号9405,我们在目录/proc/9405/fd中找到所有的文件符。
[root@bspdev datafile]# cd /proc/9405/fd
[root@bspdev fd]# ls
0 10 12 14 16 18 2 21 23 25 27 29 30 32 34 36 5 7 9
1 11 13 15 17 19 20 22 24 26 28 3 31 33 35 4 6 8
Ls命令也可以列出信息。
[root@bspdev fd]# ls -l
total 0
lr-x------ 1 oracle oinstall 64 Feb 2 02:56 0 -> /dev/null
l-wx------ 1 oracle oinstall 64 Feb 2 02:56 1 -> /dev/null
lrwx------ 1 oracle oinstall 64 Feb 2 02:56 10 -> /u01/oracle/dbs/lkinstwilson (deleted)
lrwx------ 1 oracle oinstall 64 Feb 2 02:56 34 -> /u01/oradata/WILSON/datafile/o1_mf_temp_7xt46489_.tmp
lr-x------ 1 oracle oinstall 64 Feb 2 02:56 35 -> /u01/oracle/rdbms/mesg/oraus.msb
lrwx------ 1 oracle oinstall 64 Feb 2 02:56 36 -> /u01/oradata/WILSON/datafile/rmdtest01.dbf (deleted)
l-wx------ 1 oracle oinstall 64 Feb 2 02:56 9 -> /home/oracle/oradiag_oracle/diag/clients/user_oracle/host_1437849207_76/trace/ora_9293_3085993664.trm
这个36对应的文件还存在,就是已经被删除的那个rmdtest01.dbf文件。进行拷贝出来。
[root@bspdev fd]# cp 36 /u01/oradata/WILSON/datafile/rmdtest01res.dbf
[root@bspdev fd]#
拷贝出来之后,最好进行一定转换。先将其offline,之后进行控制文件中的rename动作,最后recover和online操作。具体流程常见笔者专门的文章(http://blog.itpub.net/17203031/viewspace-773628/)。
SQL> alter database datafile 11 offline;
Database altered
Rename操作。
--报错
SQL> alter database rename file '/u01/oradata/WILSON/datafile/rmdtest01.dbf' to '/u01/oradata/WILSON/datafile/rmdtest01res.dbf';
alter database rename file '/u01/oradata/WILSON/datafile/rmdtest01.dbf' to '/u01/oradata/WILSON/datafile/rmdtest01res.dbf'
ORA-01511: 重命名日志/数据文件时出错
ORA-01141: 重命名数据文件 11 时出错 - 未找到新文件 '/u01/oradata/WILSON/datafile/rmdtest01res.dbf'
ORA-01110: 数据文件 11: '/u01/oradata/WILSON/datafile/rmdtest01.dbf'
ORA-27041: 无法打开文件
Linux Error: 13: Permission denied
Additional information: 9
究其原因,是拷贝权限为root,需要手工修改所有权信息。
[root@bspdev datafile]# ls -l | grep rmd
-rw-r----- 1 root root 1048584192 Feb 2 03:01 rmdtest01res.dbf
[root@bspdev datafile]# chown oracle:oinstall rmdtest01res.dbf
[root@bspdev datafile]# ls -l | grep rmd
-rw-r----- 1 oracle oinstall 1048584192 Feb 2 03:01 rmdtest01res.dbf
Rename文件。
SQL> alter database rename file '/u01/oradata/WILSON/datafile/rmdtest01.dbf' to '/u01/oradata/WILSON/datafile/rmdtest01res.dbf';
Database altered
SQL> recover datafile 11;
Media recovery complete.
SQL> alter database datafile 11 online;
Database altered.
使用rman中的recovery advisor工具,来判断错误是否存在。
[oracle@bspdev ~]$ rman nocatalog
Recovery Manager: Release 11.2.0.1.0 - Production on Sun Feb 2 03:06:43 2014
Copyright (c) 1982, 2009, Oracle and/or its affiliates. All rights reserved.
RMAN> connect target /
connected to target database: WILSON (DBID=3906514064)
using target database control file instead of recovery catalog
RMAN> list failure;
no failures found that match specification
恢复完成,实验成功。
5、结论
本篇文章的书写,有一半目的是揭示文件内部的运行机制,另一半是记录下Linux下使用文件描述符FD来恢复数据的方法。我们说,从运维角度看,直接绕过数据库对OS进行文件操作是非常不成熟的做法,无论是出于什么目的。很多致命的错误都是一系列的rm造成的。愿文章描述的场景永不会在所有运维生产系统中出现!