TECH
QUESTION
자주하는 질문답변 입니다.
Oracle
작성자 | 유건데이타 | 등록일 | 2015-06-22 |
제목 | ORA-1578(Data Block Corruption) ora-01578 | ||
---|---|---|---|
ORA-1578(Data Block Corruption) 조치 방법
<사전에 알아두어야 할 사항> # Oracle 8의 ROWID 1. FORMAT : OOOOOOFFFBBBBBBSSS * OOOOOO - data object number 로서 물리적인 segment의 id를 의미 * FFF - row가 위치한 datafile의 relative datafile number * BBBBBB - file 내에서의 block id * SSS - block 내에서의 row의 sequence number 2. 각 구성 요소는 64진법으로 표기되어 진다. 3. DBMS_ROWID Package는 ROWID를 10진법으로 변환하는 FUNCTION과 ROWID를 생성하는 FUNCTION을 포함하고 있다.( 자세한 내용은 BULLETIN 11366 참조) # DBA_OBJECTS에서 OBJECT_ID와 DATA_OBJECT_ID Oracle 8에서는 DBA_OBJECTS 안에 physical segment id를 의미하는 data_object_id column을 가지고 있다. 이 column의 값은 ROWID의 object id 를 구성하는데 사용된다. 이 column은 대부분 object_id 값과 동일하나 partitioned table과 clustered table안에서는 다를 수 있다. Partitioned table에서는 partition이 다른 tablespace로 이동하였을 경 우 object_id는 예전과 동일하나 data_object_id는 달라 진다. Clustered table에서는 반대로 data_object_id는 동일하나 object_id가 다 른 경우가 발생한다. (자세한 내용은 Bulletin 11604 참조) # RELATIVE DATAFILE NUMBER 8.x 의 relative datafile number는 특정 tablespace 내에서만 고유하다. 따라서 서로 다른 두개의 tablespace에 속한 datafile들이 동일한 relative datafile number를 가질 수 있다. 8.x 에서는 tablespace 당 최대 1023개의 datafile을 사용. 전체 database 의 datafile 갯수가 1023개를 넘기 전까지는 file_id와 relative_fno는 동일한 값을 갖음. # ROWID 생성 DBMS_ROWID Package의 DBMS_ROWID.ROWID_CREATE Function사용. Syntax : Function dbms_rowid.rowid_create( rowid_type in number, object_number in number, relative_fno in number, block_number in number, row_number in number ) return rowid; SQL> select dbms_rowid.rowid_create(1,10116,4,5948,0) CREATE_ROWID from dual; CREATE_ROWID ------------ AAACeEAAEAAABc8AAA < 해결 방법 > 1. HARDWARE 문제가 있다면 이를 먼저 해결한다. 2. DATA DICTIONARY 의 INDEX나 TABLE이 손상되었다면 FULL BACKUP으로부터 복구. 3. NON-DICTIONARY OBJECT일 경우 # INDEX가 손상된 경우: INDEX를 REBUILD하면 된다. # TABLE이 손상된 경우: * EXPORT BACKUP이 있다면 손상된 TABLE을 DROP하고 IMPORT. * ARCHIVE MODE라면 해당 DATAFILE을 RESTORE하고 RECOVERY. * 만약 BACKUP이 전혀 없다면 ROWID를 이용하여 손상된 BLOCK을 제외한 나머지 BLOCK의 ROW들만을 추출한다. 이것은 반드시 INDEX가 생성된 TABLE에 한해서만 적용 가능하다. 4. 손상된 BLOCK을 제외한 나머지 BLOCK의 ROW들을 추출하는 방법. ERROR MESSAGE : ORA-1578 "ORACLE data block corrupted (file # 4, block # 1562) - file# 4는 absolute datafile number 임. STEP 1 : 어떤 OBJECT에서 발생하였는가를 파악 ------ ---------------------------------- SELECT owner, segment_name, segment_type , relative_fno, partition_name FROM dba_extents WHERE file_id = 4 AND 1562 BETWEEN block_id AND ( block_id + (blocks - 1)) ; OWNER SEGMENT_NAME SEGMENT_TYPE RELATIVE_FNO PARTITION_NAME ----- ------------ ------------ ------------ -------------- SCOTT EMP TABLE 4 * RELATIVE_FNO 확인 * PARTITIONED TABLE인지 확인 STEP 2 : DATA_OBJECT_ID를 추출 ------ --------------------- SELECT DATA_OBJECT_ID FROM dba_objects WHERE owner = 'SCOTT' AND object_name = 'EMP' [ AND partition_name = *EMP_PART_2* ] ; DATA_OBJECT_ID -------------- 7033 * [ ] 안의 내용은 partitioned table일 경우에 추가되는 조건임. STEP 3 : ROWID 생성 ------- ---------- 생성해야할 ROWID는 BLOCK 1562 와 BLOCK 1563의 각 첫 번째 ROWID들이다. STEP 1,2에서 다음과 같은 정보를 얻었다. OBJECT_NUMBER = 7033, RELATIVE_FNO= 4 , BLOCK-_NUMBER=1562 각 BLOCK의 ROW SEQUENCE NUMBER는 0 부터 시작하므로 ROWID는 다음과 같이 생성할 수 있다. SQL> SELECT DBMS_ROWID.ROWID_CREATE(1,7033,4,1562,0) FROM DUAL; DBMS_ROWID.ROWID_C ------------------ AAABt5AAEAAAAYaAAA SQL> SELECT DBMS_ROWID.ROWID_CREATE(1,7033,4,1563,0) FROM DUAL; DBMS_ROWID.ROWID_C ------------------ AAABt5AAEAAAAYbAAA # 위 부분에서 기존의 BULLETIN(11340)은 잘못 되어 있다. -------------------------------------------------- 즉 기존의 BULLETIN에서는 BLOCK 1561 과 BLOCK 1563의 첫 번째 ROWID를 구해서 ROW를 추출하였다. 그러나 이렇게하면 BLOCK 1561의 ROW 들 이 누락되는 현상이 발생하게 된다. 그러므로 이러한 현상을 방지하기 위해서 는 BLOCK 1561의 첫 번째 ROWID가 아닌 BLOCK 1562의 첫번째 ROWID를 구해야 한다. STEP 4 : ROW 추출 ------ -------- * 먼저 TEMPORARY TABLE을 만든다 SQL> CREATE TABLE TEMP AS SELECT * FROM EMP WHERE 1=2; * 손상된 BLOCK 1562의 첫 번째 ROWID보다 작은 ROWID를 가진 ROW 추출 SQL>INSERT INTO TEMP SELECT * FROM EMP WHERE EMPNO > 0 AND ROWID < 'AAABt5AAEAAAAYaAAA' ; * 손상된 BLOCK 1562의 마지막 ROWID보다 큰 ROWID를 가진 ROW 추출 SQL>INSERT INTO TEMP SELECT * FROM EMP WHERE EMPNO > 0 AND ROWID >= 'AAABt5AAEAAAAYbAAA' ; * 기존의 TABLE을 DROP하고 TEMP TABLE을 RENAME한다. 위 INSERT시 WHERE 조건에 EMPNO > 0을 추가한 이유는 INDEX 에 의한 RANGE SCAN을 반드시 수행하도록 하기 위해서이다. 기존의 BULLETIN은 이 부분을 또한 간과하였다. 즉 WHERE 절에 ROWID만을 기술했을 경우 TABLE을 FULL SCAN하 지 않고 RANGE SCAN을 하기 위해서는 반드시 TABLE이 ANALYZE되어 있어야만 한다. TABLE이 ANALYZE되어 있지 않으 면 FULL SCAN을 하게 된다. 아래는 각각에 대한 EXECUTION PLAN이다. TABLE ANALYZE를 하지 않은 상태 ----------------------------- CASE 1 ) ROWID로 QUERY SQL>select * from imsiemp where rowid < 'AAABwSAAEAAABGhAAJ'; Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 TABLE ACCESS (FULL) OF 'IMSIEMP' CASE 2 ) INDEX COLUMN & ROWID로 QUERY SQL> select * from imsiemp where empno < 10 and rowid < 'AAABwSAAEAAABGhAAJ' Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 TABLE ACCESS (BY INDEX ROWID) OF 'IMSIEMP' 2 1 INDEX (RANGE SCAN) OF 'SYS_C003247' (UNIQUE) TABLE ANALYZE가 수행된 상태 --------------------------- CASE 1) ROWID로 QUERY SQL> select * from imsiemp where rowid < 'AAABwSAAEAAABGhAAJ' Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=36 Card=500 Bytes=19000) 1 0 TABLE ACCESS (BY ROWID RANGE) OF 'IMSIEMP' (Cost=36 Card=500 Bytes=19000) CASE 2 ) INDEX COLUMN & ROWID로 QUERY SQL> select * from imsiemp where empno < 10 and rowid < 'AAABwSAAEAAABGhAAJ'; Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=1 Bytes=38) 1 0 TABLE ACCESS (BY INDEX ROWID) OF 'IMSIEMP' (Cost=3 Card=1Bytes=38) 2 1 INDEX (RANGE SCAN) OF 'SYS_C003247' (UNIQUE) (Cost=2 Card=1) |
Comment | |||
---|---|---|---|
등록된 코멘트가 없습니다. |