ORACLE/DataGuard2009. 2. 23. 13:08
반응형

출처 : Oracle Technology Network

 

Oracle9i Dataguard 구성 방법
2006년 1월 22일
민연홍(Oracle OCM), DBA LG카드 중형서버운영파트


미 국의 911테러 이후 전세계 IT시스템의 화두는, 자연재해 또는 테러로 인해 서비스 손실을 입게 될 경우에도 이를 복구할 수 있는 Standby 시스템을 구축하는 것이 되었다. 서버뿐만 아니라 디스크 및 백업장비까지 손실되었을 경우에도 다른 지역에서 서비스를 기동할 수 있는 시스템 구성이 각광받게 된 것이다. 이러한 Standby시스템에서 오라클의 dataguard는 데이터 무손실을 구현할 수 있는 방법으로, 한국에서는 여러 금융기관에서도 사용하고 있다. 여기서는 dataguard에 대한 간단한 소개와 그 구성방법에 대해서 알아본다.

Part 1. dataguard 개요 및 아키텍처

  1) dataguard 란 무엇인가?
  2) switchover and failover
  3) standby DB의 종류
  4) dataguard의 서비스 종류
  5) protection mode
  6) dataguard의 시스템 구성(2가지 종류)

Part 2. standby DB 기동방법

Part 3. 시스템 구축 (실습)
  1) 리스너 설정 및 기동
  2) tnsnames.ora 설정
  3) 오라클 초기화 파라미터 설정
  4) primary DB를 online backup으로 standby DB 위치로 restore
  5) primary DB에서 standby control file을 생성해서 standby DB로 전송
  6) standby DB에서 사용할 control file을 배치
  7) standby DB 기동
  8) standby DB에 standby redo log file 생성
  9) primary DB에 standby redo log file 생성
  10) standby DB를 recovery managed mode로 기동
  11) log switch 적용
  12) primary DB 점검
  13) standby DB 점검
  14) primary DB 테이블스페이스 생성 및 데이터 입력
  15) standby DB예 데이터 입력 여부 확인
  16) takeover 하기
  17) 서비스 원복(takeover)
  18) failover 하기

PartⅠ. dataguard 개요 및 아키텍처

 1) dataguard 란 무엇인가?

   - primary DB와 standby DB를 동기화시켜, primary DB가 하드웨어 장애 등의 문제가 생겼을 경우 standby DB로 failover 또는
     switchover 시킬 수 있는 시스템 구성을 말한다.
   - Oracle Net을 통해서 primary DB의 변경정보를 standby DB로 적용시켜 운영된다.

      

 2) switchover and failover

   ① 자동실행이 아니라 DBA가 action을 취해야 한다.

   ② switchover
     - OS 작업 또는 서버 PM작업 시 사용(primary -> standby , standby -> primary)

   ③ failover
     - 디스크 fail 등 긴급상황에서 사용, dataguard 재구성 필요

 3) standby DB의 종류

   ① Physical standby database
     - block 대 block 기반으로 primary DB의 redo log를 적용시켜 standby DB를 동기화

   ② Logical standby database
     - 같은 schema 정의로 공유
     - primary DB의 sql 문장을 standby DB에 적용

 4) dataguard의 서비스 종류

   ① Log transport Services
     - primary DB에서 standby DB로 redo log 정보를 자동으로 전송

   ② Log Apply Services
     - redo log를 standby DB에 적용

   ③ Role Management Service
     - 데이터베이스는 primary/standby로 두 가지의 상대적으로 배타적인 role을 가진다.
       Role Management Service는 log transport service와 log apply service를 failover 또는 switchover의 상황에 동적으로
       변경할 수 있다.

 5) protection mode

   ① Maximum Protection - primary    DB와 standby DB의 redo log를 동기화 시킨다.
      standby DB가 네트워크 이상 등의 이유로 standby로의 전송이 안될 경우 primary DB를 halt시킨다.
      데이터는 서로 동기화되어 primary DB에서 commit을 하게 되면 standby DB에서 commit이 완료될 때까지 primary DB에서 commit 완료를 하지 않는다.
      성능에는 문제를 줄 소지가 있으나 failover 상황이 오더라도 데이터 손실은 없다. physical standby DB에만 가능하다.

   ② Maximum availability - Maximum Protection과 마찬가지로 primary DB와 standby DB를 동기화시킨다.
      단, standby DB가 네트워크 문제 등의 이유로 전송이 안될지라도 halt되지는 않는다.
      데이터는 maximum protection과 마찬가지로 primary DB에서 commit을 하게 되면 standby DB에서 commit이 완료될 때까지
      primary DB에서 commit 완료를 하지 않는다. 만약 standby DB가 unavailable상태일 경우 임시로 불일치 시킨다.
      또 다시 standby DB가 available하면 자동으로 동기화 시킨다. 성능에는 문제를 줄 소지가 있으나 failover 상황이 오더라도
      데이터 손실은 거의없다. physical standby, logical standby DB 모두 가능하다.

   ③ Maximum Performance - default protection mode이다. primary data에 대한 protection이 가장 낮다.
      primary database에 transaction이 수행되면 이것을 asynchronous 하게 standby DB에 적용한다.
      즉, maximum protection, maximum availability의 경우에는 standby DB에 적용(commit) 될 때까지 primary db의 transaction 이 적용(commit)되지 않았으나,
      Maximum Performance 모드에서는 비 동기화 시키므로 primary DB에서 standby DB가 transaction 적용이 끝날 때까지
      기다리지 않는다. 즉 standby db의 문제로 인해서 primary DB에 성능영향이 가지 않는다.
      단, failover시에는 약간의 데이터 손실을 가져올 수 있다.

 6) dataguard의 시스템 구성(2가지 종류)

   ① physical standby database 구성 (LGWR process를 사용한 Physical standby DB)
      Archiving to a Physical standby Destination Using the Logwriter Proces


      


     - primary db의 LGWR 프로세스가 standby DB로 redo log를 보내고, standby DB의 RFS 프로세스가 redo log를 standby redo
      log에 적용시킨다. archiving되면 archived redo logs가 되고 이것을 MRP process가 standby DB에 적용시킨다.

   ② logical standby DB 구성
      Archiving to a Logical standby Destination Using the Logwriter Process

      

     - logical standby DB는 primary DB에서 수행된 SQL문장을 LGWR프로세스가 standby DB로 보내고 RFS 프로세스가 받아서
      Archived redo logs에 쓴다. LSP (Logical standby process)가 standby DB에 적용시킨다.

     - primary DB에서 log switch가 일어나게 되면 standby DB의 RFS 프로세스에 이를 알려주어 log switch가 되도록 한다.

TOP
PartⅡ. standby DB 기동방법

  - 주의 standby db의 startup 방식을 보면 아래와 같다. 아래 그림을 기억해두면 편하다.
    

   ① standby DB를 read only mode에서 managed recovery mode로 변경
     - 그대로 명령 또는 shutdown immediate 이후 재기동

     - 첫번째 방법

      SQL> alter database open read only;
      SQL> recover managed standby database disconnect; 

     - 두번째 방법

      SQL> shutdown immediate 
      SQL> startup nomount
      SQL> alter database mount standby database;
      SQL> recover managed standby database disconnect;

   ② shutdown 에서 managed recovery mode 로 변경

      SQL> startup nomount
      SQL> alter database mount standby database;
      SQL> recover managed standby database disconnect;

   ③ managed recovery mode 에서 read only mode 로 변경

      SQL> recover managed standby database cancel;
      SQL> alter database open read only;

   ④ read only standby DB 에서 managed recovery mode로 변경
      (먼저 standby DB에 연결된 모든 세션을 종료)

      SQL> recover managed standby database disconnect;

     - 만약 유저의 세션 때문에 실패할 경우

      SQL> shutdown immediate
      SQL> startup nomount
      SQL> alter database mount standby database;
      SQL> recover managed standby database disconnect;

TOP
PartⅢ. 시스템 구축 (실습)

  - 실습에서 사용 할 primary DB 이름은 MIN 이고, standby DB 이름은 STBY 이다.
  - 여기서는 하나의 서버에서 2개 DB를 구성하는 방법으로 수행한다.
  - 서로 다른 두 대의 서버에서도 같은 방법으로 구성 할 수 있다.
  - primary DB는 /data1/oradata/MIN에 구성되어 있다.
  - standby DB는 /data1/oradata/STBY에 구성되어 있다.

 1) 리스너 설정 및 기동

  아래와 같은 네트워크 설정을 해준다. 각 서버마다 설정해준다.
    - MIN DB에서 설정(primary DB)

vi $ORACLE_HOME/network/admin/listener.ora
smsvr1_MIN =
   (ADDRESS_LIST =
         (ADDRESS= (PROTOCOL= TCP)(Host= smsvr1)(Port=2001))
   )
SID_LIST_smsvr1_MIN =
 (SID_LIST =
   (SID_DESC =
     (ORACLE_HOME= /u/pkg/oracle/product/9.2.0)
     (SID_NAME = MIN)
   )
 )



  -STBY DB 에서 설정(physical standby DB)

vi $ORACLE_HOME/network/admin/listener.ora
smsvr1_STBY =
   (ADDRESS_LIST =
         (ADDRESS= (PROTOCOL= TCP)(Host= smsvr1)(Port=2002))
   )
SID_LIST_smsvr1_STBY =
   (SID_LIST =
    (SID_DESC =
     (ORACLE_HOME= /u/pkg/oracle/product/9.2.0)
    (SID_NAME= STBY)
   )
  )

 2) tnsnames.ora 설정

  tnsnames.ora 파일을 설정한다. 서로 네트워크가 가능하도록 하는데 이름을 재대로 써야 한다.
  초기화 파라미터에서 log_archvie_dest_2='service=STBY LGWR SYNC AFFIRM' 일 경우
  STBY는 tnsnames.ora 에서의 접속이름을 말한다.

- MIN DB 설정 primary DB에서 설정 ( standby DB로 가는 네트워크 구성)

vi $ORACLE_HOME/network/admin/tnsnames.ora
STBY =
  (DESCRIPTION =
     (ADDRESS = (PROTOCOL= TCP)(Host= smsvr1)(Port= 2002))
     (CONNECT_DATA = (SID = STBY))
  )



- STBY DB 설정, standby DB에서 설정 (primary DB로 가는 네트워크 구성)

vi $ORACLE_HOME/network/admin/tnsnames.ora
MIN=
  (DESCRIPTION =
     (ADDRESS = (PROTOCOL= TCP)(Host= smsvr1)(Port= 2001))
     (CONNECT_DATA = (SID = MIN))
  )

 3) 오라클 초기화 파라미터 설정

- MIN DB (primary DB)

vi $ORACLE_HOME/dbs/initMIN.ora

db_name=MIN
compatible=9.2.0
control_files=('/data1/oradata/MIN/control01.ctl','/data1/oradata/MIN/control02.ctl')
shared_pool_size=64M
db_cache_size=16M
undo_management=auto
background_dump_dest=/data1/oradata/MIN/bdump
user_dump_dest=/data1/oradata/MIN/udump
core_dump_dest=/data1/oradata/MIN/cdump
log_archive_start=true
log_archive_dest_1="location=/data1/oradata/MIN/archive1"
log_archive_format=%t_%s.arc

#### 데이터가드를 위해서 변경하지 말 것
remote_archive_enable=true
dg_broker_start=true
log_archive_dest_state_1=enable
log_archive_max_processes=3
standby_file_management=auto
db_file_name_convert='/data1/oradata/STBY','/data1/oradata/MIN'
log_file_name_convert='/data1/oradata/STBY','/data1/oradata/MIN'
standby_archive_dest='/data1/oradata/MIN/archive2'

##### standby DB이면 열고, primary DB이면 닫는다.
#fal_server=STBY
#fal_client=MIN
#lock_name_space=STBY

## primary DB이면 열고, standby DB이면 닫는다.
log_archive_dest_2='SERVICE=STBY LGWR SYNC'



- STBY DB (standby DB) -주의할 것은 DB이름은 MIN 이며 instance 이름과 SID는 STBY 이다.
즉, 기동을 할 때에도 ORACLE_SID=STBY 로 설정하여 기동한다.

vi $ORACLE_HOME/dbs/initSTBY.ora

db_name=MIN
compatible=9.2.0
control_files=('/data1/oradata/STBY/control01.ctl','/data1/oradata/STBY/control02.ctl')
shared_pool_size=64M
db_cache_size=16M
undo_management=auto
background_dump_dest=/data1/oradata/STBY/bdump
user_dump_dest=/data1/oradata/STBY/udump
core_dump_dest=/data1/oradata/STBY/cdump
log_archive_start=true
log_archive_dest_1="location=/data1/oradata/STBY/archive1"
log_archive_format=%t_%s.arc

### 데이터가드를 위해서 변경하지 말 것
remote_archive_enable=true
dg_broker_start=true
log_archive_dest_state_1=enable
log_archive_max_processes=3
standby_file_management=auto
db_file_name_convert='/data1/oradata/MIN','/data1/oradata/STBY'
log_file_name_convert='/data1/oradata/MIN','/data1/oradata/STBY'
standby_archive_dest='/data1/oradata/STBY/archive2'

## standby DB일 경우 아래를 열기, primary DB일 경우 닫기
fal_server=MIN
fal_client=STBY
lock_name_space=STBY

##primary DB이면 열고, standby DB이면 닫는다.
#log_archive_dest_2='SERVICE=MIN LGWR SYNC'

-초기화 파라미터 설정에서 주의해야 할 것을 먼저 보면,
standby_file_management=auto 로 되어 있어야 primary DB에서 물리적인 테이블스페이스 및 데이터파일 추가할 경우 standby DB에 자동으로 생성된다.

standby_file_management=auto로 되어 있을 경우
db_file_name_convert='/data1/oradata/STBY','/data1/oradata/MIN'

log_file_name_convert='/data1/oradata/STBY','/data1/oradata/MIN'
의 파라미터에서 ' A 부분','B 부분'에서 B부분이 자신의 primary DB의 경로를 말하고 A부분이 변환할 standby DB의 경로를 말한다. 파라미터를 확인해보자.

fal_server, fal_client 는 standby DB에서만 사용한다. fal_server는 primary DB를 설정하고 fal_client는 standby DB를 설정해둔다. 이것을 설정할 경우 primary DB와 standby DB에 redo log의 gap이 발생했을 경우 자동으로 맞추어주는 역할을 한다.
fal_server=MIN
fal_client=STBY
lock_name_space는 한 대의 서버에서 primary, standby DB를 운영할 경우 사 용한다. 앞에서 말한 바와 같이 primary , standby DB
db_name은 같으며 instance_name(SID)만 다를 뿐이다. 같은 DB이름을 가진 instance를 한 대의 서버에서 기동하기 위해서는
lock_name_space를 지정해 주어야 한다. 만약 두 대의 서버에서 구성한다면 lock_name_space파라미터는 지정할 필요가 없다.
이것은 standby DB에서만 지정한다.
lock_name_space=STBY

 4) primary DB를 online backup으로 standby DB 위치로 restore

   
<MIN DB primary DB>

- primary DB를 24시간 서비스 이므로 shutdown이 불가능한 것을 가정하에 구성한다.
- primary DB를 online backup으로 복사한다.
- online backup을 하는 것이므로 primary DB의 redo log는 standby DB로 전송하지 않는다. standby DB구성 시 자동으로
standby DB에서 redo log가 생성된다.
SQL> select tablespace_name, file_name, bytes/1024/1024 mega from dba_data_files; 
TABLESPACE_NAME FILE_NAME MEGA
------------------------------------------------------------------------------------------------------------
SYSTEM /data1/oradata/MIN/system01.dbf 250
UNDOTBS /data1/oradata/MIN/undotbs.dbf 100
USERS /data1/oradata/MIN/users01.dbf 100
SQL> select name , bytes/1024/1024 mega from v$tempfile; 
NAME MEGA
------------------------------------------------------------
/data1/oradata/MIN/temp01.dbf 100

- 여기서는 cp 명령이 standby DB로 전송하는 것을 뜻한다. ftp로 primary db의 백업을 전송한다.
   SQL> alter tablespace system begin backup;
   SQL> !cp /data1/oradata/MIN/system01.dbf /data1/oradata/STBY/system01.dbf
   SQL> alter tablespace system end backup;
   SQL> alter tablespace undotbs begin backup;
   SQL> !cp /data1/oradata/MIN/undotbs.dbf /data1/oradata/STBY/undotbs.dbf
   SQL> alter tablespace undotbs end backup;
   SQL> alter tablespace users begin backup;
   SQL> !cp /data1/oradata/MIN/users01.dbf /data1/oradata/STBY/users01.dbf
   SQL> alter tablespace users end backup;

- tempfile은 그대로 복사를 한다. begin backup, end backup 이 필요 없다. 단 tempfile 이어야 한다.
   SQL> !cp /data1/oradata/MIN/temp01.dbf /data1/oradata/STBY/temp01.dbf

 5) primary DB에서 standby control file을 생성해서 standby DB로 전송

    <MIN DB primary DB>

