2010년 6월 3일 목요일

Oracle9i Real Application Clusters의 페일오버 기능 - CTF vs TAF

아마도 독자들이 Oracle9i Real Application Clusters를 도입하는 가장 큰 이유는 Oracle9i Real Application Clusters가 고가용성(HA)을 보장해 주기 때문일 것인데, 실제로 HA를 구현하기 위해서 가장 중요한 부분이 애플리케이션 페일오버(application failover)이다. 애플리케이션 페일오버를 간단히 설명하면, Oracle9i Real Application Clusters 환경에서 하나의 노드에 장애가 발생했을 때 애플리케이션은 나머지 살아 있는 노드들 중의 하나로부터 서비스를 받게 되므로, 엔드유저들은 장애가 발생했는지조차 모르는 것이다. 이것이 바로 오라클에서 Oracle9i Real Application Clusters를 "난공불락(Unbreakable)"이라고 내세우는 이유이다.


최근에는 HA에 대한 관심이 높아지면서 플랫폼 벤더에서도 여러 가지 HA 솔루션 - 이를테면, IP 스위치오버 - 을 제공한다. Oracle9i Real Application Clusters 사용 고객이 이러한 플랫폼 솔루션을 훌륭하게 함께 사용하는 경우도 많이 있지만, 이번 호에서는 혼돈을 피하기 위해 100% 오라클이 제공하는 솔루션만을 다루도록 한다.


Oracle9i Real Application Clusters에서 제공하는 페일오버 기능에는 CTF(Connection Time Failover)와 TAF(Transparent Application Failover)가 있다. 이 두 가지 기능 모두는 Oracle8i Parallel Server에서부터 제공되던 기능이다. 그런데, 많은 고객들이 CTF와 TAF가 상호 배타적인 기능이라고 여기는 것 같은데, TAF는 CTF 기능 위의 부가적인 기능이다. 따라서, CTF를 사용할 것인지 TAF를 사용할 것인지 고민할 필요 없이 TAF를 사용할 것인지 아닌지의 여부만 선택하면 된다.


이 글의 기반이 되는 필자의 테스트 환경은 다음과 같다. 앞으로 이어지는 테스트에서는 항상 이전의 설정을 기본으로 변경사항만 추가하도록 하겠다.


데이타베이스 서버 ‥ 2노드의 Oracle9i Real Application Clusters
운영체제 : Sun Solaris 8
오라클 데이타베이스 버전 : Oracle9i Database Release 2(9.2.0.3) 64비트
호스트명(공용 네트워크) : krrac1, krrac2
ORACLE_SID : RAC1(krrac1), RAC2(krrac2)
init.ora 파라미터




local_listener=(지정 안함) remote_listener=(지정 안함) service_names=(지정 안함) db_name="rac" rac1.instance_name="rac1" rac2.instance_name="rac2"
listener.ora(krrac1. krrac2는 HOST와 SID_NAME만 각각 krrac2, RAC2이며 나머지는 동일)
LISTENER = (DESCRIPTION_LIST = (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = krrac1) (PORT = 1521)) ) ) ) SID_LIST_LISTENER = (SID_LIST = (SID_DESC = (ORACLE_HOME = /data/oracle) (SID_NAME = RAC1) ) )


Oracle*Net 클라이언트(SQL*Plus)
운영체제 : Windows 2000 Professional
오라클 클라이언트 버전 : Oracle9i Database Release 2(9.2.0.3)
tnsnames.ora


RAC1 = (description= (address=(protocol=tcp)(host=krrac1)(port=1521)) (connect_data= (service_name=RAC)) )
자, 이제 클라이언트에서 rac1이라는 TNS alias로 RAC1에 접속할 수 있다면 모든 준비가 되었다. RAC1 인스턴스에 접속되었는지 확인해 보도록 하자<리스트 1>.


<리스트 1> RAC1 인스턴스의 접속 확인


$ sqlplus scott/tiger@rac1 SQL*Plus: Release 9.2.0.3.0 - Production on 화 Jul 8 12:20:46 2003 Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved. 다음에 접속됨: Oracle9i Enterprise Edition Release 9.2.0.3.0 - 64bit Production With the Partitioning, Real Application Clusters, OLAP and Oracle Data Mining options JServer Release 9.2.0.3.0 - Production 세션이 변경되었습니다. SQL> select userenv("instance") from dual; USERENV("INSTANCE") -------------------------- 1






그러면, 이제 각각의 기능이 어떻게 작동하는지 자세히 알아보도록 하자.


CTF


CTF(Connection Time Failover)는 가장 기본적인 페일오버 설정 방법으로, 클라이언트의 TNS alias만 아래와 같이 수정해주면 된다.


