이번 장에서는 오라클 장애 중에서 가장 난해한 부분 중 한가지 인 Block Corruption 장애를 살펴보겠습니다.
이번 장에서 살펴볼 내용을 간단히 정리하면 아래와 같습니다.
데이터가 저장되는 Block 들에 corruption 이 발생 할 경우에 Recovery 할 수 있는
가장 보편적이고 일반적인 기술들에 대해서 실습을 통해서 알아봅니다.
1. DBVerify 를 이용한 Block Recovery
2. DBMS_REPAIR 을 이용한 Block Recovery
그리고 마지막으로 공식적으로 문서화 되어 있지 않지만 아주 중요하고 막강한 Block Recovery tool 인
BBED 라는 툴을 살펴보고 BBED 툴을 이용하여 여러 가지 장애가 발생할 경우 어떻게 복구를 할 수 있는지
예를 통해서 Block Recovery 를 마무리 하겠습니다.
3. BBED를 이용한 Block recovery
Block Corruption 장애를 해결하기 위해서는 데이터가 저장되어 있는 오라클 Block 의 구조를 잘 아셔야 하며
여기서 지면 관계상 언급하지 못한 오라클의 저장구조에 대한 설명은 저자의 다른 저서인 오라클 관리 실무 책을 참고하시기 바랍니다.
1. DBVERIFY 를 이용한 Block 관리
DBVerify 유틸리티는 오라클 7.3.2 버전부터 현재까지 제공되고 있는 Data file Block과 index file block, undo block 등을 점검해 주는 유틸리티입니다.
이 유틸리티는 Database 가 Open 되어 있는 상태에서 사용하는 유틸리티라서 DB의 중단 없이 점검할 수 있습니다.
대신 점검 중인 데이터파일은 Read-Only 가 되기 때문에 만약 점검 중일 때 DML 작업이 발생하면 잠시 작업이 중단되었다가 다시 실행됩니다.
- 사용 문법 : dbv file= file_name [options]
1) 주요 옵션들
- FILE: 점검해야 할 파일명을 적습니다.
- START : 점검을 시작할 블록 번호를 적습니다.
기본값은 해당 파일의 첫번째 블록입니다.
- END : 점검을 종료할 블록 번호를 적습니다.
기본값은 해당 파일의 마지막 블록입니다
- BLOCKSIZE: 점검하기를 원하는 파일의 Block 크기를 적어주면 됩니다.
기본값은 2048 (2k)입니다.
- LOGFILE: 점검 결과를 저장할 파일명을 지정합니다. 기본값은 ‘NONE ‘ 이며 검사 결과를 화면
으로 출력해서 보여줍니다.
- FEEDBACK: 0 이상의 숫자로 설정할 수 있으며 (기본값은 0초) 검사가 진행되는 동안에 화면
에 .(dot) 을 찍어서 진행 과정을 표시해 줍니다.
- PARFILE: DBV 를 실행할 때 적용하는 각종 설정들을 이 파일에 저장해두고 불러와서
사용할 수 있습니다. (exp 나 datapump 에서 parfile 옵션과 비슷한 역할입니다)
- USERID: ASM 기반의 파일을 점검할 경우 ASM 인스턴스에 접속해야 하기 때문에 반드시
USERID 를 사용해야만 합니다.
- SEGMENT_ID: 특정 세그먼트(TABLE, INDEX, UNDO)만 골라서 검사할 수 있습니다.
이 기능은 9i 이상에서만 사용할 수 있습니다
2) DBV 의 주요 특징들
(1) Block Level 에서의 점검만 가능하고 Analyze table … validate structure 와 같이 테이블과 인덱스간의 불일치 같은 문제는 점검하지 못합니다.
(2) 오직 데이터 파일만 점검할 수 있습니다. 즉 Redo log file 이나 Control file 은 점검할 수 없습니다.
Redo Log File 을 점검하려고 시도하면 아래와 같은 에러가 나옵니다..
[ 10g 일 경우 ]
[oracle@localhost ~]$ dbv file=/home/oracle/oradata/testdb/redo01_b.log
DBVERIFY: Release 10.2.0.5.0 - Production on Mon Feb 21 08:00:51 2011
Copyright (c) 1982, 2007, Oracle. All rights reserved.
DBV-00103: Specified BLOCKSIZE (8192) differs from actual (512)
[ 11g 일 경우 ]
[oracle@localhost ~]$ dbv file=/app/oracle/oradata/testdb/redod01_a.log
DBVERIFY: Release 11.2.0.2.0 - Production on Mon Nov 25 20:19:53 2013
Copyright (c) 1982, 2009, Oracle and/or its affiliates. All rights reserved.
DBV-00100: Specified FILE (/app/oracle/oradata/testdb/redod01_a.log) not accessible
(3) ASM 파일까지 점검할 수 있습니다. 단 ASM 인스턴스에 로그인 할 수 있는 계정 정보를 함께 입력해야 합니다.
[oracle@localhost ~]$ dbv file=+ASM/TESTDB/datafile/system01.dbf
userid=system/oracle
(4) 특정 데이터 파일이 아닌 세그먼트 단위로 검사를 할 수 있습니다.
예를 들어 크기가 무척 큰 고객 테이블만 검사한다든지 특정 인덱스만 검사하는 것을 의미합니다.
자세한 사용법은 뒷부분에 나옵니다.
3) DBVerify 실행하기
(1) 특정 파일 검사하기
[oracle@localhost ~]$ dbv file=/app/oracle/oradata/testdb/example01.dbf
위 실행결과의 주요 항목의 의미는 아래와 같습니다.
** Total Pages Examined : 테스트 한 총 블록의 개수
** Total Pages Processed (Data) : 테스트 한 총 테이블 블록 개수
** Total Pages Failing (Data) : 문제가 있는 블록 개수
** Total Pages Processed (Index) : 테스트 한 총 인덱스 블록 개수
** Total Pages Failing (Index) : 문제가 있는 블록 개수
** Total Pages Processed (Other): 테이블이나 인덱스 외 다른 블록 개수
** Total Pages Empty : 비어있는 블록 개수
** Total Pages Marked Corrupt: 문제가 있어서 Corrupt Marked 된 블록 개수
** Total Pages Influx: 다른 사용자가 먼저 데이터를 변경하고 있어서 DBV를 하기 위해
다시 읽은 블록 개수
(2) 특정 세그먼트만 검사하기
여기서는 연습용으로 tt800 이라는 테이블을 만들고 데이터를 입력해서 35 MB 크기의 테이블을 생성 한 후
해당 테이블의 SEGMENT ID를 조회해서 검사해 보겠습니다.
SQL> select sum(bytes)/1024/1024 as MB from dba_segments
2 where segment_name='TT800';
SQL> select t.ts#,s.header_file,s.header_block
2 from v$tablespace t, dba_segments s
3 where s.segment_name='TT800'
4 and t.name=s.tablespace_name;
[oracle@localhost ~]$ dbv userid=scott/tiger segment_id=6.5.1035
이 방법을 이용하면 큰 Tablespace에 들어있는 여러 가지 테이블 중 특정 테이블만 골라서 점검할 수 있으며
시간과 비용 면에서 훨씬 효율적입니다.
이번에는 직접 백업 받은 파일의 블록을 corruption 하겠습니다.
테스트 할 수 있는 데이터 파일로 백업 파일 중에 /data/temp/example01.dbf 를 사용하겠습니다.
[oracle@localhost ~]$ dd if=/dev/zero of=/data/temp/example01.dbf bs=8k
( 명령 수행 도중에 CTRL + C 로 취소합니다 )
52356+0 records in
52356+0 records out
428900352 bytes (429 MB) copied, 5.09548 seconds, 84.2 MB/s
[ 10g 일 경우 ]
[oracle@localhost ~]$ dbv file=/data/temp/example01.dbf
Page 1539 is marked corrupt
Corrupt block relative dba: 0x00000603 (file 0, block 1539)
Completely zero block found during dbv:
Page 1540 is marked corrupt
Corrupt block relative dba: 0x00000604 (file 0, block 1540)
Completely zero block found during dbv:
Page 1541 is marked corrupt
Corrupt block relative dba: 0x00000605 (file 0, block 1541)
Completely zero block found during dbv:
(이하 생략합니다)
[ 11g 일 경우 ]
[oracle@localhost ~]$ dbv file=/data/temp2/example01.dbf
DBVERIFY: Release 11.2.0.2.0 - Production on Mon Nov 25 21:05:42 2013
Copyright (c) 1982, 2009, Oracle and/or its affiliates. All rights reserved.
DBV-00107: Unknown header format (0) (0)
11g 일 경우는 에러가 발생합니다.
반면 10g 일 경우는 위와 같이 Block Corruption 이 발생한 내역이 보입니다.
이 에러들을 수정하려면 정상적으로 백업 받은 백업 파일을 가지고 와서 복구를 해야 합니다.
이렇게 블록 장애 난 파일을 복구 시도할 경우 아래와 같이 에러가 발생합니다.
SQL> alter tablespace example offline;
SQL> !cp /data/temp/example01.dbf /home/oracle/oradata/testdb/
SQL> recover datafile '/home/oracle/oradata/testdb/example01.dbf';
역시 백업파일이 에러가 있어서 복구를 해도 복구가 되지 않습니다.
이런 Corruption 된 블록을 복구하시려면 우선 정상적인 백업파일을 복원 하신 후
Recover 명령을 수행하시거나 아니면 RMAN에서 Recover를 하시면 됩니다.
위의 내용으로 알 수 있듯이 직접적인 물리적 손상을 입은 블록들은 DBVerify 로 검증을 할 수 있습니다.
(3) Database 내 전체 데이터파일을 검사하는 dbv script
서버 내에 아주 많은 데이터파일이 있을 경우 일일이 하나씩 전부 검사하는 일은 무척 번거롭고 힘든 일입니다.
그래서 자동으로 모든 파일의 위치를 찾아내서 DBV로 검사하는 스크립트를 소개합니다.
사용하시는 OS환경에 맞게 수정하시면 아주 편리하게 사용하실 수 있을 것입니다.
SQL> !vi dbv.sql
set feedback off
set head off
set echo off
set linesize 200
set pagesize 3000
spool /home/oracle/dbv.sh
select '!dbv file='||name||' blocksize='||block_size||' logfile=' ||
substr(name,instr(name,'/',-1,1) +1) ||'.'|| file#||'.log'
from v$datafile
/
spool off
@/home/oracle/dbv.sh
:wq!
직접 실행해 보겠습니다.
SQL> @/home/oracle/dbv.sql
!dbv file=/home/oracle/oradata/testdb/system01.dbf blocksize=8192 logfile=system01.dbf.1.log
!dbv file=/home/oracle/oradata/testdb/undotbs01.dbf blocksize=8192
logfile=undotbs01.dbf.2.log
!dbv file=/home/oracle/oradata/testdb/sysaux01.dbf blocksize=8192 logfile=sysaux01.dbf.3.log
!dbv file=/home/oracle/oradata/testdb/users01.dbf blocksize=8192 logfile=users01.dbf.4.log
!dbv file=/home/oracle/oradata/testdb/example01.dbf blocksize=8192
logfile=example01.dbf.5.log
DBVERIFY: Release 10.2.0.5.0 - Production on Mon Feb 21 17:26:22 2011
Copyright (c) 1982, 2007, Oracle. All rights reserved.
(검사 결과 나오는 이 부분이 logfile 로 지정한 부분에 저장됩니다)
All About Oracle Backup and Recovery - 서진수 저
DBVERIFY: Release 10.2.0.5.0 - Production on Mon Feb 21 17:26:32 2011
Copyright (c) 1982, 2007, Oracle. All rights reserved.
(검사 결과 나오는 이 부분이 logfile 로 지정한 부분에 저장됩니다)
DBVERIFY: Release 10.2.0.5.0 - Production on Mon Feb 21 17:26:31 2011
Copyright (c) 1982, 2007, Oracle. All rights reserved.
(검사 결과 나오는 이 부분이 logfile 로 지정한 부분에 저장됩니다)
DBVERIFY: Release 10.2.0.5.0 - Production on Mon Feb 21 17:26:39 2011
Copyright (c) 1982, 2007, Oracle. All rights reserved.
(검사 결과 나오는 이 부분이 logfile 로 지정한 부분에 저장됩니다)
DBVERIFY: Release 10.2.0.5.0 - Production on Mon Feb 21 17:26:40 2011
Copyright (c) 1982, 2007, Oracle. All rights reserved.
이제 로그파일을 열어서 확인하시면 됩니다.
위 스크립트는 일반 파일 시스템을 사용할 경우인데 만약 데이터파일이 Raw Device
라면 스크립트가 조금 수정되어야 합니다.
변경 전 :
select '!dbv file='||name||' blocksize='||block_size||' logfile=' ||
substr(name,instr(name,'/',-1,1) +1) ||'.'|| file#||'.log'
from v$datafile
변경 후 : 아래 스크립트에서 굵은 글씨의 end 부분이 추가됩니다.
select '!dbv file='||name||' blocksize='||block_size|| ‘ end=’|| (bytes/block_size)
||' logfile=' || substr(name,instr(name,'/',-1,1) +1)||'.'||file#||'.log'
from v$datafile
윈도 기반에서도 스크립트 조금 변경되어야 합니다.
변경 전 :
select '!dbv file='||name||' blocksize='||block_size||' logfile=' ||
substr(name,instr(name,'/',-1,1) +1) ||'.'|| file#||'.log'
from v$datafile
All About Oracle Backup and Recovery - 서진수 저
변경 후 : 아래 쪽에 / 대신 \ 로 변경해주세요
select '!dbv file='||name||' blocksize='||block_size||' logfile=' ||
substr(name,instr(name,'\',-1,1) +1) ||'.'||file#||'.log'
from v$datafile
만약 점검하고자 하는 파일이 ASM 기반이면 아래와 같이 변경되어야 합니다.
select '!dbv file='||name||' blocksize='||block_size||' userid=sys/&passwd logfile=' ||
substr(name,instr(name,'/',-1,1) +1) ||'.'|| file#||'.log'
from v$datafile
기존 스크립트에서 userid 부분이 추가되어야 합니다.
위 스크립트를 수행하면 아래와 같이 sys 의 암호를 묻게 되는데 적당한 암호를 입력하시면 DBV가 작동합니다.
SQL> select '!dbv file='||name||' blocksize='||block_size||' userid=sys/&passwd logfile=' ||
2 substr(name,instr(name,'/',-1,1) +1) ||'.'||file#||'.log'
3 from v$datafile
4 ;
Enter value for passwd: oracle <- 이렇게 암호를 입력 받는 부분이 나옵니다.
old 1: select '!dbv file='||name||' blocksize='||block_size||' userid=sys/&passwd logfile=' ||
new 1: select '!dbv file='||name||' blocksize='||block_size||' userid=sys/oracle logfile=' ||
( 지면 관계상 중간 결과는 생략합니다 )
이상으로 DBVerify 를 사용하여 데이터 파일과 백업파일의 무결성을 체크하는 방법을 살펴보았습니다.
다음으로 DBMS_REPAIR 패키지를 이용하여 데이터파일과 백업파일을 검증하고 관리하는 방법을 살펴보겠습니다.
2. DBMS_REPAIR 패키지를 이용한 Block Recovery
DBMS_REPAIR 패키지는 오라클 8i 버전부터 등장한 Block Corruption 을 Detecting 하고 Repair 하는 패키지입니다.
이 패키지는 Table Block 과 Index Block 을 조사하여 문제가 있는 Block 을 수정해주는 data corruption repair 패키지를 가지고 있으며
sys 계정으로 작업하셔야 합니다.
즉 아래에서 설명하는 패키지들은 각각 수행 될 수 있다는 뜻이며 결과들이 저장되는 테이블에는 “DBA_” 라는 접두어가 붙어서 생성됩니다.
DBMS_REPAIR 패키지는 corrupt block 를 Repair 하는 것이 아닙니다.
장애 난 블록을 찾아서 그 안에 있는 내용을 고쳐주는 패키지가 아니라는 것입니다.
다만 이 패키지는 장애 난 블록을 찾아내서 해당 block 를 장애로 mark 하고 더 이상 사용 안하게 막아주고
해당 블록 장애 때문에 진행 안되던 작업을 계속 할 수 있도록 도와주는 역할을 합니다.
그리고 만약 장애 난 블록에 데이터가 있었다면 그 블록 안에 있던 내용은 전부 손실됩니다.
아래의 실습들은 10g 기준이며 11g 와 차이가 거의 없습니다.
2.1 DBMS_REPAIR 패키지
DBMS_REPAIR 패키지는 이름처럼 여러 개의 프로시저들이 모여있습니다.
아래에서 해당 프로시저들의 역할과 사용법을 알아보겠습니다.
( 각 프로시저 별로 옵션은 많지만 주로 사용하는 내용만 간략하게 살펴보겠습니다.)
1) ADMIN_TABLE 프로시저
Block Repair 를 하기 위해 필요한 관리 작업(create, drop, pur ge)을 제공해 줍니다.
이런 작업을 하기 위한 테이블 들은 항상 SYS Schema 소유로 생성됩니다.
이 테이블에 손상이 발생한 블록들의 리스트를 저장하게 됩니다.
2) CHECK_OBJECT 프로시저
Table 이나 Index 의 Block Corruption 을 체크하고 문제가 있는 블 록은
1번에서 만든 Repair table 에 기록해 줍니다.
3) DUMP_ORPHAN_KEYS 프로시저
Corrupted 된 블록들이 테이블과 관련된 것이라면 admin_table 에서 생성한 곳에 기록이 되지만
index와 관련 있는 블록들이라면 이 테이블에 기록합니다.
4) FIX_CORRUPT_BLOCKS 프로시저
CHECK_OBJECT 로 발견된 corrupt 된 Block을 Mark 해 줍니다.
5) REBUILD_FREELISTS 프로시저
Object 의 Freelists 를 재생성 합니다.
6) SEGMENT_FIX_STATUS 프로시저
ASSM 기능을 사용하고 있는 Bitmap index 가 corrupt 되었다면 이 프로 시져가 fix 해 줍니다.
7) SKIP_CORRUPT_BLOCKS 프로시저
Table 이나 Index scan 할 때 기존에 mark 된 Corrupt block 들은 확인하지 않고 건너 뜁니다.
2.2 DBMS_REPAIR 의 제약 사항 및 한계점
1) LOB 나 Cluster Index 는 지원하지 않습니다.
2) DUMP_ORPHAN_KEYS 프로시저는 BITMAP Index, Function-Based Index 는 지원하지 않으며, 3,950 bytes 이상은 지원하지 못합니다.
2.3 DBMS_REPAIR 시작하기
1) Block Corruption 찾아내기
이 과정과 관련 있는 패키지는 아래와 같습니다.
(1) CHECK_OBJECT
(2) FIX_CORRUPT_BLOCK
위 두 가지의 패키지를 사용하려면 먼저 ADMIN_TABLE 이 실행되어야 합니다.
2) admin_tables 프로시저를 실행시켜 REPAIR_TABLE 생성하기
SQL> conn / as sysdba;
Connected.
SQL> begin
2 dbms_repair.admin_tables (
3 table_name => 'REPAIR_TABLE',
4 table_type => dbms_repair.repair_table,
5 action => dbms_repair.create_action,
6 tablespace => 'USERS');
7 end;
8 /
PL/SQL procedure successfully completed.
SQL> desc repair_table; ( 또는 dba_repair_table 하셔도 됩니다)
3) admin_tables 프로시저 실행해서 Orphan-key-table 생성하기
이 테이블은 장애 난 테이블과 관련 있는 다른 Object 를 저장하는 곳입니다.
예를 들어서 관련 인덱스나 FK 등의 정보를 저장하는 곳이라고 생각하시면 됩니다.
테이블 검사를 하다가 관련있는 인덱스 등이 문제가 있을 수 있기 때문에 미리 생성해 두는 것입니다.
SQL> begin
2 dbms_repair.admin_tables (
3 table_name => 'ORPHAN_KEY_TABLE',
4 table_type => dbms_repair.orphan_table ,
5 action => dbms_repair.create_action ,
6 tablespace => 'USERS') ;
7 end;
PL/SQL procedure successfully completed.
SQL> desc orphan_key_table;
4) DB_Block_checking = true
블록이 문제가 있는지 없는지를 확인하려면 오라클 파라미터 중에서 Block checking 하는 파라미터를 true 값으로 변경해주어야 합니다.
파라미터 파일에 DB_BLOCL_CHECKING = true 로 해 줄 경우 오라클은 모든 블록을 체크하기 시작합니다.
당연히 어느 정도의 Overhead 는 감수해야 할 것입니다.
이 값을 False 로 하게 되면 SYSTEM Tablespace 만 체크하고 나머지는 체크 안 합니다.
여기까지가 블록 장애를 검사하기 전 준비 단계이고 이제 Block Corruption 을 발생시켜 복구해 보겠습니다.
5) DBMS_REPAIR 실습하기
Step 1. 위 파라미터를 설정 한 후 재 시작 합니다. (또는 alter system set 으로 적용시킵니다)
SQL> show parameter db_block_checking;
SQL> alter system set db_block_checking=true;
SQL> show parameter db_block_checking;
Step 2. Block Corruption 발생시킵니다.
SQL> !vi dd.sql
conn / as sysdba
set line 200
col tablespace_name for a10
col file_name for a50
col mb for 9999
select tablespace_name , bytes/1024/1024 MB , file_name
from dba_data_files
/
:wq!
SQL> @dd
SQL> create tablespace test10
2 datafile '/home/oracle/oradata/testdb/test01. dbf' size 256k;
SQL> @dd
SQL> create table scott.tt910 (no number, name varchar2(10)) tablespace test10;
SQL> insert into scott.tt910 values (1, ’AA’);
SQL> insert into scott.tt910 values (2, ’BB’);
SQL> commit;
SQL> select * from scott.tt910;
SQL> alter tablespace test10 offline;
여기까지 하신 후 해당 데이터 파일을 윈도로 이동시키신 후 (Winscp 등 이용)
윈도에서 에디터를 통해서 블록을 잘 수정하셔서 Block Corruption 을 만드세요.
이 부분을 잘 하셔야 합니다.
Block corruption 을 만드신 후에 저장하시고 다시 리눅스로 이동 시키신 후 아래와 같이 하세요.
SQL> recover tablespace test10;
SQL> alter tablespace test10 online;
Tablespace altered. <- Tablespace 는 정상적으로 recover 되었습니다.
SQL> select * from scott.tt910;
select * from scott.tt910
*
ERROR at line 1:
ORA-01578: ORACLE data block corrupted (file # 6, block # 12)
ORA-01110: data file 6: '/home/oracle/oradata/testdb/test01.dbf' <- 장애 발생했습니다.
Step 3. DBMS_REPAIR 을 사용하여 에러를 찾습니다.
SQL> set serveroutput on;
SQL> declare n_corrupt int;
2 begin
3 n_corrupt := 0;
4 dbms_repair.check_object (
5 schema_name => 'SCOTT',
6 object_name => 'TT910',
7 repair_table_name => 'REPAIR_TABLE',
8 corrupt_count => n_corrupt) ;
9 dbms_output.put_line('장애블록수: ' || to_char(n_ corrupt));
10 end;
11 /
장애블록수: 1 이렇게 장애 블록을 찾았습니다.
PL/SQL procedure successfully completed.
장애 블록에 대한 조금 더 자세한 정보를 알아보겠습니다.
SQL> set line 200;
SQL> col object_name for a10
SQL> col corrupt_description for a20
SQL> col repair_description for a20
SQL> select object_name,block_id, corrupt_type,marked_corrupt,
2 corrupt_description,repair_description
3 from repair_table
위 내용과 같이 12번 블록이 문제가 있다는 것이 확인되며 corrupt 되었다고 mark 되었다는 것도 알 수 있습니다.
Step 4. Corrupt 된 블록을 Fix 하겠습니다.
이 부분은 corrupt 된 블록을 찾아서 corrupt 되었다고 marking 하는 부분인데 check_object 부분에서 자동으로 해서 안 하셔도 상관없습니다.
SQL> set serveroutput on
SQL> declare n_fix int;
2 begin
3 n_fix := 0;
4 dbms_repair.fix_corrupt_blocks (
5 schema_name => 'SCOTT',
6 object_name => 'TT910' ,
7 object_type => dbms_repair.table_object,
8 repair_table_name => 'REPAIR_TABLE',
9 fix_count => n_fix) ;
10 dbms_output.put_line('fix_count: '|| to_char(n_fix));
11 end;
12 /
fix_count: 0 <- check_object 부분에서 해서 여기서는 0으로 나옵니다.
PL/SQL procedure successfully completed.
Fix 를 한다는 것은 해당 블록에 corruption 이 발생해서 사용 못하니까 더 이상 읽지도 쓰지도 말라는 표시를 한다는 (Mark 한다는) 뜻 입니다.
즉 블록 안에 있는 데이터를 복구한다는 뜻이 아니라 해당 블록을 못쓴다고 표시만 해 둔다는 뜻입니다.
Fix 를 하고 난 후에 해당 테이블에 select 를 해 보겠습니다.
SQL> select * from scott.tt910;
select * from scott.tt910
*
ERROR at line 1:
ORA-01578: ORACLE data block corrupted (file # 6, block # 12)
ORA-01110: data file 6: '/home/oracle/oradata/testdb/test01.dbf'
여전히 해당 블록에 문제가 있다고 나오고 select 가 정상적으로 수행이 안됩니다.
예를 들어 위 데이터 파일에 수많은 블록에 데이터가 들어 있다고 가정할 경우 1개의 block 가
corrupt 가 발생할 경우 전체 데이터를 읽어 오지 못하는 상황이 된다는 뜻입니다.
그래서 corrupt 된 블록의 데이터는 복구 못하니까 읽지 말고 나머지 블록만 읽어서 남아 있는
데이터만이라도 살려내고 싶을 때 아래와 같이 한가지 작업을 더 하시면 됩니다.
Step 5. Corrupt 된 block 를 Skip 하도록 설정하겠습니다.
SQL> begin
2 dbms_repair.skip_corrupt_blocks (
3 schema_name => 'SCOTT',
4 object_name => 'TT910',
5 object_type => dbms_repair.table_object,
6 flags => dbms_repair.skip_flag) ;
7 end;
8 /
PL/SQL procedure successfully completed.
이제 다시 해당 테이블에 select 해 보겠습니다.
SQL> select * from scott.tt910;
no rows selected
정상적으로 select 되는 것을 알 수 있습니다.
어떤 테이블에 corrupt 된 블록을 skip 하도록 설정이 되어 있는지는 아래의 쿼리를 수행하시면 조회 할 수 있습니다.
SQL> select owner, table_name, skip_corrupt
2 from dba_tables
3 where owner='SCOTT';
2.4 DBMS_REPAIR 패키지 활용 실습
이번 실습은 데이터가 10만 건 들어있는 scott.tt920 테이블을 생성 한 후 index 도 생성합니다.
그 후에 data block 과 index block 에 corrupt를 발생 시켜 조회가 되지 않는 장애를 만들고
DBMS_REPAIR을 이용하여 Fix 하고 Skip 하여 데이터가 조회 되도록 하겠습니다.
Step 1. 테스트 환경 만들기
SQL> @dd
SQL> alter database datafile '/home/oracle/oradata/testdb/test01.dbf' autoextend on;
SQL> create table scott.tt920 (no number, name varchar2(10)) tablespace test10;
SQL> begin
2 for i in 1..100000 loop
3 insert into scott.tt920
4 values (i,dbms_random.string('A',9));
5 end loop;
6 commit;
7 end;
8 /
SQL> select count(*) from scott.tt920;
SQL> analyze table scott.tt920 compute statistics;
SQL> col table_name for a10
SQL> col owner for a10
SQL> select table_name , owner , num_rows , blocks
2 from dba_tables
3 where table_name='TT920';
위 내용을 보면 scott.tt920 이란 테이블이 총 10만건의 데이터가 있고 370 개의 block를 사용하는 것을 알 수 있습니다.
Scott.tt920 테이블의 이름 컬럼에 인덱스를 생성하겠습니다.
SQL> create tablespace indx
2 datafile '/home/oracle/oradata/testdb/indx01.d bf' size 5M
3 autoextend on;
SQL> create index scott.indx_tt920_name on scott.tt920(name) tablespace indx;
Index created.
SQL> set line 200
SQL> col owner for a10
SQL> col index_name for a30
SQL> col table_name for a10
SQL> select owner , index_name , table_name , num_rows , leaf_blocks
2 from dba_indexes
3 where table_name='TT920';
위 내용을 보면 인덱스 역시 10만건의 데이터가 들어가 있고 leaf block 수가 294 개 임을 알 수 있습니다.
Step 2. tt920 table 이 저장된 데이터파일에 block corruption 을 발생시킵니다.
현재 tt920 테이블이 저장된 데이터 파일을 윈도로 이동시켜서 block corruption을 발생 시킨 후 다시 리눅스로 이동시킵니다.
그리고 아래와 같은 작업을 합니다.
SQL> alter tablespace test10 offline;
SQL> alter tablespace test10 online;
alter tablespace test10 online
*
ERROR at line 1:
ORA-01113: file 6 needs media recovery if it was restored from backup, or END BACKUP if it was
not
ORA-01110: data file 6: '/home/oracle/oradata/testdb/test01.dbf'
SQL> recover tablespace test10;
Media recovery complete.
SQL> alter tablespace test10 online;
Tablespace altered.
SQL> select * from scott.tt920 ;
(중간 생략)
NO NAME
---------- --------------------
2671 baBvNSwET
2672 DUowpdecd
2673 faTxAykzk
2674 UOQhNSWGe
2675 GVELPvwCF
2676 QDSyJgthY
2677 MLZlPcjdR
ERROR:
ORA-01578: ORACLE data block corrupted (file # 6, block # 26)
ORA-01110: data file 6: '/home/oracle/oradata/testdb/test01.dbf'
2295 rows selected.
Select 가 수행되다가 corrupt 난 블록에서 장애가 발생하고 이후의 데이터는 조회가 안됩니다.
Step 3. DBMS_REPAIR 패키지로 corrupt 된 블록을 찾습니다.
작업을 하기 전에 먼저 repair_table 을 초기화합니다.
SQL> select count(*) from repair_table;
COUNT(*)
-------------
2 <- 앞에서 테스트했던 2건의 데이터가 있습니다.
SQL> truncate table repair_table;
Table truncated.
SQL> select count(*) from repair_table;
COUNT(*)
--------------
0
SQL> set serveroutput on ;
SQL> declare num_corrupt int;
2 begin
3 num_corrupt := 0;
4 dbms_repair.check_object (
5 schema_name => 'SCOTT' ,
6 object_name => 'TT920' ,
7 repair_table_name => 'REPAIR_TABLE' ,
8 corrupt_count => num_corrupt );
9 dbms_output.put_line('====================== =========');
10 dbms_output.put_line('장애블록수: '||to_char(num_corrupt)) ;
11 end;
12 /
===============================
장애블록수: 5
PL/SQL procedure successfully completed.
위 결과로 확인한 결과 5개의 블록이 장애가 났음을 알 수 있습니다.
어떤 블록이 장애가 났는지 자세히 살펴보겠습니다.
SQL> set pagesize 50
SQL> select object_name, block_id, corrupt_type,marked_corrupt,
2 corrupt_description, repair_description
3 from repair_table
OBJECT_NAM BLOCK_ID CORRUPT_TYPE MARKED_CORRUPTCORRUPT_DESCRIPTION REPAIR_DESCRIPTION
--------------- -------------- ------------------- ---------------------- --------------------------- ----------------------------
TT920 26 6148 TRUE mark block software
corrupt
TT920 391 6148 TRUE mark block software
corrupt
TT920 474 6148 TRUE mark block software
corrupt
TT920 484 6148 TRUE mark block software
corrupt
TT920 493 6148 TRUE mark block software
Corrupt
위 내용을 살펴보니 26, 391, 474, 484, 493 번 블록에서 장애가 발생했고 fix까지 되어 있음이 확인됩니다.
Step 4. 관련된 인덱스 블록의 상태는 어떠한지 조회해 보겠습니다.
SQL> declare num_index int;
2 begin
3 num_index := 0;
4 dbms_repair.dump_orphan_keys (
5 schema_name => 'SCOTT' ,
6 object_name => 'INDX_TT920_NAME' ,
7 object_type => dbms_repair.index_object ,
8 repair_table_name => 'REPAIR_TABLE' ,
9 orphan_table_name => 'ORPHAN_KEY_TABLE' ,
10 key_count => num_index );
11 dbms_output.put_line('========================================');
12 dbms_output.put_line(' index key count : '||to_char(num_index));
13 end;
14 /
========================================
index key count : 1834
PL/SQL procedure successfully completed.
위 조회 내용을 보면 1834 건의 인덱스 key 에 문제가 발생했음을 알 수 있습니다.
Step 5. Corrupt 된 블록을 찾지 않도록 skip 설정하겠습니다.
현재 이 테이블에 총 10만 건의 데이터가 들어 있습니다.
그러나 지금은 5개의 블록이 장애가 나서 전체가 조회가 안되고 중지되는 일이 발생했습니다.
그래서 위 방법으로 corrupt 된 블록을 찾은 후 지금 하는 방법으로 해당 5개의 블록은 더 이상 사용하지 않도록 skip 설정 한 후
나머지 데이터라도 살려내겠습니다.
현재 상태 (corrupt block 찾고 fix까지 한 상태)로 조회를 먼저 해 보겠습니다.
SQL> select count(*) from scott.tt920;
select count(*) from scott.tt920
*
ERROR at line 1:
ORA-01578: ORACLE data block corrupted (file # 6, block # 26)
ORA-01110: data file 6: '/home/oracle/oradata/testdb/test01.dbf'
여전히 corrupt 된 block 때문에 조회가 정상적으로 안 되고 있습니다.
Corrupt 된 block 을 skip 하도록 설정하겠습니다.
SQL> begin
2 dbms_repair.skip_corrupt_blocks (
3 schema_name => 'SCOTT' ,
4 object_name => 'TT920' ,
5 object_type => dbms_repair.table_object ,
6 flags => dbms_repair.skip_flag);
7 end;
8 /
PL/SQL procedure successfully completed.
SQL> select count(*) from scott.tt920;
COUNT(*)
--------------
98166 <- 조회가 진행 됩니다.
위 조회 내용으로 봤을 때 전체 데이터 10만 건 중에서 현재 조회되는 98166 개 외에는 전부 장애가 발생했음을 알 수 있습니다.
테이블이 정리되었으므로 인덱스도 rebuild 하겠습니다.
현재 orphan key table 을 조회해보겠습니다.
SQL> select count(*) from orphan_key_table;
COUNT(*)
-------------
1834
SQL> truncate table orphan_key_table;
Table truncated.
SQL> select count(*) from orphan_key_table;
COUNT(*)
-------------
0
SQL> declare num_index int;
2 begin
3 num_index := 0;
4 dbms_repair.dump_orphan_keys (
5 schema_name => 'SCOTT' ,
6 object_name => 'INDX_TT920_NAME' ,
7 object_type => dbms_repair.index_object ,
8 repair_table_name => 'REPAIR_TABLE' ,
9 orphan_table_name => 'ORPHAN_KEY_TABLE' ,
10 key_count => num_index );
11 dbms_output.put_line('========================================');
12 dbms_output.put_line('index key count: '||to_char(num_index));
13 end ;
14 /
========================================
index key count: 0 <- 인덱스가 rebuild 되었습니다.
PL/SQL procedure successfully completed.
위 작업 내용 결과 tt920 테이블에서 corrupt 된 블록을 skip 하고 난 후 index 도 해당 블록을 더 이상 사용 안 하는 것을 알 수 있습니다.
여기까지 DBMS_REPAIR 패키지를 사용해서 corrupt 된 block 가 있는 테이블을 어떻게 조치하는지를 살펴 보았습니다.
다소 내용이 복잡 한 것 같지만 긴급한 장애가 발생했을 때 아주 요긴한 방법이 되므로 꼭 이해하시고 연습하시길 바랍니다.
3. BBED 를 이용한 Block Recovery
지금부터 설명 드리는 부분은 아주 위험한 방법임을 미리 알려드립니다.
아울러 지금부터 설명 드리는 방법을 잘 못 사용할 경우 해당 데이터파일의 모든 데이터가 손상될 수 있으며 아예 복구를 하지 못할 수도 있습니다.
그에 대한 모든 책임은 작업을 수행하신 여러분들께 있다는 것을 미리 알려드립니다.
반드시 모든 기능을 숙지하신 후에도 실무에서 반드시 필요한 경우를 제외하고는 절대로 사용 하지 마시길 당부 드립니다.
무서운 이야기를 먼저 하는 이유는 저자 조차도 이 방법을 이용할 때 아주 조심스럽기 때문입니다.
지금부터 오라클 블록 복구에 대한 저자만의 필살기를 전해드립니다.
BBed 란 Block Browser and Editor 의 약자로 이름처럼 Block 을 탐색하고 수정하는 유틸리티입니다.
Oracle 버전에 따라 조금은 다르지만 기본적으로 라이브러리 파일이 설치되며 사용하기 위해서는 Linking 작업을 추가로 해 주어야 합니다.
여기서는 우선 BBed 의 개념과 기본적인 사용법을 익힌 후 뒷부분에서 BBed 를 어떻게 사용하는지 실제 예제를 들어서 살펴보겠습니다.
다시 한번 더 강조하지만 이 방법은 Oracle 사에서 보증하거나 고쳐주지 않기 때문에 정말 조심하시고 주의해서 사용하시기 바랍니다.
연습 많이 하세요!!!!
1) BBed Link 후 실행하기 – 리눅스 버전 기반
오라클 엔진을 설치하게 되면 BBed 의 기본적인 라이브러리들이 함께 설치가 됩니다.
그러나 사용가능 상태가 아니기 때문에 이것을 사용하려면 link 라는 작업을 별도로 해주셔야 합니다.
10g 까지는 아래 방법으로 하시면 됩니다.
[oracle@localhost ~]$ cd $ORACLE_HOME/rdbms/lib
[oracle@localhost lib]$ make -f ins_rdbms.mk $ORACLE_HOME/rdbms/lib/bbed
(Linking 이 진행 됩니다)
[oracle@localhost lib]$ pwd
/home/oracle/product/10g/rdbms/lib
[oracle@localhost lib]$ ls -l bbed
-rwxr-xr-x 1 oracle dba 552665 2월 22 12:56 bbed <- 파일이 생성됩니다.
[oracle@localhost lib]$ cp bbed $ORACLE_HOME/bin/
11g부터는 아래 방법으로 하시면 됩니다.
[oracle@localhost lib]$ cd $ORACLE_HOME/rdbms/lib
[oracle@localhost lib]$ make -f ins_rdbms.m k $ORACLE_HOME/rdbms/lib/bbed
(위 생략)
gcc: /app/oracle/product/11g/rdbms/lib/ssbbded.o: 그런 파일이나 디렉토리가 없음
gcc: /app/oracle/product/11g/rdbms/lib/sbbdpt.o: 그런 파일이나 디렉토리가 없음
make: *** [/app/oracle/product/11g/rdbms/lib/bbed] 오류 1
에러가 발생합니다.
그 이유는 11g 에서는 기본적인 라이브러리 파일이 없기 때문입니다.
그래서 9i/10g 에 있는 위 라이브러리 파일을 복사해 와서 사용해야 합니다.
경로는 9i/10g 와 11g 가 동일합니다.
아래의 파일들을 9/10g 의 경로에서 11g 의 동일한 경로로 복사하세요.
[ 10g 에서 복사해야 할 파일들 ]
$ORACLE_HOME/rdbms/lib/ssbbded.o
$ORACLE_HOME/rdbms/lib/sbbdpt.o
$ORACLE_HOME/rdbms/mesg/bbedus.msb
$ORACLE_HOME/rdbms/mesg/bbedus.msg
- 아래 작업은 11g 에서 하며 10g 서버의 ip 주소가 192.168.47.133 으로 가정합니다.
[oracle@localhost ~]$ cd $ORACLE_HOME/rdbms/lib
[oracle@localhost lib]$ ls ssbbded.o
ls: ssbbded.o: 그런 파일이나 디렉토리가 없음
[oracle@localhost lib]$ ls sbbdpt.o
ls: sbbdpt.o: 그런 파일이나 디렉토리가 없음
[oracle@localhost lib]$ ls ../mesg/bbedus*
ls: ../mesg/bbedus*: 그런 파일이나 디렉토리가 없음
[oracle@localhost lib]$ pwd
/app/oracle/product/11g/rdbms/lib
[oracle@localhost lib]$ scp 192.168.47.133:/home/oracle/product/10g/rdbms/lib/ssbbded.o .
oracle@192.168.47.133's password: oracle 암호 입력
ssbbded.o 100% 2 657 2.6KB/s 00:00
[oracle@localhost lib]$ scp 192.168.47.133:/home/oracle/product/10g/rdbms/lib/sbbdpt.o .
oracle@192.168.47.133's password: oracle 암호 입력
sbbdpt.o 100% 2979 2.9KB/s 00:00
[oracle@localhost lib]$ pwd
/app/oracle/product/11g/rdbms/lib
[oracle@localhost lib]$ cd ../mesg
[oracle@localhost mesg]$ scp 192.168.47.133:/home/oracle/product/10g/rdbms/mesg/bbedus.msb .
oracle@192.168.47.133's password: oracle 암호 입력
bbedus.msb 100% 8 704 8.5KB/s 00:00
[oracle@localhost mesg]$ scp 192.168.47.133:/home/oracle/product/10g/rdbms/mesg/bbedus.msg .
oracle@192.168.47.133's password: oracle 암호 입력
bbedus.msg 100% 10KB 10.0KB/s 00:00
[oracle@localhost lib]$ make -f $ORACLE_HOME/rdbms/lib/ins_rdbms.mk BBED=$ORACLE_HOME/bin/bbed
$ORACLE_HOME/bin/bbed
(지면 관계상 Linking 이 수행되는 과정은 생략합니다)
Linking 이 끝났으면 아래와 같이 실행합니다 (모든 버전 동일합니다)
[oracle@localhost lib]$ bbed
Password: blockedit <-- 기본 암호입니다.
BBED: Release 2.0.0.0.0 - Limited Production on TueNov 26 00:49:23 2013
Copyright (c) 1982, 2009, Oracle and/or its affiliates. All rights reserved.
************* !!! For Oracle Internal Use only !!! ***************
BBED>exit
2) BBed 주요 옵션들
BLOCKSIZE 편집할 데이터 파일의 블록사이즈를 적어줍니다
MODE BBed 를 실행할 모드를 설정합니다(browser or edit)
SILENT 작업결과를 표준 출력(모니터)로 보여주는 여부를 제어합니다(Y, N)
SPOOL BBed 작업 log 를 bbed.log 파일에 저장할 것인지를 제어합니다(Y, N)
LISTFILE 작업할 파일의 목록을 적어줍니다
CMDFILE 실행할 파일들의 목록을 적어줍니다
BIFILE 변경 전 파일의 이미지를 저장할 파일명을 적어줍니다. (기본값-Bifile.bbd )
LOGFILE User log 를 저장할 파일명을 지정합니다. ( 기본값-log.bbd)
PARFILE Bbed 를 실행할 때 사용할 파라미터등을 적어두는 파일명을 지정합니다
위 옵션을 사용해서 bbed 구동 환경 설정하는 방법입니다.
SYS>select file#||' '||name||' '||bytes from v$datafile ;
FILE#||''||NAME||''||BYTES
--------------------------------------------------------------------------------
1 /app/oracle/oradata/testdb/system01.dbf 754974720
2 /app/oracle/oradata/testdb/sysaux01.dbf 681574400
3 /app/oracle/oradata/testdb/undotbs01.dbf 10485760
4 /app/oracle/oradata/testdb/users01.dbf 10485760
5 /app/oracle/oradata/testdb/example01.dbf 362414080
위 내용을 listfile 을 만들어서 거기에 등록하고 listfile 옵션을 사용해서 bbed 를 실행하겠습니다.
즉 listfile 이란 bbed 가 작업할 대상파일들의 명단입니다. 아래와 같이 만들면 됩니다.
SYS>!
[oracle@localhost ~]$ vi target.log
1 /app/oracle/oradata/testdb/system01.dbf 754974720
2 /app/oracle/oradata/testdb/sysaux01.dbf 681574400
3 /app/oracle/oradata/testdb/undotbs01.dbf 10485760
4 /app/oracle/oradata/testdb/users01.dbf 10485760
5 /app/oracle/oradata/testdb/example01.dbf 362414080
:wq!
[oracle@localhost ~]$ vi bbed.par
blocksize=8192
listfile=/home/oracle/target.log
mode=edit
:wq!
[oracle@localhost ~]$ bbed parfile=bbed.par
Password: blockedit
BBED: Release 2.0.0.0.0 - Limited Production on TueNov 26 01:07:17 2013
Copyright (c) 1982, 2009, Oracle and/or its affiliates. All rights reserved.
************* !!! For Oracle Internal Use only !!! ***************
BBED>
3) BBed 명령어들
(1) help all
BBed 관련 도움말을 보여줍니다.
BBED> help all
SET DBA [ dba | file#, block# ]
SET FILENAME 'filename'
SET FILE file#
SET BLOCK [+/-]block#
SET OFFSET [ [+/-]byte offset | symbol | *symbol ]
SET BLOCKSIZE bytes
SET LIST[FILE] 'filename'
SET WIDTH character_count
SET COUNT bytes_to_display
SET IBASE [ HEX | OCT | DEC ]
SET OBASE [ HEX | OCT | DEC ]
SET MODE [ BROWSE | EDIT ]
SET SPOOL [ Y | N ]
SHOW [ <SET parameter> | ALL ]
INFO
MAP[/v] [ DBA | FILENAME | FILE | BLOCK ]
DUMP[/v] [ DBA | FILENAME | FILE | BLOCK | OFFSET |COUNT ]
PRINT[/x|d|u|o|c] [ DBA | FILE | FILENAME | BLOCK |OFFSET | symbol | *symbol ]
EXAMINE[/Nuf] [ DBA | FILE | FILENAME | BLOCK | OFFSET | symbol | *symbol ]
</Nuf>:
N - a number which specifies a repeat count.
u - a letter which specifies a unit size:
b - b1, ub1 (byte)
h - b2, ub2 (half-word)
w - b4, ub4(word)
r - Oracle table/index row
f - a letter which specifies a display format:
x - hexadecimal
d - decimal
u - unsigned decimal
o - octal
c - character (native)
n - Oracle number
t - Oracle date
i - Oracle rowid
FIND[/x|d|u|o|c] numeric/character string [ TOP | CURR ]
COPY [ DBA | FILE | FILENAME | BLOCK ] TO [ DBA | FILE | FILENAME | BLOCK ]
MODIFY[/x|d|u|o|c] numeric/character string
[ DBA | FILE | FILENAME | BLOCK | OFFSET | sy mbol | *symbol ]
ASSIGN[/x|d|u|o] <target spec>=<source spec>
<target spec> : [ DBA | FILE | FILENAME | BLOCK | OFFSET | symbol | *symbol ]
<source spec> : [ value | <target spec options> ]
SUM [ DBA | FILE | FILENAME | BLOCK ] [ APPLY ]
PUSH [ DBA | FILE | FILENAME | BLOCK | OFFSET ]
POP [ALL]
REVERT [ DBA | FILE | FILENAME | BLOCK ]
UNDO
HELP [ <bbed command> | ALL ]
VERIFY [ DBA | FILE | FILENAME | BLOCK ]
CORRUPT [ DBA | FILE | FILENAME | BLOCK ]
명령어들과 옵션들이 많은 것을 확인 할 수 있습니다.
(2) set dba
작업하고자 하는 데이터파일과 블록을 지정합니다. 여기에 지정될 데이터파일은 listfile 에 등록되어 있어야 합니다.
BBED> set dba 2,50
DBA 0x00800032 (8388658 2,50)
위 명령은 2번 파일의 50번 블록에 작업을 하겠다 라고 설정하는 것입니다.
(3) set filename
작업을 원하는 데이터파일을 이름으로 지정합니다.
BBED> set filename '/app/oracle/oradata/testdb/example01.dbf'
FILENAME /home/oracle/oradata/testdb /example01.dbf
(4) set file
작업을 원하는 데이터파일을 번호로 지정합니다.
BBED> set file 3;
FILE# 3
(5) set block
현재 설정되어 있는 파일에서 작업을 원하는 block 을 지정합니다.
BBED> set block 20
BLOCK# 20
(6) set offset
현재 작업하기 원하는 offset 번호를 지정합니다.
(7) set blocksize
현재 작업하는 파일의 blocksize 를 지정합니다.
BBED> set blocksize 8192
BLOCKSIZE 8192
(8) set listfile
작업을 수행할 파일의 목록이 적혀있는 list file 을 지정합니다.
BBED> set listfile '/home/oracle/filelist.log'
LISTFILE /home/oracle/filelist.log
(9) set width
현재 보이는 화면의 폭을 지정합니다.
BBED> set width 200
WIDTH 200
(10) set count
Dump 명령어 수행 시 화면에 보여줄 data block 의 byte 수를 지정합니다.
BBED> set count 100
COUNT 100
(11) set ibase
내부적으로 사용되는 값들의 표현식을 지정합니다. 기본값은 10진수인데 16진수( Hexadecimal)나
8진수(Octal)로 변경 가능합니다.
(12) set mode
Bbed 수행 mode 를 설정합니다.
(13) show
현재 설정되어 있는 내용들을 보여줍니다.
BBED> show
FILE# 1
BLOCK# 1
OFFSET 0
DBA 0x00400001 (4194305 1,1)
FILENAME /app/oracle/oradata/testdb/ system01.dbf
BIFILE bifile.bbd
LISTFILE /home/oracle/target.log
BLOCKSIZE 8192
MODE Edit
EDIT Unrecoverable
IBASE Dec
OBASE Dec
WIDTH 200
COUNT 100
LOGFILE log.bbd
SPOOL No
(14) info
현재 작업중인 파일 내용을 보여줍니다.
BBED> info
File# Name Size(blks)
------ ------------------------------------------------------------ ------------
1 /app/oracle/oradata/testdb/system01.dbf 92160
2 /app/oracle/oradata/testdb/sysaux01.dbf 83200
3 /app/oracle/oradata/testdb/undotbs01.dbf 1280
4 /app/oracle/oradata/testdb/users01.dbf 1280
5 /app/oracle/oradata/testdb/example01.dbf 44240
(15) map
현재 작업중인 블록에 대한 자세한 정보를 보여줍니다.
BBED> map /v dba 1,20
File: /home/oracle/oradata/testdb/system01.dbf (1)
Block: 20 Dba:0x00400014
----------------------------------------------------------------------------
Undo Data
struct kcbh, 20 bytes @0
ub1 type_kcbh @0
ub1 frmt_kcbh @1
ub1 spare1_kcbh @2
ub1 spare2_kcbh @3
ub4 rdba_kcbh @4
ub4 bas_kcbh @8
ub2 wrp_kcbh @12
ub1 seq_kcbh @14
ub1 flg_kcbh @15
ub2 chkval_kcbh @16
ub2 spare3_kcbh @18
ub1 freespace[212] @100
ub1 undodata[7876] @312
ub4 tailchk @8188
위에서 map 명령어로 나오는 정보들에 대해서 자세히 살펴보겠습니다.
struct kcbh, 20 bytes Block header 라는 뜻입니다
ub1 type_kcbh Block type 을 정의합니다. 아래 표 참조하세요
ub1 frmt_kcbh Block Format 을 지정합니다.
ub1 spare1_kcbh 사용 안 합니다
ub1 spare2_kcbh 사용 안 합니다
ub4 rdba_kcbh RDBA (Relative Data Block Address)
ub4 bas_kcbh Scn base
ub2 wrp_kcbh Scn Wrap
ub1 seq_kcbh Sequence Number, Block Sequence Number
ub1 flg_kcbh Flag
0x01 : new block
0x02 : delayed logging change advanced SCN/seq
0x04: check value saved – block xors’s to zero
0x08: Temporary Block
ub2 chkval_kcbh Db_block_checksum=true 로 설정할 경우 checksum 값 저장
ub2 spare3_kcbh 사용 안 합니다
ub1 freespace[212] freespace
ub1 undodata[7876] Undo data
ub4 tailchk 아래부분에 tailcheck 내용을 참고하세요
위에서 살펴본 부분 말고도 블록의 종류에 따라 여러 가지 내용이 더 많을 수 있습니다.
ub1 type_kcbh 에서 언급한 블록 헤더 타입을 살펴 보겠습니다.
항 목 설 명
01 Undo segment header
02 Undo data block
03 Save undo header
04 Save undo block
05 Data segment header(temp, index, data 등)
06 ITL로 관리되는 data block
07 ITL로 관리되지 않는 Temp data block
08 Sort key
09 Sort run
10 Free list block
11 Data file header
ub4 tailchk 에서 언급한 tailcheck 를 확인하겠습니다.
일반적으로 블록주소는 SCN Base 값과 Block type , SCN Sequence number로 구성이 되어 있습니다.
예를 들어 SCN Base 값이 0x00029728 이고 block type 이 06 번 이며,
SCN Sequence Number 가 0x02 라면 tail check 부분은 0x97280602 가 되는 것입니다.
이 값을 4 bytes 로 저장하게 됩니다.
(16) (d)ump
Block 의 내용을 실제 dump 해서 화면에 보여주는 명령입니다. 보다 자세한 내용을 알기 위해 /v 옵션을 함께 사용하는 경우가 많습니다.
이 명령으로 DBA , filename, file, block, offset 등 set 명령어로 설정된 값들을 다 조회할 수 있습니다.
아래의 예는 1번 파일의 10번 블록을 offset 1-127 번까지 dump 를 수행한 내용입니다.
BBED> set width 100
WIDTH 100
BBED> dump /v dba 1,10 offset 0 count 128
File: /app/oracle/oradata/testdb/system01.dbf (1)
Block: 10 Offsets: 0 to 127 Dba:0x0040000a
-------------------------------------------------------------------
02a20000 0a004000 5a391100 00000104 l ......@.Z9......
7d950000 00003f00 36000000 48001c1c l }.....?.6...H...
0000e81f d41ec01d ac1c981b 841a7019 l ..............p.
5c184817 34162015 0c14f812 e411d010 l \.H.4. .........
bc0fa80e 940d800c 6c0b580a 44093008 l ........l.X.D.0.
1c070806 f404e003 cc02b801 00000000 l ................
00000900 2f000000 a0014000 41000400 l ..../.....@.A...
01000000 00000000 00002a00 2f000000 l ..........*./...
<16 bytes per line>
(17) (P)rint
현재 작업중인 정보를 보여줍니다.
BBED> p
kcbh.type_kcbh
--------------
ub1 type_kcbh @0 0x02
BBED> p kcbh
struct kcbh, 20 bytes @0
ub1 type_kcbh @0 0x02
ub1 frmt_kcbh @1 0xa2
ub1 spare1_kcbh @2 0x00
ub1 spare2_kcbh @3 0x00
ub4 rdba_kcbh @4 0x00400010
ub4 bas_kcbh @8 0x0013c412
ub2 wrp_kcbh @12 0x0000
ub1 seq_kcbh @14 0x01
ub1 flg_kcbh @15 0x04 (KCBHFCKV)
ub2 chkval_kcbh @16 0x5d1a
ub2 spare3_kcbh @18 0x0000
(18) e(x)amine
이 명령어는 DBA, Filename, File, Block, Offfset 등의 값을 정해진 포맷형식으로 보여줍니다.
BBED> x
kcvfh.kcvfhbfh.type_kcbh @0
------------------------
0x0b
BBED> x /b
kcvfh.kcvfhbfh.type_kcbh @0
------------------------
0x0b
BBED> x /h
kcvfh.kcvfhbfh.type_kcbh @0
------------------------
0xa20b
BBED> x /w
kcvfh.kcvfhbfh.type_kcbh @0
------------------------
0x0000a20b
BBED> x /l
kcvfh.kcvfhbfh.type_kcbh @0
------------------------
0x0000a20b
BBED> x /r
kcvfh.kcvfhbfh.type_kcbh @0
------------------------
BBED-00402: operation only allowed for data/index blocks
위 내용처럼 여러 가지 옵션이 있습니다.
/b : bytes 단위로 보여줍니다.
/h : half-word 단위로 보여줍니다.
/w : word 단위로 보여줍니다.
/l : long 단위로 보여줍니다.
/r : table/index row 를 보여줍니다.
(19) (f)ind
원하는 데이터를 찾아내는 고마운 기능입니다.
BBED> set file 6
FILE# 6
BBED> show
FILE# 6
BLOCK# 1
OFFSET 0
DBA 0x01800001 (25165825 6,1)
FILENAME /app/oracle/oradata/testdb/ test01.dbf
BIFILE bifile.bbd
LISTFILE /home/oracle/target.log
BLOCKSIZE 8192
MODE Edit
EDIT Unrecoverable
IBASE Dec
OBASE Dec
WIDTH 80
COUNT 512
LOGFILE log.bbd
SPOOL No
BBED> find /c 10
File: /home/oracle/oradata/testdb/test01.dbf (6)
Block: 1 Offsets: 342 to 391 Dba:0x01800001
------------------------------------------------------------------------
31300000 00000000 00000000 00000000 00000000 00000000 00000600 00000000
00000000 0000ffeb 552c0000 00000000 0000
<32 bytes per line>
BBED> find /c tt
BBED-00212: search string not found
위 내용은 6번 파일에서 1번 블록부터 뒤져서 10 이 들어가는 블록을 찾아달라는 명령입니다.
수행 결과를 보면 1번 블록에서 offset 342 에서 391까지 해당 데이터를 찾았다는 뜻입니다.
아래쪽 결과는 찾는 단어가 없을 경우 에러 메시지를 보여 줍니다.
이렇게 결과를 찾은 후 해당 블록을 조금 더 자세히 조사해 보겠습니다.
BBED> d /v dba 6,1 offset 342 count 50
File: /home/oracle/oradata/testdb/test01.dbf (6)
Block: 1 Offsets: 342 to 391 Dba:0x01800001
-------------------------------------------------------
31300000 00000000 00000000 00000000 l 10..............
00000000 00000000 00000600 00000000 l ................
00000000 0000ffeb 552c0000 00000000 l ........U,......
0000 l ..
<16 bytes per line>
위 결과를 보니 1번 블록을 좀더 자세히 보니 맨 윗줄에 10 이라는 글자가 보입니다.
find 명령에서 쓸 수 있는 옵션은 아래와 같습니다.
/x : 16진수 값을 찾습니다
/d : 10진수 값을 찾습니다.
/u : 부호없는 10진수 값을 찾습니다.
/o : 8진수 값을 찾습니다.
/c : 문자값을 찾습니다
find 명령어는 날짜와 숫자 값 검색은 지원하지 않습니다.
(20) copy
이 명령어는 블록을 복사하는 명령어 입니다.
BBED> copy dba 6,1 to dba 7,1
File: /home/oracle/oradata/testdb/indx01.dbf (7)
Block: 1 Offsets: 342 to 391 Dba:0x01c00001
------------------------------------------------------------------------
31300000 00000000 00000000 00000000 00000000 00000000 00000600 00000000
00000000 0000ffeb 552c0000 00000000 0000
<32 bytes per line>
위 명령어는 6번 파일의 1번 블록을 7번 파일의 1번 블록으로 복사하라는 뜻입니다.
이 명령어는 아주 위험한 명령어입니다.
위 예제를 수행하고 나면 7번 파일의 1번 블록은 6번 파일의 1번 블록과 같은 내용이 되어서
만약 7번 파일이 다른 데이터 파일일 경우 아래와 같이 에러가 납니다.
ERROR at line 1:
ORA-01122: database file 7 failed verification check
ORA-01110: data file 7: '/home/oracle/oradata/testdb/indx01.dbf'
ORA-01210: data file header is media corrupt
이 명령어는 백업 파일에서 특정 블록만을 운영 파일 등으로 복사해 올 때만 조심해서 사용해야 합니다.
(21) (m)odify
이 명령어는 블록의 내용을 변경하는 명령어입니다.
앞에서 6번 파일의 1번 블록에 offset 342 번에 10 이란 데이터가 들어 있었습니다.
그 데이터를 500으로 직접 변경해 보겠습니다.
BBED> m /c 500 dba 6,1 offset 342
File: /home/oracle/oradata/testdb/test01.dbf (6)
Block: 1 Offsets: 342 to 391 Dba:0x01800001
------------------------------------------------------------------------
35303000 00000000 00000000 00000000 00000000 00000000 00000600 00000000
00000000 0000ffeb 552c0000 00000000 0000
<32 bytes per line>
BBED> dump /v dba 6,1 offset 342 count 50
File: /home/oracle/oradata/testdb/test01.dbf (6)
Block: 1 Offsets: 342 to 391 Dba:0x01800001
-------------------------------------------------------
35303000 00000000 00000000 00000000 l 500.............
00000000 00000000 00000600 00000000 l ................
00000000 0000ffeb 552c0000 00000000 l ........U,......
0000 l ..
<16 bytes per line>
(22) sum
이 명령어는 블록의 checksum 을 계산해 주고 틀릴 경우 올바른 값으로 저장해 주는 명령입니다.
위 실습으로 내용이 변경된 6번 파일의 1번 블록을 사용해서 테스트 하겠습니다.
BBED> sum dba 6,1
Check value for File 6, Block 1:
current = 0xfcc0, required = 0xfcf4
원래 데이터가 10 이었는데 500 으로 변경했습니다. 그래서 checksum 값이 변경되어야 하지만
아직 저장을 안 해서 이전 checksum 값을 가지고 있습니다.
위 결과를 보면 current 와 required 값으로 구분되어 나옵니다.
BBED> sum dba 6,1 apply <- 변경된 값을 저장합니다.
Check value for File 6, Block 1:
current = 0xfcf4, required = 0xfcf4
BBED> sum dba 6,1
Check value for File 6, Block 1:
current = 0xfcf4, required = 0xfcf4
(23) push / pop
Bbed 명령어는 특정 파일의 블록을 메모리로 가져와서 수정하거나 조회하는 명령어입니다.
Push 명령어는 메모리의 stack 부분에 특정 블록을 강제로 적재 시키는 명령어이고
pop 명령어는 push 명령어로 적재시킨 메모리를 해제하는 명령어입니다.
(24) revert
이 명령어는 변경된 내용을 BBed 가 실행되기 전 값으로 rollback 하는 명령어입니다.
BBED> revert dba 6,1
All changes made to this block will be rolled back.Proceed? (Y/N) y
Reverted file '/home/oracle/oradata/testdb/test01.dbf', block 1
BBED> dump /v dba 6,1 offset 342
File: /home/oracle/oradata/testdb/test01.dbf (6)
Block: 1 Offsets: 342 to 391 Dba:0x01800001
-------------------------------------------------------
31300000 00000000 00000000 00000000 l 10..............
00000000 00000000 00000600 00000000 l ................
00000000 0000ffeb 552c0000 00000000 l ........U,......
0000 l ..
<16 bytes per line>
위 내용을 보면 처음에 10 이었던 값을 위에서 modify 명령어 이용해서 500 으로 변경한 후
저장했습니다. 그러나 revert 명령어로 원래 값이던 10 으로 돌렸습니다.
(25) undo
이 명령어는 가장 마지막에 실행했던 변경 내용을 rollback 하는 명령어입니다.
실습으로 확인하겠습니다.
먼저 6번 파일의 1번 블록에 offset 342 의 값이 10 인 상태에서 500 으로 변경 후 다시 600 으로 변경합니다.
그리고 undo 명령을 수행합니다.
그러면 가장 마지막에 했던 600 으로 변경하는 작업이 취소되어 해당 값은 500 으로 되어 있습니다.
BBED> m /c 500 dba 6,1 offset 342
File: /home/oracle/oradata/testdb/test01.dbf (6)
Block: 1 Offsets: 342 to 391 Dba:0x01800001
------------------------------------------------------------------------
35303000 00000000 00000000 00000000 00000000 00000000 00000600 00000000
00000000 0000ffeb 552c0000 00000000 0000
<32 bytes per line>
BBED> dump /v dba 6,1 offset 342
File: /home/oracle/oradata/testdb/test01.dbf (6)
Block: 1 Offsets: 342 to 391 Dba:0x01800001
-------------------------------------------------------
35303000 00000000 00000000 00000000 l 500.............
00000000 00000000 00000600 00000000 l ................
00000000 0000ffeb 552c0000 00000000 l ........U,......
0000 l ..
<16 bytes per line>
BBED> m /c 600 dba 6,1 offset 342
File: /home/oracle/oradata/testdb/test01.dbf (6)
Block: 1 Offsets: 342 to 391 Dba:0x01800001
------------------------------------------------------------------------
36303000 00000000 00000000 00000000 00000000 00000000 00000600 00000000
00000000 0000ffeb 552c0000 00000000 0000
<32 bytes per line>
BBED> dump /v dba 6,1 offset 342
File: /home/oracle/oradata/testdb/test01.dbf (6)
Block: 1 Offsets: 342 to 391 Dba:0x01800001
-------------------------------------------------------
36303000 00000000 00000000 00000000 l 600.............
00000000 00000000 00000600 00000000 l ................
00000000 0000ffeb 552c0000 00000000 l ........U,......
0000 l ..
<16 bytes per line>
BBED>undo
BBED> modify /x 353030 filename '/home/oracle/oradata/testdb/test01.dbf' block 1. offset 342.
File: /home/oracle/oradata/testdb/test01.dbf (6)
Block: 1 Offsets: 342 to 391 Dba:0x01800001
------------------------------------------------------------------------
35303000 00000000 00000000 00000000 00000000 00000000 00000600 00000000
00000000 0000ffeb 552c0000 00000000 0000
<32 bytes per line>
BBED> dump /v dba 6,1 offset 342
File: /home/oracle/oradata/testdb/test01.dbf (6)
Block: 1 Offsets: 342 to 391 Dba:0x01800001
-------------------------------------------------------
35303000 00000000 00000000 00000000 l 500.............
00000000 00000000 00000600 00000000 l ................
00000000 0000ffeb 552c0000 00000000 l ........U,......
0000 l ..
<16 bytes per line>
Undo 와 revert 의 차이는 rollback 시점이 처음상태이냐 아니면 가장 마지막 작업만 취소냐
하는 것입니다.
(26) verify
이 명령어는 특정 파일의 블록의 무결성을 검사하는 명령입니다.
BBED> verify dba 6,1
DBVERIFY - Verification starting
FILE = /home/oracle/oradata/testdb/test01.dbf
BLOCK = 1
Block 1 is corrupt
Corrupt block relative dba: 0x01800001 (file 0, block 1)
Bad check value found during verification
Data in bad block:
type: 11 format: 2 rdba: 0x01800001
last change scn: 0x0000.00000000 seq: 0x1 flg: 0x04
spare1: 0x0 spare2: 0x0 spare3: 0x0
consistency value in tail: 0x00000b01
check value in block header: 0xfcc0
computed block checksum: 0x34
DBVERIFY - Verification complete
Total Blocks Examined : 1
Total Blocks Processed (Data) : 0
Total Blocks Failing (Data) : 0
Total Blocks Processed (Index) : 0
Total Blocks Failing (Index) : 0
Total Blocks Empty : 0
Total Blocks Marked Corrupt : 1
Total Blocks Influx : 0
BBED> verify file 6
DBVERIFY - Verification starting
FILE = /home/oracle/oradata/testdb/test01.dbf
Block 1 is corrupt
Corrupt block relative dba: 0x01800001 (file 0, block 1)
Bad check value found during verification
Data in bad block:
type: 11 format: 2 rdba: 0x01800001
last change scn: 0x0000.00000000 seq: 0x1 flg: 0x04
spare1: 0x0 spare2: 0x0 spare3: 0x0
consistency value in tail: 0x00000b01
check value in block header: 0xfcc0
computed block checksum: 0x34
Block 12 is corrupt
Corrupt block relative dba: 0x0180000c (file 0, block 12)
Bad check value found during verification
Data in bad block:
type: 6 format: 2 rdba: 0x0180000c
last change scn: 0x0000.00177251 seq: 0x1 flg: 0x06
spare1: 0x0 spare2: 0x0 spare3: 0x0
consistency value in tail: 0x72510601
check value in block header: 0x5bec
computed block checksum: 0x3
Block 26 is corrupt
Corrupt block relative dba: 0x0180001a (file 0, block 26)
Bad check value found during verification
Data in bad block:
type: 6 format: 2 rdba: 0x0180001a
last change scn: 0x0000.00177d85 seq: 0x1 flg: 0x06
spare1: 0x0 spare2: 0x0 spare3: 0x0
consistency value in tail: 0x7d850601
check value in block header: 0x640e
computed block checksum: 0x1700
Block 391 is corrupt
Corrupt block relative dba: 0x01800187 (file 0, block 391)
Bad check value found during verification
Data in bad block:
type: 6 format: 2 rdba: 0x01800187
last change scn: 0x0000.00177d85 seq: 0x1 flg: 0x06
spare1: 0x0 spare2: 0x0 spare3: 0x0
consistency value in tail: 0x7d850601
check value in block header: 0xd28f
computed block checksum: 0x2300
Block 474 is corrupt
Corrupt block relative dba: 0x018001da (file 0, block 474)
Bad check value found during verification
Data in bad block:
type: 6 format: 2 rdba: 0x018001da
last change scn: 0x0000.00177d85 seq: 0x1 flg: 0x06
spare1: 0x0 spare2: 0x0 spare3: 0x0
consistency value in tail: 0x7d850601
check value in block header: 0xb2e9
computed block checksum: 0x34
Block 484 is corrupt
Corrupt block relative dba: 0x018001e4 (file 0, block 484)
Bad check value found during verification
Data in bad block:
type: 6 format: 2 rdba: 0x018001e4
last change scn: 0x0000.00177d85 seq: 0x1 flg: 0x06
spare1: 0x0 spare2: 0x0 spare3: 0x0
consistency value in tail: 0x7d850601
check value in block header: 0x8380
computed block checksum: 0xf00
Block 493 is corrupt
Corrupt block relative dba: 0x018001ed (file 0, block 493)
Bad check value found during verification
Data in bad block:
type: 6 format: 2 rdba: 0x018001ed
last change scn: 0x0000.00177d85 seq: 0x1 flg: 0x06
spare1: 0x0 spare2: 0x0 spare3: 0x0
consistency value in tail: 0x7d850601
check value in block header: 0x8594
computed block checksum: 0x23
DBVERIFY - Verification complete
Total Blocks Examined : 528
Total Blocks Processed (Data) : 307
Total Blocks Failing (Data) : 0
Total Blocks Processed (Index ) : 0
Total Blocks Failing (Index) : 0
Total Blocks Empty : 190
Total Blocks Marked Corrupt : 7
Total Blocks Influx : 0
(27) corrupt
이 명령어는 corrupt 된 블록을 mark 합니다.
BBED> corrupt dba 6,1
Block marked media corrupt.
이상으로 Bbed 의 주요 명령어들과 사용방법을 살펴 보았습니다.
다음 장에서는 지금까지 보셨던 명령어들을 사용해서 어떻게 장애를 직접 처리하는 지 살펴보겠습니다.
4) Bbed 를 활용한 block recovery 예제들
1) Delete 장애 복구하기
이번 실습은 bbed 를 사용해서 데이터가 delete 된 block 를 찾아서 delete 이전으로 복구하는 내용입니다.
Step 1. 아래와 같이 실습용 테이블을 만듭니다.
[ fruit 테이블 ]
no name price
01 apple 100
02 oranege 200
SQL> create table fruit
2 (no number,
3 name varchar2(10),
4 price varchar2(10)) tablespace users ;
SQL> insert into fruit values (1,'apple','100');
SQL> commit;
SQL> insert into fruit values (2,'orange','200');
SQL> commit;
SQL> select * from fruit;
SQL> select rowid,no,name,price
2 from fruit;
Step 2. Rowid 로 위 데이터가 들어 있는 블록 주소 찾기
SQL> select dbms_rowid.rowid_block_number('AAANccAAGAAAAAUAAA')
2 from fruit;
SQL> select dbms_rowid.rowid_block_number('AAANccAAGAAAAAUAAB')
2 from fruit;
두 건의 데이터 모두 20 번 블록에 들어가 있다는 것이 조회됩니다.
BBed 로 20 번 블록을 확인해 보겠습니다.
BBED> set dba 6,20
DBA 0x01800014 (25165844 6,20)
BBED> find /c apple
File: /home/oracle/oradata/testdb/test10.dbf (6)
Block: 20 Offsets: 8179 to 8191 Dba:0x01800014
----------------------------------------------------------------------------------------
6170706c 65033130 30010640 56
<32 bytes per line>
BBED> dump /v dba 6,20 offset 8179
File: /home/oracle/oradata/testdb/test10.dbf (6)
Block: 20 Offsets: 8179 to 8191 Dba:0x01800014
----------------------------------------------------------------------
6170706c 65033130 30010640 56 l apple.100..@V
<16 bytes per line>
BBED> set offset 0
OFFSET 0
BBED> set dba 6,20
DBA 0x01800014 (25165844 6,20)
BBED> find /c orange
File: /home/oracle/oradata/testdb/test10.dbf (6)
Block: 20 Offsets: 8162 to 8191 Dba:0x01800014
-------------------------------------------------------------------------------------------
6f72616e 67650332 30302c01 0302c102 05617070 6c650331 30300106 4056
<32 bytes per line>
BBED> dump /v dba 6,20 offset 8162
File: /home/oracle/oradata/testdb/test10.dbf (6)
Block: 20 Offsets: 8162 to 8191 Dba:0x01800014
-------------------------------------------------------------------
6f72616e 67650332 30302c01 0302c102 l orange.200,.....
05617070 6c650331 30300106 4056 l .apple.100..@V
<16 bytes per line>
위 블록의 내용을 보니 offset 8162 에 orange 가 입력되어 있고 offset 8179 에 apple 이 입력되
어 있음을 알 수 있습니다.
Step 3. Apple 을 delete 한 후 commit 을 수행한 후 block 내용 다시 조회
-- 터미널 1 작업 --
SQL> select * from fruit;
NO NAME PRICE
---------- -------------------- --------------------
1 apple 100
2 orange 200
SQL> delete from fruit where no=1;
1 row deleted.
SQL> commit;
Commit complete.
SQL> select * from fruit;
NO NAME PRICE
---------- -------------------- --------------------
2 orange 200
SQL> alter system checkpoint;
System altered.
-- 터미널 2 작업 --
위 작업을 수행 한 후 다른 터미널에서 bbed 를 실행해서 블록을 살펴 보겠습니다.
BBED> find /c apple
File: /home/oracle/oradata/testdb/test10.dbf (6)
Block: 20 Offsets: 8179 to 8191 Dba:0x01800014
------------------------------------------------------------------------
6170706c 65033130 30010640 56
<32 bytes per line>
BBED> dump /v dba 6,20 offset 8179 count 50
File: /home/oracle/oradata/testdb/test10.dbf (6)
Block: 20 Offsets: 8179 to 8191 Dba:0x01800014
------------------------------------------------------------------
6170706c 65033130 30010602 57 l apple.100...W
<16 bytes per line>
여전히 block 내부에는 apple 이 삭제되지 않고 존재함을 알 수 있습니다.
이 이유는 DML 을 수행하면 오라클은 해당 블록에서 직접 데이터를 지우지 않고 해당 row에 사용하지 않는다는 표시만 하게 됩니다.
그리고 freespace 에는 빈 곳으로 표시됩니다.
이렇게 해당 데이터가 delete 되었다는 표시는 해당 row의 Header 부분에 표시됩니다.
Row Header 는 Row Flag , Lock Byte (ITL entry), Column Count 로 구성이 되는데 제일 먼저 있는
Row Flag 부분은 해당 row 의 상태를 표시하는 1 byte 로 구성이 됩니다.
이곳에 표시될 수 있는 값이 아래 표에 있습니다.
Cluster Key 128
Cluster Table Member 64
Head of Row Piece 32
Deleted 16
First data piece 8
Last data piece 4
1st column 2
Last Column 1
위 표의 값들을 계산해서 row header 정보를 분석하면 해당 row 가 어떤 상태인지 알 수 있습니다.
위의 실습처럼 apple 를 삭제 한 후 row header 정보를 살펴보겠습니다.
일반 테이블에서 특정 row 가 삭제된다면 deleted 부분이 set 으로 설정이 되어 32+16+8+4 = 60 이 되어
16진수로 0x3c 값이 되고 데이터가 delete 안되었다면 deleted 부분이 not set 상태여서 32+8+4=44 로 16진수 0x2c 에 해당됩니다.
BBED> p *kdbr[0]
rowdata[17]
------------------
ub1 rowdata[17] @8172 0x3c
BBED> p *kdbr[1]
rowdata[0]
------------------
ub1 rowdata[0] @8155 0x2c
위 결과를 보면 apple 은 offset 8179 에 있었는데 row header 는 8172 임을 알 수 있으며 row header 값이 0x3c 로
delete 되었다는 내용을 알 수 있습니다.
또 orange 는 offset 8162 에 입력되어 있었는데 row header 는 8155 임을 알 수 있으며 0x2c 로 데이터가 존재한다는 것도 알 수 있습니다.
Step 4. Bbed로 복구한 후 조회하기
복구 원리는 간단합니다. 8172 의 0x3c 값을 0x2c 값으로 변경하고 저장만 하면 바로 적용됩니다.
아래와 같이 하시면 됩니다.
BBED> m /x 2c offset 8172
File: /home/oracle/oradata/testdb/test10.dbf (6)
Block: 20 Offsets: 8172 to 8191 Dba:0x01800014
----------------------------------------------------------------------------------------
2c010302 c1020561 70706c65 03313030 01060257
<32 bytes per line>
BBED> sum dba 6,20
Check value for File 6, Block 20:
current = 0xb8b8, required = 0xb8a8
BBED> sum dba 6,20 apply
Check value for File 6, Block 20:
current = 0xb8a8, required = 0xb8a8
BBED> p *kdbr[0]
rowdata[17]
-------------------
ub1 rowdata[17] @8172 0x2c
-- 터미널 2 –
다른 터미널에서 확인해 보겠습니다.
SQL> select * from fruit;
NO NAME PRICE
---------- -------------------- --------------------
2 orange 200 <- 아직 복구 전 값이 나옵니다.
SQL> alter tablespace test10 offline;
Tablespace altered.
SQL> alter tablespace test10 online;
Tablespace altered.
SQL> select * from fruit;
NO NAME PRICE
---------- -------------------- --------------------
1 apple 100 <- 다시 살아납니다.
2 orange 200
이번에는 BBed 로 2번 orange 의 row header 값을 0x3c로 변경하고 확인 해 보겠습니다.
BBED> m /x 3c offset 8155
File: /home/oracle/oradata/testdb/test10.dbf (6)
Block: 20 Offsets: 8155 to 8191 Dba:0x01800014
----------------------------------------------------------------------------------------------
3c000302 c103066f 72616e67 65033230 302c0103 02c10205 6170706c 65033130
30010602 57
<32 bytes per line>
BBED> sum dba 6,20 apply
Check value for File 6, Block 20:
current = 0xa8a8, required = 0xa8a8
BBED> p *kdbr[1]
rowdata[0]
-------------------
ub1 rowdata[0] @8155 0x3c <- 삭제 표시 되었습니다.
-- 터미널 2 –
다른 터미널에서 이 내용이 반영되었는지 확인하겠습니다.
SQL> alter tablespace test10 offline;
Tablespace altered.
SQL> alter tablespace test10 online;
Tablespace altered.
SQL> select * from fruit;
NO NAME PRICE
---------- -------------------- --------------------
1 apple 100
예상대로 orange 가 보이지 않습니다.
2) BBED로 Block corruption 장애 복구하기
Oracle 8i부터는 DBMS_REPAIR 이란 패키지가 나와서 Block Corrupt 장애가 나도 쉽게 해결할 수 있음을 앞에서 살펴보았습니다.
그러나 여기서는 Bbed 를 사용하여 corrupt 된 block 를 recovery 해 보겠습니다.
DBMS_REPAIR 과 BBed 와는 아주 중요한 차이가 있습니다.
어떤 블록에 있는 특정 row 에서 corruption 이 발생했을 경우 DBMS_REPAIR 는 해당 블록 전체를 Corruption 처리해서 skip 하게 됩니다.
그 말은 해당 블록 안에 있는 전체 내용이 손상된다는 뜻입니다.
그러나 BBED 는 corruption이 발생한 row 만 corruption 처리를 하기 때문에 그 블록 안에 있는 데이터들은 살려 낼 수 있습니다.
이 실습을 위해 테스트 테이블 tt930 을 생성하고 데이터를 5건 입력하겠습니다.
그리고 특정 row 에 corruption 을 발생시켜 DBMS_REPAIR 과 BBED 로 복구해서 차이를 살펴보겠습니다.
< 실습 1. DBMS_REPAIR 로 block corruption 복구하기 >
Step 1. 실습용 테이블 tt930 테이블을 생성합니다.
SQL> !vi dd.sql
conn / as sysdba
set line 200
col tablespace_name for a10
col file_name for a50
col mb for 9999
select tablespace_name , bytes/1024/1024 MB , file_name
from dba_data_files
/
:wq!
SQL> @dd
Connected.
TABLESPACE MB FILE_NAME
----------------- -------- --------------------------------------------------
EXAMPLE 183 /home/oracle/oradata/testdb/example01.dbf
USERS 20 /home/oracle/oradata/testdb/users01.dbf
SYSAUX 340 /home/oracle/oradata/testdb/sysaux01.dbf
UNDOTBS1 50 /home/oracle/oradata/testdb/undotbs01.dbf
SYSTEM 460 /home/oracle/oradata/testdb/system01.dbf
TEST10 1 /home/oracle/oradata/testdb/test10.dbf
6 rows selected.
SQL> create table tt930 (no number,name varchar2(10)) tablespace test10;
Table created.
SQL> insert into tt930 values (1,'AAAAA');
1 row created.
SQL> insert into tt930 values (2,'BBBBB');
1 row created.
SQL> insert into tt930 values (3,'CCCCC');
1 row created.
SQL> insert into tt930 values (4,'DDDDD');
1 row created.
SQL> insert into tt930 values (5,'EEEEE');
1 row created.
SQL> commit;
Commit complete.
SQL> select * from tt930;
NO NAME
---------- --------------------
1 AAAAA
2 BBBBB
3 CCCCC
4 DDDDD
5 EEEEE
SQL> select dbms_rowid.rowid_block_number('AAANcdAAGAAAAAcAAA') from tt930;
DBMS_ROWID.ROWID_BLOCK_NUMBER('AAANCDAAGAAAAACAAA')
---------------------------------------------------------------------------------
28
28
28
28
28
위 5건의 데이터가 모두 28번 블록에 들어 있다는 것이 확인됩니다.
Step 2. 1 AAAAA row 를 corruption 발생시킨 후 조회하겠습니다.
윈도로 위 데이터 파일을 가져온 후 에디터에서 AAAAA 를 수정하여 block corruption 을 발생시킨 후 리눅스로 보내서 아래와 같이 작업 하겠습니다.
SQL> alter tablespace test10 offline;
Tablespace altered.
SQL> alter tablespace test10 online;
Tablespace altered.
SQL> select * from tt930;
select * from tt930
*
ERROR at line 1:
ORA-01578: ORACLE data block corrupted (file # 6, block # 28)
ORA-01110: data file 6: '/home/oracle/oradata/testdb/test10.dbf'
예상대로 28번 block 에 corruption 이 발생해서 조회가 되지 않습니다.
Step 3. DBMS_REPAIR 로 복구하겠습니다.
-- corruption detect –
SQL> set serveroutput on
SQL>
SQL> declare num_corrupt int;
2 begin
3 num_corrupt := 0;
4 dbms_repair.check_object (
5 schema_name => 'SYS' ,
6 object_name => 'TT930' ,
7 repair_table_name => 'REPAIR_TABLE',
8 corrupt_count => num_corrupt );
9
10 dbms_output.put_line('===================================');
11 dbms_output.put_line(' 장애블록수 :'|| to_char(num_corrupt));
12 end;
13 /
===================================
장애블록수 :1
PL/SQL procedure successfully completed.
1 개의 블록에 장애가 발생했음을 알 수 있습니다.
보다 자세한 내용을 살펴보겠습니다.
SQL> col object_name for a10
SQL> col corrupt_description for a20
SQL> col repair_description for a30
SQL> set line 200
SQL> select object_name,block_id,corrupt_type,marked_corrupt,
2 corrupt_description, repair_description
3 from repair_table ;
OBJECT_NAM BLOCK_ID CORRUPT_TYPE MARKED_CORRUPT CORRUPT_DESCRIPTION REPAIR_DESCRIPTION
---------------- ------------- -------------------------------------------- ----------------------------- ------------------------TT930 28 6148 TRUE mark block soft ware
corrupt
28번 블록이 에러 났다는 것을 알 수 있습니다.
-- skip 설정하겠습니다.
SQL> begin
2 dbms_repair.skip_corrupt_blocks (
3 schema_name => 'SYS' ,
4 object_name => 'TT930' ,
5 object_type => dbms_repair.table_object ,
6 flags => dbms_repair.skip_flag );
All About Oracle Backup and Recovery - 서진수 저
7 end;
8 /
PL/SQL procedure successfully completed.
Step 4. Tt930 테이블을 다시 조회합니다.
SQL> select * from tt930;
no rows selected
한 건의 데이터도 안 나옴을 알 수 있습니다.
즉 DBMS_REPAIR 은 블록에서 특정 row 만 장애 나도 블록 전체를 corrupt 처리해서 해당 블록
내부에 있는 데이터를 모두 손실하게 되는 문제가 있습니다.
반면에 Bbed 는 장애 난 row 만 corruption 처리를 하기 때문에 나머지 데이터는 전부 살려낼
수 있습니다. 동일한 장애 발생 시킨 후 BBED 로 복구해 보겠습니다.
< 실습 2. BBED 로 block corruption 복구하기 >
Step 1. 실습용 테이블 tt940 을 생성합니다.
SQL> @dd
Connected.
TABLESPACE MB FILE_NAME
---------------- ------- --------------------------------------------------
EXAMPLE 183 /home/oracle/oradata/testdb/example01.dbf
USERS 20 /home/oracle/oradata/testdb/users01.dbf
SYSAUX 340 /home/oracle/oradata/testdb/sysaux01.dbf
UNDOTBS1 50 /home/oracle/oradata/testdb/undotbs01.dbf
SYSTEM 460 /home/oracle/oradata/testdb/system01.dbf
TEST10 1 /home/oracle/oradata/testdb/test10.dbf
6 rows selected.
SQL> create table tt940 (no number, name varchar2(10)) tablespace test10;
Table created.
All About Oracle Backup and Recovery - 서진수 저
SQL> insert into tt940 values (1,'AAAAA'); <- 이 row 에 장애를 발생시킬 예정입니다.
1 row created.
SQL> insert into tt940 values (2,'BBBBB');
1 row created.
SQL> insert into tt940 values (3,'CCCCC');
1 row created.
SQL> insert into tt940 values (4,'DDDDD');
1 row created.
SQL> insert into tt940 values (5,'EEEEE');
1 row created.
SQL> commit;
Commit complete.
SQL> select * from tt940;
NO NAME
---------- --------------------
1 AAAAA
2 BBBBB
3 CCCCC
4 DDDDD
5 EEEEE
SQL> select rowid, no , name from tt940;
ROWID NO NAME
---------------------------------- ---------- --------------------
AAANceAAGAAAAAkAAA 1 AAAAA
AAANceAAGAAAAAkAAB 2 BBBBB
AAANceAAGAAAAAkAAC 3 CCCCC
AAANceAAGAAAAAkAAD 4 DDDDD
AAANceAAGAAAAAkAAE 5 EEEEE
SQL> select dbms_rowid.rowid_block_number('AAANceAAGAAAAAkAAA') from tt940;
DBMS_ROWID.ROWID_BLOCK_NUMBER('AAANCEAAGAAAAAKAAA')
------------------------------------------------------------------------------
36
36
36
36
36
36번 블록에 저장되어 있다는 것이 확인됩니다.
Step 2. AAAAA row 에 장애를 발생시킵니다.
윈도로 해당 데이터파일을 가져와서 에디터로 해당 row 를 corruption 발생 시킨 후 리눅스로 보내고 아래와 같이 진행하겠습니다.
SQL> alter tablespace test10 offline;
Tablespace altered.
SQL> alter tablespace test10 online;
alter tablespace test10 online
*
ERROR at line 1:
ORA-01113: file 6 needs media recovery if it was restored from backup, or END BACKUP if it was
not
ORA-01110: data file 6: '/home/oracle/oradata/testdb/test10.dbf'
SQL> recover tablespace test10;
Media recovery complete.
SQL> alter tablespace test10 online;
Tablespace altered.
SQL> select * from tt940;
select * from tt940
*
ERROR at line 1:
ORA-01578: ORACLE data block corrupted (file # 6, block # 36)
ORA-01110: data file 6: '/home/oracle/oradata/testdb/test10.dbf'
36번 블록에 corruption 이 발생한 것을 알 수 있습니다.
Step 3. BBED 를 실행시켜 해당 row 를 복구합니다.
[oracle@localhost ~]$ vi filelist.log
1 /home/oracle/oradata/testdb/system01.dbf 482344960
2 /home/oracle/oradata/testdb/undotbs01.dbf 52428800
3 /home/oracle/oradata/testdb/sysaux01.dbf 356515840
4 /home/oracle/oradata/testdb/users01.dbf 20971520
5 /home/oracle/oradata/testdb/example01.dbf 192020480
6 /home/oracle/oradata/testdb/test10.dbf 4325376
:wq!
[oracle@localhost ~]$ vi bbed.par
blocksize=8192
listfile=/home/oracle/filelist.log
mode=edit
:wq!
[oracle@localhost ~]$ bbed parfile=bbed.par
Password: blockedit
BBED: Release 2.0.0.0.0 - Limited Production on ThuFeb 24 13:19:11 2011
Copyright (c) 1982, 2007, Oracle. All rights reserved.
************* !!! For Oracle Internal Use only !!! ***************
BBED> set dba 6,36
DBA 0x01800024 (25165860 6,36)
BBED> corrupt block 36 <- 장애 난 블록을 corrupt 처리 합니다.
Warning: contents of previous BIFILE will be lost. Proceed? (Y/N) y
Block marked media corrupt.
BBED> p kcbh
struct kcbh, 20 bytes @0
ub1 type_kcbh @0 0x06
ub1 frmt_kcbh @1 0xa2
ub1 spare1_kcbh @2 0x00
ub1 spare2_kcbh @3 0x00
ub4 rdba_kcbh @4 0x01800024
ub4 bas_kcbh @8 0x00000000
ub2 wrp_kcbh @12 0x0000
ub1 seq_kcbh @14 0xff
ub1 flg_kcbh @15 0x04 (KCBHFCKV)
ub2 chkval_kcbh @16 0x4267
ub2 spare3_kcbh @18 0x0000
오라클은 특정 블록이 corrupt 되면 해당 블록의 sequence number 를 0xff 로 설정하며
이것은 seq_kcbh 에서 조회가 가능합니다. 위의 굵은 표시로 해 둔 곳입니다.
이 값을 다른 사용 가능한 값 (예를 들어 0x01 등) 으로 변경하면 block corruption 이 취소됩니다.
BBED> m /x 01 dba 6,36 offset 14
File: /home/oracle/oradata/testdb/test10.dbf (6)
Block: 36 Offsets: 14 to 525 Dba:0x01800024
------------------------------------------------------------------------
01046742 00000100 00001ed7 00003274 18000000 00000200 32002100 80010600
1900dd01 00000509 80007401 20000080 00004873 18000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000001 0500ffff 1c005c1f
( 지면 관계상 중간 결과 출력은 생략합니다 )
<32 bytes per line>
BBED> p kcbh
struct kcbh, 20 bytes @0
ub1 type_kcbh @0 0x06
ub1 frmt_kcbh @1 0xa2
ub1 spare1_kcbh @2 0x00
ub1 spare2_kcbh @3 0x00
ub4 rdba_kcbh @4 0x01800024
ub4 bas_kcbh @8 0x00000000
ub2 wrp_kcbh @12 0x0000
ub1 seq_kcbh @14 0x01
ub1 flg_kcbh @15 0x04 (KCBHFCKV)
ub2 chkval_kcbh @16 0x4267
ub2 spare3_kcbh @18 0x0000
또한 sequence number 는 마지막 8 bytes 에 해당 row 의 corruption 여부를 등록해 두는데
우리는 아래와 같이 tailchk 를 이용해서 조회를 할 수 있습니다.
해당 row 에 장애가 생기면 이 row 의 값이 0x000006ff 로 설정이 되는데 이 값 역시 새로운 SCN 으로 변경해 주어야 합니다.
BBED> p tailchk
ub4 tailchk @8188 0x000006ff
BBED> m /x 01060000 dba 6,36 offset 8188
File: /home/oracle/oradata/testdb/test10.dbf (6)
Block: 36 Offsets: 8188 to 8191 Dba:0x01800024
------------------------------------------------------------------------
01060000 <- 원래 06ff 였던 값을 사용 가능한 0x10 (010) 으로 변경했습니다.
<32 bytes per line>
BBED> sum dba 6,36 apply
Check value for File 6, Block 36:
current = 0x4267, required = 0x4267
그 후에 해당 블록의 변경된 checksum 값을 저장하고 적용하는 것으로 corruption 이 발생한 블록의 수정 작업을 마쳤습니다.
이제 다른 창에서 장애 난 테이블을 다시 조회 해 보겠습니다.
기억해야 할 것은 위에서 DBMS_REPAIR 로는 Block Recovery 를 한 후에도 해당 블록에 있던 모든 내용이 조회가 되지 않았다는 것입니다.
SQL> select * from tt940;
select * from tt940
*
ERROR at line 1:
ORA-01578: ORACLE data block corrupted (file # 6, block # 36)
ORA-01110: data file 6: '/home/oracle/oradata/testdb/test10.dbf'
SQL> alter tablespace test10 offline;
Tablespace altered.
SQL> alter tablespace test10 online;
Tablespace altered.
SQL> select * from tt940;
NO NAME
---------- --------------------
1 AzAAA <- 필자가 발생시킨 Corruption 난 row 입니다.
2 BBBBB
3 CCCCC
4 DDDDD
5 EEEEE
위에서 보듯이 해당 블록에 모든 데이터가 조회 됩니다.
위 실습을 다시 정리합니다.
필자는 1번 row 의 AAAAA 를 AzAAA 로 강제 수정해서 Block corruption 을 발생시켰습니다.
위 5건의 데이터는 모두 6번 파일의 36번 블록에 들어가 있었는데 강제로 내용을 수정시켜서
Block corruption 을 발생 시킨 후 DBMS_REPAIR 로 Block Recovery 를 했으나 36번 블록 전체가
Corruption 처리 되어서 모든 데이터가 전부 조회가 되지 않았습니다.
그러나 BBED 로 Corruption 된 Row 만 찾아서 Recovery 한 후 조회하니까 모든 데이터가 전부 조회 되었습니다.
BBED 를 활용한 아주 막강한 Block Recovery 기능이니 잘 숙지하셔서 요긴하게 사용하시기 바랍니다.
실습 3. Noarchive log mode 에서 복구가 안 되는 데이터 파일 Open 하기
이번 경우는 Noarchive log mode 에서 Archive redo log file 이 없어서 복구가 안되는 데이터파일의 블록을 제어해서 open 을 시키는 과정입니다.
Step 1. 현재 상황 확인 후 다수의 Log Switch 발생하기
SYS>archive log list;
Database log mode No Archive Mode
Automatic archival Disabled
Archive destination /data/arc2
Oldest online log sequence 17
Current log sequence 19
SYS>alter system switch logfile;
System altered.
SYS>/
System altered.
( 지면 관계상 수행 과정은 생략하지만 반복해서 Log Switch 를 발생시키세요 )
Step 2. DB 종료 후 현재 상태를 Close Backup 수행합니다.
SYS>shutdown immediate;
Database closed.
Database dismounted.
ORACLE instance shut down.
SYS>!
[oracle@localhost ~]$ mkdir /data/backup/close/noarch/
[oracle@localhost ~]$ cp /app/oracle/oradata/testdb/* /data/backup/close/noarch/
[oracle@localhost ~]$ exit
exit
Step 3. 백업 파일의 내용과 Checkpoint SCN 정보를 다르게 만듭니다.
SYS>startup
ORACLE instance started.
Total System Global Area 422670336 bytes
Fixed Size 1344616 bytes
Variable Size 260049816 bytes
Database Buffers 155189248 bytes
Redo Buffers 6086656 bytes
Database mounted.
Database opened.
SYS>alter system checkpoint ;
System altered.
SYS>/
System altered.
SYS>/
System altered.
SYS>alter system switch logfile;
System altered.
SYS>/
System altered.
Step 4. DB 종료 후 백업해 둔 users01.dbf 파일을 운영 경로로 복사 후 Open 시도합니다.
SYS>shutdown immediate;
Database closed.
Database dismounted.
ORACLE instance shut down.
SYS>!
[oracle@localhost ~]$ cp /data/backup/close/noarch/users01.dbf /app/oracle/oradata/testdb/
[oracle@localhost ~]$ exit
exit
SYS>startup
ORACLE instance started.
Total System Global Area 422670336 bytes
Fixed Size 1344616 bytes
Variable Size 260049816 bytes
Database Buffers 155189248 bytes
Redo Buffers 6086656 bytes
Database mounted.
ORA-01113: file 4 needs media recovery
ORA-01110: data file 4: '/app/oracle/oradata/testdb/users01.dbf'
SYS>recover datafile '/app/oracle/oradata/testdb/users01.dbf' ;
ORA-00279: change 1904232 generated at 11/26/2013 04:36:04 needed for thread 1
ORA-00289: suggestion : /data/arc2/26_1_823319766.arc
ORA-00280: change 1904232 for thread 1 is in sequence #26
Specify log: {<RET>=suggested | filename | AUTO | CANCEL}
ORA-00326: log begins at change 1952656, need earlier change 1904232
ORA-00334: archived log: '/data/arc2/26_1_823319766.arc'
SYS>alter database open;
alter database open
*
ERROR at line 1:
ORA-01113: file 4 needs media recovery
ORA-01110: data file 4: '/app/oracle/oradata/testdb/users01.dbf'
위와 같이 복구가 필요하다고 나오지만 No Archive Log Mode 이므로 복구할 수 없어서 복구도 안되고 Open 도 안됩니다.
이럴 경우 기존 방법은 해당 파일만 Offline drop 처리하고 DB를 Open 하면 되지만
그럴 경우 Offline Drop 된 파일에 저장되어 있던 내용은 모두 손실됩니다.
이런 경우를 BBED 를 활용해서 장애 난 파일의 블록을 직접 조작해서 OPEN 시켜 보겠습니다.
SYS>select checkpoint_change# from v$database;
CHECKPOINT_CHANGE#
--------------------------
1905030 <-- 정상적인 checkpoint scn 정보
SYS>select change# from v$recover_file ;
CHANGE#
---------------
1904232 <-- users01.dbf 의 문제되는 예전 scn 정보
위에서 보는 바와 같이 정상적인 Checkpoint SCN 은 1905030 인데 장애난 users01.dbf 는
1904232 라서 Open 이 되지 않습니다.
이런 경우를 해결하는 방법은 users01.dbf 의 블록을 조작해서 정보를 동일하게 세팅을 하면 됩니다.
Step 5. bbed 를 사용해서 system01.dbf 의 정보를 확인 한 후 users01.dbf 의 정보를 system01.dbf 의 정보로 업데이트 함.
[oracle@localhost ~]$ vi /home/oracle/target.log
1 /app/oracle/oradata/testdb/system01.dbf 754974720
2 /app/oracle/oradata/testdb/sysaux01.dbf 681574400
3 /app/oracle/oradata/testdb/undotbs01.dbf 10485760
4 /app/oracle/oradata/testdb/users01.dbf 10485760
5 /app/oracle/oradata/testdb/example01.dbf 362414080
:wq!
[oracle@localhost ~]$ vi bbed.par
blocksize=8192
listfile=/home/oracle/target.log
mode=edit
:wq!
[oracle@localhost ~]$ bbed parfile=bbed.par
Password: blockedit
BBED: Release 2.0.0.0.0 - Limited Production on TueNov 26 04:55:07 2013
Copyright (c) 1982, 2009, Oracle and/or its affiliates. All rights reserved.
************* !!! For Oracle Internal Use only !!! ***************
BBED> set filename '/app/oracle/oradata/testdb/system01.dbf'
FILENAME /app/oracle/oradata/testdb/ system01.dbf
BBED> p kcvfhckp
struct kcvfhckp, 36 bytes @484
struct kcvcpscn, 8 bytes @484
ub4 kscnbas @484 0x001d14ed
ub2 kscnwrp @488 0x0000
ub4 kcvcptim @492 0x319eb358
ub2 kcvcpthr @496 0x0001
union u, 12 bytes @500
struct kcvcprba, 12 bytes @50 0
ub4 kcrbaseq @500 0x00000021
ub4 kcrbabno @504 0x000002e4
ub2 kcrbabof @508 0x0010
ub1 kcvcpetb[0] @512 0x02
ub1 kcvcpetb[1] @513 0x00
ub1 kcvcpetb[2] @514 0x00
ub1 kcvcpetb[3] @515 0x00
ub1 kcvcpetb[4] @516 0x00
ub1 kcvcpetb[5] @517 0x00
ub1 kcvcpetb[6] @518 0x00
ub1 kcvcpetb[7] @519 0x00
BBED> p kcvfhcpc
ub4 kcvfhcpc @140 0x0000013a
BBED> p kcvfhccc
ub4 kcvfhccc @148 0x00000139
5.2 users01.dbf 로 변경 후 정보확인
BBED> set filename '/app/oracle/oradata/testdb/users01.dbf'
FILENAME /app/oracle/oradata/testdb/ users01.dbf
BBED> p kcvfhckp
struct kcvfhckp, 36 bytes @484
struct kcvcpscn, 8 bytes @484
ub4 kscnbas @484 0x001d0e68
ub2 kscnwrp @488 0x0000
ub4 kcvcptim @492 0x319ea534
ub2 kcvcpthr @496 0x0001
union u, 12 bytes @500
struct kcvcprba, 12 bytes @50 0
ub4 kcrbaseq @500 0x0000001a
ub4 kcrbabno @504 0x00000107
ub2 kcrbabof @508 0x0010
ub1 kcvcpetb[0] @512 0x02
ub1 kcvcpetb[1] @513 0x00
ub1 kcvcpetb[2] @514 0x00
ub1 kcvcpetb[3] @515 0x00
ub1 kcvcpetb[4] @516 0x00
ub1 kcvcpetb[5] @517 0x00
ub1 kcvcpetb[6] @518 0x00
ub1 kcvcpetb[7] @519 0x00
BBED> p kcvfhcpc
ub4 kcvfhcpc @140 0x0000012b
BBED> p kcvfhccc
ub4 kcvfhccc @148 0x0000012a
5.3 users01.dbf 의 내용을 system01.dbf 와 동일하게 변경한다.
BBED> m /x ed141d00 dba 4,1 offset 484
BBED-00209: invalid number (ed141d00) <-- 에러 발생할 경우 아래와 같이 하세요
BBED> m /x ed14 dba 4,1 offset 484
BBED> set offset +2
BBED> m /x 1d00
BBED> m /x 58b39e31 dba 4,1 offset 492
Warning: contents of previous BIFILE will be lost. Proceed? (Y/N) Y
File: /app/oracle/oradata/testdb/users01.dbf (4)
Block: 1 Offsets: 492 to 1003 Dba:0x01000001
------------------------------------------------------------------------
58b39e31 01000000 1a000000 07010000 10000000 02000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
(중간 내용 생략)
<32 bytes per line>
BBED> m /x 3a010000 dba 4,1 offset 140
File: /app/oracle/oradata/testdb/users01.dbf (4)
Block: 1 Offsets: 140 to 651 Dba:0x01000001
------------------------------------------------------------------------
3a010000 85b39e31 2a010000 6b3f1a00 00000000 3da35e30 01003500 32000000
c24c0000 10000000 02000000 00000000 00000000 00000000 00000000 00000000
( 중간 내용 생략 )
<32 bytes per line>
BBED> m /x 39010000 dba 4,1 offset 148
File: /app/oracle/oradata/testdb/users01.dbf (4)
Block: 1 Offsets: 148 to 659 Dba:0x01000001
------------------------------------------------------------------------
39010000 6b3f1a00 00000000 3da35e30 01003500 32000000 c24c0000 10000000
02000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
(중간 내용 생략)
<32 bytes per line>
BBED> sum dba 4,1 apply
Check value for File 4, Block 1:
current = 0x3e95, required = 0x3e95
Step 6. SQLPLUS 에서 recovery 해서 Open 합니다.
SYS>recover datafile '/app/oracle/oradata/testdb/users01.dbf' ;
Media recovery complete.
SYS>alter database open;
Database altered.
정상적으로 잘 Open 되는 것이 확인됩니다.
지면 관계상 모두 언급할 수는 없지만 여기서 살펴 본 것 외에도 일반적인 방법으로 복구를 할 수 없는 아주 다양한 경우를 해결할 수 있습니다.
혹시라도 일반적인 방법으로 살려 낼 수 없어서 BBED 를 사용해서라도 시도를 해 보고 싶은 분 들은 저자에게 연락을 해 주세요.
다시 한번 더 강조하지만 BBED 라는 방법은 잘 못 사용될 경우 완전히 복구가 불가 한 더 큰 문제를 만들 수 있으므로 아주 주의해야 합니다!!
그리고 반드시 기억 하셔야 할 것은 사람도 병원이 있다고 해도 평소에 건강관리 잘해서 아프지 않는 것이 가장 최선이듯이
평상시에 백업을 아주 철저히 하시길 당부 드립니다.