- primary DB에서 standby control file을 생성해서 standby DB로 전송한다
   SQL> alter database create standby controlfile as '/data1/oradata/STBY/stbyctl.ctl';

 6) standby DB에서 사용할 control file을 배치

    <STBY DB standby DB의 control file>

- standby DB에서 사용할 control file을, standby DB의 초기화 퍼라미터(initSTBY.ora)파일에 있는 control file 위치에 배치한다.
SQL> !cp /data1/oradata/STBY/stbyctl.ctl /data1/oradata/STBY/control01.ctl
SQL> !cp /data1/oradata/STBY/stbyctl.ctl /data1/oradata/STBY/control02.ctl

 7) standby DB 기동

    <STBY DB standby DB 에서 수행>

- standby DB를 기동시킨다. startup mount standby database 라는 명령은 없다. nomount까지 기동한 후 standby DB로 mount를 한다.

   SQL> startup nomount
   SQL> alter database mount standby database;

 8) standby DB에 standby redo log file 생성

    <STBY DB standby DB에서 수행>

우리는 처음으로 standby DB를 구성하였으므로 standby redo log를 추가해 주어야 한다.
여기서 중요한 것은 primary redo log와 standby redo log가 같은 크기를 가지고 있어야 한다.
만약 다를 경우에는 차후에 ORA-16139 media recovery required
에러가 발생하면서 takeover나 failover가 정상적으로 수행되지 않을 수 있다.

SQL> select * from v$logfile;

GROUP#

STATUS TYPE

MEMBER

---------------------------------------------------------------

1

ONLINE

/data1/oradata/STBY/log01a.log

2

ONLINE

/data1/oradata/STBY/log02a.log

3

 

/data1/oradata/STBY/log03a.log

SQL> alter database add standby logfile
           '/data1/oradata/STBY/stbylog01a.log' size 10M;
SQL> alter database add standby logfile
           '/data1/oradata/STBY/stbylog02a.log' size 10M;
SQL> alter database add standby logfile
           '/data1/oradata/STBY/stbylog03a.log' size 10M;
SQL> select * from v$logfile;

 

GROUP#STATUS

TYPE

MEMBER

------------------------------------------------------------------

1

ONLINE

/data1/oradata/STBY/log01a.log

2

ONLINE

/data1/oradata/STBY/log02a.log

3

ONLINE

/data1/oradata/STBY/log03a.log

4

STANDBY

/data1/oradata/STBY/stbylog01a.log

5

STANDBY

/data1/oradata/STBY/stbylog02a.log

6

STANDBY

/data1/oradata/STBY/stbylog03a.log



 9) primary DB에 standby redo log file 생성

    <MIN DB primary DB 에서 수행>

서버문제가 발생했을 경우 takeover를 해야 하므로 primary DB도 standby DB가 될 수 있기 때문에
미리 standby redo log를 만든다.

SQL> select * from v$logfile;

 

GROUP#>

STATUS TYPE

MEMBER

---------------------------------------------------------------

1

ONLINE

/data1/oradata/MIN/log01a.log

2

ONLINE

/data1/oradata/MIN/log02a.log

3

ONLINE

/data1/oradata/MIN/log03a.log


SQL> alter database add standby logfile
          '/data1/oradata/MIN/stbylog01a.log' size 10M;
SQL> alter database add standby logfile
          '/data1/oradata/MIN/stbylog02a.log' size 10M;
SQL> alter database add standby logfile
          '/data1/oradata/MIN/stbylog03a.log' size 10M;

SQL> select * from v$logfile;

GROUP#

STATUS TYPE

MEMBER

-------------------------------------------------------------

1

ONLINE

/data1/oradata/MIN/log01a.log

2

ONLINE

/data1/oradata/MIN/log02a.log

3

ONLINE

/data1/oradata/MIN/log03a.log

4

STANDBY

/data1/oradata/MIN/stbylog01a.log

5

STANDBY

/data1/oradata/MIN/stbylog02a.log

6

STANDBY

/data1/oradata/MIN/stbylog03a.log

 10) standby DB를 recovery managed mode로 기동

    <STBY DB standby DB 에서 수행>

standby DB를 recovery managed mode로 변경한다.
standby DB서버에서 MRP 프로세스가 생긴다.

   SQL> recover managed standby database disconnect;

 11) log switch 적용

    <MIN DB primary DB 에서 수행>

standby DB를 구성하는 동안 primary DB와 gap이 생겼을 것이다.
current redo log를 적용시킨다.

   SQL> alter system archive log current;

 12) primary DB 점검

            PROCESS STATUS
           ------------- ---------------
           ARCH       CLOSING
           ARCH       CLOSING
           LGWR       WRITING SQL> select dest_id "ID",destination,status,target, schedule,process,mountid mid from v$archive_dest order by dest_id;
=> destination 2번에 우리는 service=STBY로 설정하였다. STBY는 tnsnames.ora에 나오는 접속이름이었다. STATUS=VALID 상태이고 STANDBY 이어야 한다.


=> 아무런 에러도 나와서는 안된다. 여기에서 에러가 났다면 primary DB를 먼저 기동하고 standby DB를 기동했을 경우 발생할 수도 있으나, standby로 전송이 안된 것일 수도 있으므로 다른 것도 확인을 해보아야 한다. 만약 standby DB를 먼저 기동하고 recovery managed mode에서 MRP 프로세스를 띄우고 그리고 나서야 primary DB를 기동시켰다면 아래에서는 아무런 메세지도 나와서는 안된다. 예제에서는 primary DB를 먼저 기동했으므로 메세지가 발생했을 것이다.

        MESSAGE         TIMESTAMP
        --------------------------
        --------------------------- SQL> select dest_id id,database_mode db_mode,recovery_mode, protection_mode,standby_logfile_count "SRLs", standby_logfile_active ACTIVE, archived_seq# from v$archive_dest_status;
==> db_mode가 MOUNTED_STANDBY 이어야 한다. recovery_mode 가 managed가 되어 있어야 primary DB에서 전송된 redo log정보를 standby DB에 적용시킬 수 있다.

        ID DB_MODE         RECOVER PROTECTION_MODE      SRLs ACTIVE ARCHIVED_SEQ#
        --- --------------- ------- -------------------- ---- ------ -------------
       1 OPEN            IDLE    MAXIMUM PERFORMANCE     0      0        45
       2 MOUNTED-STANDBY MANAGED MAXIMUM AVAILABILITY    2      0        45
       3 OPEN            IDLE    MAXIMUM PERFORMANCE     0      0         0

13) standby DB 점검
14) primary DB 테이블스페이스 생성 및 데이터 입력
        <MIN DB primary DB>
test 테이블스페이스를 만들고 테이블을 만들고 데이터를 넣어본다. 주의할 것은 db_file_name_convert 에서 나오는 것처럼 /data1/oradata/MIN 안에만 생성을 해야 한다. 그래야 standby DB에 데이터파일이 자동으로 생기게 된다.
또한 파라미터에서 standby_file_management=auto로 되어 있어야 standby DB에 테이블스페이스의 데이터파일이 생긴다.
SQL> create tablespace test datafile '/data1/oradata/MIN/test01.dbf' size 10M; SQL> select tablespace_name, file_name, bytes/1024/1024 mega from dba_data_files;

            

15) standby DB예 데이터 입력 여부 확인 <STBY DB standby DB>
- primary DB에서 만든 테이블스페이스가 있는지 데이터는 들어갔는지 확인한다.
recovery managed mode를 해제하고 read only로 open한다.
SQL> recover managed standby database cancel; SQL> alter database open read only;
==> 정상적으로 primary DB에서 만든 테이블스페이스가 적용되었으며, test 라는 테이블에 데이터 insert가 정상적으로 된 것을 확인할 수 있다.


- 다시 recovery managed mode로 만들어서 primary 에서 전송된 redo log 정보가 standby DB에 적용되도록 한다. fal_server, fal_client 파라미터가 설정되어 있으므로 자동으로 gap이 생긴 부분을 맞추어 준다.
DB를 open 상태에서도 recovery managed mode로 변경이 가능하다.
SQL> recover managed standby database disconnect;
16) takeover
시스템 문제가 발생하였다. takeover를 수행한다.
①<MIN DB primary DB>
가 장 먼저 primary DB를 standby로 변경을 한 후 standby DB를 primary DB로 변경 해야한다. standby DB를 primary로 먼저 변경하면 primary DB가 standby DB로될 때까지 기다리게 된다.
이제 primary DB를 standby DB로 변경한 후 기존의 primary DB를shutdown 한다.
SQL> alter database commit to switchover to physical standby with session shutdown wait; SQL> shutdown immediate
② <STBY DB standby DB>
standby DB를 primary DB로 바꾸고 shutdown 한다. 유저접속은 없으므로 with session shutdown 절은 안 들어가도 된다.
SQL> alter database commit to switchover to primary; SQL> shutdown immediate
③ 파라미터를 변경한다. # 을 붙인 것을 빼거나 추가해서 설정해준다.
<MIN DB primary DB>

vi $ORACLE_HOME/dbs/initMIN.ora
<
변경전
>
#####standby DB
이면 열고, primary DB이면 닫는다
.
#fal_server=STBY
#fal_client=MIN
#lock_name_space=STBY


##primary DB
이면 열고, standby DB이면 닫는다.
log_archive_dest_2='SERVICE=STBY LGWR SYNC'

<변경후>
#####standby DB
이면 열고, primary DB이면 닫는다
.
fal_server=STBY
fal_client=MIN
lock_name_space=STBY

##primary DB이면 열고, standby DB이면 닫는다.
#log_archive_dest_2='SERVICE=STBY LGWR SYNC'


<STBY DB standby DB>
>vi $ORACLE_HOME/dbs/initSTBY.ora <변경전> ##standby 이면 열고, primary DB이면 닫는다. fal_server=MIN fal_client=STBY lock_name_space=STBY
##primary DB
이면 열고, standby DB이면 닫는다. #log_archive_dest_2='SERVICE=MIN LGWR SYNC'

<변경후>
##standby
이면 열고, primary DB이면 닫는다. #fal_server=MIN #fal_client=STBY #lock_name_space=STBY

##primary DB이면 열고, standby DB이면 닫는다. #log_archive_dest_2='SERVICE=MIN LGWR SYNC'



④ <MIN DB , new standby DB> - 새롭게 standby DB가 된 MIN DB를 recovery managed mode로 변경한다. SQL> startup nomount SQL> alter database mount standby database; SQL> recover managed standby database disconnect; SQL> select process, status from v$managed_standby; ==> MRP 프로세스 기동 확인

        PROCESS STATUS
        ---------------- ------------
        ARCH    CONNECTED
        ARCH    CONNECTED
       MRP0    WAIT_FOR_LOG
     RFS     WRITING
     RFS     ATTACHED

⑤ <STBY DB, new primary DB> - new primary DB를 기동한다. SQL> startup
- 확인하기 SQL> select i.instance_name, i.status instance_status, d.name dbname, d.database_role db_role, d.switchover_status switchover_status , d.protection_mode from v$database d, v$instance i;
=> 중요한 점검포인트 이다. TO_STANDBY 인지 확인한다.

   

(17) 서비스 원복(takeover) 다시 원복을 시킨다. STBY DB를 standby DB로 변경한 후 MIN DB를primary DB로 변경한다.

<STBY DB, new primary DB> - primary DB를 standby DB로 만들고 나서 standby DB를 primary DB로 만든다.
순서를 잊지 말자.
SQL> alter database commit to switchover to physical standby with session shutdown wait; SQL> shutdown immediate
<MIN DB new standby DB> - MIN DB를 primary DB로 변경한다. SQL> alter database commit to switchover to primary ; SQL> shutdown immediate
<MIN DB primary DB> - 초기화 파라미터를 변경한다.

vi $ORACLE_HOME/dbs/initMIN.ora
<원복시킨다>
#####standby DB이면 열고, primary DB이면 닫는다.
#fal_server=STBY
#fal_client=MIN
#lock_name_space=STBY

##primary DB이면 열고, standby DB이면 닫는다.
log_archive_dest_2='SERVICE=STBY LGWR SYNC'


<STBY DB standby DB>
vi $ORACLE_HOME/dbs/initSTBY.ora <원복시킨다> ##standby 이면 열고, primary DB이면 닫는다. fal_server=MIN fal_client=STBY lock_name_space=STBY
##primary DB
이면 열고, standby DB이면 닫는
.
#log_archive_dest_2='SERVICE=MIN LGWR SYNC'




18) failover

- primary DB가 있는 건물이 무너지고 디스크 이상으로 인해서 DB 데이터파일이 손상되었다.
standby DB를 긴급하게 기동시켜야 한다. MIN DB를 shutdown abort로 Down시키고(디스크 fail 장애),
standby DB를 primary DB로 기동시킨다.

- failover을 한 후에 시스템을 복구해서 MIN DB를 primary DB, STBY DB를 standby DB로 원래대로 구성하려면 Dataguard를 재구성 해야 한다.

즉, failover을 했다면 failover이후에 new primary DB인 STBY DB를 통해서 MIN DB를 standby DB로 구성하고
takeover 시키면 된다.

<MIN DB primary DB>
- 디스크장애상황
SQL> shutdown abort

<STBY DB standby DB>
- recovery managed mode를 해제(cancel)가 아닌 끝내도록(finish) 한다. primary DB로 변경한다.
SQL> recover managed standby database finish;
SQL> alter database commit to switchover to primary;
$ORACLE_HOME/dbs/initSTBY.ora 파일에서 primary DB로 파라미터를 설정하고 기동한다.
SQL> startup

<주의> 만약 standby DB인 STBY DB를 primary DB로 failover하던 도중 ORA-16139 media recovery required
에러가 나면서 recovery 하라고 나온다면?

이럴 경우엔 아래와 같은 명령을 사용하도록 한다.
Log stream을 standby DB에 적용하지 못한 경우에 발생할 수 있다.
이러한 사항은 여러 문제로 인해서 발생할 수 있으므로 발생했다면 오라클에 공식적으로 문의 해야한다.
아래와 같이 skip하면 skip하는 만큼의 gap을 DB에 적용하지 못할 수 있으니 주의해야 한다.
SQL> alter database recover managed standby database finish skip wait;
SQL> alter database commit to switchover to primary;
$ORACLE_HOME/dbs/initSTBY.ora 파일에서 primary DB로 파라미터를설정하고 기동한다.
SQL> startup 

 

반응형

'ORACLE > DataGuard' 카테고리의 다른 글

DataGuard  (0) 2008.12.13
Posted by [PineTree]
ORACLE/INSTALL2009. 2. 23. 11:36
반응형

Oracle 9i 데이터베이스를 설치하기 위하여 다음 사항을 확인해야 한다.

 

1. CentOS 5.2 설치 전이라면, swap 공간을 물리 메모리의 4배로 잡아준다. 

    이미 설치하였다면 Oracle 인스턴스가 시작되기 전에 미리 할당 작업을 완료한다.

2. CentOS 5.2 가 기본적으로 X-window로 설치되어 있어야 한다. (Server-GUI 선택하면 될 듯)

3. Oracle 9i 설치를 위해 하드디스크의 사용 가능한 용량이 4GB 이상이어야 한다.

 

< 사용자 생성 및 그룹 생성 >

groupadd dba

groupadd oinstall

useradd -g oinstall -G dba oracle

passwd oracle

 

< 디렉토리 생성 >

mkdir -p /opt/oracle/product/9.2.0.4

mkdir -p /opt/oracle/oradata

chown -R oracle:oinstall /opt/oracle

chmod 755 /opt/oracle/oradata

 

< Oracle 파일 다운로드 >

http://www.oracle.com/technology/software/products/oracle9i/htdocs/linuxsoft.html

위 사이트에서 파일을 다운로드 받는다. (32bit CPU 기준이다.)

 

< 다운로드 받은 압축 파일 압축 해제 >

사이트에 명시된되로 "gunzip 파일명" 으로 압축을 해제하고,

"cpio -idmv < 파일명" 으로 최종 압축을 해제해서 "Disk1~3" 디렉토리가 생성된 것을 확인한다.

 

< 요구되는 패키지를 검사 >

rpm -q compat-db

              compat-gcc-34

              compat-gcc-34-c++

              compat-libgcc-296

              compat-libstdc++-296

              compat-libstdc++-33

              gcc

              gcc-c++

              glibc

              glibc-common

              glibc-devel

              glibc-headers

              libgcc

              make

              libXp

 

< root 계정 로그 아웃 >

이제 root 계정의 접속은 끊고, 위에서 생성한 oracle 계정으로 접속한다. (su - oracle)

참고로 oracle 인스톨 중에 root 계정이 필요하니, 필히 su - 명령어의 사용 그룹에 oracle 계정 추가!!

 

< 환경 설정 >

# vi ~oracle/.bash_profile

 

ORACLE_BASE=/opt/oracle

ORACLE_HOME=$ORACLE_BASE/920

ORACLE_SID=ORCL

LD_LIBRARY_PATH=$ORACLE_HOME/lib

PATH=$PATH$ORACLE_HOME/bin

 

export ORACLE_BASE ORACLE_HOME ORACLE_SID LD_LIBRARY_PATH

 