RAC_CTF = (description= (load_balance=off) (failover=on) (address=(protocol=tcp)(host=krrac1)(port=1521)) (address=(protocol=tcp)(host=krrac2)(port=1521)) (connect_data= (service_name=RAC) ) )
로드 밸런싱(load balancing)은 이후에 별도로 자세히 설명하므로, 일단 여기서는 로드 밸런싱에 대한 언급은 하지 않기로 하자. 이와 같이 설정한다면, 일단 RAC_CTF를 사용하여 접속하는 클라이언트들은
krrac1의 1521 포트로 기동된 리스너를 컨택한다.
그 리스너가 RAC라는 서비스를 하고 있다면 RAC라는 서비스에 등록되어 있는 인스턴스에 접속한다.
만일 1에서 리스너가 응답이 없거나, 2에서 RAC라는 서비스가 없다면, 다음에 지정된 어드레스인 krrac2의 1521 포트로 기동된 리스너를 컨택한다. 여기서 리스너가 응답이 없다면 에러를 리턴하며 접속을 하지 못하게 된다.
2와 마찬가지 과정으로 RAC 서비스에 등록되어 있는 인스턴스를 접속한다. 여기서 RAC 서비스를 찾을 수 없다면, 마찬가지로 에러를 리턴하며 접속을 하지 못하게 된다.
따라서, 새로이 접속하는 클라이언트에 대하여는 krrac1에서 어떤 장애가 일어나더라도 문제없이 krrac2로 페일오버가 된다. 그렇다면, 기존에 이미 접속되어 있던 클라이언트들은 어떻게 될까? 리스너에만 장애가 있다면 당연히 영향이 없을 것이지만, 만일 인스턴스가 중단되었다면? 물론 클라이언트는 ORA-3113/3114 에러를 만나게 되며 기존의 접속 상태가 끊어지고, 새로 접속하면 위와 같은 과정을 거쳐 krrac2로 접속하게 된다. 실행되고 있던 DML은 살아 있는 RAC2 인스턴스에서 트랜잭션 복구를 수행하여 모두 롤백된다.


여기서 각 호스트의 리스너가 어떤 포트로 기동되어 있는지, 어떤 서비스에 어떤 인스턴스가 등록되어 있는지는 <리스트 2>와 같은 방법으로 알 수 있다.


<리스트 2> 각 호스트의 리스너 포트와 서비스별 인스턴스 등록 확인


krrac1|/data/oracle> lsnrctl services LSNRCTL for Solaris: Version 9.2.0.3.0 - Production on 08-JUL-2003 14:09:47 Copyright (c) 1991, 2002, Oracle Corporation. All rights reserved. Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=krrac1)(PORT=1521))) Services Summary... Service "RAC" has 1 instance(s). Instance "RAC1", status READY, has 1 handler(s) for this service... Handler(s): "DEDICATED" established:1 refused:0 state:ready LOCAL SERVER Service "RAC1" has 1 instance(s). Instance "RAC1", status UNKNOWN, has 1 handler(s) for this service... Handler(s): "DEDICATED" established:0 refused:0 LOCAL SERVER The command completed successfully






여기서 독자들은 뭔가 좀 특이한 점을 볼 수 있을 것이다. 현재 리스너에는 RAC와 RAC1 두 가지 서비스가 등록되어 있으며, 각각의 서비스에는 RAC1 인스턴스가 등록되어 있다. 그렇다면, 왜 두 가지 서비스가 등록되어 있을까? 이것을 이해하기 위해서는 Oracle8i Database에서 도입된 동적 등록(dynamic registration) 방식에 대하여 우선 알아야 한다. 위에서 status 항목을 자세히 살펴보면, RAC 서비스에 대하여는 READY인 반면 RAC1 서비스에 대하여는 UNKNOWN으로 나와 있는 것을 볼 수 있다. 즉, 리스너는 현재 RAC 서비스를 통해서는 RAC1 인스턴스가 살아 있다(READY)는 사실을 알 수 있지만, RAC1 서비스를 통해서는 RAC1 인스턴스의 상태를 알 수 없다(UNKNOWN). 여기서 한 번 RAC1 인스턴스를 셧다운한 후 똑 같은 테스트를 해 보자<리스트 3>.


<리스트 3> RAC1 인스턴스의 셧다운 후 테스트 실행


krrac1|/data/oracle> lsnrctl services listener_rac1 LSNRCTL for Solaris: Version 9.2.0.3.0 - Production on 08-JUL-2003 14:25:51 Copyright (c) 1991, 2002, Oracle Corporation. All rights reserved. Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=krrac1)(PORT=1521))) Services Summary... Service "RAC1" has 1 instance(s). Instance "RAC1", status UNKNOWN, has 1 handler(s) for this service... Handler(s): "DEDICATED" established:0 refused:0 LOCAL SERVER The command completed successfully






