测试场景: 某一个非核心表空间使用的数据文件丢失或损坏,无备份,有归档,对其进行恢复。
为什么一定是非核心表空间,因为SYSTEM这种系统表空间一旦损坏必须全库进行恢复,无法通过其他手段进行恢复。
敲重点:备份非常重要、备份非常重要、备份非常重要
没有备份、也没有归档是无法进行数据恢复的,能够在无备份情况下恢复 一定是开启了归档,并且归档日志保存完整。
SQL> alter database mount;
操作已执行
已用时间: 359.952(毫秒). 执行号:0.
SQL> alter database add archivelog 'type=local,dest=/dm8/arch1,file_size=64,space_limit=0';
操作已执行
已用时间: 0.712(毫秒). 执行号:0.
SQL> alter database archivelog;
操作已执行
已用时间: 0.312(毫秒). 执行号:0.
SQL> alter database open;
操作已执行
已用时间: 373.182(毫秒). 执行号:0.
SQL> select name,status$,arch_mode from v$database;
行号 NAME STATUS$ ARCH_MODE
---------- ---- ----------- ---------
1 PROD 4 Y
已用时间: 0.528(毫秒). 执行号:263982.
SQL>
RLOG_APPEND_LOGIC:(必须大于1才可以使用日志挖掘功能)
是否启用在日志中记录逻辑操作的功能, 取值范围 0、 1、 2、3
0: 不启用; 1、 2、 3 启用。
1: 如果有主键列, 记录 UPDATE 和 DELETE操作时只包含主键列信息,若没有主键列则包含所有列信息;
2:不论是否有主键列, 记录UPDATE 和 DELETE 操作时都包含所有列的信息;
3: 记录 UPDATE 时包含更新列的信息以及ROWID, 记录 DELETE 时只有 ROWID
SQL> sp_set_para_value(1,'RLOG_APPEND_LOGIC',1);
DMSQL 过程已成功完成
已用时间: 2.361(毫秒). 执行号:1403.
SQL> select para_name, para_value from v$dm_ini where para_name in ('ARCH_INI','RLOG_APPEND_LOGIC');
行号 PARA_NAME PARA_VALUE
---------- ----------------- ----------
1 RLOG_APPEND_LOGIC 1
2 ARCH_INI 1
已用时间: 2.443(毫秒). 执行号:1404.
在这一步里,我们要进行以下几步操作:
1、创建了一个名为TC01的表空间,创建一个用户TEST_TC 设置他的默认表空间为TC01。
2、然后在TEST_TC用户下创建一张表,在表里构造一些数据。
3、通过系统视图进行操作确认。
--创建表空间
SQL> create tablespace TC01 datafile '/dm8/data/PROD/TC01.DBF' size 50;
操作已执行
已用时间: 13.068(毫秒). 执行号:263974.
--创建用户,指定默认表空间
SQL> CREATE user TEST_TC IDENTIFIED BY TEST00001 DEFAULT TABLESPACE TC01 DEFAULT INDEX TABLESPACE TC01;
操作已执行
已用时间: 33.409(毫秒). 执行号:263975.
--在新建的用户下创建表
SQL> CREATE TABLE TEST_TC.T1 (ID INT , NAME VARCHAR(50));
操作已执行
已用时间: 51.978(毫秒). 执行号:263976.
--写入一条数据
SQL> INSERT INTO TEST_TC.T1 VALUES (1,'数据文件恢复测试');
影响行数 1
已用时间: 0.519(毫秒). 执行号:263977.
SQL> commit;
操作已执行
已用时间: 0.838(毫秒). 执行号:263978.
--通过视图查询TC01表空间下已经有了T1表
SQL> select tablespace_NAME,segment_name,segment_type from dba_segments where tablespace_name='TC01';
行号 TABLESPACE_NAME SEGMENT_NAME SEGMENT_TYPE
---------- --------------- ------------ ------------
1 TC01 T1 TABLE
已用时间: 87.608(毫秒). 执行号:263979.
--查询TC01表空间的数据文件路径
SQL> select tablespace_name,file_name,round(bytes/1024/1024) mb from dba_data_files where tablespace_name='TC01';
行号 TABLESPACE_NAME FILE_NAME MB
---------- --------------- ----------------------- --
1 TC01 /dm8/data/PROD/TC01.DBF 50
已用时间: 3.026(毫秒). 执行号:263980.
在这里我们模拟一些业务操作,以便我们验证数据恢复是否成功。
SQL> update TEST_TC.t1 set id=id+1;
影响行数 1
已用时间: 0.547(毫秒). 执行号:263983.
SQL> update TEST_TC.t1 set id=id+1;
影响行数 1
已用时间: 0.547(毫秒). 执行号:263983.
SQL> commit;
操作已执行
已用时间: 0.672(毫秒). 执行号:263984.
SQL> select * from TEST_TC.t1;
行号 ID NAME
---------- ----------- ------------------------
1 3 数据文件恢复测试
已用时间: 0.951(毫秒). 执行号:263985.
通过控制台调用操作系统命令,使用其他文件覆盖数据文件TC01.DBF
SQL> host cp /dm8/bin/disql /dm8/data/PROD/TC01.DBF
SQL>
数据库无法open
SQL> shutdown immediate;
操作已执行
已用时间: 0.224(毫秒). 执行号:0.
SQL> startup
2 ;
连接丢失
SQL>
数据库服务启动失败
[root@bogon bin]# systemctl start DmServiceTEST.service
Job for DmServiceTEST.service failed because the control process exited with error code.
See "systemctl status DmServiceTEST.service" and "journalctl -xe" for details.
[root@bogon bin]#
查看启动过程无法完成,报错信息也不是很有针对性,嗯~ o( ̄▽ ̄)o
[root@bogon bin]# ./dmserver path=/dm8/data/PROD/dm.ini
file dm.key not found, use default license!
version info: develop
DM Database Server x64 V8 1-2-18-21.11.11-150669-10013-ENT startup...
Normal of FAST
Normal of DEFAULT
Normal of RECYCLE
Normal of KEEP
Normal of ROLL
Database mode = 0, oguid = 0
License will expire on 2022-11-11
begin redo pwr log collect, last ckpt lsn: 173291 ...
redo pwr log collect finished
main rfil[/dm8/data/PROD/PROD01.log]'s grp collect 0 valid pwr record, discard 0 invalid pwr record
EP[0]'s cur_lsn[173291], file_lsn[173291]
begin redo log recover, last ckpt lsn: 173291 ...
redo log recover finished
ndct db load finished
浮点数例外
现在整个数据库服务已经无法启动了,我们要通过修改控制文件先把数据库服务恢复。
表空间、数据库文件信息都写在控制文件里,我们只需要删除控制文件中损坏的数据文件信息就可以让服务启动。
控制文件是一个ctl结尾的二进制文件,无法直接进行修改,需要转为文本。
[dmdba@bogon bin]$ ./dmctlcvt type=1 src=/dm8/data/PROD/dm.ctl dest=/dm8/dmctl.txt
DMCTLCVT V8
convert ctl to txt success!
在控制文件里 找到 TC01 的描述和 /dm8/data/PROD/TC01.DBF 数据文件的表述,整段全部都删掉。
[dmdba@bogon bin]$ vi /dm8/dmctl.txt
#===============================================
# table space name
ts_name=TC01
# table space ID
ts_id=12
# table space status
ts_state=0
# table space cache
ts_cache=NORMAL
# DSC node number
ts_nth=0
# table space create time
ts_create_time=DATETIME '2022-1-18 19:22:22'
# table space modify time
ts_modify_time=DATETIME '2022-1-18 19:22:22'
# table space encrypt flag
ts_encrypt_flag=0
# table space copy num
ts_copy_num=0
# table space region size flag
ts_size_flag=0
#-----------------------------------------------
# file path
fil_path=/dm8/data/PROD/TC01.DBF
# mirror path
mirror_path=
# file id
# whether the file is auto extend
autoextend=1
# file create time
fil_create_time=DATETIME '2022-1-18 19:22:22'
# file modify time
fil_modify_time=DATETIME '2022-1-18 19:22:22'
# the max size of file
fil_max_size=0
# next size of file
fil_next_size=0
#===============================================
将文件文本重新转为ctl文件,覆盖原来的控制文件
[dmdba@bogon bin]$ ./dmctlcvt type=2 src=/dm8/dmctl.txt dest=/dm8/data/PROD/dm.ctl
DMCTLCVT V8
convert txt to ctl success!
重启数据库成功,o( ̄▽ ̄)ブ
[root@bogon bin]# ./dmserver path=/dm8/data/PROD/dm.ini
file dm.key not found, use default license!
version info: develop
DM Database Server x64 V8 1-2-18-21.11.11-150669-10013-ENT startup...
Normal of FAST
Normal of DEFAULT
Normal of RECYCLE
Normal of KEEP
Normal of ROLL
Database mode = 0, oguid = 0
License will expire on 2022-11-11
begin redo pwr log collect, last ckpt lsn: 173291 ...
redo pwr log collect finished
main rfil[/dm8/data/PROD/PROD01.log]'s grp collect 0 valid pwr record, discard 0 invalid pwr record
EP[0]'s cur_lsn[173291], file_lsn[173291]
begin redo log recover, last ckpt lsn: 173291 ...
redo log recover finished
ndct db load finished
ndct fill fast pool finished
iid page's trxid[49430]
NEXT TRX ID = 49431
pseg_collect_mgr_items, total collect 0 active_trxs, 0 cmt_trxs, 0 pre_cmt_trxs, 0 active_pages, 0 cmt_pages, 0 pre_cmt_pages, 0 mgr pages, 0 mgr recs!
total 0 active crash trx, pseg_crash_trx_rollback sys_only(0) begin ...
pseg_crash_trx_rollback end, total 0 active crash trx, include 0 empty_trxs, 0 empty_pages which only need to delete mgr recs.
pseg_crash_trx_rollback end
pseg recv finished
nsvr_startup end.
aud sys init success.
aud rt sys init success.
systables desc init success.
ndct_db_load_info success.
nsvr_process_before_open begin.
nsvr_process_before_open success.
total 0 active crash trx, pseg_crash_trx_rollback sys_only(0) begin ...
pseg_crash_trx_rollback end, total 0 active crash trx, include 0 empty_trxs, 0 empty_pages which only need to delete mgr recs.
pseg_crash_trx_rollback end
SYSTEM IS READY.
登录数据库正常
[dmdba@bogon bin]$ ./disql SYSDBA/dameng123@localhost:5237
服务器[localhost:5237]:处于普通打开状态
登录使用时间 : 1.411(ms)
disql V8
SQL>
经过检查 用户、表空间、表 都已经不见了
SQL> select username,default_tablespace from dba_users where username='TEST_TC';
未选定行
已用时间: 5.974(毫秒). 执行号:500.
SQL> select tablespace_name from dba_tablespaces WHERE tablespace_name='TC01';
未选定行
已用时间: 2.519(毫秒). 执行号:501.
SQL> select tablespace_NAME,segment_name,segment_type from dba_segments where segment_name='T1';
未选定行
已用时间: 120.287(毫秒). 执行号:502.
SQL>
下面开始进行数据恢复。
我们这里对用户进行了重命名TEST_TC 改为了 TEST_TC01
SQL> create tablespace TC01 datafile '/dm8/data/PROD/TC01_1.DBF' size 50;
操作已执行
已用时间: 63.315(毫秒). 执行号:508.
SQL> CREATE user TEST_TC01 IDENTIFIED BY TEST00001 DEFAULT TABLESPACE TC01 DEFAULT INDEX TABLESPACE TC01;
操作已执行
已用时间: 51.114(毫秒). 执行号:509.
通过归档日志的创建日志进行筛选
SQL> select NAME,FIRST_TIME from v$archived_log where FIRST_TIME >'2022-01-19 12:00:00';
行号 NAME FIRST_TIME
---------- ------------------------------------------------------------------------ --------------------------
1 /dm8/arch1/ARCHIVE_LOCAL3_0x6910E0E3[0]_2022-01-19_13-03-59.log 2022-01-19 13:03:14.606557
2 /dm8/arch1/ARCHIVE_LOCAL3_0x6910E0E3[0]_2022-01-19_13-39-42.log 2022-01-19 13:37:31.975000
3 /dm8/arch1/ARCHIVE_LOCAL3_0x6910E0E3[0]_2022-01-19_13-45-56.log 2022-01-19 13:39:59.013000
3 rows got
已用时间: 1.523(毫秒). 执行号:520.
这里有几个日志文件就需要执行几次。
SQL> call dbms_logmnr.add_logfile('/dm8/arch1/ARCHIVE_LOCAL3_0x6910E0E3[0]_2022-01-19_13-03-59.log');
DMSQL 过程已成功完成
已用时间: 42.461(毫秒). 执行号:511.
SQL> call dbms_logmnr.add_logfile('/dm8/arch1/ARCHIVE_LOCAL3_0x6910E0E3[0]_2022-01-19_13-39-42.log');
DMSQL 过程已成功完成
已用时间: 0.512(毫秒). 执行号:512.
SQL> call dbms_logmnr.add_logfile('/dm8/arch1/ARCHIVE_LOCAL3_0x6910E0E3[0]_2022-01-19_13-45-56.log');
DMSQL 过程已成功完成
已用时间: 1.070(毫秒). 执行号:513.
SQL> Call dbms_logmnr.start_logmnr(
options => 2128
,starttime=> to_date('2022-01-18 00:00:00','yyyy-mm-dd hh24:mi:ss')
,endtime => to_date('2022-01-18 23:59:59','yyyy-mm-dd hh24:mi:ss')
);
DMSQL 过程已成功完成
已用时间: 188.525(毫秒). 执行号:514.
options整个参数用于配置挖掘模式。
提供如下表所列的可选模式,各模式可以通过 + 或者按位或来进行组合。
其它位的值 如 1、 4、 8等目前不支持,配置后不会报错,但是没有效果。
例如,组合全部模式,则取值 计算方法为 16+64+2048=2128,那么 OPTIONS值取就是 2128。
这时已经可以看到我们之前执行的语句了。
SQL> select sql_redo from V$logmnr_contents where sql_redo like '%T1%' ORDER BY timestamp;
行号 SQL_REDO
---------- -----------------------------------------------------------------------------------------
1 CREATE TABLE TEST_TC.T1 (ID INT , NAME VARCHAR(50))
2 INSERT INTO "TEST_TC"."T1"("ID", "NAME") VALUES(1, '数据文件恢复测试')
3 UPDATE "TEST_TC"."T1" SET "ID" = 2 WHERE "ID" = 1 AND "NAME" = '数据文件恢复测试'
4 UPDATE "TEST_TC"."T1" SET "ID" = 3 WHERE "ID" = 2 AND "NAME" = '数据文件恢复测试'
因为我们重建用户的时候修改了用户名,可以通过字符串替换的方式修改sql语句。
另外我们发现上面的语句查出来的数据结尾都没有分号,为了方便执行,我们也把分号拼接上。
SQL> select replace(sql_redo,'TEST_TC','TEST_TC01')||';' from V$logmnr_contents where sql_redo like '%T1%' ORDER BY timestamp;
行号 REPLACE(SQL_REDO,'TEST_TC','TEST_TC01')||';'
---------- --------------------------------------------------------------------------------------------
1 CREATE TABLE TEST_TC01.T1 (ID INT , NAME VARCHAR(50));
2 INSERT INTO "TEST_TC01"."T1"("ID", "NAME") VALUES(1, '数据文件恢复测试');
3 UPDATE "TEST_TC01"."T1" SET "ID" = 2 WHERE "ID" = 1 AND "NAME" = '数据文件恢复测试';
4 UPDATE "TEST_TC01"."T1" SET "ID" = 3 WHERE "ID" = 2 AND "NAME" = '数据文件恢复测试';
我们测试的环境SQL脚本比较少,所以手动执行都可以。
如果有大量的SQL语句需要执行,就需要生成一个脚本文件来批量运行。
首先:格式处理,除掉表头、去掉行号、去掉返回的行数等无用的提示。
这样我们就获得了干干净净的SQL查询结果集了
SQL> set HEA off
SQL> select replace(sql_redo,'TEST_TC','TEST_TC01')||';' from V$logmnr_contents where sql_redo like '%T1%' ORDER BY timestamp;
1 CREATE TABLE TEST_TC01.T1 (ID INT , NAME VARCHAR(50));
2 INSERT INTO "TEST_TC01"."T1"("ID", "NAME") VALUES(1, '数据文件恢复测试');
3 UPDATE "TEST_TC01"."T1" SET "ID" = 2 WHERE "ID" = 1 AND "NAME" = '数据文件恢复测试';
4 UPDATE "TEST_TC01"."T1" SET "ID" = 3 WHERE "ID" = 2 AND "NAME" = '数据文件恢复测试';
已用时间: 51.065(毫秒). 执行号:527.
SQL> set LINESHOW off
SQL> /
CREATE TABLE TEST_TC01.T1 (ID INT , NAME VARCHAR(50));
INSERT INTO "TEST_TC01"."T1"("ID", "NAME") VALUES(1, '数据文件恢复测试');
UPDATE "TEST_TC01"."T1" SET "ID" = 2 WHERE "ID" = 1 AND "NAME" = '数据文件恢复测试';
UPDATE "TEST_TC01"."T1" SET "ID" = 3 WHERE "ID" = 2 AND "NAME" = '数据文件恢复测试';
已用时间: 47.678(毫秒). 执行号:530.
SQL> set TIMING off
SQL> /
CREATE TABLE TEST_TC01.T1 (ID INT , NAME VARCHAR(50));
INSERT INTO "TEST_TC01"."T1"("ID", "NAME") VALUES(1, '数据文件恢复测试');
UPDATE "TEST_TC01"."T1" SET "ID" = 2 WHERE "ID" = 1 AND "NAME" = '数据文件恢复测试';
UPDATE "TEST_TC01"."T1" SET "ID" = 3 WHERE "ID" = 2 AND "NAME" = '数据文件恢复测试';
SQL>
将SQL的输出导出到/dm8/t1.sql 这个文件里。 输出完成后,对1.sql还要进行简单的编辑。
这个文件,把第一行的查询语句,和最后一个的spool off 关闭语句删掉 就可以了。
SQL> spool /dm8/t1.sql append
SQL> select replace(sql_redo,'TEST_TC','TEST_TC01')||';' from V$logmnr_contents where sql_redo like '%T1%' ORDER BY timestamp;
CREATE TABLE TEST_TC01.T1 (ID INT , NAME VARCHAR(50));
INSERT INTO "TEST_TC01"."T1"("ID", "NAME") VALUES(1, '数据文件恢复测试');
UPDATE "TEST_TC01"."T1" SET "ID" = 2 WHERE "ID" = 1 AND "NAME" = '数据文件恢复测试';
UPDATE "TEST_TC01"."T1" SET "ID" = 3 WHERE "ID" = 2 AND "NAME" = '数据文件恢复测试';
SQL> spool off
通过 start 调用sql脚本,所有语句就都执行了。别忘了commti 提交事务。
SQL> start /dm8/t1.sql
SQL> CREATE TABLE TEST_TC01.T1 (ID INT , NAME VARCHAR(50));
操作已执行
SQL> INSERT INTO "TEST_TC01"."T1"("ID", "NAME") VALUES(1, '数据文件恢复测试');
影响行数 1
SQL> UPDATE "TEST_TC01"."T1" SET "ID" = 2 WHERE "ID" = 1 AND "NAME" = '数据文件恢复测试';
影响行数 1
SQL> UPDATE "TEST_TC01"."T1" SET "ID" = 3 WHERE "ID" = 2 AND "NAME" = '数据文件恢复测试';
影响行数 1
SQL> commit;
操作已执行
SQL>
最后 我们通过 查询T1 表里的数据来验证恢复结果。
可以看到已经恢复了文件损坏前的数据了。
SQL> select * from TEST_TC01.T1;
3 数据文件恢复测试
恢复完别忘了对数据库进行备份。
以下为 备份数据库、备份表空间、备份表的语句。
SQL> backup database backupset '/dm8/backup/20220119' ;
操作已执行
SQL> backup tablespace TC01 backupset '/dm8/backup/20220119_TC01';
操作已执行
SQL> backup table test_tc01.t1 backupset '/dm8/backup/20220119_TC01_T1';
操作已执行
达梦技术社区地址:https://eco.dameng.com