# source ~oracle/.bash_profile

 

< 나머지 설정_1 >

su -

# cd /usr/lib

# ln -s libstdc++-3-libc6.2-2-2.10.0.so libstdc++-libc6.1-1.so.2

 

< 패키지 다운로드 및 인스톨 >

http://oss.oracle.com/projects/compat-oracle/dist/files/RedHat/compat-libcwait-2.1-1.i386.rpm

http://oss.oracle.com/projects/compat-oracle/dist/files/RedHat/compat-oracle-rhel4-1.0-5.i386.rpm

 

rpm -ivh compat-libcwait-2.1-1.i386.rpm

rpm -ivh compat-oracle-rhel4-1.0-5.i386.rpm --nodeps

 

< 나머지 설정_2 >

su -

# cd /usr/bin

# ln -s gcc34 gcc32

 

< 나머지 설정_3 >

su -

# cd /usr/lib

# ln -s libgdbm.so.2.0.0 libdb.so.2

 

< 나머지 설정_3에서 NETCA/DBCA 실패할 경우 >

cd $ORACLE_HOME

rm JRE

ln -s $ORACLE_BASE/jre/1.3.1 JRE

cd JRE/bin

ln -s java jre

cd i386/native_threads/

ln -s java jre

9.2.0.8 패치 이전에 적용한다.

 

< oracle 설치 >

# cd /opt/oracle/Disk1

# export LANG=en

# ./runinstaller

 

1) Welcome : Next

2) Inventory Location : /oracle/oraInventoryss
3) UNIX Group Name : oinstall 
4) 루트권한으로 : /tmp/orainstRoot.sh 실행 !!
(새로운 터미널을 열고, su - 명령으로 root권한 획득 후 실행)
5) 위 명령어 실행후 : Continue
6) File Locations : 대부분 기본값 => Next
7) Available Products : Oracle9i Database 9.2.0.4.0 => Next
8) Installation Types : Enterprise Edition (2.84GB) => Next
9) Database Configuration : General Purpose => Next
10) Database Identification : ORCL(대표DB명) => Next
11) Database File Location : 대부분 기본 값 => Next
12) Database Character Set :
Choose one of the common character sets :
Korean KO16KSC5601 <== 이거선택(EUC-KR) => Next
13) Summary => Next
14) Setup Privileges
루트권한으로 : /opt/oracle/product/920/root.sh
Enter the full pathname of the local bin directory: [/usr/local/bin]: <엔터키입력>

15) 위 명령어 실행후 : OK

16) Database Configuration Assistant
SYS Password:
Confirm SYS Passwork :
SYSTEM Password :
Confirm SYSTEM Password :
위 항목 비밀번호 입력

17) Agent Configuration Assistant 가 에러가 나서 취소된다. 무시한다.

18) 완료되면 => Next
19) End of Installation => EXIT

이렇게되면. 오라클 설치가 끝나고.
에이전트 및 웹서버 시작이 된다.
기본 DB 및 리스너도 시작이 되어 있다.


반응형
Posted by [PineTree]
ORACLE/RAC2009. 2. 23. 11:22
반응형
출처 : http://kr.blog.yahoo.com/dbacool/1157

RAC(OPS) 환경하에서 양쪽 Node의 archived log file을 RMAN을 사용하여 동시에 BACKUP 받는 방법
======================================================================================

ORACLE 9i 이전 버전
-------------------

Oracle 8i까지는 다음과 같은 Script를 통하여 Backup을 받을 수 있었습니다.

1) Script Name: arch_backup.rcv

run{
allocate channel node_1 type disk connect 'system/manager@v92hp1';
allocate channel node_2 type disk connect 'system/manager@v92hp2';

backup filesperset 1
(archivelog until time 'SYSDATE' thread 1 channel node_1)
(archivelog until time 'SYSDATE' thread 2 channel node_2);

release channel node_1;
release channel node_2;

}

2) 수행 방법

$ rman target=system/manager catalog=rman_user/rmanpw cmdfile='arch_backup.rcv' log='arch_backup.log'



ORACLE 9i 이후 버전
-------------------

그러나 Oracle9i 이상부터는 Archived file backup전에 다음과 같은 설정을 먼저
해 주셔야만 합니다.

1) Configuration 설정

$ rman target=system/manager catalog=rman_user/rmanpw
RMAN> Show all;
RMAN> CONFIGURE DEVICE TYPE DISK PARALLELISM 2;
RMAN> configure default device type to disk;
RMAN> configure channel 1 device type disk connect 'system/manager@v92hp1';
RMAN> configure channel 2 device type disk connect 'system/manager@v92hp2';

위 설정은 backup을 Disk에 받는 경우로 가정하고 device type을 모두 disk로 설정하였습니다.
만일 backup solution을 사용하여 tape에 받는다면 device type을 'sbt_tape'으로 변경해 주시면 됩니다

몇개의 Channel을 설정할 것인가에 따라 PARALLELISM의 값을 반드시맞춰 주어야 합니다.
이것을 맞춰주지 않으면 다음과 같은 형태의 Error가 발생하면서 다른 Node의 archive file들을 인식하지
못하게 됩니다.(실제로 Archived file들은 정상적으로 존재합니다)

RMAN-06059: expected archived log not found, lost of archived log compromises recoverability
ORA-19625: error identifying file /u01/64bit/app/oracle/product/9.2.0/admin/V92HP/arch/arch1_146.dbf
ORA-27037: unable to obtain file status
HP-UX Error: 2: No such file or directory
Additional information: 3

위 설정은 한번만 수행해 주시면 됩니다.
만일 CHANNEL을 잘못 설정하였으면 다음과 같은 명령으로 Clear 해 주시면 됩니다.

RMAN> configure channel 1 device type disk clear;


2)Archived file을 Backup 받습니다.

RMAN> run { backup
format='/u01/64bit/app/oracle/product/9.2.0/admin/V92HP/arch/%U'
archivelog all delete input;
}



ADDITIONAL INFORMATION(1)
-------------------------
RAC 환경 하에서 일부 Archived file들이 OS에서 삭제 되었을 경우 다음과 같은 명령을 통하여
validation check를 수행한 후에 backup을 수행하여 주십시요

RMAN> allocate channel for maintenance type disk connect 'system/manager@v92hp1';
RMAN> allocate channel for maintenance type disk connect 'system/manager@v92hp2';
RMAN> crosscheck archivelog all;

만약에 Configuration에서 이미 Channel을 설정해 주었다면
Channel allocation 없이 바로 crosscheck명령어를 수행해 주시면 됩니다.


ADDITIONAL INFORMATION(2)
-------------------------
Channel Configuration 설정시에 Backup FORMAT을 함께 설정하려면 다음과 같은 형태로 수행합니다.

RMAN> CONFIGURE DEVICE TYPE DISK PARALLELISM 2;
RMAN> configure default device type to disk;
RMAN> configure channel 1 device type disk connect 'system/manager@v92hp1' FORMAT '/arch/bkup%t_s%s_s%p';
RMAN> configure channel 2 device type disk connect 'system/manager@v92hp2' FORMAT '/arch/bkup%t_s%s_s%p';


ADDITIONAL INFORMATION(3)
-------------------------
Tape device를 사용할 경우 device type은 'sbt_tape'을 사용합니다.

RMAN> CONFIGURE DEVICE TYPE 'SBT_TAPE' PARALLELISM 2;
RMAN> configure default device type to 'sbt_tape';
RMAN> configure channel 1 device type 'sbt_tape' connect 'system/manager@v92hp1' FORMAT 'bkup%t_s%s_s%p';
RMAN> configure channel 2 device type 'sbt_tape' connect 'system/manager@v92hp2' FORMAT 'bkup%t_s%s_s%p';
반응형
Posted by [PineTree]
ORACLE/ADMIN2009. 2. 12. 15:45
반응형
emca
emca -config dbcontrol db -repos recreate -SID ora11g -PORT 1525 -ORACLE_HOME /u0/app/ora11g/product/11gr2/db_1 -DBCONTROL_HTTP_PORT 9999


반응형

'ORACLE > ADMIN' 카테고리의 다른 글

유용한 DICTIONARY 뷰 ..  (0) 2009.03.06
PCTFREE, PCTUSED  (0) 2009.02.25
INSERT /*+ APPEND */ VS CTAS  (0) 2009.02.06
Query Rewrite와 MView Refresh.  (0) 2009.02.05
MView의 생성  (0) 2009.02.05
Posted by [PineTree]
ORACLE/TUNING2009. 2. 9. 23:01
반응형

DUAL에 통계정보가 일반적으로 없는 8i와 9i에서 DUAL 테이블의 UNION ALL 혹은 UNION 의 실행계획에서 cardinality가 1이 아닌 것으로 계산되어 Cost가 높게 계산되는 CBO 버그에 대해서 테스트해봤다..

 

 

-- 다음과 같이 여러가지 경우의 DUAL UNION (ALL) DUAL에 대해 10053 trace를 수행함.
alter session set db_file_multiblock_read_count=32;
alter session set events '10053 trace name context forever,level 1';

 

-- 8.1.7.4는 UNION ALL과 UNION 이 동일하게 card=41임.

-- Block의 수는 1개로 인식됨.

 

Oracle8i Enterprise Edition Release 8.1.7.4.0 - 64bit Production

DB_FILE_MULTIBLOCK_READ_COUNT = 32

QUERY
SELECT /*+ all_rows */ 1 FROM DUAL UNION ALL SELECT 2 FROM DUAL


***************************************
BASE STATISTICAL INFORMATION
***********************
Table stats    Table: DUAL   Alias: DUAL
  TOTAL ::  (NOT ANALYZED)    CDN: 41  NBLKS:  1  TABLE_SCAN_CST: 1  AVG_ROW_LEN:  100
***************************************
SINGLE TABLE ACCESS PATH
  TABLE: DUAL     ORIG CDN: 41  CMPTD CDN: 41
  Access path: tsc  Resc:  1  Resp:  1
  BEST_CST: 1.00  PATH: 2  Degree:  1
***************************************
OPTIMIZER STATISTICS AND COMPUTATIONS
***************************************
GENERAL PLANS
***********************
Join order[1]: DUAL [DUAL]
Best so far: TABLE#: 0  CST:          1  CDN:         41  BYTES:          0
Final:
  CST: 1  CDN: 41  RSC: 1  RSP: 1  BYTES: 0

 


-- 9.2.0.7은 UNION ALL과 UNION, all_rows와 first_rows_1에 따라 다른 결과
-- first_rows_1/UNION ALL인 경우에만 card=1 이고 나머지 경우는 모두 4072임.

-- 4072건으로 인식되는 경우 블럭수가 100개로 인식되는 Case도 있음.
-- 특이사항) 본문에는 없지만 테스트결과
--           first_rows_10이면 card=10, first_rows_100이면 card=100, first_rows_1000이면 card=1000 임.

-- ** 9i에서 dynamic_sampling(2) 힌트를 사용하면 정상적으로 card=1 의 결과를 얻을 수 있다.

 

-- 1. all_rows/UNION ALL

Oracle9i Enterprise Edition Release 9.2.0.7.0 - 64bit Production

DB_FILE_MULTIBLOCK_READ_COUNT = 32

QUERY
SELECT /*+ all_rows */ 1 FROM DUAL UNION ALL SELECT 2 FROM DUAL

***************************************
BASE STATISTICAL INFORMATION
***********************
Table stats    Table: DUAL   Alias: DUAL
  TOTAL ::  (NOT ANALYZED)    CDN: 4072  NBLKS:  100  AVG_ROW_LEN:  100
_OPTIMIZER_PERCENT_PARALLEL = 0
***************************************
SINGLE TABLE ACCESS PATH
  TABLE: DUAL     ORIG CDN: 4072  ROUNDED CDN: 4072  CMPTD CDN: 4072
  Access path: tsc  Resc:  8  Resp:  8
  BEST_CST: 8.00  PATH: 2  Degree:  1
***************************************
OPTIMIZER STATISTICS AND COMPUTATIONS
***************************************
GENERAL PLANS
***********************
Join order[1]:  DUAL[DUAL]#0
Best so far: TABLE#: 0  CST:          8  CDN:       4072  BYTES:          0
Final - All Rows Plan:
  JOIN ORDER: 1
  CST: 8  CDN: 4072  RSC: 8  RSP: 8  BYTES: 0
  IO-RSC: 8  IO-RSP: 8  CPU-RSC: 0  CPU-RSP: 0

 

-- 2. first_rows_1/UNION ALL

QUERY
SELECT /*+ first_rows(1) */ 4 C FROM DUAL UNION ALL SELECT /*+ first_rows(1) */ 6 C FROM DUAL

-- 2.1 case 1
***************************************
BASE STATISTICAL INFORMATION
***********************
Table stats    Table: DUAL   Alias: DUAL
  TOTAL ::  (NOT ANALYZED)    CDN: 4072  NBLKS:  100  AVG_ROW_LEN:  100
_OPTIMIZER_PERCENT_PARALLEL = 0
***************************************
SINGLE TABLE ACCESS PATH
  TABLE: DUAL     ORIG CDN: 4072  ROUNDED CDN: 4072  CMPTD CDN: 4072
  Access path: tsc  Resc:  8  Resp:  8
  BEST_CST: 8.00  PATH: 2  Degree:  1

-- 2.2 case 2
***************************************
SINGLE TABLE ACCESS PATH (First K Rows)
  TABLE: DUAL     ORIG CDN: 1  ROUNDED CDN: 1  CMPTD CDN: 1
  Access path: tsc  Resc:  2  Resp:  2
  BEST_CST: 2.00  PATH: 2  Degree:  1
***********************

-- 2.3 final Decision
***********************
Join order[1]:  DUAL[DUAL]#0
Best so far: TABLE#: 0  CST:          2  CDN:          1  BYTES:          0
Final - First K Rows Plan:
  JOIN ORDER: 1
  CST: 2  CDN: 1  RSC: 2  RSP: 2  BYTES: 0
  IO-RSC: 2  IO-RSP: 2  CPU-RSC: 0  CPU-RSP: 0
  First K Rows Plan


--3. all_rows/UNION

QUERY
SELECT /*+ all_rows */ 6 C FROM DUAL UNION  SELECT 7 C FROM DUAL

***************************************
OPTIMIZER STATISTICS AND COMPUTATIONS
***************************************
GENERAL PLANS
***********************
Join order[1]:  DUAL[DUAL]#0
Best so far: TABLE#: 0  CST:          8  CDN:       4072  BYTES:          0
    SORT resource      Sort statistics
      Sort width:            3 Area size:       43008 Max Area size:       43008   Degree: 1
      Blocks to Sort:       10 Row size:           10 Rows:       4072
      Initial runs:          1 Merge passes:        1 IO Cost / pass:         14
      Total IO sort cost: 12
      Total CPU sort cost: 0
      Total Temp space used: 0
Final - All Rows Plan:
  JOIN ORDER: 1
  CST: 20  CDN: 4072  RSC: 20  RSP: 20  BYTES: 0
  IO-RSC: 20  IO-RSP: 20  CPU-RSC: 0  CPU-RSP: 0


--4. first_rows_1/UNION

-- UNION은 SORT OPERATION이 수행되므로 이 경우 CBO는 'All Rows Plan'만 고려되었음.

QUERY
SELECT /*+ first_rows(1) */ 8 C FROM DUAL UNION SELECT /*+ first_rows(1) */ 9 C FROM DUAL

***************************************
OPTIMIZER STATISTICS AND COMPUTATIONS
***************************************
GENERAL PLANS
***********************
Join order[1]:  DUAL[DUAL]#0
Best so far: TABLE#: 0  CST:          8  CDN:       4072  BYTES:          0
    SORT resource      Sort statistics
      Sort width:            3 Area size:       43008 Max Area size:       43008   Degree: 1
      Blocks to Sort:       10 Row size:           10 Rows:       4072
      Initial runs:          1 Merge passes:        1 IO Cost / pass:         14
      Total IO sort cost: 12
      Total CPU sort cost: 0
      Total Temp space used: 0
Final - All Rows Plan:
  JOIN ORDER: 1
  CST: 20  CDN: 4072  RSC: 20  RSP: 20  BYTES: 0
  IO-RSC: 20  IO-RSP: 20  CPU-RSC: 0  CPU-RSP: 0

 

 

-- 10.2.0.3 (FAST DUAL)

-- optimizer_mode, union all 혹은 union, _optimizer_cost_model=(io,cpu) 에 관계없이 모두 card=1
-- (dual의 통계정보 존재 여부와도 상관 없음)
Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bit Production

****************
QUERY BLOCK TEXT
****************
SELECT 1 FROM DUAL
*********************
QUERY BLOCK SIGNATURE
*********************
qb name was generated
signature (optimizer): qb_name=SEL$2 nbfros=1 flg=0
  fro(0): flg=0 objn=258 hint_alias="DUAL"@"SEL$2"
*****************************
SYSTEM STATISTICS INFORMATION
*****************************
  Using WORKLOAD Stats
  CPUSPEED: 1023 millions instructions/sec
  SREADTIM: 1 milliseconds
  MREADTIM: 1 millisecons
  MBRC: 16.000000 blocks
  MAXTHR: 525863936 bytes/sec
  SLAVETHR: 3824640 bytes/sec