그러면, RAC 서비스가 사라진 것을 볼 수 있다. 반면에, RAC1 서비스는 여전히 RAC1 인스턴스의 상태를 모르고 있다. 즉, RAC 서비스는 동적으로 등록된 서비스이며, 이러한 서비스에 등록된 인스턴스는 지속적으로 자신의 상태를 리스너에 업데이트시켜 준다.
동적 등록을 구성하는 방법은 다음과 같다.
SERVICE_NAMES 파라미터 값을 설정해 준다. 이것이 바로 리스너에 등록하는 서비스의 이름으로, 디폴트 값은 db_name.db_domain이 된다. 즉, 예제에서는 db_domain 값이 설정되어 있지 않으므로 그냥 rac가 된다. 주의할 점은 service_names를 임의로 설정하는 경우 Oracle9i Real Application Clusters 데이타베이스 내의 페일오버 설정에 참여하는 모든 인스턴스들은 같은 값으로 설정해야 한다는 것이다. 앞서 설명한 RAC_CTF TNS alias를 보면 service_name 값을 RAC로 사용하여 서로 다른 리스너 어드레스를 등록한 것을 확인할 수 있다.




LOCAL_LISTENER 파라미터 값을 설정해 준다. 즉 어느 리스너에 자신의 상태를 업데이트시켜 줄 것인지를 정해주는 부분으로, 디폴트 값은 (ADDRESS = (PROTOCOL=TCP)(HOST=)(PORT=1521))이 된다. 즉, 로컬 호스트의 1521 포트로 기동되는 리스너에 대하여는 LOCAL_LISTENER 값을 설정하지 않더라도 자동으로 동적 등록이 설정된다.
이 예제에서는 SERVICE_NAMES, LOCAL_LISTENER 값을 둘 다 설정하지 않았지만 lsnrctl services에서 동적 등록이 되어 있는 것을 확인할 수 있다. 모두 디폴트 값을 취했기 때문이다. 즉, db_name을 SERVICE_NAMES로 사용하고, 1521 포트의 리스너를 사용하고자 한다면 이 예제와 같이 아무것도 설정할 필요가 없다.


LOCAL_LISTENER를 설정할 때는 디폴트 값처럼 TNS alias 구문으로 설정해 줄 수도 있지만 다른 방법도 있다. 서버의(클라이언트가 아니라 서버임에 유의하자) tnsnames.ora 파일에 다음과 같은 항목을 추가해 보자.
LISTENER_RAC1 = (description= (address=(protocol=tcp)(host=krrac1)(port=1521)) )
그리고 init.ora 파일에는 LOCAL_LISTENER=LISTENER_RAC1라고 지정해 보자. lsnrctl services에서와 마찬가지로 동적 등록이 이루어진 것을 확인할 수 있다. 즉, 클라이언트에서 tnsnames.ora 파일에 TNS alias를 설정하여 사용하듯이, 서버에서도 LOCAL_LISTENER에 대하여 TNS alias를 설정하여 사용할 수 있다. 물론 여기서 설정하는 값은 listener.ora 파일에 지정하는 리스너 이름과는 아무런 상관이 없다. Oracle9i Database Release 2(V9.2)부터는 LOCAL_LISTENER 값을 인스턴스가 기동중인 상태에서도 아래와 같이 동적으로 바꿀 수 있다.
SQL>alter system set local_listener="LISTENER_RAC1"; System altered.
이 때 물론 서버의 tnsnames.ora에는 LISTENER_RAC1이라는 항목이 미리 지정되어 있어야 하며, 만일 이것이 없을 때에는 오라클 에러가 나타나며 실패하게 된다. 또한 SPFile(Server Parameter File)이 아닌 텍스트 방식의 init.ora를 사용하는 경우 인스턴스를 내렸다 올리면 위 설정은 없어지고 원래대로 돌아가므로, 설정을 지속시키려면 init.ora 파일에 값을 명시적으로 설정해야 한다.


자, 그럼 여기서 한 가지 의문이 생길 수 있다. "그렇다면, 정적으로 등록된 서비스(즉 위 예제에서 status UNKNOWN으로 등록된 RAC1 서비스)는?" 결론적으로 말하면, 동적 등록 방식이 정적 등록 방식을 오버라이드(override)하게 되므로 정적 등록 방식은 다음과 같이 listener.ora 파일을 수정하여 제거하는 것이 좋다.
LISTENER = (DESCRIPTION_LIST = (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = krrac1) (PORT = 1521)) ) ) )
여기서 또 한 가지. 바로 위에 설정한 내용이 listener.ora의 디폴트 값이다. 결국 listener.ora 파일에 아무런 내용이 없어도 된다는 이야기가 된다. 사실 1521 포트를 사용하면서 LISTENER라는 이름의 리스너를 사용한다면 실제로 listener.ora 파일 자체가 필요가 없다. lsnrctl 명령어는 디폴트로 LISTENER라는 이름의 리스너에 대하여 수행되므로, 시동/중단하는 데에도 아무런 지장이 없다. 한 번 listener.ora 파일을 제거하고 테스트해 보도록 하자. 여태까지와 마찬가지 결과를 얻을 것이다.


