在 Oracle 归档模式下直接 rm dbf 数据文件并重启数据库还有救吗?

2023-11-01 18:33:37 浏览数 (1)

作者 | JiekeXu

来源 |公众号 JiekeXu DBA之路(ID: JiekeXu_IT)

如需转载请联系授权 | (个人微信 ID:JiekeXu_DBA)

大家好,我是 JiekeXu,很高兴又和大家见面了,今天和大家一起来看看在 Oracle 归档模式下直接 rm dbf 数据文件并重启数据库还有救吗?欢迎点击上方蓝字“JiekeXu DBA之路”关注我的公众号,标星或置顶,更多干货第一时间到达!

在 Oracle 归档模式下直接 rm data.dbf 数据文件并重启数据库还有救吗?为何会有这样的问题,要追溯到上周一位朋友咨询的事情,他那里一个 Oracle 单机的环境因为表空间不足了,需要扩容表空间,则通过添加表空间数据文件的命令加入了一个数据文件,执行成功后呢便可以继续写入数据了。

不一会儿发现自己添加的数据文件路径不对,将其加到根文件系统了,而不是统一存放数据的目录,他想这是刚加的数据文件,发现路径不对,则直接用类似于 rm data.dbf 的命令给删除了,这个时候导致数据库宕机了【PS:不知为何他这个数据库会宕机,按理来说从操作系统删除一个数据文件不会导致 Oracle 数据库宕机的】。他肯定没有看我以前写的 案例|RAC 添加表空间误将数据文件放本地处理办法,不然也不会出现这样的问题。

一、模拟问题

因为不是我个人亲身经历的事情,没有全部的素材,这里我用我的 Oracle 单机 19.12 版本的测试环境模拟一下大概的流程。

模拟业务表空间和业务用户,且可能有数据写入。

代码语言:javascript复制
--模拟业务表空间
create tablespace jiekexu_data datafile '/u01/app/oracle/oradata/TESTOGG/jiekexu_data01.dbf' size 10m;

--已经提前在 users 表空间创建了 jiekexu 用户,这里将其修改为新建的表空间 JIEKEXU_DATA 

alter user jiekexu quota unlimited on jiekexu_data;
alter user jiekexu default tablespace jiekexu_data;  
  
--连接到新建用户查看表及新建表模拟数据写入
        
conn jiekexu/jiekexu;
select count(*) from JiekeXu.T1;

  COUNT(*)
----------
        12
    
create table t2 as select * from t1;
代码语言:javascript复制

下面模拟因表空间不足了扩容表空间的操作且可能还有业务数据写入的操作。

代码语言:javascript复制
select file_id,file_name,bytes/1024/1024/1024 Gb from dba_data_files where tablespace_name='JIEKEXU_DATA';
alter tablespace jiekexu_data add datafile '/u01/app/oracle/oradata/TESTOGG/jiekexu_data02.dbf' size 1m;

insert into t2 select * from t2;
insert into t2 select * from t2;
insert into t2 select * from t2;
insert into t2 select * from t2;
insert into t2 select * from t2;
insert into t2 select * from t2;
insert into t2 select * from t2;
insert into t2 select * from t2;
insert into t2 select * from t2;
insert into t2 select * from t2;
commit;

select count(*) from t2;

  COUNT(*)
----------
     12288
代码语言:javascript复制

接下来啊发现路径错误,操作系统层直接删除新加的数据文件,我这里用 mv 代替了,养成良好的习惯,保证数据安全。他则是直接使用了 rm -rf jiekexu_data02.dbf 命令删除了文件,然后登录数据库时发现宕机了。

代码语言:javascript复制
$ mv /u01/app/oracle/oradata/TESTOGG/jiekexu_data02.dbf /u01/app/oracle/oradata/TESTOGG/jiekexu_data02.dbf_bak

$ sqlplus / as sysdba

SQL*Plus: Release 19.0.0.0.0 - Production on Mon Oct 23 17:13:26 2023
Version 19.12.0.0.0

Copyright (c) 1982, 2021, Oracle.  All rights reserved.

Connected to an idle instance.

17:13:27 SYS@test>
代码语言:javascript复制

我这里模拟的是删除文件后还有可能有数据写入,因为正常情况下 Oracle 实例是不会宕机的。

代码语言:javascript复制
--再次进入数据库插入数据

conn jiekexu/jiekexu;
insert into t2 select * from t2;
insert into t2 select * from t2;
insert into t2 select * from t2;
insert into t2 select * from t2;
insert into t2 select * from t2;
commit;
代码语言:javascript复制

