[Oracle 11gR2] Data Guard 기반의 백업 오프로딩(Backup Offloading) 구조 구현하기 – Part4

Data Guard 기반의 백업 오프로딩(Backup Offloading) 구조
아래 순서대로 “Data Guard 기반의 백업 오프로딩(Backup Offloading)” 환경구성 및 모의훈련을 실시할 예정입니다.


Part3에서는 서버 중지 및 재기동 절차에 대해 알아봤는데요, Part4에서는 데이터 전송 테스트를 통해 Data Loss가 어느 정보 발생할 수 있는지 확인해 보겠습니다.


  1. 데이터용 테이블스페이스와 인덱스용 테이블스페이스 생성
    • 데이터용 테이블스페이스와 인덱스용 테이블스페이스를 통합해서 하나로 생성해도 되지만, DB운영 상 권장 사항이므로 별도로 분리하겠습니다.
      -- 데이터용 테이블스페이스 (100M)
      CREATE TABLESPACE TS_DATA 
      DATAFILE SIZE 100M 
      AUTOEXTEND ON NEXT 10M MAXSIZE UNLIMITED;
      
      -- 인덱스용 테이블스페이스 (100M)
      CREATE TABLESPACE TS_INDX 
      DATAFILE SIZE 100M 
      AUTOEXTEND ON NEXT 10M MAXSIZE UNLIMITED;
      
  2. 카달로그를 동기화하고 테이블스페이스 반영 확인
    • Primary DB 서버를 통해 Catalog DB 서버에 접속합니다.
      [oracle@primary-db ~]$ rman target sys/orclsys@pri_db catalog rman_admin/orclsys@cat_db
      
    • 카달로그를 동기화시킨후 report schema; 명령어를 실행하여 생성된 테이블스페이스가 카탈로그 정보에 업데이트되었는지 확인해 봅니다.
      RMAN> resync catalog;
      starting full resync of recovery catalog
      full resync complete
      RMAN> report schema;
      Report of database schema for database with db_unique_name PRIDB
      List of Permanent Datafiles
      ===========================
      File Size(MB) Tablespace           RB segs Datafile Name
      ---- -------- -------------------- ------- ------------------------
      1    750      SYSTEM               YES     /u01/app/oracle/oradata/ORCL11G/system01.dbf
      2    620      SYSAUX               NO      /u01/app/oracle/oradata/ORCL11G/sysaux01.dbf
      3    75       UNDOTBS1             YES     /u01/app/oracle/oradata/ORCL11G/undotbs01.dbf
      4    5        USERS                NO      /u01/app/oracle/oradata/ORCL11G/users01.dbf
      5    100      TS_DATA              NO      /u01/app/oracle/oradata/PRIDB/datafile/o1_mf_ts_data_ntdy5l6k_.dbf
      6    100      TS_INDX              NO      /u01/app/oracle/oradata/PRIDB/datafile/o1_mf_ts_indx_ntdy5wm6_.dbf
      
      List of Temporary Files
      =======================
      File Size(MB) Tablespace           Maxsize(MB) Tempfile Name
      ---- -------- -------------------- ----------- --------------------
      1    29       TEMP                 32767       /u01/app/oracle/oradata/ORCL11G/temp01.dbf
      
  3. 스키마 생성 및 권한 부여
    • 스키마를 생성하고 데이터 생성을 위한 권한을 부여합니다.
      -- 유저 생성 (dguser)
      CREATE USER dguser IDENTIFIED BY dguser123
      DEFAULT TABLESPACE TS_DATA
      QUOTA UNLIMITED ON TS_DATA
      QUOTA UNLIMITED ON TS_INDX;
      
      -- 데이터 생성을 위해서는 CONNECT와 RESOURCE 권한이 필요합니다.
      GRANT CONNECT, RESOURCE TO dguser;
      
  4. 테이블 및 인덱스 생성
    • 테이블 및 인덱스를 각각 TS_DATA 및 TS_INDX에 생성합니다.
      -- 테이블 및 인덱스 생성
      CREATE TABLE dguser.tb_sysdate (
          seq_id   NUMBER,
          log_date TIMESTAMP DEFAULT SYSTIMESTAMP,
          remark   VARCHAR2(50),
          -- seq_id를 PK로 지정하고 인덱스는 TS_INDX에 생성
          CONSTRAINT pk_tb_sysdate PRIMARY KEY (seq_id) 
          USING INDEX TABLESPACE TS_INDX
      ) TABLESPACE TS_DATA;
      
      -- 생성된 테이블 및 인덱스 확인
      SET LINESIZE 200;
      COL OBJECT_TYPE FOR A15;
      COL OWNER FOR A15;
      COL OBJECT_NAME FOR A15;
      COL TABLESPACE_NAME FOR A15;
      SELECT 'TABLE' AS OBJECT_TYPE, OWNER, TABLE_NAME AS OBJECT_NAME, TABLESPACE_NAME 
      FROM DBA_TABLES WHERE TABLE_NAME = 'TB_SYSDATE'
      UNION ALL
      SELECT 'INDEX', OWNER, INDEX_NAME, TABLESPACE_NAME
      FROM DBA_INDEXES WHERE INDEX_NAME = 'PK_TB_SYSDATE' ;
      OBJECT_TYPE     OWNER           OBJECT_NAME     TABLESPACE_NAME
      --------------- --------------- --------------- ---------------
      TABLE           DGUSER          TB_SYSDATE      TS_DATA
      INDEX           DGUSER          PK_TB_SYSDATE   TS_INDX
      
  5. Primary DB에 일정 시간 간격으로 데이터를 입력하는 프로시저 생성
    • NORMAL 모드와 CRASH 모드를 시뮬레이션할 수 있도록 구성
      SQL> GRANT EXECUTE ON DBMS_LOCK TO dguser;
      SQL> GRANT ALTER SYSTEM TO dguser;
      
      SQL> CREATE OR REPLACE PROCEDURE dguser.proc_test_insert (
          p_mode IN VARCHAR2, -- NORMAL, CRASH
          p_count IN NUMBER,
          p_interval IN NUMBER
      )
      IS
          v_next_id   NUMBER;
      BEGIN
          FOR i IN 1..p_count LOOP
              -- 1. 현재 테이블에서 최대값을 찾아 다음 ID 계산 (데이터가 없으면 1로 시작)
              SELECT NVL(MAX(seq_id), 0) + 1 INTO v_next_id FROM dguser.tb_sysdate;
      
              -- 2. 계산된 ID로 데이터 입력
              INSERT INTO dguser.tb_sysdate (seq_id, remark) 
              VALUES (v_next_id, 'Data Guard Test - ' || v_next_id);
              
              COMMIT; 
              
              -- 3. NORMAL 모드의 경우, 100번마다 아카이브 로그 강제 생성 (전송 테스트용)
              IF p_mode = 'NORMAL' AND MOD(i, 100) = 0 THEN
                  EXECUTE IMMEDIATE 'ALTER SYSTEM ARCHIVE LOG CURRENT';
              END IF;
              
              SYS.DBMS_LOCK.SLEEP(p_interval);
          END LOOP;
      
          EXCEPTION
              WHEN OTHERS THEN
                  DBMS_OUTPUT.PUT_LINE('Error: ' || SQLERRM);
      END;
      /
      
  6. Standby DB에서 전송되는 데이터를 확인
    1. NORMAL 모드 테스트: 1000번 Loop를 실행하며, 100번 마다 'ALTER SYSTEM ARCHIVE LOG CURRENT' 명령어 실행
      < Primary DB >
      SQL> exec dguser.proc_test_insert('NORMAL', 1000, 0.5)
      
      -- dguser.proc_test_insert 프로시저가 실행되는 동안 아래 쿼리를 실행하여 Archive Log 파일의 전송 확인
      SQL> SET LINESIZE 200;
      COL DEST_ID FOR 9;
      COL STATUS FOR A10;
      COL DATABASE_MODE FOR A20;
      COL MAX_SEQ FOR 99999;
      COL SENT_SEQ FOR 99999;
      COL APP_SEQ FOR 99999;
      COL TRANS_GAP FOR 99999;
      COL APPLY_GAP FOR 99999;
      SELECT DEST_ID, STATUS, DATABASE_MODE,
             (SELECT MAX(SEQUENCE#) FROM V$ARCHIVED_LOG WHERE DEST_ID=1) AS MAX_SEQ,
             ARCHIVED_SEQ# AS SENT_SEQ,
             APPLIED_SEQ# AS APP_SEQ,
             (SELECT MAX(SEQUENCE#) FROM V$ARCHIVED_LOG WHERE DEST_ID=1) - ARCHIVED_SEQ# AS TRANS_GAP,
             ARCHIVED_SEQ# - APPLIED_SEQ# AS APPLY_GAP
      FROM   V$ARCHIVE_DEST_STATUS
      WHERE  DEST_ID IN (1, 2);
      
      -- Primary에서 GAP 확인
      SELECT STATUS, GAP_STATUS FROM V$ARCHIVE_DEST_STATUS WHERE DEST_ID = 2;
      
      < Standby DB >
      -- GAP 확인
      SELECT * FROM V$ARCHIVE_GAP;
      
      -- 가장 최근 전송받은 시퀀스 확인
      SELECT 'RECEIVED_SEQ' AS TYPE,  THREAD#, MAX(SEQUENCE#) FROM V$ARCHIVED_LOG GROUP BY THREAD#
      UNION ALL
      SELECT 'APPLIED_SEQ', THREAD#, MAX(SEQUENCE#) AS APPLIED_SEQ FROM V$ARCHIVED_LOG WHERE APPLIED='YES' GROUP BY THREAD#;
      
      -- MRP 프로세스가 갭을 메우고 현재 로그까지 따라왔는지 확인
      SELECT PROCESS, STATUS, SEQUENCE# FROM V$MANAGED_STANDBY WHERE PROCESS LIKE 'MRP%';
      
    2. CRASH 모드 테스트: 1000번 Loop를 실행하며, Loop가 종료되면 'SHUTDOWN ABORT' 명령어 실행
      < Primary DB >
      -- 최대 seq_id 값을 확인합니다.
      SQL> SELECT SEQ_ID AS BEFORE_MAX, LOG_DATE, REMARK 
      FROM   DGUSER.TB_SYSDATE
      WHERE  SEQ_ID = (SELECT MAX(SEQ_ID) FROM DGUSER.TB_SYSDATE) ;
      
      -- 아래 쉘 스크립트를 생성/실행합니다. sector_1 종료 후 sector_2가 실행됩니다.
      ---- sector_1을 백그라운드로 실행하려면 sqlplus / as sysdba <<EOF & 로 변경합니다.
      [oracle@primary-db scripts]$ vi crash_test.sh
      #!/bin/bash
      
      # sector 1
      sqlplus / as sysdba <<EOF
      exec dguser.proc_test_insert('CRASH', $1, $2);
      exit;
      EOF
      
      # sector 2
      sqlplus / as sysdba <<EOF
      shutdown abort;
      exit;
      EOF
      
      [oracle@primary-db scripts]$ ./crash_test.sh 20000 0
      
      < Standby DB >
      -- Primary DB가 종료된 후 실행
      SQL> ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL;
      SQL> ALTER DATABASE OPEN;
      
      -- Primary DB의 (before_max+1000)값과 Standby DB의 trans_max값을 비교하여 Data Loss를 확인해 봅니다.
      SQL> SELECT SEQ_ID AS TRANS_MAX, LOG_DATE, REMARK 
      FROM   DGUSER.TB_SYSDATE
      WHERE  SEQ_ID = (SELECT MAX(SEQ_ID) FROM DGUSER.TB_SYSDATE) ;
      
  7. 반복해서 CRASH 테스트를 하려면 Part3: 서버 중지 및 재기동 절차 준용
    1. Standby DB Shutdown 및 MOUNT 상태로 재기동
    2. Primary DB 재기동

You may also like...

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다