물론, LISTENER라는 이름이 아닌 다른 이름의 리스너를 사용하고자 한다면 1521 포트를 사용하더라도 이름을 지정하기 위해서 listener.ora 파일이 필요하다. 이미 눈치 빠른 독자들은 알아챘겠지만, 동적 등록 방식이 우수하므로 앞으로는 항상 동적 등록 방식을 쓰도록 하자는 것이다. 동적 등록 방식은 세 가지 측면에서 과거의 정적 등록 방식보다 우수하다.
주기적으로 인스턴스가 리스너에 자신의 상태를 알려주므로, CTF 설정에서 인스턴스에 장애가 있을 경우 앞서 설정한 페일오버 과정에서 장애가 발생한 인스턴스에 접속을 시도하는 오버헤드가 없다. 즉, 리스너까지만 가면 인스턴스의 상태를 알 수 있다.
Oracle9i Database의 새로운 기능인 리스너(커넥션) 로드 밸런싱을 사용할 수 있다.
TAF를 사용할 수 있다.


그렇다면 TAF란 무엇인가? 이것에 대해 자세히 알아보도록 하자.


TAF


앞서 잠깐 설명한 대로 CTF의 약점은 기존에 접속되어 있던 클라이언트들에 대해서는 다시 한 번 접속을 시도해야 페일오버가 이루어진다는 점이다. 바로 이것을 극복할 수 있는 솔루션이 바로 TAF(Transparent Application Failover)이다. 앞서 설정한 클라이언트쪽의 TNS alias를 다음과 같이 수정해 보자. 물론 리스너에는 반드시 인스턴스가 동적으로 등록되어 있어야 한다.


RAC_TAF = (description= (load_balance=off) (failover=on) (address=(protocol=tcp)(host=krrac1)(port=1521)) (address=(protocol=tcp)(host=krrac2)(port=1521)) (connect_data= (service_name=RAC) (failover_mode=(type=select)(method=basic)) ) )
CTF와 비교하여 다른 점은 failover_mode라는 항목이 설정되었다는 점이다. 이것을 설정하는 방법을 설명하기에 앞서, 일단 위와 같이 TAF가 설정되어 있는 경우에 이미 접속되어 있던 클라이언트들은 장애상황에서 어떻게 되는지부터 알아보자. 예를 들어, 클라이언트에서 100건의 레코드를 리턴하는 Select를 수행중이었고, 현재 50건이 Fetch된 상태에서 RAC1 인스턴스가 중단되었다고 가정한다면,
살아 있는 인스턴스인 RAC2에서 새로이 Oracle Server Process (Foreground Process)를 띄운다.




이렇게 새로이 만들어진 프로세스로 50건을 Fetch하던 클라이언트의 세션 정보가 옮겨진다. 이때 복구되는 내용은 다음과 같다.
SQL을 실행하던 사용자의 로그인 정보
실행하던 SQL 명령어
Fetch하던 커서
서버측 프로그램 변수(예를 들면, PL/SQL package states)




이렇게 세션 정보가 복구되면 수행되던 Select 문장이 Fetch하던 나머지 50건이 Fetch된다. 따라서 클라이언트는 RAC1 인스턴스에 장애가 발생했다는 사실을 전혀 모르게 된다(Transparent Application). 물론 2번 과정에서 세션 정보를 복구하는 과정이 있으므로 50건 Fetch 후 다시 나머지 50건의 Fetch가 시작되기까지 약간 지연될 수 있다.


TAF와 CTF의 가장 큰 차이점은 재접속이 필요 없다는 점이다. 앞서 CTF 방식에서는 접속되어 있던 클라이언트들은 장애 발생 시 일단 ORA-3113/3114 에러를 만나게 되며, 재접속을 해야 페일오버가 되었다는 점을 기억하자. 이 때 기존에 접속되어 있던 클라이언트와 별도로 새로 접속하는 클라이언트들은 앞서 설명한 CTF가 작동하여 페일오버가 이루어진다.


그러면, failover_mode의 각각의 항목에 대하여 알아보도록 하자.


TYPE


SESSION : 이 방식은 재접속은 필요 없지만, 예제와는 달리 Select가 Fetch하던 도중에 장애를 만나면 Fetch가 도중에 실패하게 된다. 데이타 웨어하우스와 같이 다량의 레코드를 한 번에 Select하는 경우가 아니라면 이 방식을 쓰는 것이 좋다.
SELECT : 예제와 같은 방식으로, 재접속도 필요 없을 뿐더러 Fetch하던 레코드까지 복구해 준다. 다량의 레코드를 Select하는 애플리케이션에 적절한 방법이다.
NONE : 디폴트 값으로 TAF가 작동하지 않는다.




METHOD