***************************************
BASE STATISTICAL INFORMATION
***********************
Table Stats::
  Table: DUAL  Alias: DUAL
    #Rows: 1  #Blks:  1  AvgRowLen:  2.00
***************************************
SINGLE TABLE ACCESS PATH
  Table: DUAL  Alias: DUAL    
    Card: Original: 1  Rounded: 1  Computed: 1.00  Non Adjusted: 1.00
  Access Path: TableScan
    Cost:  2.01  Resp: 2.01  Degree: 0
      Cost_io: 2.00  Cost_cpu: 7271
      Resp_io: 2.00  Resp_cpu: 7271
  Best:: AccessPath: TableScan
         Cost: 2.01  Degree: 1  Resp: 2.01  Card: 1.00  Bytes: 0
***************************************
OPTIMIZER STATISTICS AND COMPUTATIONS
***************************************
GENERAL PLANS
***************************************
Considering cardinality-based initial join order.
Permutations for Starting Table :0
***********************
Join order[1]:  DUAL[DUAL]#0
***********************
Best so far: Table#: 0  cost: 2.0065  card: 1.0000  bytes: 0
(newjo-stop-1) k:0, spcnt:0, perm:1, maxperm:80000
*********************************
Number of join permutations tried: 1
*********************************
Final - All Rows Plan:  Best join order: 1
  Cost: 2.0065  Degree: 1  Card: 1.0000  Bytes: 0
  Resc: 2.0065  Resc_io: 2.0000  Resc_cpu: 7271
  Resp: 2.0065  Resp_io: 2.0000  Resc_cpu: 7271


반응형

'ORACLE > TUNING' 카테고리의 다른 글

SQL Trace와 TKPROF 유틸리티  (0) 2009.03.19
통계정보의 이해  (0) 2009.03.03
Oracle 10g에서 Index 힌트의 변화  (0) 2009.02.09
ORACLE HINT 정리  (0) 2009.02.09
DML의 INSERT 성능 향상  (0) 2009.02.09
Posted by [PineTree]
ORACLE/TUNING2009. 2. 9. 22:46
반응형
전통적으로 index 힌트를 사용하는 방법은 다음과 같다.

select /*+ index(t_index t_index_idx1) */ count(*)
from t_index
where c1 > 0 and c2 > 0;


이 방식의 한 가지 단점은 Index 이름이 변경되는 경우에는 해당 인덱스를 사용하지 못할 수도 있다는 것이다. Oracle 10g에서는 다음과 같은 새로운 Syntax가 추가되었다.

select /*+ index(t_index t_index(c1)) */ count(*)
from t_index
where c1 > 0 and c2 > 0;


즉, index 이름 대신 Index를 구성하는 컬럼명을 사용할 수 있다. 다중 컬럼 인덱스인 경우에는 /*+ index(t_index t_index(c1,c2,c3)) */ 와 같이 사용할 수 있다.

Index의 이름이 변경될 때의 피해도 줄일수 있고, 힌트도 훨씬 명확해지는 장점이 있다. 어떤 컬럼을 사용하는 인덱스인지 힌트를 보고 바로 알 수 있다.

10.2.0.3 버전에서는 아래와 같은 힌트가 추가되었다.

    * index_rs
    * index_rs_asc
    * index_rs_desc

Index range scan 전용 힌트가 추가된 셈이다. Optimizer의 일부 문제로 인해 Index 힌트를 사용하는 경우 index range scan이 효율적임에도 불구하고 index full scan(index fast full scan과 헷갈리면 안됨!!)이 선택되는 문제가 발견되었다. 그래서 이런 문제를 원천적으로 해결할 수 있도록 새로운 힌트가 추가된 것이다.

10g 사용자라면 이러한 새로운 힌트를 사용함으로써 index 힌트 사용에서 오는 일부 껄끄러운 문제를 손쉽게 해결할 수 있을 듯하다.
반응형
Posted by [PineTree]
ORACLE/TUNING2009. 2. 9. 21:36
반응형

/*+ ALL_ROWS */
explicitly chooses the cost-based approach to optimize a statement
block with a goal of best throughput (that is, minimum
total resource consumption) 

전체 RESOURCE 소비를 최소화 시키기 위한 힌트.
             Cost-Based 접근방식.


 


/*+ CHOOSE */
causes the optimizer to choose between the rule-based
approach and the cost-based approach for a SQL statement
based on the presence of statistics for the tables accessed by
the statement 
             Acess되는 테이블에 통계치 존재여부에 따라
             Optimizer로 하여금 Rule-Based Approach와 Cost-Based Approach
             중 하나를 선택할수 있게 한다.
             Data Dictionary가 해당테이블에 대해 통계정보를 가지고 있다면
             Optimizer는 Cost-Based Approach를 선택하고,
             그렇지 않다면 Rule-Based Approach를 선택한다.



/*+ FIRST_ROWS */
explicitly chooses the cost-based approach to optimize a statement
block with a goal of best response time (minimum
resource usage to return first row)
가장 좋은 응답 시간의 목표로 문 블록을 최적화하기 위해 cost-based 접근
방법을 선택합니다. (첫번째 행을 되돌려 주는 최소의 자원 사용)

/*+ RULE */
explicitly chooses rule-based optimization for a statement
block 
  Rule-Based 최적화를 사용하기위해.

/*+ AND_EQUAL(table index) */
explicitly chooses an execution plan that uses an access path
that merges the scans on several single-column indexes 

 single-column index의 merge를 이용한 access path 선택.
             적어도 두개이상의 index가 지정되어야한다.


/*+ CLUSTER(table) */
explicitly chooses a cluster scan to access the specified table 
  지정된 테이블Access에 Cluster Scan 유도.
             Cluster된 Objects에만 적용가능.


/*+ FULL(table) */
explicitly chooses a full table scan for the specified table 
해당테이블의 Full Table Scan을 유도.


/*+ HASH(table) */
explicitly chooses a hash scan to access the specified table 
지정된 테이블Access에 HASH Scan 유도


/*+ HASH_AJ(table) */
transforms a NOT IN subquery into a hash antijoin to access
the specified table 

NOT IN SubQuery 를 HASH anti-join으로 변형


/*+ HASH_SJ (table) */
transforms a NOT IN subquery into a hash anti-join to access
the specified table 

 correlated Exists SubQuery 를 HASH semi-join으로 변형



/*+ INDEX(table index) */
explicitly chooses an index scan for the specified table
그 명시된 테이블을 위하여, 색인 scan을 고르는

/*+ INDEX_ASC(table index) */
explicitly chooses an ascending-range index scan for the specified
table 

INDEX HINT와 동일 단,ASCENDING 으로 SCAN함을 확실히 하기위함.



/*+ INDEX_COMBINE(table index) */
If no indexes are given as arguments for the INDEX_COMBINE
hint, the optimizer uses whatever Boolean combination
of bitmap indexes has the best cost estimate. If particular
indexes are given as arguments, the optimizer tries to use
some Boolean combination of those particular bitmap indexes. 

 INDEX명이 주어지지 않으면 OPTIMIZER는 해당 테이블의
             best cost 로 선택된 Boolean combination index 를 사용한다.
             index 명이 주어지면 주어진 특정 bitmap index 의
             boolean combination 의 사용을 시도한다.



 



/*+ INDEX_DESC(table index) */
explicitly chooses a descending-range index scan for the specified
table 
 지정된 테이블의 지정된 index를 이용 descending으로 scan
             하고자할때 사용.


/*+ INDEX_FFS(table index) */
causes a fast full index scan to be performed rather than a full
table scan 

 full table scan보다 빠른 full index scan을 유도.


/*+ MERGE_AJ (table) */
transforms a NOT IN subquery into a merge anti-join to access
the specified table 

 not in subquery를 merge anti-join으로 변형



/*+ MERGE_SJ (table) */
transforms a correlated EXISTS subquery into a merge semi-join
to access the specified table 

correalted EXISTS subquery를 merge semi-join으로 변형



/*+ ROWID(table) */
explicitly chooses a table scan by ROWID for the specified
table 

지정된 테이블의 ROWID를 이용한 Scan 유도


/*+ USE_CONCAT */
forces combined OR conditions in the WHERE clause of a
query to be transformed into a compound query using the
UNION ALL set operator 

 조건절의 OR 를 Union ALL 형식으로 변형한다.
             일반적으로 변형은 비용측면에서 효율적일때만 일어난다.




/*+ ORDERED */
causes Oracle to join tables in the order in which they appear
in the FROM clause 

from절에 기술된 테이블 순서대로 join이 일어나도록 유도.



/*+ STAR */
forces the large table to be joined last using a nested-loops join
on the index 

 STAR QUERY PLAN이 사용가능하다면 이를 이용하기위한 HINT.
             STAR PLAN은 규모가 가장큰 테이블이 QUERY에서 JOIN ORDER상
             마지막으로 위치하게 하고 NESTED LOOP 으로 JOIN이 일어나도록
             유도한다.
             적어도 3개 테이블 이상이 조인에 참여해야하며 LARGE TABLE의
             CONCATENATED INDEX는 최소 3컬럼 이상을 INDEX에 포함해야한다.
             테이블이 ANALYZE 되어 있다면 OPTIMIZER가 가장효율적인 STAR PLAN을
             선택한다.    





/*+ DRIVING_SITE (table) */
forces query execution to be done at a different site from that
selected by Oracle 

QUERY의 실행이 ORACLE에 의해 선택된 SITE가 아닌 다른 SITE에서
             일어나도록 유도.


/*+ USE_HASH (table) */
causes Oracle to join each specified table with another row
source with a hash join 

각 테이블간 HASH JOIN이 일어나도록 유도.



/*+ USE_MERGE (table) */
causes Oracle to join each specified table with another row
source with a sort-merge join 

지정된 테이블들의 조인이 SORT-MERGE형식으로 일어나도록 유도.



/*+ USE_NL (table) */
causes Oracle to join each specified table to another row
source with a nested-loops join using the specified table as the
inner table 

테이블의 JOIN 시 테이블의 각 ROW가 INNER 테이블을 NESTED LOOP
             형식으로 JOIN 한다.

.



/*+ APPEND */ , /*+ NOAPPEND */
specifies that data is simply appended (or not) to a table; existing
free space is not used. Use these hints only following the
INSERT keyword.
데이타가 테이블로 단순히 덧붙여진다는 (or not)것 명시합니다; 무료인
현존하는 영역은 사용되지 않습니다.
단지 그 삽입 키 핵심어를 따르는 이 암시를 사용하시오.

/*+ NOPARALLEL(table) */
disables parallel scanning of a table, even if the table was created
with a PARALLEL clause
그 테이블이 PARALLEL 문절로 새로 만들어졌다면 테이블의 평행  순차 검색을
사용하지 않게 함


/*+ PARALLEL(table, instances) */
allows you to specify the desired number of concurrent slave
processes that can be used for the operation.
DELETE, INSERT, and UPDATE operations are considered for
parallelization only if the session is in a PARALLEL DML
enabled mode. (Use ALTER SESSION PARALLEL DML to
enter this mode.)
PARALLEL hint를 사용하면 query에 포함된 table의 degree를 설정할 수 있다.
예를 들어, 다음과 같이 hint를 적어 degree 4로 parallel query option을
실행하도록 할 수 있다.
이 때 parallel이란 글자와 괄호( '(' )사이에 blank를 넣지 않도록 한다.
select /*+ PARALLEL(emp, 4) */ * from emp; 
 
 

/*+ PARALLEL_INDEX
allows you to parallelize fast full index scan for partitioned
and nonpartitioned indexes that have the PARALLEL attribute
parallelize에 당신에게 빠른 가득한 색인 scan을 허락합니다. 그런데,
그것은 PARALLEL 속성을 가지고 있는 색인을 분할했고 nonpartitioned했습니다.

/*+ NOPARALLEL_INDEX */
overrides a PARALLEL attribute setting on an index
병렬이 색인을 나아가는 것을 속하게 하는 대체


/*+ CACHE */
specifies that the blocks retrieved for the table in the hint are
placed at the most recently used end of the LRU list in the
buffer cache when a full table scan is performed
그 블록이 찾아서 가져왔다는 것을 명시합니다. 그리고 그 테이블을 위해
그 암시에 놓여집니다. 그런데, 그것은 가장 요즈음 사용된 언제 그 버퍼 캐쉬,
가득한 테이블 scan에 있는 LRU 리스트의 끝입니다. 수행됩니다.

/*+ NOCACHE */
specifies that the blocks retrieved for this table are placed at
the least recently used end of the LRU list in the buffer cache
when a full table scan is performed
그 명시합니다. 그리고, 그 블록은 이 테이블을 위해 검색되면서 요즈음 사용된
언제 그 버퍼 캐쉬, 가득한 테이블 scan에 있는 LRU 리스트의 가장 작은 끝에
놓여집니다. 수행됩니다.

/*+ MERGE (table) */
causes Oracle to evaluate complex views or subqueries before
the surrounding query
오라클이 그 둘러싸는 질의 전에 복잡한 뷰나 부속 조회를 평가하게 합니다.

/*+ NO_MERGE (table) */
causes Oracle not to merge mergeable views
오라클이 mergeable 뷰를 합병하지 않게 하지 않습니다

/*+ PUSH_JOIN_PRED (table) */
causes the optimizer to evaluate, on a cost basis, whether or
not to push individual join predicates into the view
개개 접합을 미는 것이 그 뷰 안으로 단정 하든 간에 비용 방식으로 최적자가
평가하게 합니다.

/*+ NO_PUSH_JOIN_PRED (table) */
Prevents pushing of a join predicate into the view
접합 술부 중에서 그 뷰로 밀면서, 막는

/*+ PUSH_SUBQ */
causes nonmerged subqueries to be evaluated at the earliest
possible place in the execution plan
원인은 그 실행 계획에서의 가장 이른 가능한 장소에 평가되는 부속 조회를
nonmerged했습니다.

/*+ STAR_TRANSFORMATION */
makes the optimizer use the best plan in which the transformation
has been used.
최적자가 그 변형이 사용된 가장 좋은 계획을 사용하는 제작


반응형
Posted by [PineTree]
ORACLE/TUNING2009. 2. 9. 21:33
반응형

생각의 전환이 DB를 최적화 시킨다
모든 DML을 INSERT로 변경하자

 

아직도 수많은 기업에서 성능 최적화를 수행하면 서도 주어진 SQL만을 그대로 최적화하려고 하는 경우가 많다. 물론 주어진 SQL의 튜닝을 통해 성능을 최적화할 수 있는 것은 분명하다. 하지만, 우리가 생각을 전환하여 기존이 방식과 다른 방식으로 수행하여 엄청난 성능 향상을 기대할 수 있는 경우도 많다. 대용량 데이터베이스로 변하고 있는 시점에서 성능 최적화를 위해 사용자 생각의 전환은 반드시 필요한 요소다.

 

권순용 | kwontra@hanmail.net

 

 

우리 주위에는 프로젝트의 성능을 향상시키기 위해 SQL 최적화에 전념하는 사이트들이 많이 있을 것이다. SQL 최적화를 통해 크게 성능 향상을 기대할 수 있는 것은 사실이다. 예전에는 성능 저하가 발생하는 경우 SQL 튜닝 보다도 해당 시스템의 CPU 또는 디스크 등의 자원을 증설하는 부분에 초점을 맞추었었다. 이와 달리 SQL을 튜닝하여 성능을 최적화하고자 하는 것은 매우 고무적인 현상임에는 틀림 없다.

 

그 만큼 관리자들의 생각이 IT 선진화로 가는 것은 아닐까? 하지만 아직도 SQL 튜닝을 고정 관념에 맞춰 그리고 그 자체로 튜닝하고자 하는 경우가 많은 것 같다. 우리가 조금만 다르게 생각한다면 SQL 튜닝의 효과를 배가 시킬 수 있다는 것을 아는가? 주어진 SQL을 그대로 보지않고 다르게 보는 순간 우리에게는 새로운 세상이 펼쳐질 것이다.

 

데이터 삭제를 DELETE로 수행하지 않고 데이터의 갱신을 UPDATE로 수행하지 않는다면 우리에게는 새로운 세상이 펼쳐질 것이다. 이제부터 이와 같은 현상에 대해 하나하나 자세히 확인해 보자.

 

 

DML은 왜 성능을 저하시키는가?

 

 

우리가 데이터를 저장하기 위해서는 INSERT를 수행하게 되고 데이터를 제거하기 위해서는 DELETE를 수행하게 된다. 또한, 기존의 데이터를 변경하기 위해서는 UPDATE를 수행하게 된다. 이와 같은 사실은 개발을 한번이라도 한 사람이라면 아니 SQL에 관련된 책을 한번이라도 본 사람이라면 누구나 알 수 있을 것이다.

 

