여기서는 DBMS의 고가용성(HA)을 보장해주는 여러 제품군들 가운데 Oracle RAC를 살펴보기로 한다. Oracle RAC의 특징 중 하나는 Load Balancing과 FailOver 기능으로, 이중에서도 RAC를 이용하는 가장 큰 이유는 바로 Application FailOver 기능 때문일 것이다. Application FailOver는 하나의 노드에 장애가 발생했을 때 다른 살아 있는 노드에서 이 Application의 서비스를 대신해주는 것으로, 실제 사용자는 서비스 중이던 노드에 장애가 일어났는지를 감지하지 못하고 정상적으로 서비스를 이용할 수 있는 기능을 의미한다.

 Oracle RAC에서 Fail-Over를 가능하게 해주는 것이 Oracle 9i부터 소개된 방법인 CTF(Connection Time Failover)와 TAF(Transparent Application Failover)이다. CTF는 클라이언트가 해당 데이터베이스로 접속을 시도했으나, 해당 서버에 장애가 발생하여 접속하지 못할 경우 살아 있는 다른 서버로 접속할 수 있게 해주는 기능이고 TAF는 클라이언트가 RAC에서 어느 한 노드에 접속하여 작업하던 도중 장애가 발생했을 경우 살아 있는 다른 노드에서 수행 중이던 작업을 계속할 수 있게 해주는 기능이다. 먼저 Oracle RAC의 Load Balancing 기능을 먼저 살펴보자.

 Client-Side Connect-Time Load Balancing

 Client-Side Connect-Time Load Balancing의 특징은 이용 가능한 Listener 리스트 중에서 랜덤(random)하게 접속 요청을 하는 것이다. 이는 클라이언트의 tnsnames.ora에 “LOAD_ BALANCE=ON”을 설정함으로써 가능하다.