但是在执行上面的插入的时候,我这里出现报错了,不能够打开删除的这个文件,看来是插入数据的时候已经往这个数据文件写数据了,但是 OS 层被删除了,直接报错找不到文件了。前面两条 insert 插入成功,后面的 3 条插入失败,这也算符合预期,接着往下看吧。

代码语言:javascript复制
14:25:25 SYS@testogg> conn jiekexu/jiekexu;
insert into t2 select * from t2;
insert into t2 select * from t2;
insert into t2 select * from t2;
insert into t2 select * from t2;
insert into t2 select * from t2;
Connected.
14:25:27 JIEKEXU@testogg> 
12288 rows created.

Elapsed: 00:00:00.03
14:25:27 JIEKEXU@testogg> 
24576 rows created.

14:25:27 JIEKEXU@testogg> insert into t2 select * from t2
*
ERROR at line 1:
ORA-01116: error in opening database file 31
ORA-01110: data file 31: '/u01/app/oracle/oradata/TESTOGG/jiekexu_data02.dbf'
ORA-27041: unable to open file
Linux-x86_64 Error: 2: No such file or directory
Additional information: 3

14:25:27 JIEKEXU@testogg> insert into t2 select * from t2
*
ERROR at line 1:
ORA-01116: error in opening database file 31
ORA-01110: data file 31: '/u01/app/oracle/oradata/TESTOGG/jiekexu_data02.dbf'
ORA-27041: unable to open file
Linux-x86_64 Error: 2: No such file or directory
Additional information: 3

14:25:27 JIEKEXU@testogg> insert into t2 select * from t2
*
ERROR at line 1:
ORA-01116: error in opening database file 31
ORA-01110: data file 31: '/u01/app/oracle/oradata/TESTOGG/jiekexu_data02.dbf'
ORA-27041: unable to open file
Linux-x86_64 Error: 2: No such file or directory
Additional information: 3

14:25:27 JIEKEXU@testoggcommit;
14:25:34   2  
14:25:37 JIEKEXU@testogg> select count(*) from t2;

  COUNT(*)
----------
     49152
14:26:01 JIEKEXU@testogg> commit;

Commit complete.
代码语言:javascript复制

查看表空间使用率才用到 19.32% 但是上面的插入操作却报错了,找不到新加的数据文件。

代码语言:javascript复制
代码语言:javascript复制
14:26:18 SYS@testogg> SELECT a.tablespace_name,round(total/1024/1024/1024) "Total g", 
14:27:39   2  round(free/1024/1024/1024) "Free g",ROUND((total-free)/total,4)*100 "USED%" 
14:27:39   3  FROM (SELECT tablespace_name,SUM(bytes) free FROM DBA_FREE_SPACE 
14:27:39   4  GROUP BY tablespace_name ) a, 
14:27:39   5  (SELECT tablespace_name,SUM(bytes) total FROM DBA_DATA_FILES 
14:27:39   6  GROUP BY tablespace_name) b 
14:27:39   7  WHERE a.tablespace_name=b.tablespace_name  and b.tablespace_name='JIEKEXU_DATA';

TABLESPACE_NAME                   Total g     Free g      USED%
------------------------------ ---------- ---------- ----------
JIEKEXU_DATA                            0          0      19.32

查看 alert 告警日志发现也是找不到文件了,这里提示找不到 31 号文件。但是在他的库中不仅看到找不到数据文件且数据库直接宕机了。

代码语言:javascript复制
代码语言:javascript复制
2023-10-20T14:25:27.476828 08:00
Errors in file /u01/app/oracle/diag/rdbms/testogg/testogg/trace/testogg_mz00_14146.trc:
ORA-01110: data file 31: '/u01/app/oracle/oradata/TESTOGG/jiekexu_data02.dbf'
ORA-01565: error in identifying file '/u01/app/oracle/oradata/TESTOGG/jiekexu_data02.dbf'
ORA-27037: unable to obtain file status
Linux-x86_64 Error: 2: No such file or directory
Additional information: 7
Checker run found 1 new persistent data failures
2023-10-20T14:26:55.856636 08:00
Control autobackup written to DISK device

handle '/u01/app/oracle/flash_recovery_area/TESTOGG/autobackup/2023_10_20/o1_mf_s_1150727215_lm47fhqr_.bkp'
代码语言:javascript复制
关闭数据库