DML 작업을 수행한다면 그리고 DML 작업을 수행해야 하는 데이터가 매우 많다면 많은 시간이 소요될 거라고 누구나 생각할 것이다. 그렇다면 이와 같이 대용량의 데이터에 대해 DML 작업을 수행하는 경우 어떤 이유에서 많은 시간이 소요되는 것일까? 이에 대해서는 많은 사람들이 정확한 개념을 갖고 있지 못하는 것 같다. ‘나를 알고 적을 안다면 100전 100승이 되듯이’ DML 작업의 성능을 최적화하기 위해서는 DML 작업이 왜 성능을 저하시키는지를 알아야 할 것이다. DML 작업이 왜 성능을 저하시키는지 정확히 이해하지 못한다면 우리는 어떤 방법을 사용해도 성능을 향상시킬 수 없을 것이다.

 

그럼 첫 번째로 INSERT의 성능 저하를 확인해 보자. INSERT는 데이터를 저장하는 SQL 중 하나로 아래와 같은 이유에서 많은 데이터의 저장 시 성능을 저하시키게 된다.

 

·로그 기록
·HWM BUMP UP
·인덱스의 개수
·롤백을 위한 로그 기록
·디스크 I/O

 

INSERT 작업은 위와 같이 4가지 현상에 의해 성능이 저하된다. 이는 어떤 데이터베이스를 이용해도 동일하게 발생하는 현상이다. 데이터베이스는 작업의 수행도 중요하지만 작업이 실패하거나 또는 다른 장애에 의해 시스템이 재기동 되는 등의 데이터베이스 장애에 대해 데이터를 보호해야 하는 중요한 책임을 가지고 있다.

 

이와 같은 이유에서 실제 데이터베이스에서 INSERT 작업을 수행하기 전에 어떤 작업을 수행하는지에 대한 로그를 기록해야 한다. 이와 같은 기법을 선 로그(LOG AHEAD) 기법이라고 한다. 실제 INSERT를 수행하기 전에 로그를 기록하기 때문에 우리는 언제든지 INSERT 작업 중 데이터베이스에 문제가 발생해도 복구가 가능하게 되는 것이다. 실제 INSERT 작업과 관계없는 로그를 기록해야 하기 때문에 INSERT의 성능은 저하된다.

 

그렇다면 HWM BUMP UP에 의한 성능 저하는 무엇을 의미하는 것인가? HWM BUMP UP은 오라클 데이터베이스의 내부적인 요소이다. 실제 INSERT를 수행하게 되면 해당 테이블에 할당되어 있는 공간에 데이터를 저장하게 되며 해당 공간을 익스텐트라고 부르게 된다. 익스텐트에는 HWM가 설정되어 있어 데이터는 HWM 앞의 블록에만 저장된다. 이 뜻은 무엇을 의미하는 것인가?

 

HWM 앞까지 데이터를 저장한 후에는 HWM가 뒤로 후진해야만 데이터를 INSERT할 수 있다는 의미가 된다. 이를 HWM BUMP UP이라 하며 많은 데이터를 INSERT하게 되면 HWM BUMP UP은 많은 횟수가 발생하게 될 것이다. 하지만 HWM BUMP UP은 고비용의 내부적인 작업이다. 따라서 대용량의 데이터를 저장한다면 HWM BUMP UP의 횟수 증가로 INSERT의 성능은 저하된다.

 

INSERT의 속도와 인덱스의 개수는 INSERT의 성능 향상을 위해 매우 중요한 요소이다. 데이터를 테이블에 저장하는 것은 여유 공간을 가지고 있는 데이터 블록에 해당 데이터를 저장하면 된다. 하지만 인덱스에는 정해진 위치가 존재하게 되므로 정해진 위치를 찾는 프로세스가 수행된다. 따라서 해당 테이블에 인덱스가 10개라면 이와 같이 저장되는 데이터에 대해 인덱스에서의 위치를 찾기 위해 정해진 위치를 찾는 프로세스가 10번 수행되어야 할 것이다. 이와 같기 때문에 인덱스의 개수가 많다면 INSERT의 성능이 저하되는 것은 당연한 사실일 것이다.

 

어떤 사이트에서 어떤 테이블에 10개의 인덱스가 존재했으며 이를 최적화하여 5개의 인덱스로 변경한 적이 있다. 단지 10개의 인덱스를 5개로 감소시키는 순간 SQL의 변경 없이 INSERT 작업은 4배정도의 성능이 향상되었다. 이는 4배의 성능 향상이 중요한 것이 아니라 인덱스가 INSERT 작업에 많은 부하를 발생시킨다는 중요한 사실을 우리에게 전해주는 것일 것이다.

 

롤백을 위한 로그 기록은 해당 작업을 수행한 후 작업을 취소하는 경우 이전 데이터로 복구하기 위해 이전 데이터의 값을 저장하는 것을 의미한다. 이와 같은 작업 또한 실제 데이터를 저장하는 작업과는 별개로 수행되므로 INSERT의 성능 저하를 발생시키게 된다.

 

HWM BUMP UP을 제외한 로그 기록, 인덱스 및 롤백을 위한 로그 기록은 모두 디스크 I/O를 발생시킨다. 또한, 실제 데이터를 저장하는 작업에서도 디스크 I/O가 발생하게 된다. 이와 같이 모든 단계에서 디스크 I/O가 발생하기 때문에 INSERT의 성능은 저하될 것이다.

 

두 번째로 UPDATE의 성능 저하를 확인해 보자. UPDATE는 이미 저장되어 있는 데이터에 대해 변경 작업을 수행하는 것이다. 이와 같은 UPDATE는 아래와 같은 요소에 의해 성능 저하가 발생하게 된다.

 

·로그 기록
·UPDATE 컬럼이 사용된 인덱스의 개수
·롤백을 위한 로그 기록
·디스크 I/O

 

UPDATE의 경우에는 HWM BUMP UP은 발생하지 않게 된다. 또한, 인덱스의 개수도 해당 테이블에 존재하는 모든 인덱스는 아니며 UPDATE가 수행되는 컬럼이 사용된 인덱스의 개수를 의미하게 된다. UPDATE가 수행되면 해당 컬럼을 인덱스의 컬럼으로 구성하고 있는 인덱스만을 갱신하게 된다. 그렇기 때문에 UPDATE 컬럼이 사용된 인덱스의 개수에 의해 UPDATE 성능은 저하된다.

 

INSERT에 비해 UPDATE는 성능을 저하시키는 항목이 더 적다. 하지만, 동일한 양에 대해 INSERT와 UPDATE를 수행한다면 UPDATE가 성능 저하를 더 많이 발생시키게 된다. 이와 같은 이유는 왜일까? 분명히 성능 저하의 요소는 INSERT가 더 많기 때문에 INSERT가 더 많은 부하를 발생시킨다고 생각하기 쉽다. 이는 로그 기록과 롤백을 위한 로그 기록의 방식 차이 때문이다. INSERT 작업은 로그에 이전 데이터라는 것은 존재하지 않는다.

 

INSERT 작업이 수행된 데이터의 위치 정보만을 가지게 된다면 우리는 언제든지 롤백을 수행할 수 있으며 장애 시 복구도 어렵지 않게 된다. 하지만, UPDATE의 경우에는 이전 데이터의 값이 존재하기 때문에 이전 데이터를 로그에 기록하게 된다. 따라서 로그 기록 및 롤백을 위한 로그 기록에서 UPDATE가 INSERT에 비해 더 많은 데이터를 기록해야 하므로 디스크 I/O의 증가로 UPDATE의 성능은 INSERT의 성능보다 더욱 저하되게 된다.

 

세 번째로 DELETE 작업은 어떠한가? DELETE는 저장되어 있는 데이터를 삭제하는 기능을 수행하게 된다. 위와 같은 DELETE는 아래와 같은 항목들에 의해 성능이 저하된다.

 

·로그 기록
·인덱스의 개수
·롤백을 위한 로그 기록
·디스크 I/O

 

다른 항목은 INSERT 또는 UPDATE와 동일하다. 차이라면 로그 기록 및 롤백을 위한 로그 기록시 삭제되는 데이터의 이전 데이터와 이후 데이터를 모두 기록해야 한다는 것이다. 물론, 롤백을 위한 로그 기록 시에는 이전 데이터의 값과 위치 정보만을 가지게 된다. 결국, 이와 같은 이유로 동일한 양의 데이터를 DELETE하는 경우 INSERT에 비해 더 많은 디스크 I/O가 발생하게 되므로 성능은 저하되게 된다.

 

다양한 성능 저하의 요소를 가지는 DML 작업에서 대용량의 데이터에 대해서 어떤 방식으로 작업을 수행해야만 성능을 보장 받을 수 있겠는가?

 

 

INSERT의 성능 저하 요소를 감소시키자

 

 

일반적으로 DELETE 또는 UPDATE의 경우에는 INDEX의 개수만을 최적화하여 성능을 향상시킬 수 있다. 하지만, INSERT의 경우에는 위와 같은 성능 저하의 요소 중 아래와 같은 요소에 대해 획기적으로 감소시킬 수 있게 된다.

 

·로그 기록
·HWM BUMP UP
·롤백을 위한 로그 기록

 

위와 성능 저하 요소는 우리가 조금만 깊이 있게 고려한다면 최소화 시킬 수 있으며 위의 항목을 최소화 시킨다면 디스크 I/O는 자동으로 감소하게 된다. 그렇다면 어떻게 위의 성능 저하의 요소를 감소시킬 수 있겠는가?

 

첫 번째로 HWM BUMP UP과 롤백을 위한 로그 기록을 감소시키는 방법을 확인해보자. HWM BUMP UP을 제거하기 위해서는 HWM를 이동시키지 않고 데이터를 HWM 뒤에 존재하는 블록에 저장하면 될 것이다. 이와 같다면 롤백 또한 이전 데이터의 정보를 하나 하나 기록하는 것이 아니라 HWM의 위치 정보 하나만을 기록한다면 롤백 수행 시 HWM 위치 뒤에 존재하는 모든 블록을 제거한다면 롤백을 수행한 것과 동일한 현상이 발생할 것이다.

 

결국, HWM를 고정시키고 데이터를 HWM 뒤에 존재하는 블록에 저장시킨다면 HWM BUMP UP과 롤백을 위한 로그 기록에 의해 발생하는 성능 저하는 해결될 수 있을 것이다. 이와 같이 INSERT를 수행하는 방법이 바로 직접 로딩(DIRECT LOADING) 방식이다. 직접 로딩 방식을 사용한다면 HWM 뒤의 블록에 데이터를 저장하게 되므로 두 가지 문제는 모두 해결될 수 있을 것이다. 그렇다면 직접 로딩은 어떻게 사용하는 것인가?

 

·INSERT /*+ APPEND */ …… SELECT ……

 

위와 같이 SELECT를 수행하여 해당 데이터를 테이블에 INSERT하는 경우에 APPEND 힌트를 사용하여 직접 로딩을 수행하게 된다.

 

두 번째로 로그 기록을 확인해 보자. 우리가 테이블에 데이터를 저장하는 경우 일반적으로는 LOGGING 상태이므로 앞서 언급한 모든 로그를 기록하게 된다. 하지만, 직접 로딩(DIRECT LOADING) 기법을 사용하게 된다면 HWM 뒤에 존재하는 블록에 데이터를 저장하게 되므로 별도의 로그를 기록하지 않아도 복구 시 HWM 뒤의 블록에 존재하는 데이터를 제거하면 될 것이다. 이와 같은 이유에서 직접 로딩 방식의 경우에는 로그를 기록하지 않는 NOLOGGING 방식으로 데이터를 저장할 수 있게 된다.

 

결국, 위와 같이 NOLOGGING 상태에서 직접 로딩을 수행한다면 앞서 언급한 3가지의 성능 저하 요소를 대부분 제거할 수 있으며 이로 인해 디스크 I/O는 감소하게 된다. 상황에 따라 다르지만 대용량의 데이터에 대해 NOLOGGING 상태의 직접 로딩은 일반 INSERT에 비해 10배 이상 성능을 향상시킬 수도 있으며 그 이상의 성능 향상을 기대할 수도 있다.

 

 

DELETE와 UPDATE의 성능을 최적화하자

 

 

대용량의 데이터에 대해 DELETE 또는 UPDATE를 수행한다면 엄청난 성능 저하가 발생할 수 있다. DELETE 또는 UPDATE는 INSERT에 비해 더 많은 자원을 사용하게 되며 직접 로딩 또는 NOLOGGING 상태와 같은 방법이 존재하지 않게 된다. 많은 데이터에 대해서는 과거에나 지금이나 성능 저하를 감수할 수 밖에는 없을 것이다. 하지만, 이와 같은 대용량의 DELETE 또는 UPDATE에 대해서도 성능을 최적화 시키는 방법은 존재한다.

 

우리는 데이터를 변경하기 위해서는 항상 UPDATE를 사용해야 한다고 생각하고 데이터를 삭제하기 위해서는 항상 DELETE를 수행해야 한다고 생각한다. 일반적으로 생각한다면 당연한 사실일 것이다. 하지만, 이와 같은 고정 관념으로는 대용량의 데이터에서 더 이상의 성능 향상을 기대할 수 없을 것이다.

 

대용량의 데이터에 대해 UPDATE 또는 DELETE를 수행하는 경우 최적의 성능을 보장 받기 위해서는 기존의 사고 방식에서 벗어나야 할 것이다. 결국, 생각하는 방식의 전환만이 대용량의 데이터에 대해 UPDATE와 DELETE의 성능을 최적화하는 유일한 방법이 될 것이다.

 

대용량의 데이터에 대해 데이터의 변경에 대해 UPDATE로 작업을 수행하지 말고 데이터의 삭제에 대해 DELETE로 작업을 수행하지 말아야 한다. UPDATE는 INSERT로 변경하고 DELETE 또한 INSERT로 변경하는 순간 최적의 성능을 기대할 수 있을 것이다. UPDATE를 INSERT로 DELETE를 INSERT로 수행하는 것이야말로 우리의 기존 고정 관념을 파괴하는 행위일 것이다. 이제는 이와 같은 기존의 고정 관념을 파괴하여 성능을 최적화해야 할 것이다.

 

그렇다면 어떤 이유에서 UPDATE 또는 DELETE 대신 INSERT를 사용해야 하는가? 이유는 간단하다. INSERT는 UPDATE와 DELETE와는 달리 직접 로딩과 NOLOGGING이 가능하기 때문이다. 이와 같은 성능 향상의 요소는 우리에게 엄청난 혜택을 제공하기 때문이다.

 

그렇다면 어떻게 UPDATE를 INSERT로 변경하고 DELETE를 INSERT로 변경할 수 있겠는가? 물론, 데이터 삭제에 INSERT를 사용해야 하기 때문에 작업 절차는 복잡해 질 수 있다. 예를 들어, TEST 테이블의 크기가 100GB이며 그 중 50GB에 해당하는 데이터를 삭제해야 한다고 가정하자. 그렇다면 DELETE를 수행하는 순간 우리는 엄청난 시간을 기다려야 해당 작업을 종료할 수 있을 것이다.

 

DELETE를 INSERT로 변경한다면 어떻게 되겠는가? 우선, TEST 테이블과 동일 구조의 TEST_IMSI 테이블을 생성한 후 TEST 테이블로부터 데이터가 삭제된 후 남게 되는 데이터만을 조회하여 TEST_IMSI 테이블에 INSERT를 수행한다. 해당 작업에는 TEST_IMSI 테이블을 NOLOGGING 상태로 변경한 후 해당 테이블에 직접 로딩을 수행해야 할 것이다. 고성능의 디스크를 사용하는 시스템이라면 1GB에 1분 정동의 INSERT 시간이 소요된다.

 

50GB의 데이터를 INSERT하면 되므로 최적화된다면 50분 정도의 시간이 소요될 것이다. 이 얼마나 빠른 속도인가? 물론, 경우에 따라 병렬 프로세싱을 이용해야 할 수 도 있다. 이와 같이 작업을 수행했다고 모든 것이 종료되는 것은 아니다. TEST 테이블을 TEST_BACKUP으로 이름을 변경하고 TEST_IMSI 테이블을 TEST 테이블로 이름을 변경해야 할 것이다. 물론, 인덱스가 필요하다면 인덱스도 생성해야 할 것이다.

 

이와 같이 작업 절차는 복잡해 지지만 DELETE를 수행하는 것보다는 10배 아니 그 이상의 성능 향상을 기대할 수 있을 것이다. UPDATE의 경우도 이와 다르지 않다. UPDATE 후의 데이터를 임시 테이블에 INSERT를 수행하고 해당 테이블의 이름을 변경한다면 기존 테이블에는 UPDATE 후의 데이터가 저장되므로 INSERT를 이용하여 UPDATE를 대신할 수 있게 된다.

 

이와 같은 방식으로 기존의 방식에서 벗어날 수 있다면 우리는 최적의 성능을 기대할 수 있을 것이다.

 

우리가 가지고 있는 기존의 방식을 버린다는 것은 쉬운 일이 아닐 것이다. 하지만, 더 좋은 방법이 있다면 과감히 새로운 방법을 선택하는 것도 필요할 것이다. 데이터의 갱신과 제거를 UPDATE와 DELETE로 구현하는 것이 아니라 INSERT로 구현한다면 많은 사람들이 의아해 할지도 모른다. 하지만, 분명히 가능한 일이며 이를 통해 우리는 INSERT의 최고의 아키텍쳐인 NOLOGGING과 직접 로딩을 이용할 수 있다는 것을 명심하길 바란다.

 

 