BASIC : On-demand 방식으로 페일오버가 필요할 때 살아있는 인스턴스쪽으로 Oracle Server Process를 기동시킨다.
PRECONNECT : 페일오버가 이루어질 인스턴스에 미리 Oracle Server Process를 기동시켜 페일오버시 발생하는 오버헤드를 미리 줄여 놓는 방식이다. 물론 페일오버 속도는 향상되지만, 자원을 낭비할 수 있다.
그렇다면, 여기서 또 의문점이 생긴다. PRECONNECT 메소드를 사용하는 경우 미리 어느 인스턴스로 페일오버할지를 결정해 놓아야 한다. 다음과 같이 클라이언트의 tnsnames.ora를 수정해 보자.
RAC_TAF = (description= (load_balance=off) (failover=on) (address=(protocol=tcp)(host=krrac1)(port=1521)) (address=(protocol=tcp)(host=krrac2)(port=1521)) (connect_data= (service_name=RAC) (failover_mode=(type=select)(method=preconnect)(backup=RAC2)) ) ) RAC2 = (description= (address=(protocol=tcp)(host=krrac2)(port=1521)) (connect_data= (service_name=RAC)) )
즉 PRECONNECT 메소드를 사용하는 경우에는 반드시 backup이라는 항목이 함께 설정되어야 한다. 이 때 backup에는 TNS alias로 지정하며, 여기에 지정할 TNS alias는 반드시 미리 설정되어 있어야 한다(예제에서는 RAC2). 특히, 이 때 지정한 TNS alias가 올바로 설정된 값이 아니어도 접속할 때 따로 에러를 리턴하지 않는다는 점에 주의하자.


물론, 이 예제에서는 2노드 환경이므로 한 쪽에서 장애가 발생하면 나머지 살아 있는 쪽은 하나밖에 없으므로 왜 이것이 필요할까 궁금해 하는 독자들이 있을 것이다. 위와 같이 설정해 놓고 RAC_TAF라는 TNS alias로 접속한 후 <리스트 4>와 같이 SQL을 실행해 보자. 참고로 <리스트 4>의 SQL은 PRECONNECT 메소드뿐만 아니라 모든 경우의 TAF 설정이 제대로 작동하는지 알아보기 위해서 사용할 수 있다.


<리스트 4> TAF 설정이 올바로 되었는지 확인하는 SQL


SQL> SELECT 2 INST_ID, SID, MACHINE, FAILOVER_TYPE, FAILOVER_METHOD, FAILED_OVER
3 FROM GV$SESSION 4 WHERE USERNAME="SYSTEM" 5 AND PROGRAM NOT LIKE "%(P%";
INST_ID SID MACHINE FAILOVER FAILOVER_METHOD FAILED






1 20 ORACLE\leesb-kr SELECT PRECONNECT NO


2 19 ORACLE\leesb-kr NONE NONE NO






이렇게 PRECONNECT 메소드를 사용하는 경우에는 최초에 접속하는 주(primary) 인스턴스와 백업 인스턴스에 동시에 접속한다는 것을 알 수 있다. 이때 RAC1 인스턴스를 셧다운시키면 다음과 같은 결과가 나와야 한다.


INST_ID SID MACHINE FAILOVER FAILOVER_METHOD FAILED






2 19 ORACLE\leesb-kr SELECT PRECONNECT YES




여기서 주목해야 할 점은 PRECONNECT 메소드를 사용할 때는 항상 주 인스턴스와 백업 인스턴스를 지정해야 하므로 로드 밸런싱을 할 수 없다는 사실이다. 따라서 PRECONNECT 메소드는 각 인스턴스에 접속하는 클라이언트가 업무별/지역별로 분할되어 있는 경우에 사용할 수 있다.


페일오버 테스트 시나리오


자, 이렇게 페일오버를 설정했다면 실제로 장애 상황별로 제대로 작동하는지 살펴보아야 할 것이다. 보통 페일오버 테스트에서 고려하는 장애 상황은 다음과 같다.
오라클 인스턴스 장애 : 간단하게 shutdown abort 명령으로 인스턴스 장애 상황을 만들 수 있다.
노드 장애(네트워크 장애) : 클라이언트 입장에서는 노드 자체가 장애가 있는 경우(reboot 또는 halt 명령)나 네트워크 장애가 있는 경우(네트워크 디바이스의 셧다운 또는 네트워크 라인의 unplug)나 같은 상황이므로 똑 같은 결과가 나와야 한다.
리스너 장애 : 간단하게 리스너 프로세스를 중단하든지 lsnrctl stop으로 셧다운해서 장애 상황을 만들 수 있다. 인스턴스는 살아 있으면서 리스너만 장애가 있는 경우에는 항상 기존에 접속되어 있던 클라이언트는 페일오버 설정과 관계 없이 아무런 문제가 없어야 하며, 오직 새로이 접속하는 클라이언트만 페일오버가 이루어진다는 점을 기억하자.
이 세 가지 장애 상황에서 페일오버가 모두 정상적으로 이루어진다면 설정이 성공적으로 이루어졌다고 볼 수 있다.
마지막으로 클라이언트 페일오버를 테스트할 때, 오라클 인터커넥트(Oracle9i Real Application Clusters 노드 간의 개별 네트워크)에 대한 장애는 클라이언트 페일오버와 전혀 상관이 없으며, 이 인터커넥트의 장애 발생시에는 인스턴스의 장애가 당연히 뒤따르므로 이를 고려하여 테스트하여야 한다. 오라클 인터커넥트의 장애 발생시 어떤 결과가 일어나는가에 대해서는 본지 2003년 봄호에 실린 필자의 "Oracle9i Real Application Clusters - Cache Fusion의 작동 원리와 활용"을 참고하기 바란다.