这个时候我们手动模拟关闭数据库吧。

正常 shu immediate 无法关闭数据库,这里只能用 shutdown abort 关闭数据库了。

二、解决问题

mount 数据库 offline 数据文件

这个时候我们启动数据库到 mount,然后先 offline 这个数据文件再打开数据库。

注意:对于归档模式 alter database datafile ‘file_name’ offline 和 offline drop 没有什么区别,因为 offline 之后都需要进行 recover 才可以 online。

代码语言:javascript复制
alter database datafile 'file_name' offline;
or
alter database datafile 'file_name' offline drop;
代码语言:javascript复制

offline drop 命令不会删除数据文件,只是将数据文件的状态更改为 recover。offline drop 命令相当于把一个数据文件至于离线状态,并且需要恢复,并非删除数据文件。 数据文件的相关信息还会存在数据字典和控制文件中。

后台告警日志有如下信息

代码语言:javascript复制
user-specified limit on the amount of space that will be used by this
database for recovery-related files, and does not reflect the amount of
space available in the underlying filesystem or ASM diskgroup.
2023-10-20T15:01:45.036618 08:00
Errors in file /u01/app/oracle/diag/rdbms/testogg/testogg/trace/testogg_mz01_18870.trc:
ORA-01110: data file 31: '/u01/app/oracle/oradata/TESTOGG/jiekexu_data02.dbf'
ORA-01565: error in identifying file '/u01/app/oracle/oradata/TESTOGG/jiekexu_data02.dbf'
ORA-27037: unable to obtain file status
Linux-x86_64 Error: 2: No such file or directory
Additional information: 7
代码语言:javascript复制
***删除表空间数据文件***

那么如何删除表空间中的数据文件呢?注意:生产环境慎用这个命令!!!

代码语言:javascript复制
alter tablesapce 'tablespace_name' drop datafile 'datafile_name';
代码语言:javascript复制

该语句会删除磁盘上的文件并更新控制文件和数据字典中的信息,删除之后的原数据文件序列号可以重用。

注意,该语句只能是 datafile online 的时候才可以使用。如果说对应的数据文件已经是 offline for drop,那么仅针对 dictionary managed tablespaces 可用。

违反下列任何一个条件,该 datafile 均不能被 drop: 1)必须为空,否则会报:ORA-03262: the file is non-empty。值得注意的是,non-empty 的含义是有 extent 被分配给了 table,而不是该 table 中有无 rows,此时若是使用 drop table xxx 是不行的,必须使用 drop table xxx purge;或者在已经使用了 drop table xxx 的情况下,再使用 purge table “xxx表在回收站中的名称”来 purge 该表,否则空间还是不释放,datafile 依然 drop 不掉。

2)不能是所属表空间的第一个数据文件 以上两者可以通过 drop tablespace 来达到目的。

3)不能在 read-only 表空间中。----经测试只读表空间是可以的

4)不能被 offline,否则会报:ORA-03264: cannot drop offline datafile of locally managed tablespace

5)系统表空间 Cannot be a datafile that is part of the system tablespace, even if it is not the first datafile of the system tablespace ----该条来源于 How to Drop a Datafile From a Tablespace (文档 ID 111316.1)

接下来我们已经打开数据库了,但是 31 号数据文件还是 offline 的,要是这个数据文件上写入了新的数据,那么如果没有开归档没有任何备份的情况下有可能就会丢失。所幸运的是数据库处于归档模式,且保留了两三天的归档日志,可以从归档日志中 recover 恢复数据文件,然后再将其 online 则恢复正常。

重建数据文件的语法
代码语言:javascript复制
代码语言:javascript复制
alter database create datafile '/u01/app/oracle/oradata/TESTOGG/jiekexu_data02.dbf'  [as '/data/oradata/jiekexu_data02.dbf'];

这个语法通常用在数据文件损坏,但没有备份(数据文件)的情况下,但是有完整的归档日志,可以通过归档日志来恢复数据文件!也就是说是产生一个新的数据文件,可以和原路径一样,也可以不一样,使用场景一般是数据文件所在的磁盘损坏,不能恢复到原来文件(或者意外 rm -rf data01.dbf 删除了),而在原来目录下或者其他路径下产生一个新的数据文件!还有一个可能是备库磁盘满了主库添加的数据文件无法传到备库,也可以使用此语法。

代码语言:javascript复制
alter database create datafile 31;
代码语言:javascript复制
alter database create datafile 的作用