-- TNSNAMES.ORA ghlee = (DESCRIPTION = (LOAD_BALANCE=ON) (ADDRESS = (PROTOCOL = TCP)(HOST = 192.128.25.18)(PORT = 1521)) #ghlee1 (ADDRESS = (PROTOCOL = TCP)(HOST = 192.128.25.19)(PORT = 1521)) #ghlee2 (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = GHLEE))) -- 첫 번째 접속 시도 [/oracle/product/10.2.0/network/admin> sqlplus scott/tiger@ghlee SCOTT@ghlee1 >@in.sql INSTANCE_NUMBER INSTANCE_NAME HOST_NAME -------------- -------------- -------------- 1 ghlee1 ghlee1 -- 두 번째 접속 시도 [/oracle/product/10.2.0/network/admin> sqlplus scott/tiger@ghlee INSTANCE_NUMBER INSTANCE_NAME HOST_NAME --------------- --------------- -------------- 1 ghlee1 ghlee1 -- 세 번째 접속 시도 [/oracle/product/10.2.0/network/admin> sqlplus scott/tiger@ghlee INSTANCE_NUMBER INSTANCE_NAME HOST_NAME --------------- --------------- -------------- 1 ghlee1 ghlee1 -- 네 번째 접속 시도 [/oracle/product/10.2.0/network/admin> sqlplus scott/tiger@ghlee INSTANCE_NUMBER INSTANCE_NAME HOST_NAME --------------- --------------- -------------- 2 ghlee2 ghlee2 -- 다섯 번째 접속 시도 [/oracle/product/10.2.0/network/admin> sqlplus scott/tiger@ghlee INSTANCE_NUMBER INSTANCE_NAME HOST_NAME --------------- --------------- -------------- 1 ghlee2 ghlee2

 위의 결과에서 볼 수 있는 것처럼 랜덤하게 접속을 시도한다. 만약 “LOAD_BALANCE=OFF”로 설정되어 있다면 List에 명시된 처음 Address (HOST = 192.128.25.18)로 먼저 접속을 시도하고 응답이 없을 경우 TCP TIMEOUT까지 대기한 후 다음 Address list (HOST = 192.128.25.19 )로 접근을 시도한다.

 Server-Side Connect-Time Load Balancing

 Server-Side Connect-Time Load Balancing이라는 것은 서버 측에 설정하는 것으로서 동작 방식은 다음과 같다.

<그림 1> Server-Side Connect-Time Load Balancing

 PMON Process가 Listener에 Performance 정보와 함께 Service를 동적으로 등록한다. <표 1>과 같은 정보들이 주기적으로 기록된다.

<표 1> 주기적으로 기록된 정보

  클라이언트가 Connection을 요청하면, 클라이언트의 tnsnames.ora 파일에 기술된 Protocol address 중에 랜덤으로 하나의 접속에 성공한다. 그 Listener가 ghlee1의 Listener이다. ghlee1의 Listener는 ghlee1과 ghlee2 instance의 load의 양을 비교한다. 여기서는 ghlee2의 load의 양이 더 적으므로 ghlee2를 선택한다.

  Ghlee1의 Listener는 클라이언트에게 ghlee2의 Listener로 connection하도록 redirect 한다.

  클라이언트는 ghlee2의 Listener로 접속한다. Listener는 server process를 생성하고 Listener로부터 connection 요청을 상속받아 이제부터는 Listener를 거치지 않고 바로 클라이언트와 통신한다.

이와 같은 방식으로 실행하기 위해서는 ghlee1의 Listener에 ghlee2의 Node의 상태 정보나, Service의 정보들이 PMON에 의해 Listener에 주기적으로 갱신해줘야 하는데 이를 위해서는 Instance가 Listener에 자동으로 등록되는 방식을 사용해야 한다. 이를 Dynamic Register 방식이라 하고, 이를 위해서는 몇 가지 설정이 필요하다.

 Dynamic Register

 8i 이전까지는 Database Instance의 정보를 listener.ora 파일에 수동으로 기술했지만, 8i 이후부터는 스스로 Listener에 등록할 수 있다. PMON process가 현재의 상태와 Instance의 load 정보를 Listener에게 보내는 방식이다. 8i 이전까지는 아래와 같이 listener.ora 파일에 Instance에 대한 사항을 기술해주었다.

-- LISTENER.ORA LISTENER_GHLEE1 = (DESCRIPTION_LIST = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = 192.128.25.18)(PORT = 1521) )#ghlee1_vip (ADDRESS = (PROTOCOL = TCP)(HOST = 192.128.25.30)(PORT = 1521) ) #ghlee1_real ip )) SID_LIST_LISTENER_GHLEE1 = (SID_LIST = (SID_DESC = (SID_NAME = GHLEE1) (ORACLE_HOME = /oracle/product/10.2.0))) -- Parameter들의 값 GHLEE1@SQL> show parameter listener NAME TYPE VALUE ----------------------------------- ----------- local_listener string remote_listener string SQL> show parameter service NAME TYPE VALUE ----------------------------------- ----------- service_names SQL> show parameter db_name NAME TYPE VALUE ----------------------------------- ----------- ------ db_name string GHLEE SQL> show parameter domaine_name &#52091; 설정되어 있지 않음 -- Listener의 Service 상태 정보 [/oracle/product/10.2.0/network/admin> lsnrctl services Service "GHLEE" has 1 instance(s). Instance "GHLEE1", status READY, has 1 handler(s) for this service... Handler(s): "DEDICATED" established:6 refused:0 state:ready LOCAL SERVER Service "GHLEE1" has 1 instance(s). Instance "GHLEE1", status UNKNOWN, has 1 handler(s) for this service... Handler(s): "DEDICATED" established:0 refused:0 LOCAL SERVER

 위의 정보에서 보면 지금 Listener에는 “GHLEE”와 “GHLEE1”이라는 두 개의 서비스가 등록되어 있고, 각 서비스는 “GHLEE1” Instance에 등록되어 있음을 확인할 수 있다. 그리고 “GHLEE” 서비스는 상태 정보가 “READY”인 반면, “GHLEE1” 서비스의 상태 정보는 “UNKNOWN”임을 확인할 수 있다. “GHLEE”라는 서비스는 동적으로 등록된 서비스이므로 PMON에 의해 상태를 지속적으로 Listener에게 업데이트해줌으로써 상태 정보를 확인할 수 있어 “READY”라고 보이며, GHLEE1이라는 서버는 위의 listener.ora 파일에서 기술한 정보이므로 동적으로 등록되지 않아 상태 정보를 확인할 수 없어 “UNKNOWN”이라고 보이는 것이다. Dynamic Register의 구성 방법은 다음과 같다.

 ● Service Registration 구성
이것은 초기화 파라미터 중 “SERVICE_NAMES” 파라미터에 값을 설정함으로써 가능하다. 이 이름이 바로 Listener에 등록하는 서비스 이름으로 Default 이름은 DB Install 시에 설정한 dbname.domain이다. 여기서는 SERIVCE_NAMES 파라미터를 설정하지 않았어도, dbname.domain에 의해 서비스 이름이 “GHLEE”가 되었다.

 ● Default Local Listener Registration 방법
Default로 PMON process는 TCP/IP Port 1521을 사용하는 local Listener에 자동으로 서비스 정보를 등록한다. 위의 예제에서는 Default Port를 사용하고 dbname.domain에 의해서 “GHLEE”라는 서비스가 자동으로 등록되었다.

 ● NON-Default Listener Registration 방법
PMON에 의해 Local Listener에 등록을 원하고 TCP/IP가 Port 1521을 사용하지 않을 경우는 다음과 같이 LOCAL_ LISTENER PARAMETER를 설정하면 된다.

 LOCAL_LISTENER = <listener_alias> → Parameter file 기술