클라이언트 로드 밸런싱 vs 리스너 로드 밸런싱


자, 이제는 Oracle9i Real Application Clusters의 로드 밸런싱에 대해 알아보도록 하자. "로드 밸런싱(load balancing)"이란 말 그대로 공평하게 부하(load)를 나누어 준다는 뜻이다. 즉, 100개의 클라이언트가 있다면 2노드 Oracle9i Real Application Clusters 환경에서는 각 인스턴스별로 50개씩 똑같이 나누어 주고 싶을 것이고, 차후에 다시 50개의 새로운 클라이언트가 접속한다면 다시 각각 25개씩 나누어 주어 언제나 공평하게 부하를 유지하고 싶을 것이다.


하지만, 이제까지의 예제와 같이 TNS alias에서 load_balance=off로 설정한 경우에는 어느 클라이언트가 어느 인스턴스로 접속할지 수동으로 모두 설정해 주어야 하는 번거로움이 있었다. 만일 이것이 자동으로 된다면? 즉, 100개의 클라이언트가 무작위로 접속할 때 자동으로 50개씩 나누어진다면 훨씬 더 편리할 것이다.


사실 Oracle9i Real Application Clusters가 나오기 이전에 Oracle Parallel Server를 사용하는 고객들은 로드 밸런싱을 잘 사용하지 못했다고 할 수 있다. 왜냐하면, Oracle Parallel Server에서 발생하는 핑(ping) 현상으로 인해 보통 불가피하게 노드별로 데이타를 분할하여 데이타베이스를 구성하므로, 업무별/지역별로 클라이언트를 나누어 특정 인스턴스를 지정해서 접속해야 했기 때문이다.


하지만, Oracle9i Real Application Clusters에서 완벽하게 이루어지는 Cache Fusion의 기능으로 더 이상 데이타의 분할이 필요 없게 되었으므로, 클라이언트는 Oracle9i Real Application Clusters로 구성된 데이타베이스 내의 어떤 인스턴스에 접속하더라도 성능에는 아무런 영향을 주지 않는다.


오라클의 로드 밸런싱 설정은 다음 두 가지가 있다.


클라이언트 로드 밸런싱 : 클라이언트가 같은 SERVICE_NAME을 사용하는 인스턴스들 중에 무작위로 선택하여 접속하는 방법이다.




리스너(커넥션) 로드 밸런싱 : Oracle9i Real Application Clusters의 새로운 기능으로 리스너가 각 인스턴스의 부하에 대한 정보를 가지고 있어서 항상 부하가 덜 걸린 인스턴스쪽으로 클라이언트를 접속시켜 주는 방법이다.
그러면 이제 각각의 방법을 어떻게 구현할 수 있는지 차례로 살펴보자.


클라이언트 로드 밸런싱


앞서 설정했던 BASIC 메소드의 TAF 설정을 다음과 같이 수정해 보자.


RAC_TAF = (description= (load_balance=on) (failover=on) (address=(protocol=tcp)(host=krrac1)(port=1521)) (address=(protocol=tcp)(host=krrac2)(port=1521)) (connect_data= (service_name=RAC) (failover_mode=(type=select)(method=basic)) ) )
다른 것은 그대로 둔 채로 load_balance 부분만 on으로 설정하면 클라이언트 로드 밸런싱이 구성된다. 이제 반복적으로 접속하여 결과를 보도록 하자.
SQL> conn scott/tiger@rac_taf Connected. SQL> select userenv("instance") from dual; USERENV("INSTANCE") -------------------------- 2 SQL> conn scott/tiger@rac_taf Connected. SQL> select userenv("instance") from dual; USERENV("INSTANCE") -------------------------- 1
위와 같이 클라이언트는 인스턴스를 바꿔가며 RAC1, RAC2에 무작위로 접속하는 것을 볼 수 있다. 라운드로빈(round-robin) 방식이 아닌 무작위(random) 방식이므로, 특별한 패턴을 찾을 수는 없지만, 접속 횟수가 많아질수록 균일하게 분배되는 것을 알 수 있을 것이다. 즉, 예제와 같이 2노드의 Oracle9i Real Application Clusters 환경에서 100회 반복하여 접속한다면, 거의 50:50으로 나누어지는 것을 확인할 수 있을 것이다.


클라이언트 로드 밸런싱을 하는 경우 특별히 동적 등록이 필요하지는 않으나, 예제와 같이 TAF와 함께 사용하는 경우에는 반드시 동적 등록이 구성되어 있어야 한다.


리스너(커넥션) 로드 밸런싱