제공 : DB포탈사이트 DBguide.net


반응형
Posted by [PineTree]
ORACLE/OWI2009. 2. 8. 12:19
반응형

Direct path read

 

 

목차

  • 1 Basic Info
  • 2 Parameter & Wait Time
    • 2.1 Wait Parameters
    • 2.2 Wait Time
  • 3 Check Point & Solution
    • 3.1 Parallel Query의 성능을 높인다
    • 3.2 I/O 시스템L의 성능을 높인다.
      • 3.2.1 I/O 개요
      • 3.2.2 어플리케이션 레이어(Application Layer)
      • 3.2.3 오라클 메모리 레이어(Oracle Memory Layer)
      • 3.2.4 오라클 세그먼트 레이어(Oracle Segment Layer)
      • 3.2.5 OS/디바이스 레이어(Device Layer)
      • 3.2.6 Direct Path I/O
    • 3.3 _DB_FILE_DIRECT_IO_COUNT 의 조정
  • 4 Event Tip
    • 4.1 direct path read와 undo
    • 4.2 데이터 파일에 대한 direct path read의 증명
    • 4.3 DB_FILE_DIRECT_IO_COUNT
    • 4.4 Direct Read I/O크기 알기

 

Basic Info

 

direct path read 이벤트대기는 Parallel Query 수행시 슬레이브 세션(Slave Session)이 수행하는 direct path I/O 에 의해 발생한다. direct path I/O 는 SGA 내의 버퍼캐쉬를 거치지 않고 세션의 PGA 로 직접 블록을 읽어 들이는 것으로 direct read 는 I/O 방식(synchronous I/O, asynchronous I/O)에 상관없이 수행될 수 있으나, 하드웨어 플랫폼과 DISK_ASYNCH_IO 파라미터에 영향을 받는다. Direct read I/O 는 일반적으로 디스크에 위치한 임시(temporary) 세그먼트를 액세스 하는 동안 사용된다. 이러한 작업은 정렬(sort), 병렬조회(parallel query) 및 해쉬조인(hash join)시에 발생한다.

 

슬레이브 세션이 direct path read 를 수행하는 동안 코디네이터 세션(Coordinator Session)은 슬레이브 세션으로부터 응답이 오기를 기다리며 PX Deq: Excute Reply 이벤트를 대기하는 것으로 관찰된다. Parallel Query 수행시 발생하는 direct path read 대기는 필연적인 것이다. 만일 direct path read 이벤트의 대기시간이 지나치게 높게 나온다면 다음과 같은 관점에서 튜닝포인트를 찾아보아야 한다.


이 이벤트의 대기횟수와 대기시간은 오해의 소지가 있을 수 있다. 만일 비동기식 I/O가 사용되지 않았다면, 세션은 I/O 가 완료될 때까지 대기한다. 하지만, I/O 요청이 시작된 시점부터 대기시간을 계산하지 않고, I/O 요청이 완료된 후 데이터를 액세스 할 때 direct path read 대기이벤트를 기다리게 된다. 따라서 대기시간은 상당히 짧게 나타난다.

 

만일 비동기식 I/O 가 사용가능하고 현재 사용 중이라면, 세션은 다수의 direct read 요청을 한 후 , PGA 내부에 캐쉬 된 블록들에 대한 처리를 진행한다. 세션이 PGA내부에 캐쉬 된 블록들이 없어서 처리를 진행하지 못하는 시점에 direct path read 대기이벤트가 발생된다. 따라서, 읽기 요청횟수는 대기횟수와 동일하지 않다. 이러한 불일치 때문에, V$SYSTEM_EVENT 와 V$SESSION_EVENT 뷰에서 보여지는 direct path read 대기이벤트의 수치는 신뢰할 수 없다.

 

LOB 세그먼트를 읽을 때 발생되는 direct path read 대기는, 오라클 8.1.7부터는 direct path read(lob) 대기이벤트로 별도로 구분된다. 보통 direct path read 대기이벤트는 임시(temporary) 또는 일반 테이블스페이스로부터 direct read 오퍼레이션을 수행하는 SQL문에 의해 발생된다. ORDER BY, GROUP BY, UNION, DISTINCT, ROLLUP과 같이 정렬이 필요한 함수를 수행하는 SQL문은, PGA 내부에서 허용할 수 있는 것 보다 더 큰 데이터를 정렬해야 할 때 소트 런(sort run)들을 임시 테이블스페이스에 기록한다. 임시 테이블스페이스의 소트 런들은 최종 결과를 만들기 위해 순차적으로 PGA로 읽혀지고 머지 된다. 소트 런들을 PGA로 읽어 들이는 동안 해당 세션은 direct path read 대기이벤트를 대기한다. Hint또는 옵티마이저의 판단에 의해 MERGE 조인을 수행하는 SQL문 또한 정렬작업이 필요하다.

 

힌트(hint)또는 옵티마이저의 판단에 의해HASH 조인을 수행하는 SQL문은 PGA 내부에서 허용할 수 없는 크기의 해쉬 파티션들을 임시 테이블스페이스에 기록한다. 임시 테이블스페이스에 기록된 해쉬 파티션은 SQL문의 조건에 맞는 레코드를 찾기 위해 다시 PGA로 읽어 들여진다. 해쉬 파티션을 PGA로 읽어 들이는 동안 해당 세션은 direct path read 대기이벤트를 대기한다.

 

V$SESSION_EVENT 뷰의 TOTAL_WAITS 또는 TIME_WAITED 값을 이용하여 direct path read 대기이벤트를 평가하지 않는 것이 좋다. 대신에 아래의 쿼리를 사용하여 대량의 direct read 를 수행하고 있는 세션을 찾아낼 수 있다. physical reads direct 는 parent세션에 의해 시작된 direct reads와 parent세션이 관리하는 슬레이브 프로세스에 의해 발생된 모든 direct reads 의 합으로 구성된다.

select a.name, b.sid, b.value,
round((sysdate - c.logon_time) * 24) hours_connected
from v$statname a, v$sesstat b, v$session c
where b.sid = c.sid
and a.statistic# = b.statistic#
and b.value > 0
and a.name = 'physical reads direct'
order by b.value;
NAME SID VALUE HOURS_CONNECTED
------------------------- ---- ---------- ------------------------
physical reads direct 2 41 980
physical reads direct 4 41 980
physical reads direct 5 445186 980

 

대량의 direct reads 를 발생시키는 세션을 찾는 것 이외에, 세션이 데이터를 읽어오는 위치 (임시 테이블스페이스, 데이터파일 등), 대기를 발생시키는 SQL문을 파악하여야 한다. 다음의 쿼리는 이것에 대한 답을 줄 수 있다. 임시 테이블스페이스에서 데이터를 읽는 세션은 소트 또는 해쉬 세그먼트를 읽고 있을 것이다. 데이터파일로부터 데이터를 읽고 있는 세션은 병렬 쿼리 슬레이브(parallel query slave)이다.

select a.event,
a.sid,
c.sql_hash_value hash_value,
decode(d.ktssosegt,
1,'SORT', 2,'HASH', 3,'DATA',
4,'INDEX',5,'LOB_DATA',6,'LOB_INDEX',
null) as segment_type,
b.tablespace_name,
b.file_name
from v$session_wait a, dba_data_files b, v$session c, x$ktsso d
where c.saddr = d.ktssoses(+)
and c.serial# = d.ktssosno(+)
and d.inst_id(+) = userenv('instance')
and a.sid = c.sid
and a.p1 = b.file_id
and a.event = 'direct path read'
union all
select a.event,
a.sid,
d.sql_hash_value hash_value,
decode(e.ktssosegt,
1,'SORT', 2,'HASH', 3,'DATA',
4,'INDEX',5,'LOB_DATA',6,'LOB_INDEX',
null) as segment_type,
b.tablespace_name,
b.file_name
from v$session_wait a, dba_temp_files b, v$parameter c,
v$session d, x$ktsso e
where d.saddr = e.ktssoses(+)
and d.serial# = e.ktssosno(+)
and e.inst_id(+) = userenv('instance')
and a.sid = d.sid
and b.file_id = a.p1 - c.value
and c.name = 'db_files'
and a.event = 'direct path read'
order by 1,2;
EVENT SID HASH_VALUE SEGMENT TABLESPACE_N FILE_NAME
------------------ ---- ---------------- ----------- ------------------ -----------------
direct path read 8 511952958 SORT TEMP_BATCH temp_batch_01.dbf
direct path read 9 3138787393 ORDERS orders_01.dbf
direct path read 11 3138787393 ORDERS orders_01.dbf
direct path read 12 3138787393 ORDERS orders_01.dbf
direct path read 14 3138787393 ORDERS orders_01.dbf

 

임시 테이블스페이스로부터 소트 세그먼트를 읽고 있는 세션은, SORT_AREA_SIZE (또는 오라클 9i에서 PGA_AGGREGATE_TARGET 을 사용하는 경우 work area size )가 메모리 소트를 수행할 만큼 충분치 않다는 것을 의미한다. 하지만 이것은 문제가 되지 않는다. 모든 소트가 메모리에서만 수행되는 것은 현실적으로 불가능하기 때문이다. 하지만, 많은 멀티패스( multi pass ) 소트는 가능한 방지해야 한다. 왜냐하면, 멀티패스 소트는 임시 테이블스페이스에 대한 상당한 I/O 를 유발하며 매우 느리기 때문이다. SQL문이 멀티패스 소트를 하는지 어떻게 확인할 수 있을까? 오라클 9i이전 버전에서는 쉽지 않은 일이다. 10032 트레이스 이벤트를 설정한 후 트레이스파일을 확인해야만 한다. 하지만, 오라클 9i부터는, 소트를 수행하는 SQL문의 hash value를 이용하여 V$SQL_WORKAREA 또는 V$SQL_WORKAREA_ACTIVE 뷰를 조회해 보는 것만으로도 확인이 가능하다. 소트에 대한 더욱 자세한 사항은 International Oracle Users Group (IOUG) 2004 conference proceedings (www.ioug.org) 의 “If Your Memory Serves You Right” 기술백서를 참고하라.

 

이러한 경우, 튜닝의 목표는 디스크 소트 횟수를 최소화하는 것이다. SORT_AREA_SIZE (또는 PGA_AGGREGATE_TARGET )을 크기를 증가함으로써 디스크 소트 횟수를 줄일 수 있다. 하지만, 이것은 극단적으로 SORT_AREA_SIZE 가 작게 설정된 경우가 아니라면 근본적인 해결방법이라고는 할 수 없다. 먼저 어플리케이션에서 소트가 반드시 필요한지를 확인해야 한다. 어플리케이션들은 DISTINCT와 UNION 함수를 남용하여 사용하는 경향이 있다. 가능하다면 UNION보다는 UNION ALL을 사용하고, SORT MERGE 보다는 HASH 조인, HASH 조인보다는 NESTED LOOPS조인을 사용하도록 해야 한다. 또한 옵티마이저가 드라이빙 테이블을 올바르게 선택했는지도 확인할 필요가 있다. 결합 인덱스의 컬럼을 자주 쓰이는 ORDER BY절과 잘 맞게 설정해 놓으면 소트를 피할 수 도 있다. 오라클 9i라면 PGA_AGGREGATE_TARGET 을 사용하여 SQL work area 를 자동적으로 할당 받아 사용할 수 있도록 설정하는 것도 고려해 볼 만 하다. 통계적으로 보았을 때, 자동적으로 메모리관리를 하도록 하면 메모리소트의 비율을 더 높여준다.

 

세그먼트를 읽고 있는 세션을 발견했다면, HASH_AREA_SIZE (오라클 9i 에서 PGA_AGGREGATE_TARGET 을 사용하고 있는 경우는 work area size )가 작아서 메모리에 해쉬 테이블을 수용하지 못하는 경우이다. 해결방법은 이미 언급됐던 것과 유사하다. 만약 HASH_AREA_SIZE 가 너무 작은 경우만 아니라면 HASH_AREA_SIZE (또는 PGA_AGGREGATE_TARGET )를 조정하기 전에 먼저 어플리케이션과 SQL문을 튜닝해야 한다.

 

만약에 병렬 쿼리(parallel query) 슬레이브에서 direct reads가 발생한다면, 병렬 스캔(parallel scan)이 parent SQL문에 적합한지와 슬레이브의 개수가 적당한지 확인해야 한다. 또한 쿼리 슬레이브들이 시스템의 CPU와 디스크 자원을 모두 써버리지는 않는지도 확인해야 한다. Parent SQL문의 hash value와 쿼리 슬레이브들이 수행하는 SQL의 hash value가 동일하지 않기 때문에 parent SQL문을 찾는 것은 쉽지 않은 일이다. 오라클 8.1.5에서 V$PX_SESSION 뷰가 소개되기 전까지는 더욱 힘든 일이었다. 아래의 2가지 예제는, 병렬 쿼리가 수행될 때 parent SQL문장을 찾는 방법을 오라클 8.1.5이전 버전과 이후 버전에 대해서 각각 설명한다.