Listener_alias =
(DESCRIPTION= (ADDRESS=(PROTOCOL=TCP_(HOST= node1_vip)(PORT=1524))
→ 서버의 tnsnames.ora 파일에 기술
or
LOCAL_LISTENER =“(ADDRESS=(PROTOCOL=TCP)(HOST =node1_vip)(PORT=1524))” → Parameter 변경

 ● Remote Listener Registration 방법
PMON process에 의해 remote Listener를 등록하고 싶다면, 다음과 같이 REMOTE_LISTENER 파라미터를 설정해야 한다.

 REMOTE_LISTENER=< listener_alias> → Parameter file 기술
Listener_alias =
(DESCRIPTION= (ADDRESS=(PROTOCOL=TCP_(HOST =node2_vip)(PORT=1521))
or
REMOTE_LISTENER=“(ADDRESS=(PROTOCOL=TCP_(HOST =node2_vip)PORT=1521))”

 위와 같이 등록했을 때 LISTENER SERVICES를 확인하면 다음과 같다.

-- PMON에 의해 자동으로 등록하는 방법을 이용하기 위해 listener.ora 파일에 SID 정보를 기술하지 않는다. GHLEE2에도 다음의 상황처럼 기술한다. LISTENER_GHLEE1 = (DESCRIPTION_LIST = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = 192.128.25.18)(PORT = 1521))#ghlee1_vip (ADDRESS = (PROTOCOL = TCP)(HOST = 192.128.25.30)(PORT = 1521))#ghlee1_real ip )) -- service_names에 ghlee, ghlee1 서비스를 설정한다. SQL> alter system set service_names='GHLEE', 'GHLEE1' scope=BOTH sid='GHLEE1'; -- remote_listener에 ghlee2의 vip와 port 등을 설정한다. SQL> alter system set remote_listener='(address= (protocol=tcp)(host=192.128.25.19)(PORT=1521)' scope=BOTH sid='GHLEE1'; -- Listener Service의 정보 확인. 위의 Parameter 값들의 설정으로 인해 PMON에 의해서 자동으로 등록된 것을 확인할 수 있다. [ghlee1@/oracle/product/10.2.0/network/admin> lsnrctl services Service "GHLEE" has 2 instance(s). Instance "GHLEE1", status READY, has 1 handler(s) for this service... Handler(s): "DEDICATED" established:0 refused:0 state:ready LOCAL SERVER Instance "GHLEE2", status READY, has 1 handler(s) for this service... Handler(s): "DEDICATED" established:0 refused:0 state:ready REMOTE SERVER (ADDRESS=(PROTOCOL=TCP)(HOST=GHLEE2-vip)(PORT=1521)) Service "GHLEE1" has 1 instance(s). Instance "GHLEE1", status READY, has 1 handler(s) for this service... Handler(s): "DEDICATED" established:0 refused:0 state:ready LOCAL SERVER Service "GHLEE2" has 1 instance(s). Instance "GHLEE2", status READY, has 1 handler(s) for this service... Handler(s): "DEDICATED" established:0 refused:0 state:ready REMOTE SERVER (ADDRESS=(PROTOCOL=TCP)(HOST=GHLEE2-vip)(PORT=1521))

위와 같이 Server에 Dynamic Register 방식으로 구성하게 되면 각 Listener는 Server의 상태 정보를 가지고 있어 좀 더 Idle한 서버에서 클라이언트의 요청을 받을 수 있는 Load Balancing을 구현할 수 있다. 이제부터 Oracle RAC의 강력한 기능인 Fail-Over에 대해 살펴보도록 하겠다.

 Fail-Over

 CTF는 앞에서 언급했던 것처럼, connection할 당시 해당 서버에 장애가 발생했을 경우 이용 가능한 서버로 connection을 요청하는 방법으로 클라이언트의 tnsnames.ora 파일에 아래와 같이 설정하면 된다.

-- 클라이언트의 TNSNAMES.ORA ghlee = (DESCRIPTION = (LOAD_BALANCE=OFF)(LOAD_BALANCE=ON) (ADDRESS = (PROTOCOL = TCP)(HOST = 192.128.25.18)(PORT = 1521)) (ADDRESS = (PROTOCOL = TCP)(HOST = 192.128.25.19)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = GHLEE))) -- 10g RAC부터는 VIP를 사용하는데, 만약 NODE1이 DOWN되면 VIP NODE2가 가지고 감으로써 NODE2의 Listener에서 node1_vip, node2_vip를 둘 다 Listening한다. 그러므로 TCP TIMEOUT까지 대기할 필요가 없다

 TAF는 서버에 접속하여 작업 중일 때 작업 중이던 데이터베이스에 문제가 발생했을 경우에 이용 가능한 다른 Node에서 진행 중이던 작업을 계속 해주는 것으로서, 엔드유저는 이 데이터베이스에 장애를 감지하지 못하는 Fail-Over 방식이다. TAF의 Mode에는 현재 접속 중이던 Session을 Fail-Over시키는 Session 방식과 실행 중이던 Select 문을 다른 Node에서 장애가 발생한 시점 이후부터 다시 Select를 실행해주는 Select 방식이 있다.

 Fail-Over Method에는 Session이 맺어 있던 Node를 이용할 수 없을 경우 Oracle이 자동으로 살아있는 Instance 쪽으로 다시 접속하는 BASIC 방식과 Instance에 미리 Session을 접속해 Fail-Over 시에 세션을 새로 맺는 오버헤드를 줄이는 방식인 Preconnect 방식이 존재한다. 설정 방법은 다음과 같다.

 1. TYPE=SELECT, METHOD=BASIC

-- 클라이언트의 TNSNAMES.ORA test_basic1 = (DESCRIPTION = (FAILOVER=ON) (LOAD_BALANCE=ON) (ADDRESS = (PROTOCOL = TCP)(HOST =192.128.25.18)(PORT = 1521)) (ADDRESS = (PROTOCOL = TCP)(HOST =192.128.25.19)(PORT = 1521)) (CONNECT_DATA = (SERVICE_NAME = GHLEE) (FAILOVER_MODE = (TYPE=SELECT) (METHOD=BASIC)))) -- Session접속 [/oracle/product/10.2.0/network/admin>sqlplus scott/tiger@test_basic1 SQL>@se INST_ID USERNAME SID SERIAL# FAILOVER_M FAILOVER_TYPE FAI SERVICE_NAME ---------- --------------- ------- ---------- ---------- 1 SCOTT 2162 13814 BASIC SELECT NO GHLEE -- 접속한 Session에서 Long Query 실행 SQL> select * from t1; -- instance kill(접속된 Instance인 ghlee1을 Kill한다) [ghlee1@/oracle/product/10.2.0/network/admin>srvctl stop instance -d ghlee -i ghlee1 -o abort Sun Apr 12 10:30:55 KST 2009 -- 위의 접속되어 있는 Session의 상태는 INSTANCE가 죽을 당시 잠시 멈춰 있다가 다시 실행되는 현상을 확인할 수 있다. FAILED_OVER Column의 상태를 보면 “YES”로 되어서 Fail-Over되었음을 알 수 있다. SQL> @se INST_ID USERNAME SID SERIAL# FAILOVER_M FAILOVER_TYPE FAI SERVICE_NAME ---------- --------------- ------- ---------- ---------- 2 SCOTT 2177 10 BASIC SELECT YES GHLEE

 

<표 2> 결과 정보

 2. TYPE=SELECT, METHOD=PRECONNECT

-- 클라이언트의 TNSNAMES.ORA. Preconnect 방식을 이용할 때는 반드시 BACKUP을 명시해야 한다. test_preconn1 = (DESCRIPTION = (FAILOVER=ON) (LOAD_BALANCE=ON) (ADDRESS = (PROTOCOL = TCP)(HOST = 192.128.25.18)(PORT = 1521)) (ADDRESS = (PROTOCOL = TCP)(HOST = 192.128.25.19)(PORT = 1521)) (CONNECT_DATA = (SERVICE_NAME = CAFE4DB) (FAILOVER_MODE = (TYPE=SELECT) (METHOD=PRECONNECT) (BACKUP=PRECONNECT2) ))) PRECONNECT2 = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = 192.128.25.19)(PORT = 1521)) (CONNECT_DATA = (service_name = GHLEE) (instance_name = GHLEE2) ) ) -- Session 접속 (미리 2개의 Session이 연결되어 있는 것을 확인할 수 있다) [/oracle/product/10.2.0/network/admin>sqlplus scott/tiger@test_preconn1 SQL>@se INST_ID USERNAME SID SERIAL# FAILOVER_M FAILOVER_TYPE FAI SERVICE_NAME ---------- --------------- ------- ---------- ---------- 1 SCOTT 2162 13814 PRECONNECT SELECT NO GHLEE 2 SCOTT 1065 10351 NONE NONE NO GHLEE --접속한 Session에서 Long Query 실행 SQL> select * from t1; -- instance kill(접속된 Instance인 ghlee1을 Kill한다) [ghlee1@/oracle/product/10.2.0/network/admin>srvctl stop instance -d ghlee -i ghlee1 -o abort Sun Apr 12 10:35:25 KST 2009 -- 위의 접속되어 있는 Session의 상태는 INSTANCE가 죽을 당시 잠시 멈춰 있다가 다시 실행되는 현상을 확인할 수 있다. FAILED_OVER Column의 상태를 보면 "YES"로 되어서 Fail-Over되었다는 것을 알 수 있다. SQL> @se INST_ID USERNAME SID SERIAL# FAILOVER_M FAILOVER_TYPE FAI SERVICE_NAME ---------- --------------- ------- ---------- ---------- 2 SCOTT 1065 10351 PRECONNECT YES NO GHLEE

 

<표 3> 결과 정보

 3. TYPE=SESSION, METHOD=BASIC

-- 클라이언트의 TNSNAMES.ORA test_basic2 = (DESCRIPTION = (FAILOVER=ON) (LOAD_BALANCE=ON) (ADDRESS = (PROTOCOL = TCP)(HOST = 192.128.25.18)(PORT = 1521)) (ADDRESS = (PROTOCOL = TCP)(HOST = 192.128.25.19)(PORT = 1521)) (CONNECT_DATA = (SERVICE_NAME = CAFE4DB) (FAILOVER_MODE = (TYPE=SESSION) (METHOD=BASIC)))) -- Session 접속 [/oracle/product/10.2.0/network/admin>sqlplus scott/tiger@test_basic2 SQL>@se INST_ID USERNAME SID SERIAL# FAILOVER_M FAILOVER_TYPE FAI SERVICE_NAME ---------- --------------- ------- ---------- ---------- 1 SCOTT 1050 1822 BASIC NO GHLEE -- 접속한 Session에서 Long Query 실행 SQL> select * from t1; -- instance kill(접속된 Instance인 ghlee1을 Kill한다) [ghlee1@/oracle/product/10.2.0/network/admin>srvctl stop instance -d ghlee -i ghlee1 -o abort Sun Apr 12 10:40:01 KST 2009 -- 이 방식은 Session만 보장하는 방식으로 실행되고 있던 Select는 "ora-25401 : can not continue fetches"라는 Error를 뿌리면서 종료된다. SQL> @se INST_ID USERNAME SID SERIAL# FAILOVER_M FAILOVER_TYPE FAI SERVICE_NAME ---------- --------------- ------- ---------- ---------- 2 SCOTT 1023 10231 BASIC YES GHLEE

 

<표 4> 결과 정보

 4. TYPE=SESSION, METHOD=PRECONNECT

-- 클라이언트의 TNSNAMES.ORA test_preconn2 = (DESCRIPTION = (FAILOVER=ON) (LOAD_BALANCE=ON) (ADDRESS = (PROTOCOL = TCP)(HOST = 192.128.25.18)(PORT = 1521)) (ADDRESS = (PROTOCOL = TCP)(HOST = 192.128.25.19)(PORT = 1521)) (CONNECT_DATA = (SERVICE_NAME = CAFE4DB) (FAILOVER_MODE = (TYPE=SESSION) (METHOD=PRECONNECT) (BACKUP=PRECONNECT2) ))) PRECONNECT2 = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = 192.128.25.19)(PORT = 1521)) (CONNECT_DATA = (service_name = GHLEE) (instance_name = GHLEE2))) -- Session 접속 (미리 2개의 Session이 연결되어 있는 것을 확인할 수 있다) [/oracle/product/10.2.0/network/admin>sqlplus scott/tiger@test_preconn2 SQL>@se INST_ID USERNAME SID SERIAL# FAILOVER_M FAILOVER_TYPE FAI SERVICE_NAME ---------- --------------- ------- ---------- ---------- 1 SCOTT 1098 143 PRECONNECT SESSION NO GHLEE 2 SCOTT 1023 159 NONE NONE NO GHLEE -- 접속한 Session에서 Long Query 실행 SQL> select * from t1; -- instance kill(접속된 Instance인 ghlee1을 Kill한다) [ghlee1@/oracle/product/10.2.0/network/admin>srvctl stop instance -d ghlee -i ghlee1 -o abort Sun Apr 12 10:45:23 KST 2009 -- Instance에 장애가 발생함과 동시에 "ora-25401 : can not continue fetches"라는 Error를 뿌리면서 종료된다. SQL> @se INST_ID USERNAME SID SERIAL# FAILOVER_M FAILOVER_TYPE FAI SERVICE_NAME ---------- --------------- ------- ---------- ---------- 2 SCOTT 1023 159 PRECONNECT SESSION YES GHLEE

 

<표 5> 결과 정보

 아쉬운 점은 Instance가 종료된 이후 Node가 다시 정상적으로 이용 가능하다고 하더라도 RAC 내에서 자동으로 Fail-Over된 Session을 다시 원래의 Node로 돌려주는 기능은 아직 구현되어 있지 않다는 것이다. DBA가 원래 Node의 Instance로 돌려주어야 한다.

exec dbms_service.disconnect_session(service_name); or ALTER SYSTEM DISCONNECT SESSION 'sid, serial' POST_TRANSACTION;

 

본문에 사용한 스크립트

in.sql
col host_name format a15
col INSTANCE_NAME format a15
select INSTANCE_NUMBER,INSTANCE_NAME,HOST_NAME from v$instance;

se.sql
col service_name format a15
select instance_id, username, sid, serial#, failover_method, failover_type, failed_over, service_name
from gv$session
where program='sqlplus.exe'

OLTP 환경에서는 수행 시간이 긴 Select 문은 거의 존재하지 않으므로 보통은 Session 방식으로 설정하여 사용하며, DW 환경과 같이 수행시간이 긴 Select 문에서는 Select 방식을 많이 선호한다. 아쉽게도 DML은 아직 지원되고 있지 않다. Preconnect 방식은 오버헤드는 줄여줄 수 있지만 하나의 Session을 맺을 때 미리 하나의 Session을 더 맺어두는 방식으로 한번 연결에 총 두 개의 Session을 맺으므로 시스템에 Idle한 Server Process가 하나 더 생성되는 방식이므로 리소스의 낭비를 초래할 수 있다. 각자의 환경에 맞게 Fail-Over 환경을 구성함으로써 효율적인 HA를 구성해 사용하길 바란다.

참고자료
1. Oracle Net Services Administrator's Guide
2. http://metalink.oracle.com

'Database > Oracle' 카테고리의 다른 글

Admin Workshop 1 - 구조 10g  (0) 2009.12.30
Query 실행 과정 10g  (1) 2009.12.30
Backup and Recover  (0) 2009.12.30
Admin Workshop 1 - 구조  (0) 2009.12.15
Query 실행 과정  (1) 2009.12.15

Controlfile Recovery Requires RESETLOGS

In response to a small discussion on the oracle-l mailing list last week I thought I’d put together a quick demo of exactly what I was referring to in my email. Basically I was discussing how even when you do a normal shutdown – leaving your database in a consistent state that doesn’t require recovery – if you lose your controlfiles then you still will need to execute the “recovery” commands and then open resetlogs.

Here’s the test, which I did on one of our lab systems…

Setup

nap01:~/oradata/jt10g$ sqlplus / as sysdba

SQL*Plus: Release 10.2.0.1.0 - Production on Wed May 9 12:55:07 2007

Copyright (c) 1982, 2005, Oracle.  All rights reserved.

Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit Production
With the Partitioning, OLAP and Data Mining options

SQL> col member format a40
SQL> select l.status, member
  2  from v$logfile inner join v$log l using (group#);

STATUS           MEMBER
---------------- ----------------------------------------
INACTIVE         /u04/oracle/oradata/jt10g/redo03.log
CURRENT          /u04/oracle/oradata/jt10g/redo02.log
INACTIVE         /u04/oracle/oradata/jt10g/redo01.log

SQL> alter database backup controlfile
  2  to '/u04/oracle/oradata/jt10g/backup.ctl';

Database altered.

SQL> alter system switch logfile;

System altered.

SQL> shutdown immediate;
Database closed.
Database dismounted.
ORACLE instance shut down.
  1. We have a valid backup of the controlfile.

  2. I issued a logfile switch since Joel Patterson specifically asked about it in the email discussion.

  3. The database had a clean, normal shutdown.

Now let’s simulate the loss of controlfiles only and walk through the recovery:

nap01:~/oradata/jt10g$ rm control*.ctl
nap01:~/oradata/jt10g$ sqlplus / as sysdba

SQL> startup
ORACLE instance started.

Total System Global Area  629145600 bytes
Fixed Size                  1980712 bytes
Variable Size             180356824 bytes
Database Buffers          440401920 bytes
Redo Buffers                6406144 bytes
ORA-00205: error in identifying control file, check alert log for more info

SQL> shutdown abort;
ORACLE instance shut down.

Recovery

At this point we have lost all of our control files. Rather than just do the RESETLOGS right away, first we’ll try one or two other ideas.

First let’s just restore the backup controlfile and try to open the database with no recovery at all.

nap01:~/oradata/jt10g$ cp backup.ctl control01.ctl
nap01:~/oradata/jt10g$ cp backup.ctl control02.ctl
nap01:~/oradata/jt10g$ cp backup.ctl control03.ctl
nap01:~/oradata/jt10g$ sqlplus / as sysdba

SQL*Plus: Release 10.2.0.1.0 - Production on Wed May 9 13:22:24 2007

Copyright (c) 1982, 2005, Oracle.  All rights reserved.

Connected to an idle instance.

SQL> startup
ORACLE instance started.

Total System Global Area  629145600 bytes
Fixed Size                  1980712 bytes
Variable Size             180356824 bytes
Database Buffers          440401920 bytes
Redo Buffers                6406144 bytes
Database mounted.
ORA-01589: must use RESETLOGS or NORESETLOGS option for database open

SQL>

Interesting. We know that the database itself doesn’t need recovery; in fact we just have a controlfile that’s less than five minutes out of date. So let’s just try opening NORESETLOGS.

SQL> alter database open noresetlogs;
alter database open noresetlogs
*
ERROR at line 1:
ORA-01610: recovery using the BACKUP CONTROLFILE option must be done

Oracle requires us to run the recovery command even though we had a clean shutdown. Huh? There are no archived logs to apply! Well I guess we can start by doing a CANCEL-based recovery and just canceling right away (since there are no archived logs to apply).

SQL> recover database using backup controlfile until cancel;
ORA-00279: change 775140 generated at 05/09/2007 07:00:38 needed for thread 1
ORA-00289: suggestion :
/u04/oracle/product/10.2.0/db_1/dbs/arch1_5_621627183.dbf
ORA-00280: change 775140 for thread 1 is in sequence #5

Specify log: {<RET>=suggested | filename | AUTO | CANCEL}
CANCEL
ORA-01547: warning: RECOVER succeeded but OPEN RESETLOGS would get error below
ORA-01152: file 1 was not restored from a sufficiently old backup
ORA-01110: data file 1: '/u04/oracle/oradata/jt10g/system01.dbf'

ORA-01112: media recovery not started

Well it claims that we can’t open the database but let’s just give it a try.

SQL> alter database open;
alter database open
*
ERROR at line 1:
ORA-01589: must use RESETLOGS or NORESETLOGS option for database open

SQL> alter database open noresetlogs;
alter database open noresetlogs
*
ERROR at line 1:
ORA-01588: must use RESETLOGS option for database open

SQL> alter database open resetlogs;
alter database open resetlogs
*
ERROR at line 1:
ORA-01152: file 1 was not restored from a sufficiently old backup
ORA-01110: data file 1: '/u04/oracle/oradata/jt10g/system01.dbf'

There you are. Looks like Oracle requires a RESETLOGS once you’ve even started that recovery process with the backup control file. This appears to be a rare case where you can do a complete recovery but Oracle still requires a RESETLOGS.

But even the RESETLOGS won’t work yet – we need to do some recovery first.

Things To Look Out For and Secret DBA Tricks

Well let’s finish up. First off, Oracle actually needs to look at the ONLINE logs. By the way, there’s one thing to be careful of here… check it out:

SQL> col member format a40
SQL> select l.status, member
  2  from v$logfile inner join v$log l using (group#);

STATUS           MEMBER
---------------- ----------------------------------------
INACTIVE         /u04/oracle/oradata/jt10g/redo01.log
CURRENT          /u04/oracle/oradata/jt10g/redo02.log
INACTIVE         /u04/oracle/oradata/jt10g/redo03.log

That information is actually WRONG. Why? Because we did a log switch right before we shutdown. This view is showing you the BACKUP controlfile. So the file redo02.log was CURRENT when we took the backup. However the file redo03.log is actually the one we want. This view will also have old values for log sequence numbers… so just be careful.

Last Monday (when we were originally doing this at our “tech night”) another guy at IT Convergence showed us another great Secret DBA Trick™ to confirm what file you need. Look above and note that Oracle will tell you exactly what sequence number it’s looking for. You can check the sequence number for any logfile on unix like this:

nap01:~/oradata/jt10g$ strings redo01.log|head -3
z{|}
JT10G
Thread 0001, Seq# 0000000004, SCN 0x0000000b05b5-0x0000000bd34f
nap01:~/oradata/jt10g$ strings redo02.log|head -3
z{|}
JT10G
Thread 0001, Seq# 0000000005, SCN 0x0000000bd34f-0x0000000bf612
nap01:~/oradata/jt10g$ strings redo03.log|head -3
z{|}
JT10G
Thread 0001, Seq# 0000000006, SCN 0x0000000bf612-0xffffffffffff

So you can see that Oracle is required to do manual recovery using an online log. Usually Oracle does this automatically – but in this specific scenario you have to do it manually!

In the spirit of experimentation we’ll first apply just log sequence 5 and try to open. If that doesn’t work then we’ll try log sequence 6. We will also still try to avoid the RESETLOGS if possible.

nap01:~/oradata/jt10g$ sqlplus / as sysdba

SQL> recover database using backup controlfile until cancel;
ORA-00279: change 775140 generated at 05/09/2007 07:00:38 needed for thread 1
ORA-00289: suggestion :
/u04/oracle/product/10.2.0/db_1/dbs/arch1_5_621627183.dbf
ORA-00280: change 775140 for thread 1 is in sequence #5

Specify log: {<RET>=suggested | filename | AUTO | CANCEL}
/u04/oracle/oradata/jt10g/redo02.log
ORA-00279: change 783890 generated at 05/09/2007 13:11:59 needed for thread 1
ORA-00289: suggestion :
/u04/oracle/product/10.2.0/db_1/dbs/arch1_6_621627183.dbf
ORA-00280: change 783890 for thread 1 is in sequence #6
ORA-00278: log file '/u04/oracle/oradata/jt10g/redo02.log' no longer needed for
this recovery

Specify log: {<RET>=suggested | filename | AUTO | CANCEL}
CANCEL
ORA-01547: warning: RECOVER succeeded but OPEN RESETLOGS would get error below
ORA-01152: file 1 was not restored from a sufficiently old backup
ORA-01110: data file 1: '/u04/oracle/oradata/jt10g/system01.dbf'

ORA-01112: media recovery not started

Looks like we still need to apply the last log.

SQL> recover database using backup controlfile;
ORA-00279: change 783890 generated at 05/09/2007 13:11:59 needed for thread 1
ORA-00289: suggestion :
/u04/oracle/product/10.2.0/db_1/dbs/arch1_6_621627183.dbf
ORA-00280: change 783890 for thread 1 is in sequence #6

Specify log: {<RET>=suggested | filename | AUTO | CANCEL}
/u04/oracle/oradata/jt10g/redo03.log
Log applied.
Media recovery complete.
SQL> alter database open noresetlogs;
alter database open noresetlogs
*
ERROR at line 1:
ORA-01588: must use RESETLOGS option for database open

Well there you have it. RESETLOGS is required.

SQL> alter database open resetlogs;

Database altered.

And after all of that work… we’re back online! New incarnation, but no data loss. And a neat new trick for checking logfile sequence numbers.

'Database > Oracle' 카테고리의 다른 글

Query 실행 과정 10g  (1) 2009.12.30
10g RAC의 Load Balancing과 Failover  (1) 2009.12.30
Admin Workshop 1 - 구조  (0) 2009.12.15
Query 실행 과정  (1) 2009.12.15
힌트 종류  (0) 2009.12.15
DBMS별 날짜 포멧
Oracle - MS SQL - DB2 UDB 의 서로 다른 날짜 형식을 맞추기위한 SQL문

--------------------------------------------------------------------------------
DBMS 별 시간, 날짜 조회 쿼리
--------------------------------------------------------------------------------
1. Oracle
- 날짜+시분초 까지 조회가능
select sysdate from dual;

- 날짜+밀리초+시간존 까지 조회
select current_timestamp from dual;

2. MS SQL
- 날짜 + 밀리초 단위까지 조회가능
select getdate();

3. DB2 UDB
- 날짜+밀리초까지 조회 가능
select current timestamp from sysibm.sysdummy1;
- 날짜만 조회
select current date from sysibm.sysdummy1;
- 밀리초 단위의 시간만 조회
select current time from sysibm.sysdummy1;

--------------------------------------------------------------------------------
DBMS 별 default date format
--------------------------------------------------------------------------------
1. Oracle
한글 : YYYY/MM/DD                       영어 : DD-MON-YYYY

2. MS SQL
한글 :  YYYY/MM/DD HH:MI:SS      영어 : MM-DD-YYYY HH:MI:SS

3. DB2 UDB
TIMESTAMP 타입 : YYYY-MM-DD-HH:MI:SS.MMMMMM
DATE 타입 : YYYY-MM-DD

TIME 타입 : HH:MI:SS.MMMMMM

--------------------------------------------------------------------------------
날짜 포맷 변환
--------------------------------------------------------------------------------
[ 형식 : 'YYYY.MM.DD' ]
1. Oracle : TO_CHAR(date_exp, 'YYYY.MM.DD')
2. MSSQL : CONVERT(VARCHAR, date_exp, 102)
3. DB2 : REPLACE(CHAR(DATE(date_exp),ISO), '-', '.')

[ 형식 : 'HH:MI:SS' ]
1. Oracle : TO_CHAR(date_exp, 'HH:MI:SS')
2. MSSQL : CONVERT(VARCHAR, date_exp, 108)
3. DB2 : CHAR(TIME(date_exp) , JIS )

[ 형식 : 'YYYY/MM/DD' ]
1. Oracle : TO_CHAR(date_exp, 'YYYY/MM/DD')
2. MSSQL : CONVERT(VARCHAR, date_exp, 111)
3. DB2 : REPLACE(CHAR(DATE(date_exp), ISO), '-', '/')

[ 형식 : 'YYYYMMDD' ]
1. Oracle : TO_CHAR(date_exp, 'YYYYMMDD')
2. MSSQL : CONVERT(VARCHAR, date_exp, 112)
3. DB2 : CHAR(DATE(date_exp))

[ 형식 : 'HH24:MI:SS' ]
1. Oracle : TO_CHAR(date_exp, 'HH24:MI:SS')
2. MSSQL : CONVERT(VARCHAR(8), date_exp, 114)
3. DB2 : CHAR(TIME(date_exp))

[ 형식 : 'YYYY.MM.DD HH24:MI' ]
1. Oracle : TO_CHAR(date_exp, 'YYYY.MM.DD HH24:MI')
2. MSSQL : CONVERT(VARCHAR, date_exp, 102) + ' ' + CONVERT(VARCHAR(5), date_exp, 114)
3. DB2 : REPLACE(CHAR(DATE(date_exp), ISO), '-', '.') || CAST( TIME(date_exp) AS CHAR(5))

[ 형식 : 'YYYY/MM/DD HH24:MI:SS' ]
1. Oracle : TO_CHAR(date_exp, 'YYYY/MM/DD HH24:MI:SS')
2. MSSQL : CONVERT(VARCHAR, date_exp, 111) + ' ' + CONVERT(VARCHAR(8), date_exp, 114)
3. DB2 : REPLACE(CHAR(DATE(date_exp), ISO), '-', '/') || CAST( TIME(date_exp))

- http://www.dbguide.net/ 발취

'Database' 카테고리의 다른 글

DBMS별 날짜 포멧  (0) 2010.01.27
오픈소스 DBMS 라이센스의 이해  (0) 2009.12.30
지금 우리에게 필요한 건 정렬의 제거이다  (0) 2009.12.04

눈이 정말 펑펑 내리는 구나~
올해 눈이 정말 많이 내린다~


오빠 차는 이미 미라 상태.. ㅋㅋ
낼 이래서 탈 수는 있나?

'Story > Diary' 카테고리의 다른 글

[Movie]세븐데이즈  (0) 2009.12.30
[Movie] 나는 전설이다  (0) 2009.12.30
[Movie]전우치  (0) 2009.12.27
[Movie]아바타  (0) 2009.12.27
[Movie] 전우치  (0) 2009.12.27

+ Recent posts