최초에 RAC1 인스턴스에는 100개의 클라이언트, RAC2 인스턴스에는 10개의 클라이언트가 접속되어 있었다고 가정하자. 이후에 다시 100개의 새로운 클라이언트를 클라이언트 로드 밸런싱을 설정하여 접속시킨다면? 결과는 새로운 100개의 클라이언트들은 50:50으로 균일하게 분배되어 결국 최종적으로는 RAC1 인스턴스는 150개, RAC2 인스턴스에는 60개의 클라이언트가 붙을 것이다.


이렇게 되면 RAC1 인스턴스 입장에서는 불공평한 결과가 될 수 있다. 즉 RAC2 인스턴스가 훨씬 더 여유가 있다는 것을 미리 알아서, 새로이 들어오는 100개의 클라이언트를 5:95로 분배하여 결국 105:105가 되도록 해준다면 각각의 인스턴스 입장에서는 언제나 공평한 결과를 만들 수 있다.


바로 이것이 Oracle9i Real Application Clusters에서 새로 등장한 리스너(커넥션) 로드 밸런싱이며, 이것을 위해서는 서버쪽의 tnsnames.ora와 init.ora에 다음과 같은 설정을 추가해야 한다. krrac1의 tnsnames.ora에 다음과 같은 항목을 추가해 보자.


LISTENER_RAC2 = (description= (address=(protocol=tcp)(host=krrac2)(port=1521)) )
그리고 SYSDBA 사용자로 다음과 같이 remote_listener 파라미터 값을 변경해 보자.
SQL> alter system set remote_listener="LISTENER_RAC2"; System altered.
이제 krrac2(krrac1이 아님)의 리스너를 확인해 보자. <리스트 5>와 같이 RAC 서비스에 기존에 등록되어 있던 RAC2 인스턴스와 함께, RAC1 인스턴스도 추가로 동적으로 등록되어 있는 것을 볼 수 있다.


<리스트 5> 리스너 로드 밸런싱에서 리스너 확인


krrac2|/data/oracle> lsnrctl services LSNRCTL for Solaris: Version 9.2.0.3.0 - Production on 14-JUL-2003 15:29:55 Copyright (c) 1991, 2002, Oracle Corporation. All rights reserved. Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=krrac2)(PORT=1521))) Services Summary... Service "RAC" has 2 instance(s). Instance "RAC1", status READY, has 1 handler(s) for this service... Handler(s): "DEDICATED" established:0 refused:0 state:ready REMOTE SERVER (DESCRIPTION=(address=(protocol=tcp)(host=krrac1)(port=1521))) Instance "RAC2", status READY, has 1 handler(s) for this service... Handler(s): "DEDICATED" established:25 refused:0 state:ready LOCAL SERVER The command completed successfully






그러면, 이제 krrac2의 tnsnames.ora, RAC2 인스턴스의 remote_listener 값을 마찬가지로 올바르게 설정했다면 krrac1의 리스너에도 <리스트 6>과 같이 RAC1, RAC2 인스턴스가 모두 등록되어 있는 것을 확인할 수 있을 것이다.


<리스트 6> 리스너 로드 밸런싱에서 인스턴스의 등록 확인


krrac1|/data/oracle> lsnrctl services LSNRCTL for Solaris: Version 9.2.0.3.0 - Production on 14-JUL-2003 15:33:52 Copyright (c) 1991, 2002, Oracle Corporation. All rights reserved. Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=krrac2)(PORT=1521))) Services Summary... Service "RAC" has 2 instance(s). Instance "RAC1", status READY, has 1 handler(s) for this service... Handler(s): "DEDICATED" established:0 refused:0 state:ready REMOTE SERVER (DESCRIPTION=(address=(protocol=tcp)(host=krrac1)(port=1521))) Instance "RAC2", status READY, has 1 handler(s) for this service... Handler(s): "DEDICATED" established:25 refused:0 state:ready LOCAL SERVER The command completed successfully






앞서 설명한 동적 등록이 무엇을 의미하는가를 기억한다면, 여기서 각각의 인스턴스가 remote_listener 파라미터를 사용함으로써 상대방 노드의 리스너에 자신의 상태를 지속적으로 업데이트해 주고 있다는 것을 눈치챌 수 있다.