Creates a new empty datafile in place of an old one–useful to re-create a datafile that was lost with no backup.

前提是数据库开启归档模式且自原数据文件创建以来的 archive log 都可用.

该命令通过查看 control file 提取出创建文件的 DDL命令,初始化创建该文件(此刻文件内容为空),然后利用 archive log 重做所有的 redo.可在没有数据文件备份时使用。

recover 恢复数据文件

这个时候我们利用归档日志恢复被删除的数据文件,注意归档日志应该是数据文件被删除时到此刻的日志都保留,不能中断或者有删除。

代码语言:javascript复制
recover datafile 31;
代码语言:javascript复制

有可能在恢复的时候会报错 ORA-00279、ORA-00289、ORA-00280 然后我们接着选择 AUTO 自动应用归档日志完成整个恢复操作。

代码语言:javascript复制
Specify log: {=suggested | filename | AUTO | CANCEL}
AUTO
Log applied.
Media recovery complete.
代码语言:javascript复制
online 数据文件

等待上步操作完成,我们则可以将删除的数据 online 了。

代码语言:javascript复制
alter database datafile 31 online;
代码语言:javascript复制
select file#,status,name,online_time from v$datafile where status='RECOVER';
select file#,status from v$datafile_header where file#=31;
代码语言:javascript复制
代码语言:javascript复制
15:23:17 SYS@testogg> select file#,status from v$datafile_header where file#=31;

     FILE# STATUS
---------- -------
        31 ONLINE

15:23:19 SYS@testogg> c/datafile_header/datafile
  1* select file#,status from v$datafile where file#=31
15:23:35 SYS@testogg> /

     FILE# STATUS
---------- -------
        31 ONLINE
代码语言:javascript复制

三、检查数据完整性

切归档打检查点

代码语言:javascript复制
alter system switch logfile;
alter system switch logfile;
alter system checkpoint;
alter system switch logfile;
alter system checkpoint;
代码语言:javascript复制

查看数据文件物理位置,启停数据库(非必须)

代码语言:javascript复制
代码语言:javascript复制
15:38:07 JIEKEXU@testogg> !ls -l /u01/app/oracle/oradata/TESTOGG/jiekexu_data02.dbf
-rw-r----- 1 oracle oinstall 1056768 Oct 20 15:33 /u01/app/oracle/oradata/TESTOGG/jiekexu_data02.dbf
15:39:38 JIEKEXU@testogg> conn / as sysdba
Connected.
15:39:46 SYS@testogg> shu immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
15:40:16 SYS@testogg> startup
ORACLE instance started.

Total System Global Area 6878657840 bytes
Fixed Size                  8912176 bytes
Variable Size            4143972352 bytes
Database Buffers         2717908992 bytes
Redo Buffers                7864320 bytes
Database mounted.
Database opened.
15:41:32 SYS@testogg> exit 
Disconnected from Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.12.0.0.0
19coggtest:/u01/app/oracle/oradata/TESTOGG(testogg)$ ll jiekexu*
-rw-r----- 1 oracle oinstall 10493952 Oct 20 15:41 jiekexu_data01.dbf
-rw-r----- 1 oracle oinstall  1056768 Oct 20 15:41 jiekexu_data02.dbf
-rw-r----- 1 oracle oinstall  1056768 Oct 20 14:30 jiekexu_data02.dbf_bak     --恢复出来的文件和原来 mv 的大小一致
代码语言:javascript复制

最后的提醒: 1.在数据库出现故障之时,尽可能保护现场,做操作之前要考虑后果别百度了就不分青红皂白的直接操作,导致不可逆的破坏,数据可能永久性丢失 2.使用 alter database create datafile 命令之前需要慎重,评估是否所有的归档日志都存在

参考链接

代码语言:javascript复制
HOW TO RECOVER OFFLINE DROPPED DATAFILE IN ARCHIVELOG MODE (文档 ID 286355.1)
https://docs.oracle.com/en/database/oracle/oracle-database/23/admin/managing-tablespaces.html#GUID-F9840816-246E-46DF-9B85-A0BFB3E0E0D8
https://www.xifenfei.com/2016/10/alter-database-create-datafile-recovery.html
http://blog.itpub.net/26736162/viewspace-2124605
https://www.modb.pro/db/47441
代码语言:javascript复制

全文完,希望可以帮到正在阅读的你,如果觉得此文对你有帮助,可以分享给你身边的朋友,同事,你关心谁就分享给谁,一起学习共同进步~~~

0 人点赞