--오라클 8.1.5이전 버전
--Note: 아래의 쿼리는 SYS 유저에 의해 수행되는 병렬 쿼리 문장들을 구분할 수 없다
--왜냐하면, SYS 유저들은 동일한 AUDSID를 공유하기 때문이다.
select decode(ownerid,2147483644,'PARENT','CHILD') stmt_level,
audsid,
sid,
serial#,
username,
osuser,
process,
sql_hash_value hash_value,
sql_address
from v$session
where type <> 'BACKGROUND'
and audsid in (select audsid
from v$session
group by audsid
having count(*) > 1)
order by audsid, stmt_level desc, sid, username, osuser;
STMT_L AUDSID SID SERIAL# USERNAME OSUSER PROCESS HASH_VALUE SQL_ADDR
------ -------- ---- ---------- ------------- ---------- ----------- ---------------- --------
PARENT 3086501 20 779 INTREPID cdh8455 16537 3663187692 A0938E54
CHILD 3086501 12 841 INTREPID cdh8455 16544 817802256 A092E1CC
CHILD 3086501 14 2241 INTREPID cdh8455 16546 817802256 A092E1CC
CHILD 3086501 17 3617 INTREPID cdh8455 16540 817802256 A092E1CC
CHILD 3086501 21 370 INTREPID cdh8455 16542 817802256 A092E1CC
아래의 쿼리는 오라클 8.1.5이상 버전에서 사용된다.
select decode(a.qcserial#, null, 'PARENT', 'CHILD') stmt_level,
a.sid,
a.serial#,
b.username,
b.osuser,
b.sql_hash_value,
b.sql_address,
a.degree,
a.req_degree
from v$px_session a, v$session b
where a.sid = b.sid
order by a.qcsid, stmt_level desc;
STMT_L SID SERIAL# USERNAME OSUSER HASH_VALUE SQL_ADDR DEG REQ_DEG
--------- ----- -------- ------------- --------- ------------- -------------- ------ -------
PARENT 20 779 INTREPID cdh8455 3663187692 A0938E54
CHILD 17 3617 INTREPID cdh8455 817802256 A092E1CC 4 4
CHILD 21 370 INTREPID cdh8455 817802256 A092E1CC 4 4
CHILD 12 841 INTREPID cdh8455 817802256 A092E1CC 4 4
CHILD 14 2241 INTREPID cdh8455 817802256 A092E1CC 4 4

Parameter & Wait Time

Wait Parameters

Direct path read 대기이벤트의 대기 파라미터는 다음과 같다.

  • P1 : Absolute File#
  • P2 : Starting Block#
  • P3 : 블록수

Wait Time

 

I/O관련 이벤트이므로 타임아웃이 발생하지 않으며, 세션은 I/O가 완료될 때까지 대기한다

 

Check Point & Solution

 

Parallel Query의 성능을 높인다

 

Parallel Query를 수행하는 과정에서의 direct path read 대기는 필연적인 것으로 이 대기 자체를 튜닝하는 것은 불가능하다. 오히려 SQL 튜닝을 통해 Parallel Query 자체의 성능을 개선시키는 것이 옳은 접근 방법이다. 시스템의 용량에 비해 불필요하게 Parallel Query를 수행하는 것은 오히려 성능을 저하시키는 요인이 된다. 한가지 기억할 것은 데이터 파일에 대해 직접 읽기 작업을 수행하기 전에 읽기의 대상이 되는 객체의 더티 블록이 데이터 파일에 기록이 되어야 한다는 것이다. 즉 체크포인트가 발생하게 된다. 이 작업을 수행하는 동안 코디네이터 세션은 enq: TC - contention 대기를 겪게 된다.

 

I/O 시스템의 성능을 높인다.

 

I/O 개요

 

I/O는 오라클에서 가장 중요한 자원이다. 오라클이 제공하는 모든 기능은 결국 어떻게 하면 데이터를 파일에 잘 쓰고 잘 읽느냐에 관한 것이다. 따라서 자연스럽게 오라클 성능문제 중 많은 수가 I/O와 관련이 있다. I/O와 관련된 오라클의 성능문제를 이해하려면 오라클에서의 I/O 작업은 여러 개의 레이어(Layer)로 이루어져 있다는 사실을 이해해야 한다. 오라클에서의 I/O 작업 레이어를 다음과 같이 나누어 설명해 보기로 하자.

 

1. 어플리케이션 레이어 : select/insert/update/delete/truncate...

2. 오라클 메모리 레이어: Buffer cache | PGA

3. 오라클 세그먼트 레이어: Datafile, tempfile, Tablespace, Segment

4. OS/디바이스 레이어: Asynch I/O, Direct I/O, Raw device, RAID, ...

 

I/O 성능문제의 원인을 파악하는 것과 해결책을 찾는 과정은 항상 1 -> 2 -> 3 -> 4 의 순서를 따라야 한다. 논리적인 면에서뿐만 아니라 경제적인 면에서 더욱 그렇다. 각각의 레이어에 대해 필요한 사전지식과 메커니즘에 대해서 논의해보자.

 

어플리케이션 레이어(Application Layer)

 

어플리케이션을 효과적으로 구현하여, 불필요한 I/O를 최소화해야 한다. 비효율적인 어플리케이션을 그대로 두고 시스템을 튜닝한다는 것은 거의 불가능하다. 오라클은 I/O를 효율적으로 처리하는 다양한 기법들을 제공한다. Parallel Query, Parallel DML, Nologging, Direct load, Direct read 등이 대표적인 예이다. 오라클의 버전이 올라갈수록 더욱 효과적으로 작업을 처리할 수 있는 강력한 SQL 기능이 추가된다. 해석함수( Analytical Function )가 대표적인 경우인데, 이 함수를 잘 이용하면 I/O를 크게 줄이면서 다양한 집계기능을 구현할 수 있다. 이러한 최신의 SQL 문장을 사용하여 I/O부하를 줄이도록 해야 한다. 데이터의 성격에 따라 클러스터(Cluster), IOT, 파티셔닝(Partitioning), 비트맵 인덱스(Bitmap Index)등의 기능을 적절히 사용해서 I/O를 효과적으로 사용하는 것 또한 어플리케이션의 몫이다.

 

오라클 메모리 레이어(Oracle Memory Layer)

 

버퍼 캐시는 오라클 I/O 관리의 핵심이다. 자주 사용하는 블록들을 메모리에 캐시함으로써 물리적인 I/O를 줄일 수 있다. 오라클의 버전이 올라감에 따라 버퍼 캐시를 처리하는 알고리즘이 끊임없이 개선되고, 더불어 새로운 관리 방법들이 제공된다. 버퍼 캐시를 효과적으로 사용하게 되면 물리적 I/O가 줄어들고 자연스럽게 I/O 성능문제가 해결되는 경우가 많다. 오라클이 제공하는 기능들에는 다음과 같은 것들이 있다.

  • 첫째, Touch count 기반의 효율적인 LRU 알고리즘을 제공한다.
  • 둘째, Buffer Pinning 기법을 통해 불필요한 래치 경합을 줄이고, 현재의 읽기 작업에 사용될 확률이 높은 블록들을 메모리에서 밀려나지 않게끔 한다.
  • 셋째, 다중 버퍼 풀( Multiple buffer pool ) 기능을 이용하면 휘발성 블록과 메모리 상주 블록을 구분해서 효과적으로 관리할 수 있다. 시스템에서 보편적으로 자주 사용되는 객체들은 Default 버퍼를 사용한다. 비교적 주기적으로 사용되는 작은 크기의 객체들은 Keep 버퍼에 상주시키는 것이 좋다. 반대로 아주 적은 빈도로 사용되는 큰 크기의 객체들은 Recycle 버퍼를 사용함으로써 중요한 메모리 영역을 낭비하는 일을 방지할 수 있다.
  • 넷째, 오라클 9i부터는 블록 크기를 2K ~ 32K까지 사용 가능하다. 객체의 속성을 고려하여 큰 사이즈의 블록을 사용하는 것이 유리한 경우(가령 로우의 크기가 크고 풀테이블스캔으로 데이터를 읽는 경우가 많은 경우)에는 큰 크기의 블록을 사용함으로써 성능 개선 효과를 얻을 수 있다.
  • 다섯째, 메모리에 올릴 필요가 없는 대용량의 데이터를 처리할 때는 버퍼 캐시를 우회하는 방법을 사용할 수 있다. 이러한 기능을 direct path I/O 라고 부른다. Direct path I/O 를 사용하면 SGA영역을 거치지 않기 때문에 메모리 공유를 위한 동기화 메커니즘이 불필요하고 그만큼 성능이 개선된다. Direct path I/O 의 반대말은 conventional path I/O 로 SGA 즉, 버퍼 캐시를 경유하는 것을 말한다. 오라클은 영구 세그먼트(Permanent Segment)와 임시 세그먼트(Temporary Segment) 모두에 대해 direct path I/O 를 지원한다. Parallel Query나 Parallel DML등은 영구 세그먼트에 대해 direct path I/O 를 사용한다. 정렬 작업은 임시 세그먼트에 대해 direct path I/O 를 사용한다. LOB 세그먼트는 약간 독특한 처리 메커니즘을 가지고 있는데, LOB 컬럼 생성시 부여하는 스토리지(Storage) 속성에 따라 direct path I/O 를 사용할 수도 있고, conventional path I/O 를 사용할 수도 있다.

오라클 세그먼트 레이어(Oracle Segment Layer)

 

일반적인 데이터들은 데이터파일에 저장된다. 임시 테이블스페이스(Temporary tablespace)를 사용할 경우, 기본적으로 데이터 파일이 아닌 임시파일(Temp file)에 데이터를 저장한다. 오라클 7.3 이전 버전에서는 정렬 작업을 위해 영구 테이블스페이스(Permanent Tablespace)를 사용해야 했는데 이 경우 과도한 익스텐트의 할당과 해제로 인해 많은 성능문제가 야기되며, 특히 ST 락 경합으로 인한 성능 저하 현상이 생기는 경우가 많았다. 오라클 8i부터 사용가능한 임시 테이블스페이스(Temporary tablespace)와 임시파일(Tempfile) 기능을 사용하면 ST 락 경합은 더 이상 문제가 되지 않는다. 오라클 8i부터 제공되는 LMT( Locally Managed Tablespace )와 오라클 9i부터 제공되는 ASSM( Automatic Segment Space Management )을 사용하면 익스텐트 및 세그먼트 공간의 부적절한 관리에서 오는 성능문제를 대부분 해결할 수 있다. 대용량의 테이블은 파티션(Partition)을 이용해서 관리하는 것이 유리한 경우가 많다. 관리적인 측면에서뿐만 아니라, 대량의 데이터를 처리하는 경우 원하는 범위만을 스캔하는 것이 가능하므로 필요한 I/O 범위를 줄이는 효과가 있다.

 

OS/디바이스 레이어(Device Layer)

 

오라클은 가능하면 비동기 I/O(Asynchronous IO)를 사용할 것을 권장한다. 비동기 I/O는 읽기 작업뿐만 아니라 특히 DBWR이나 LGWR등 쓰기작업을 수행하는 프로세스가 비동기적으로 작업을 처리할 수 있도록 해줌으로써 I/O 작업의 속도를 전반적으로 개선시켜준다. 불행히도 많은 OS들에서 진정한 비동기 I/O는 로디바이스(Raw device)에서만 사용한 것으로 알려져있다. 비동기 I/O를 사용하는 것이 불가능하다면 OS차원에서 Direct I/O를 사용하는 것이 바람직하다. Direct I/O를 사용하는 경우 OS의 버퍼 캐시를 우회함으로써 불필요한 I/O 작업을 최소화한다. DBWR 프로세스를 복수개로 사용하는 것 또한 방법이 될 수 있다. 컨트롤 파일(control file)의 개수나 리두로그 파일의 개수가 불필요하게 많다면 복구가 가능한 최소한만큼만 유지하는 것도 도움이 된다.


Direct I/O를 사용하는 경우에는 로디바이스를 사용할 필요가 없다는 견해가 많다. Direct I/O를 사용할 경우 OS의 버퍼 캐시를 경유하지 않으므로 그 작동 방식이 로디바이스와 거의 동일하기 때문이다. 하지만, 로디바이스가 I/O 성능개선의 중요한 방법이라는 사실 자체는 의심의 여지가 없는 것으로 보인다.


한가지 유념할 것은 로디바이스나 Direct I/O가 비효율적으로 과다하게 I/O를 수행하는 어플리케이션에 대한 무조건적인 해결책이 아니라는 사실이다. 가령 매우 비효율적인 I/O를 수행하는 어플리케이션의 속도를 개선시키기 위해 기존의 파일 시스템을 로디바이스로 수정했다고 가정하자. 어플리케이션의 성능이 개선될까? 불행하게도 그렇지 않을 확률이 있다. 로디바이스를 사용함으로써 I/O 작업 자체는 빨라지지만, OS에서 제공하는 버퍼 캐시를 사용하지 못하기 때문이다. 가령 파일 시스템을 사용할 경우에는 100만번의 Physical Read중 실제로는 10만번만이 실제 디스크 읽기를 유발하고, 나머지는 90만번은 OS의 버퍼 캐시에서 이루어질 수 있다. 이런 경우에 로디바이스를 사용하면 100만번의 Physical Read가 모두 디스크 읽기로 이어지기 때문에 오히려 어플케이션의 성능이 저하될 수도 있다. 어플리케이션의 적절한 튜닝이 항상 우선이며, I/O 시스템의 성능 개선은 I/O 시스템이 “실제로” 느린 경우에만 필요하다.


RAID로 구성된 I/O 시스템을 사용하는 경우에는 RAID 레벨(level)을 신중하게 선택해야 한다. 리두 로그 파일과 같이 쓰기작업이 왕성한 자원에 대해 RAID-5를 사용하는 것은 성능에 큰 문제를 일으킨다. 데이터파일의 경우에도 RAID-5는 문제를 일으키는 경우가 많다. 가능하면 RAID1+0이나 RAID0+1을 사용하는 것이 기본적인 권고안이다. 파일들을 물리적으로 분리시킴으로써 디스크간의 경합을 피하는 것 또한 중요하다. SAN과 같이 통합된 스토리지 시스템을 이용하는 경우에는 해당 엔지니어와의 협의를 통해 파일들간에 적절한 분산이 이루어지도록 설정할 필요가 있다. 또한 아카이브 모드로 데이터베이스를 운영하는 경우에는 리두 로그와 아카이브 로그간의 경합이 발생하지 않도록 구성해 주어야 한다.
I/O 시스템을 변경하는 것은 많은 시간와 돈을 요구하는 경우가 많기 때문에 항상 최후의 선택사항으로 남겨 두어야 한다. 1~4 단계만으로 문제가 해결되지 않는 경우에만 5단계의 튜닝을 고려하는 것이 바람직하다.

 

RAID의 정의에 대해서만 간단하게 알아보기로 하자. RAID는 Redundant Arrays of Inexpensive(Independent) Disks의 약자로 복수개의 디스크를 조합해서 I/O 시스템을 구성하는 기술을 말한다. 소프트웨어적으로 구현될 수도 있으나 성능 면에서는 하드웨어적으로 구현된 것이 훨씬 유리하다.

  • RAID0 : 스트라이핑(Striping). 데이터를 여러 개의 디스크에 분할 저장하도록 구성하는 것을 말한다. 데이터의 부하가 자동으로 분산되므로 성능면에서 유리하지만, 하나의 디스크만 고장나도 전체 I/O가 불가능해지는 단점이 있다.
  • RAID1 : 미러링(Mirroing). 여러 개의 디스크에 같은 데이터를 저장하는 기법을 말한다. 하나의 디스크가 고장나도 I/O에 아무런 장애가 없다는 점에서 신뢰성이 높다. 하지만 항상 원하는 용량의 2배에 해당하는 디스크가 필요하다는 단점이 있다.
  • RAID5 : 최소한 3개의 디스크를 사용해서 분산 저장하는 방식으로, 데이터 저장시 패리티(Parity) 비트를 저장해서 이후 하나의 디스크가 고장나도 패리티 비트를 이용해서 복구가 가능한 기법을 말한다. 디스크 활용도가 가장 높다고 할 수 있지만 패리티 저장에 따른 부하로 쓰기작업이 왕성한 경우 성능이 저하되는 문제점이 있다.

오라클은 데이터파일이나 컨트롤 파일, 리두 로그 파일에 대해서 RAID5보다는 RAID0+1 또는 RAID1+0을 사용할 것을 권장한다. RAID0+1은 물리적인 스트라이핑에 논리적인 미러링을 구현하는 것을 말하며, RAID1+0은 물리적인 미러링에 논리적인 스트라이핑을 구현하는 것을 말한다.


메타링크 문서번호 30286.1 에서 RAID 구성에 대한 자세한 정보를 제공한다.

 

Direct Path I/O

 

오라클의 I/O는 기본적으로 SGA(버퍼 캐시)를 경유한다. 하지만 특수한 상황에서는 SGA를 우회해서 PGA에 데이터를 올린다. 데이터를 공유할 필요가 없을때는 버퍼 캐시에 데이터를 적재하는 과정에서 발생하는 오버헤드를 피함으로써 성능을 개선하는 것이 가능하다. 버퍼 캐시내의 변경된 블록을 데이터파일에 기록하는 것은 DBWR 고유의 작업이다. 반면 버퍼 캐시를 우회하는 쓰기 작업은 개별 프로세스가 직접 수행하게 된다. 이처럼 버퍼 캐시를 우회하는 I/O 작업을 direct path I/O 라고 부른다. 오라클은 다음과 같은 경우에 direct path I/O 를 사용한다.

  1. 정렬작업을 위해 정렬 세그먼트(Sort segment)를 읽고 쓰는 경우. direct path read temp , direct path write temp 이벤트를 대기한다.
  2. Parallel Query를 위해 데이터파일을 읽는 경우. direct path read 이벤트를 대기한다.
  3. PDML이나 CTAS를 위해 데이터파일을 쓰는 경우. direct path write 이벤트를 대기한다.
  4. NOCACHE 속성으로 생성된 LOB 세그먼트를 읽고 쓰는 경우. direct path read(lob) , direct path write(lob) 이벤트를 대기한다.
  5. I/O 시스템이 데이터를 읽어서 오라클에 반환하는 속도보다 훨씬 빠른 속도로 버퍼를 요구할 때. 이 경우 오라클 성능개선을 위해 readahead I/O (이후에 읽을 것으로 판단되는 데이터를 미리 한꺼번에 읽는 I/O 작업을 말함)를 이용한다. 이 경우 direct path read 이벤트를 대기한다.

Direct path I/O 와 관련된 통계값에 대해 정리하면 다음과 같다.

  • physical reads : 디스크에서 읽은 블록수. Direct path I/O 여부와 무관하게 물리적인 읽기 작업이 발생할 경우에는 항상 증가한다.
  • physical reads direct : Direct path I/O 를 통해 읽은 블록수. LOB 데이터에 대한 direct path I/O 는 포함하지 않는다.
  • physical reads direct (lob) : LOB 데이터를 direct path I/O 를 통해 읽는 블록수
  • physical writes : 디스크에 기록한 블록수. Direct path I/O 여부와 무관하게 물리적인 쓰기 작업이 발생할 경우에는 항상 증가한다.
  • physical writes direct : Direct path I/O 를 통해 기록한 블록수. LOB 데이터에 대한 direct path I/O 는 포함하지 않는다.
  • physical writes direct(lob) : LOB 데이터를 direct path I/O 를 통해 기록한 블록수
  • sort(disk) : 디스크를 이용한 정렬 작업 회수. 디스크를 이용한 정렬 작업이 발생할 경우에는 정렬 세그먼트에 대해 direct path I/O 를 사용한다.
  • sort(memory) : 메모리를 이용한 정렬 작업 회수

물리적인 읽기 작업 중 버퍼 캐시를 경유한( conventional path I/O ) 읽기 작업은 다음 공식으로 계산할 수 있다.

conventional physical reads = physical reads –( physical reads direct + physical reads direct(lob) )


Direct path I/O 의 성능문제는 대부분 I/O 시스템의 성능과 직접적인 관련이 있다. Direct path I/O 는 버퍼 캐시를 경유하지 않기 때문에 동기화에 따른 오버헤드가 없다. 따라서 경합으로 인한 성능저하 현상이 발생하지 않는다. 정렬작업과 같은 경우를 제외하고는 튜닝 작업을 통해 direct path I/O에서의 대기회수 및 대기시간을 줄이는 것은 불가능하다. 만일 direct path I/O 의 수행 성능상에 문제가 생긴다고 판단되면 I/O 자체의 성능을 개선시키는 것에 초점을 맞추는 것이 옳다.

 

오라클의 direct path I/O 는 OS의 direct IO와 다른 개념이라는 것에 유의하자. 오라클의 direct path I/O 는 SGA의 버퍼 캐시를 경유하지 않는 것이고, OS의 direct I/O는 OS의 버퍼 캐시를 경유하지 않는 것이다. 오라클의 캐시와 OS의 캐시를 같이 사용하는 것을 더블 버퍼링(double-buffering)이라고 부르는데, 보통 성능에 이롭지 않은 것으로 알려져 있다.
하지만 더블버퍼링이 성능에 미치는 영향의 정도는 시스템의 특성이나 어플리케이션의 특성에 따라 다를 수 있다.

 

_DB_FILE_DIRECT_IO_COUNT 의 조정

 

_DB_FILE_DIRECT_IO_COUNT 히든 파라미터의 값이 direct path I/O 에서의 최대 I/O 버퍼 크기를 결정한다. 오라클 9i부터 이 값은 기본적으로 1M의 값을 가진다. 하지만 실제로는 O/S나 하드웨어 설정에 따라 최대값이 결정된다. 이 값을 높이면 Parallel Query의 성능이 높아질 수도 있으나, 대부분 실제 사용가능한 값은 1M보다 작은 값이므로 실제로는 변경할 필요가 없다.

 

Event Tip

 

direct path read와 undo

 

direct path read 가 비록 데이터파일에서 직접 데이터를 읽지만, 언두를 참조하는 메커니즘은 동일하다. 즉, direct path read 는 SGA를 경유하지 않을 뿐, 읽기 일관성(Read consistency)을 보장하는 방법은 동일하다. 이것을 증명하는 방법은 크기가 작은 언두 테이블스페이스(Undo tablespace)를 생성한 후, Parallel Query를 수행하면서 다른 세션에서 DML을 과다하게 수행할때 ORA-01555(Snapshot too old) 에러가 나는 것을 관찰하는 것이다.

 

ERROR at line 1:
ORA-12801: error signaled in parallel query server P002
ORA-01555: snapshot too old: rollback segment number 68 with name "_SYSSMU68$" too small

 

위의 에러는 곧 PQ 슬레이브 세션이 데이터파일에 대해 direct read 를 수행하면서 변경된 블록을 발견하면 언두 데이터를 참조하는 것으로 해석할 수 있다.

 

데이터 파일에 대한 direct path read의 증명

 

PQ 수행시 슬레이브 세션에서의 direct path read가 임시 영역이 아닌 데이터파일에 대한 direct path read 인 것을 어떻게 증명할 수 있을까? 하나의 세션에서 PQ를 수행한 후, PQ가 수행되는 동안 다른 세션에서 V$SESSION_WAIT 뷰를 조회해서 P1 값을 얻으면 어떤 파일에 대한 direct path read 인지 알 수 있다. 아래 스크립트를 보자.

Session A : Degree가 4 인 pq_test 테이블에 대해 PQ를 여러번 수행하면서 direct path read 유발
declare
v_count number;
begin
for idx in 1 .. 100 loop
select count(*) into v_count from pq_test;
end loop;
end;
/
Session B: Session A에서 발생한 PQ의 슬레이브 세션에 대해 direct path read 이벤트를 캡쳐한다.(Session A의 SID = 162)
set serveroutput on size 100000
declare
begin
for px in (select * from v$px_session where qcsid = 162) loop
for wait in (select * from v$session_wait where
sid = px.sid and event like '%direct path read%') loop
dbms_output.put_line('SID='||wait.sid ||
', P1=' || wait.P1);
end loop;
end loop;
end;
/
Session B의 수행결과는 아래와 같다.
SID=138, P1=1
SID=152, P1=1
SID=144, P1=1
...
SID=142, P1=1
SID=144, P1=1
SID=138, P1=1
direct path read 대기이벤트의 P1 = file#이므로 해당되는 파일이 실제 데이터파일인지 확인할 수 있다.
SQL>exec print_table('select * from v$datafile where file# = 1'); 을
FILE# : 1
...
BLOCK_SIZE : 8192
NAME :
C:\ORACLE\PRODUCT\10.1.0\ORADATA\UKJADB\SYSTEM01.DBF
PLUGGED_IN : 0
BLOCK1_OFFSET : 8192
AUX_NAME : NONE
위와 같이 system01.dbf라는 데이터 파일에 대한 direct path read임을 알 수 있다.

 

DB_FILE_DIRECT_IO_COUNT

 

DB_FILE_DIRECT_IO_COUNT 파라미터는 direct path read 성능에 영향을 미칠 수 있다. 해당 파라미터는 direct reads , direct writes 에 대한 최대 I/O 버퍼크기로 설정해야 한다. 오라클 8i까지는 대부분의 플랫폼에서 기본 설정값은 64 블록이었다. 따라서 

DB_BLOCK_SIZE 가 8K인 경우 direct reads , direct writes 에 대한 최대 I/O 버퍼크기는 512K(8K*64)이다. 최대 I/O 버퍼 크기는 하드웨어의 한계 값에 의해서도 제한된다. 오라클 9i에서는 DB_FILE_DIRECT_IO_COUNT 파라미터는 hidden 파라미터로 변경되었고, 블록수가 아니라 바이트(byte)단위로 변경되었다. 오라클 9i의 기본 설정값은 1 MB이다. 실질적인 direct I/O 크기는 하드웨어 환경설정(configuration) 및 한계 값에 의해서도 영향을 받는다

 

Direct Read I/O크기 알기

 

세가지 방법으로 실제적인 direct read I/O 크기를 알 수 있다.

  • direct read 를 수행하는 세션에 대해 10046 트레이스 이벤트를 레벨 8로 설정한다. P3 파라미터는 읽은 블록수를 나타낸다. 아래의 예제의 경우, 블록 크기가 8K 이므로 direct path read I/O 크기는 64K(8K*8블록)이다. 또한, V$SESSION_WAIT 뷰를 조회하여 direct path read 대기이벤트의 P3값을 확인할 수 있다.

WAIT #1: nam='direct path read' ela= 4 p1=4 p2=86919 p3=8
WAIT #1: nam='direct path read' ela= 5 p1=4 p2=86927 p3=8
WAIT #1: nam='direct path read' ela= 10 p1=4 p2=86935 p3=8
WAIT #1: nam='direct path read' ela= 39 p1=4 p2=86943 p3=8
WAIT #1: nam='direct path read' ela= 5 p1=4 p2=86951 p3=8
WAIT #1: nam='direct path read' ela= 38 p1=4 p2=86959 p3=8
...

  • O/S의 truss, tusc, trace, 또는strace를 이용하여 direct reads 혹은 direct writes 를 수행하는 UNIX 프로세스를 트레이스 한다. 오라클 9i에서 truss 리포트의 일부분으로 direct I/O 크기가 64K임을 알 수 있다.

9218/1: kaio(AIOWAIT, 0xFFBECE98) = 1
9218/1: lwp_cond_signal(0xFEB7BFA0) = 0
9218/3: pread64(256, "0602\0\001\0 ~13C19AEE }".., 65536, 0x0FC26000) = 65536
9218/1: lwp_cond_signal(0xFEB69FA0) = 0
9218/4: pread64(256, "0602\0\001\0 ~1BC19AEFE7".., 65536, 0x0FC36000) = 65536

  • 10357 트레이스 이벤트를 설정하여(예: alter session set events '10357 trace name context forever, level 1') direct I/O를 수행하는 세션에 대한 디버그 정보를 확인한다.

Unix process pid: 4375, image: oracle@kccdeds73 (P000)
*** SESSION ID:(9.18) 2004-02-08 21:47:01.908
DBA Range Initialized: length is 1570, start dba is 0100602b
kcblin: lbs=fc86c1cc flag=8 slot_cnt=32 slot_size=65536 state obj=24321224
kcblin: state objects are: Call=243a2210,Current Call=243a2210, Session=24321224
kdContigDbaDrCbk:starting from tsn 5
kdContigDbaDrCbk:starting from rdba 0100602b
kdContigDbaDrCbk:returning 1570 blocks
kcblrs:issuing read on slot : 0
kcbldio:lbs=fc86c1cc slt=fc86408c typ=0 async=1 afn=4 blk=602b cnt=8 buf=fc87fe00
kcblrs:issuing read on slot : 1
kcbldio:lbs=fc86c1cc slt=fc864210 typ=0 async=1 afn=4 blk=6033 cnt=8 buf=fc89fe00
kcblcio: lbs=fc86c1cc slt=fc86408c type=0 afn=4 blk=602b cnt=8 buf=fc87fe00
...

 

이전의 예제에서, 트레이스 파일은 쿼리 슬레이브(query slave)#0 의 수행 내역이다. direct path 오퍼레이션을 위해 32개의 I/O 슬롯이 가용하다(slot_cnt=32). 하나의 슬롯이 I/O 단위이며, 각각의 슬롯은 65536 bytes이다(slot_size=65536). 읽기 오퍼레이션 시 비동기식 I/O가 사용된다(async=1). 쿼리 슬레이브는 데이터파일 #4를 읽는다(afn=4). 읽어 들이는 블록개수는 8개이다(cnt=8). 블록크기는 8K이므로 65536 bytes를 전송한다.

 

이 경우, direct I/O 슬롯크기는 프로세스가 1 MB 전부를 사용하지 못하게 한다.
_DB_FILE_DIRECT_IO_COUNT 파라미터의 기본 설정값은 1 MB이다. 슬롯크기는 10351 이벤트를 사용하여 변경할 수 있다. 또한 슬롯의 개수도 10353 이벤트를 사용하여 변경할 수 있다. Caution : 앞에서 기술한 정보를 이용하여, 자신이 사용하는 시스템에 대한 direct I/O 의 처리량을 파악할 수 있다. 슬롯크기와 direct I/O 슬롯의 개수에 대한 기본 설정값을 쉽게 변경하지 마라. 변경 작업 전에 자신이 사용하는 하드웨어의 한계를 파악해야 하며, 어플리케이션과 SQL문을 최적화시키는 데 초점을 맞추어야 한다.

 

 

제공 : DB포탈사이트 DBguide.net

반응형
Posted by [PineTree]
ORACLE/TUNING2009. 2. 7. 22:51
반응형
Oracle 10g R2에서 사람들이 전혀 눈치채지 못한 큰 변화가 있었다.
흔히 MBRC라고 줄여서 부르는 db_file_multiblock_read_count 파리미터가 공식적으로 몰락한 것이다.
물론 여전히 사용 가능하지만, 그 내부 작동 방식으로 보아서 이 파라미터는 사실상 가치가 없어진 셈이다.

사실 이러한 조짐은 이전부터 있었다.
바로 System Statistics가 9i에서 소개된 것이다. 9i에서 System Statistics를 수집하면 다음과 같은 데이터가 sys.aux_stats$ 테이블에 저장된다.

SQL> SELECT * FROM sys.aux_stats$;
SNAME    PNAME    PVAL1    PVAL2
SYSSTATS_INFO    STATUS        COMPLETED
SYSSTATS_INFO    DSTART        12-30-2007 15:21
SYSSTATS_INFO    DSTOP        12-30-2007 15:21
SYSSTATS_INFO    FLAGS    1  
SYSSTATS_MAIN    SREADTIM    1         <-- Single Block Read Time
SYSSTATS_MAIN    MREADTIM    2        <-- Mulit Block Read Time
SYSSTATS_MAIN    CPUSPEED    500     <-- CPU Speed
SYSSTATS_MAIN    MBRC    8               <-- Multi Block Read Count
SYSSTATS_MAIN    MAXTHR    -1  
SYSSTATS_MAIN    SLAVETHR    -1

System Statistics가 없는 상황에서는 db_file_multiblock_read_count, optimizer_index_cost_adj와 같은 파라미터들이 Index Scan과 Table Scan중 어느 것을 선택할지를 결정하는데 큰 역할을 한다.

하지만, System Statistics가 수집된 경우에는?
위의 값들을 보면 알겠지만, System Statistics는 Index Scan과 Table Scan을 결정하는데 필요한 모든 중요한 정보들을 다 가지고 있다. 따라서 Oracle은 SREADTIME, MBREADTIM, CPUSPEED, MBRC 등의 정보를 이용해 비용을 계산한다.

그러면, System Statistics가 활성화되어 있으면 db_file_multiblock_read_count 파라미터는 무시되는 것인가? Optimizer에 의해 실행 계획이 생성될 때는 무시되지만 실제 Fetch과정에는 db_file_multiblock_read_count 크기만큼 Multi Block I/O를 한다.

즉, Optimizer에 의해서는 사용되지 않고 실제 쿼리를 실행하고 Fetch하는 단계에서만 사용된다.

이러한 원리는 Oracle 10g R1에서도 거의 비슷하다. Oracle 9i와 10g의 차이점은 System Statistics가 없을 때의 동작 방식이다. Oracle 10g에서는 System Statistics에 다음과 같은 항목이 추가되었다.

SNAME    PNAME    PVAL1    PVAL2
SYSSTATS_MAIN    CPUSPEEDNW    1845.90945194599  
SYSSTATS_MAIN    IOSEEKTIM    10  
SYSSTATS_MAIN    IOTFRSPEED    4096  

즉, 기본적인 CPU Speed와 IO Seek Time, IO Transfer Speed 값이 추가되었다. 이것을 흔히 "Noworkload" System Statistics라고 부른다. 말 그대로 Oracle이 하는 일과 무관하며 순수하게 Hardware로부터 취득한 정보라는 의미이다. Oracle은 Noworkload에 대해서는 적절한 기본값을 가지고 있으며, 필요하다면 이 값을 이용한다.

Oracle 10g R1은 System Statistics가 없으면 CPUSPEEDNW, IOSEEKTIM, IOTFRSPEED 값과 db_file_multiblock_read_count 값을 함께 이용해서 비용을 계산한다. 즉, db_file_multiblock_read_count 파라미터가 사용되기는 하되 9i와 같이 직접 사용되는 것이 아니라 Noworkload 통계 정보와 함께 조합되어서 사용된다.

하지만, Oracle 10g R2에서 또 한번의 변화가 생겼다. Oracle 10g R2에는 Multi Block I/O와 관련된 파라미터가 다음과 같이 세개로 늘어났다.

    * db_file_multiblock_read_count
    * _db_file_optimizer_read_count
    * _db_file_exec_read_count

즉, db_file_multiblock_read_count라는 파라미터가 Optimizer가 사용할 값(_db_file_optimizer_read_count)과 실행시에 사용할 값(_db_file_exec_read_count)으로 세분화된 것이다.
이 파라미터들의 사용방식은 다음과 같다.

1. _db_file_optimizer_read_count 파라미터는 System Statistics가 없는 경우에 Optimizer가 비용을 계산한기 위해 사용한다(앞서 설명한 바와 같이 Noworkload 통계값과 같이 사용). System Statistics가 수집된 경우에는 이 값은 무시된다.

2. _db_file_exec_read_count 파라미터는 쿼리를 실행하는 과정에서 Multi Block I/O를 수행할 때 한번에 읽을 블록수를 결정한다. 이 값은 System Statistics의 수집 여부와 무관하게 사용된다.

3. db_file_optimizer_read_count 파라미터값을 명시적으로 변경하면 _db_file_optimizer_read_count 값과 _db_file_exec_read_count 값이 모두 같이 변경된다.

전체적인 사용방식은 이전 버전과 동일하지만, db_file_multiblock_read_count 파라미터가 사실상 없어진 것과 마찬가지이며, 목적에 따라 명확하게 구분된 두 개의 히든 파라미터로 나뉘어졌다. 우리가 설사 db_file_multiblock_read_count 값을 지정하더라도 Oracle은 내부적으로 서로 다른 두 개의 히든 파라미터를 사용하는 셈이다.

언뜻 복잡해 보이는 위의 논의들은 사실은 다음과 같은 결론을 위한 것이다.

"System Statistics 정보를 잘 수집하자. 너무나 좋은 기능이다."

System Statistics 정보를 이용하면 기존에 Optimizer의 부족한 판단을 보완해주기 위해 db_file_multiblock_read_count, optimizer_index_cost_adj 같은 파라미터의 값을 변경해줄 수고가 줄어들며, 훨씬 정확한 정보에 기반한 비용 계산이 가능해진다. 더불어 db_file_multiblock_read_count 파라미터는 더 이상 Optimizer에게 영향을 주지 못하고 말 그대로 Multi Block I/O를 실제로 수행할 경우에만 사용되므로 훨씬 직관적이고 오해의 소지가 없는 셈이다.

많은 시스템들이 이 정보를 잘 사용하고 있지만, 특정 시스템은 아직 잘 모르거나 아니면 새로운 기능에 대한 두려움 때문에 사용하지 못하고 있을 것이다. 만일 사용하고 있지 않다면 반드시 테스트해보기 바란다.

Oracle 11g에서는 또 한번의 변화가 있는 것 같다. _db_file_optimizer_read_count 파라미터의 값이 10g R2에서는 8이다. Table Scan을 지나치게 선호하지 않도록 비교적 낮은 값을 지정한 것으로 보인다. 하지만 11g에서는 놀랍게도 기본값이 128로 지정되어 있다. 사용 방식이 바뀐 것인지...? 이 부분은 추후에 테스트가 필요할 것이다.

출처 : http://ukja.tistory.com/85
반응형
Posted by [PineTree]