즉, 예제와 같이 리스너(커넥션) 로드 밸런싱을 사용하는 경우 클라이언트는 접속할 때 다음과 같은 과정을 거치게 된다.
클라이언트는 일단 TNS alias의 address_list의 첫 번째 항목인 krrac1의 1521 포트로 기동된 리스너에 컨택한다.
krrac1의 리스너는 RAC1, RAC2 인스턴스가 모두 등록되어 있으며, 각 인스턴스는 자신이 서비스가 가능한 상태인지의 여부와, 자신이 가지고 있는 부하(접속해 있는 클라이언트의 개수)를 지속적으로 업데이트해 주고 있으므로, 1에서 리스너에 컨택한 클라이언트는 RAC1과 RAC2 중 어느 것이 적은 수의 클라이언트를 가지고 있는지 알 수 있다.
만일 RAC1의 클라이언트 수가 적다면, 로컬 노드(krrac1)에서 Oracle Server Process(Foreground Process)를 만들어서 커넥션을 맺어 준다.
만일 RAC2의 클라이언트 수가 적다면, address_list의 다음에 나오는 항목(리모트 노드 : krrac2의 1521 포트)에 등록되어 있는 리스너로 리다이렉트시켜 준다.
krrac2의 리스너는 이제 Oracle Server Process(Foreground Process)를 만들어서 커넥션을 맺어 준다.
따라서, 리스너의 서비스에 remote_listener를 사용하여 Oracle9i Real Application Clusters의 모든 인스턴스가 등록되어 있기만 하다면, 언제나 공평한 분배(로드 밸런싱)가 이루어질 수 있다.
설정 후 테스트는 다음과 같이 해볼 수 있다.
RAC1 인스턴스에만 로드 밸런싱을 사용하지 않고 미리 상당수의 커넥션을 맺어 놓는다(이를테면 10개라고 하자.)
이제 RAC_TAF TNS alias를 가지고 반복적으로 접속해 보자. 10개까지는 RAC2에만 접속되어 각 인스턴스에 10개씩의 클라이언트가 만들어질 것이다.
이어서 지속적으로 RAC_TAF TNS alias를 가지고 반복적으로 접속해 본다면, 이제는 각 인스턴스에 거의 번갈아서 접속하면서 클라이언트 개수의 총합이 언제나 50:50으로 유지되는 것을 볼 수 있을 것이다.
마지막으로 주의할 점은, 리스너(커넥션) 로드 밸런싱은 항상 클라이언트 로드 밸런싱을 오버라이드하므로, remote_listener가 설정되어 리스너에 여러 인스턴스가 등록되어 있는 경우, 아래와 같이 TNS alias에서 클라이언트 로드 밸런싱을 사용하지 않더라도, 리스너(커넥션) 로드 밸런싱은 계속 이루어진다는 사실이다. 즉, 아래와 같이 TNS alias를 설정한 경우에 로드 밸런싱이 일어나고 있다면, 일단 remote_listener가 설정되어 있는지 우선 확인해 보도록 하자.
RAC_TAF = (description= (load_balance=off) (failover=on) (address=(protocol=tcp)(host=krrac1)(port=1521)) (address=(protocol=tcp)(host=krrac2)(port=1521)) (connect_data= (service_name=RAC) (failover_mode=(type=select)(method=basic)) )
remote_listener 값은 Oracle9i Database Release 2(9.2)부터는 아래와 같이 동적으로 해제할 수 있다.
SQL> alter system set remote_listener=""; System altered.
이제까지 로드 밸런싱을 설명하면서 계속 TAF를 예로 들었지만, CTF 설정(failover_mode 부분 제고)으로도 똑 같은 로드 밸런싱을 구현할 수 있으며, 예제와 똑 같은 테스트를 할 수 있다.


난공불락의 Oracle9i Real Application Clusters


필자가 Oracle9i Real Application Clusters를 도입하고자 하는 독자들에게 하고 싶은 말은, 일단 싱글 인스턴스 환경에서 시스템을 문제 없이 사용해 왔다면, Oracle9i Real Application Clusters로의 전환 이후에도 문제 없는 시스템을 유지할 수 있으며, 나아가 오라클이 자랑하는 "난공불락 Oracle9i Real Application Clusters" 놀라운 기능을 마음껏 누릴 수 있다는 것이다.


마지막으로 필자가 3회의 연재를 통해 언급한 몇 가지 주의사항을 다시 한 번 정리하며 끝을 맺기로 한다.


싱글 인스턴스로 운영중이던 애플리케이션은 Oracle9i Real Application Clusters로 전환 시 Cache Fusion이 완벽하게 작동하므로 별도의 데이타 분할이 필요 없다.




Oracle9i Real Application Clusters에서 고속 인터커넥트는 Cache Fusion을 위해 필수이며, HA로 구성되어 있는 것이 바람직하다.




싱글 인스턴스 환경에서 튜닝 되지 않은 SQL들은 Oracle9i Real Application Clusters 환경에서 더욱 성능이 악화될 수 있으니, Oracle9i Real Application Clusters 전환 전에 애플리케이션을 반드시 점검하도록 하자.




global cache cr request가 과다하게 보이는 경우, 무엇보다도 SQL 튜닝이 되어 있는지 먼저 확인하고, 읽기 액세스가 대부분인 테이블스페이스에 한하여 gc_files_to_locks 파라미터를 신중하게 고려해 본다.




페일오버 설정은 각 장애 유형별로 철저히 테스트해 보아야 하며, 애플리케이션 분할이 되어 있지 않다면 로드 밸런싱을 하는 것이 바람직하다.




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

댓글 없음:

댓글 쓰기

팔로어