Open Source RDBMS - Seamless, Scalable, Stable and Free

한국어 | Login |Register

Current Events
Join our developers event to win one of the valuable prizes!
posted 3 years ago
viewed 4314 times
Share this article

CUBRID Broker Story

여타의 RDBMS와 달리 CUBRID는 BROKER라는 중간 레이어를 가진다.

BROKER가 왜 필요한지 알아보고 BROKER 장단점을 알아본다면 CUBRID를 좀 더 이해할 수 있게 될 것이다.

CURBID를 사용하는 개발자들은 모두 알다시피, CUBRID를 사용할 때는 ‘JDBC driver – BROKER – DB server’로 연결되는 3-tier 구조를 사용하게 된다.  CUBRID를 처음 접하는 사람들이 많이 궁금해 하는 부분 중 하나가 ‘CUBRID는 왜 3-tier 구조일까?’라고 한다. ‘커넥션URL에 BROKER 포트를 넣어야 한다는데, BROKER는 무엇을 하는 것인지?’와 같은 궁금증을 가질 수 있다.

이 기사에서 BROKER가 왜 필요한지, 어떤 기능을 수행하는 지와 장단점 등에 대해 간략히 이야기해 보고자 한다.

CUBRID 클라이언트 모듈

데이터베이스는 데이터의 집합체이고, 이 데이터베이스에 대해 데이터를 조작하는 서버와, 서버에 요청을 보내는 클라이언트가 있는 것이 일반적인 형태이다.

Figure 1: CUBRID Process Architecture

CUBRID도 동일한 구조를 가지고 있는데, 프로세스 구조로 보면 데이터베이스 서버 역할을 하는 프로세스가 cub_server 프로세스이고, cub_server 프로세스에 요청을 보낼 수 있는 유일한 클라이언트 모듈은 C로 작성한 라이브러리인 libcubridcs.so이다. libcubridcs.so를 사용하는 네이티브 애플리케이션을 제작할 경우 다음과 같은 형태가 될 것이다.

cubrid-process-architecture.png

네이티브 애플리케이션에서 SELECT 문을 실행시킬 경우 클라이언트 모듈과 서버 프로세스는 각각 어떤 일을 하는지 대략의 흐름을 보면 다음과 같다.

cubrid-request-process.png

그림에서 나타낸 질의 처리 과정은 매우 축약한 형태로 플랜 캐시가 사용되는지, 트랜잭션 격리도 또는 수행하는 질의 형태에 따라 처리과정이 달라질 수 있는데, 여기에서는 간략한 모델만 예로 사용하였다.

그림에서 보는 바와 같이 CUBRID의 클라이언트 모듈은 애플리케이션 프로그램에서 전달하는 요청을 서버로 전달하는 역할만 수행하는 것이 아니라 많은 부분을 처리하고 있다. 간략히 요약하면 클라이언트 모듈은 쿼리를 수행하기 위한 플랜을 직접 생성하여 서버로 전달하고, 서버는 클라이언트가 전달한 플랜에 따라 데이터를 조회/조작하는 일을 한다.

클라이언트 모듈에서 쿼리 플랜을 생성하기 위해서는 많은 정보가 필요하다. 예를 들어

SELECT * from foo where id = 1

...와 같은 질의가 수행된다고 가정해 보자. 질의를 처리하기 위해서는 CUBRID SQL 문법에 맞는지 검사하는 작업을 제일 처음에 한다. 즉, SQL문을 최소 단위의 토큰으로 자르고, 구문 분석을 수행하는 파서가 클라이언트 모듈에 있게 된다.

구문 검사가 완료된 후에는 SQL이 의미상 오류가 없는지를 검사한다. 몇 가지 예를 들면, foo가 존재하는 테이블인지, 현재 사용자가 SELECT권한을 가지고 있는지, id는 foo 테이블에 있는 컬럼인지, id 컬럼이 숫자형 값과 비교가 가능한지 등 여러 가지가 검사 대상이 될 수 있다. 만약 foo가 뷰라면 뷰 변환을 했을 때 동일한 검사로 확장되어야 하고, 조인 쿼리 및 조회 조건이 많아질수록 해당 구문에 대한 오류 검사도 많아지게 될 것이다. 이런 종류의 검사를 수행하기 위해 필요한 스키마 정보를 필요한 시점마다 서버에 요청을 보내는 것은 매우 비효율적일 수 밖에 없기 때문에 스키마에 대한 정보를 클라이언트에 캐시해 두고 있다.

또한 lock 캐시도 필요하게 된다. 예와 같은 쿼리가 실행될 경우 foo 테이블에 대한 조회가 끝날 때까지 테이블에 대한 스키마 변경과 같은 작업은 발생하지 않아야 한다. 테이블이 조회될 동안 테이블이 변경되지 않도록 하는 방법은 잘 알려진 것처럼 lock을 획득하는 것이다. CUBRID의 경우 테이블을 조회 할 경우에는 테이블에 대한 IS lock을 얻는다. IS lock이 획득된 동안에는 해당 테이블에 데이터 입력, 조회는 가능하지만, 테이블 스키마를 변경한다거나, 테이블 전체를 풀스캔하여 변경하는 작업등은 IS lock이 릴리즈될 때까지 대기되어야 한다. 클라이언트 모듈에서는 조회하고자 하는 테이블에 대해서는 IS lock을 획득해 둬야 하는데 필요한 시점마다 lock을 요청하거나 lock을 이미 획득했는지 서버에 요청하는 것 또한 성능상 문제를 유발시킬 수 있기 때문에 lock을 클라이언트에 캐시해 두게 된다.

마지막으로 쿼리를 최적화하고 플랜을 생성하는 단계를 수행하게 되는데, 사용 가능한 인덱스가 존재하는지, 조인질의인 경우 어떤 조인 방법을 사용할지, ORDER BY구문이 있을 경우 정렬을 수행하는 과정을 생략할 수 있는지 등을 판단하게 되고, 최적화된 질의 수행 방법을 서버가 실행할 수 있는 형태로 변환하여 서버에 요청하게 된다.

질의가 처리되는 과정을 단순화하여 살펴보았는데, 질의가 처리되기 위해 필요한 절차는 클라이언트 모듈에서 처리하고, 서버는 정해진 방법에 따라 데이터를 조회하기 위한 작업이 수행된다고 보면 된다. 따라서, 앞에서 설명한 방식과 같이 처리되기 위해서 클라이언트에 많은 모듈을 가지게 되어 있다. CUBRID 전체 구조는 다음 그림에서 보는 바와 같다.

cubrid-client-server-modules.png

데이터베이스 서버에 요청을 보내는 클라이언트 모듈의 구성을 대략 살펴 보았는데, CUBRID가 왜 3-tier 구성을 가지는지 대략 짐작 했을 것으로 생각한다. 앞에서 설명한 클라이언트 라이브러리는 C인터페이스를 제공하고 있는데, JAVA 또는 PHP로 작성하는 애플리케이션에서 사용하기 위해서는 어떤 구성이 가능할까? 2-tier 방식으로 구성한다면 두 가지 방법이 가능할 것이다.

첫 번째는 클라이언트 모듈을 JAVA, PHP등 에서 호출 가능한 형태로 제공하는 형태이다. 이와 같이 구성할 경우 가장 큰 문제점은 (JDBC 때문에) 애플리케이션이 매우 무거워질 수 있다는 점이다. 클라이언트 모듈에서 질의 처리를 위한 많은 단계를 수행하기 때문에 CPU, 메모리 사용량이 증가하게 되어 애플리케이션 환경의 리소스가 가장 큰 문제가 될 수 있다.

두 번째 방법은 애플리케이션의 요청을 받는 부분과 질의 처리에 필요한 부분을 분리하여 질의 처리과정은 서버에서 모두 수행하도록 구조를 변경하는 형태가 될 것이다. 이 방법은 현재의 구조를 대부분 변경해야 하는 매우 큰 작업이 될 수 있을 것이다. 그렇지만, 이 방법은 변경을 위한 개발 과정의 어려움이나 변경 비용보다도 ‘바꾸어서 과연 이익이 무엇일까?’와 같은 근본적인 질문이 필요하다. CUBRID 개발랩에서는 변경 시의 장점이 없다고 판단하고 있다. DBMS 서버에서는 데이터베이스에 대한 관리, 디스크에 저장된 데이터를 메모리 구조체로 변환하고 관리하기 위한 버퍼 관리, 동시성 제어, 장애 복구를 위한 관리 등 DBMS가 갖춰야 할 기능 대부분을 수행하기 때문에 클라이언트보다 많은 일을 처리할 수 밖에 없고 당연히 시스템 리소스도 많이 사용한다. 대부분의 시스템의 한계 상황은 DBMS 서버가 처리할 수 있는 최대치인 경우가 많다. 현재와 같이 서버가 수행할 일부를 클라이언트에서 수행할 수 있다면 리소스 활용 면에서 유용하고, 확장 가능한 이점이 있다. 지금처럼 클라이언트 모듈에서 많은 일을 한다면 클라이언트가 사용하는 필요한 리소스만큼 DBMS 서버는 리소스를 더 사용할 수 있게 되므로, 서버가 처리할 수 있는 최대량은 증가할 수 있다. 때문에 현재의 구조를 유지하는 측면이 더 나은 것이다.

앞에서 2-tier로 연동하기 위한 방법 두 가지를 살펴보았다. 이 두 가지 방법이 모두 적합하지 않으므로, 다른 대안으로 3-tier구조를 생각해 볼 수 있을 것이다. 현재 DBMS 서버와 클라이언트 모듈이 동작하는 구조는 그대로 두고, JAVA, PHP등의 요청에 대해 처리해 줄 수 있는 중간 레이어를 둠으로써 애플리케이션에서 사용하는 드라이버는 상대적으로 단순한 작업만 하고, 요청에 대한 처리는 중간 레이어와 DBMS 서버에서 처리하는 형태를 떠올려 볼 수 있다. 이렇게 드라이버와 데이터베이스 서버 중간에 놓인 것이 CUBRID BROKER이다.

CUBRID BROKER

BROKER는 단일 프로세스는 아니고, cub_broker와 cub_cas라는 두 개의 프로세스가 있다. cub_cas는 드라이버의 질의 요청에 대해 실제 질의를 처리하는 프로세스로서 데이터베이스 서버의 클라이언트로서 쿼리 플랜을 만드는 등의 앞에서 설명한 작업을 한다. 드라이버에서 생성한 커넥션 중 실제 요청을 처리중인 커넥션은 하나의 cub_cas에 대응된다.(드라이버의 커넥션과 cub_cas의 대응은 뒤에서 좀더 다루기로 한다.) cub_broker 프로세스 역할은 드라이버에서 새로운 커넥션 요청이 올 경우 어떤 cub_cas를 할당할지를 결정하는 것과, cub_cas 프로세스를 관리하는 것이다. 하나의 cub_broker 프로세스는 다수의 cub_cas를 관리한다. cub_broker와 N개의 cub_cas가 하나의 BROKER 그룹이라고 한다. 사용자 설정에 따라 이런 그룹을 여러 개 운영할 수 있다.

위에 있는 첫번째 그림은 JDBC와 같이 브로커를 사용하는 애플리케이션이 커넥션 요청을 했을 때 프로세스간의 연결 과정을 나타낸다. 애플리케이션에서 커넥션을 요청할 경우 cub_broker로 요청을 보내고, cub_broker는 서비스 가능한 cub_cas를 선택하여 애플리케이션의 커넥션을 cub_cas로 전달함으로써 드라이버와 cub_cas가 연결이 맺어지도록 한다. cub_cas는 드라이버가 요청하는 DB 서버에 접속하기 위해 cub_master에 접속하여 cub_server에 대한 커넥션 요청을 보내게 되고, cub_master는 cub_cas로부터 연결된 커넥션을 cub_server로 이동시킴으로써 cub_cas와 cub_server간의 커넥션이 생성된다. 드라이버에서 커넥션을 끊는 경우 cub_cas와 cub_server간의 연결은 계속 유지된다.

BROKER의 필요성과 처리 과정 관점에서 살펴보았는데, 이런 3-tier 구조이기 때문에 갖는 장단점이 어떤 것이 있을지 살펴보도록 하겠다.

BROKER 단점

가장 먼저 생각할 수 있는 단점은 복잡해 보인다는 것이다. 애플리케이션 프로그램을 수행시키기 위해 서버에서 준비해야 할 것이 늘어나는 것도 있고, 처리과정을 이해하는데 필요한 부분이 늘어나는 것도 어려움이 될 수 있을 것이다.

엄밀하게 보면 데이터베이스 서버와 BROKER는 별개의 콤포넌트로 동작한다. DBMS 서버 입장에서는 cub_cas는 하나의 클라이언트이다. BROKER 입장에서 DBMS 서버는 드라이버 요청에 따라 선택적으로 연결해야 할 대상으로 볼 수 있다. 이런 관계가 처음 CUBRID를 접할 때 경우에 따라서는 어렵게 느껴지는 대상은 아닐까 생각한다. 예를 들어, 애플리케이션에서 사용 할 커넥션 수를 설정해 뒀는데 커넥션이 부족하여 늘려야 하는 경우가 있다고 가정하자. 이 경우 애플리케이션에서 사용하는 커넥션 수가 늘었기 때문에 BROKER 설정에서 최대 커넥션을 받을 수 있는 cub_cas가 구동 될 수 있도록 설정을 바꿔야 하고, 늘어난 cub_cas가 cub_server에 접속 가능하도록 cub_server의 설정도 바꿔야 한다. 이와 같이 커넥션이 늘어나는 설정을 변경하는 것이 왜 2개의 설정을 바꿔야 하는 것인지 이해하기 어려울 수도 있지만, 앞에서 간단히 언급한 것처럼 BROKER와 데이터베이스 서버가 별개의 역할을 수행할 수 있도록 한 것이 가장 크다고 생각한다. 그렇지만, 이 부분에 대해 개선을 고려 중이다. BROKER와 데이터베이스 서버가 독립적으로 운영됨으로써 구성의 유연성은 증가될 수 있는데, 제약을 두는 것은 확장성 측면에서 좋지 않은 방향이 될 가능성이 높다고 생각한다.

CUBRID 설정은 cubrid.conf와 cubrid_broker.conf 두가지 파일을 통해서 설정하는데, 데이터베이스 서버 및 클라이언트 라이브러리에 대한 동작은 cubrid.conf를 통해서 설정하고, BROKER에 대한 동작은 cubrid_broker.conf를 통해 설정한다.

CUBRID Configuration Files

두 번째 단점은 홉 증가에 따른 성능 저하이다. 처리단계가 늘어나기 때문에 성능이 떨어지는 것은 당연하게 생각될 수 있을 것이다. 성능은 응답시간(response time)과 처리량(throughput)으로 측정할 수 있는데, 처리하는 단계가 증가하면 응답시간도 길어지게 된다. 만약 JDBC를 이용해서 단일 쓰레드로 쿼리를 반복 수행하는 작업을 한다면 처리시간이 길어질 수 있을 것이다. 쿼리 하나를 수행할 때 응답시간의 차이는 수 ms 이내의 차이가 발생하겠지만, 반복횟수가 증가하게 되면 큰 차이가 될 수도 있다. 응답시간이 길어지는 가장 큰 부분은 통신횟수의 증가인데, 단일 클라이언트가 아닌 멀티 클라이언트 환경에서는 통신 증가에 따른 시간 증가가 어느 정도 상쇄된다. 또한, 서버 부하가 극단적으로 높아지는 환경인 경우에는 처음에 이야기한 것처럼 데이터베이스 서버와 클라이언트 모듈을 분리함으로써 서버가 활용할 수 있는 리소스를 더 확보할 수 있는 장점이 있다. 서버가 활용할 수 있는 리소스가 많아진다는 것은 그만큼 처리량이 늘어날 수 있다는 것을 의미하게 된다. 즉, 성능 중 처리량 관점에서는 크게 영향을 받지 않거나, 더 나은 성능을 줄 수 있으므로 우려할 부분은 아니라고 생각한다. 요즘은 connection pooling과 statement pooling을 사용하는 것이 일반적인데, connection/statement 풀링은 드라이버에서 보내는 요청 자체를 줄이기 때문에 2-tier든 3-tier구조이건 성능 개선 효과는 클 수 있다.

세 번째 단점으로는 BROKER라는 중간 레이어가 있기 때문에 장애 포인트가 늘어나는 점이다. 물론 CUBRID는 여기에 대한 대비를 하고 있다. CUBRID는 HA를 지원하고 BROKER에 대한 이중화도 지원하고 있다. 애플리케이션에서 CUBRID에 접속하기 위한 커넥션 URL은 서버주소, 포트번호, DB이름, DB사용자이름, DB사용자 패스워드로 구성되는데, 해당 IP/port로 접속이 실패할 경우 연결을 시도할 접속정보를 추가로 설정할 수 있다. 즉, BROKER를 이중화하여 한쪽이 장애가 발생할 경우 다른 BROKER를 통하여 서비스함으로써 장애에 대해 대비할 수 있다.

BROKER 장점

지금부터는 BROKER가 가지는 장점들을 살펴보고자 한다. 첫 번째로 커넥션을 처리하는 cub_cas를 공유할 수 있는 점이다. 앞에서 이야기한 것처럼 애플리케이션에서 생성한 커넥션 중 실제 질의를 처리하는 커넥션은 cub_cas와 1:1로 연결되어 있다. 즉 동시에 처리중인 커넥션 수만큼의 cub_cas 프로세스가 동작해야한다. 만약 애플리케이션에서 생성한 커넥션은 2개인데, cub_cas는 하나만 있을 경우 어떻게 처리될까? 두 개의 커넥션을 편의상 C1, C2라고하고, C1이 cub_cas에 연결되어 질의를 처리하고 있다고 가정해보자. 이때 C2가 커넥션 요청을 보내게 되면 cub_broker는 가용한 cub_cas가 있는지 찾는다. 이때는 C1을 처리하고 있는 cub_cas 프로세스 하나만 있다. 이 경우 현재 서비스중인 cub_cas는 C1과 커넥션을 끊고 C2를 처리하게 되는데, C1과의 커넥션이 끊어질 수 있는 시점은 C1에서 커넥션을 끊었거나, 트랜잭션이 종료된 후 다음 질의를 시작하기 이전 시점일 경우에만 가능하다. C1, C2 두개의 커넥션에 대해 2개의 cub_cas가 실행중이라면 중간에 커넥션이 끊어지는 경우없이 C1, C2 각각의 질의를 처리하지만, cub_cas가 하나인 경우에는 C1, C2각각 트랜잭션이 종료되는 시점에 cub_cas와 커넥션이 끊어지면서 cub_cas를 공유하는 형태로 사용하게 된다. 드라이버 입장에서는 트랜잭션 종료 후에 자신이 가진 커넥션이 앞의 상황으로 인해 끊어질 수 있다는 것을 인지하고 있으므로 커넥션이 끊어진 경우 재연결을 시도하게 된다. 만약 커넥션 풀링을 하고 있고, 커넥션이 풀에서 대기 상태에서 서버가 재시작되는 경우가 있다고 하더라도 앞의 상황과 동일한 것으로 인식되어 커넥션을 복구하게 되므로 애플리케이션입장에서는 커넥션의 유효성을 별도로 검사하지 않아도 된다. 두 개의 커넥션이 하나의 cub_cas를 공유하는 과정의 처리 흐름은 다음과 같다.

cub_cas-connction-management.png

하지만, 자주 사용되는 커넥션이 앞에서 설명한 것처럼 하나의 cub_cas를 두고 경쟁하게 되는 것은 분명 성능상의 문제를 발생시키므로 적절한 수의 cub_cas가 구동될 수 있도록 설정되어야 한다. 그렇지만, 커넥션 풀에 생성한 커넥션 대부분이 잠들어 있는 상황이라면 커넥션을 공유하는 것이 중요할 수 있다. 이 부분을 활용한 것이 CAS for Oracle/MySQL로서 뒤에서 조금 더 설명하도록 한다.

두 번째 장점으로는 모니터링과 로깅을 들 수 있다. JDBC등의 애플리케이션에서 요청한 모든 질의는 BROKER를 통하게 되므로 BROKER의 상태를 통해 처리 과정을 일부 파악할 수 있다. BROKER 상태를 모니터링하는 유틸리티를 통해 상태정보를 확인할 수 있는데, cub_cas별로 보여주는 상태정보는 다음과 같은 의미를 가진다.

  • IDLE – cub_cas가 드라이버와 연결되지 않은 상태.
  • CLIENT WAIT –애플리케이션의 커넥션에서 트랜잭션이 진행중인 상태이며 애플리케이션이 요청을 보내기를 기다리는 상태. 예를 들어 INSERT, UPDATE 두 개의 질의를 수행한 후 커밋해야 하는데, INSERT가 수행 완료된 후 UPDATE 질의가 요청되기 이전 시점까지 CLIENT WAIT 상태를 가진다. 이 상태가 지속되는 것은 애플리케이션에서 요청을 보내지 못하는 상황으로 볼 수 있다.
  • CLOSE WAIT – 트랜잭션이 완료된 상태에서 애플리케이션이 다음 질의를 시작하지 않은 상태. 이 상태에서 새로운 커넥션의 요청이 올 경우 IDLE인 cub_cas가 없을 경우 앞에서 설명한 것처럼 커넥션이 바뀔 수 있다.
  • BUSY – 데이터베이스 서버로 질의 수행을 요청한 상태. 수행중인 질의문을 모니터링 유틸리티에서 확인 할 수 있다.
이 외에 BROKER에서 처리되고 있는 QPS, 에러 수, slow query 수 등의 정보도 알 수 있다. 모니터링 외에 BROKER에서 수행되는 정보는 로그 파일을 통해 남는데, 커넥션 관련 정보 및 질의 수행에 대한 정보를 얻을 수 있다. 다음은 JDBC를 이용한 간략한 애플리케이션을 수행한 경우 어떤 형태로 쿼리 로그가 남는지의 예를 보여준다. 질의가 수행될 경우 언제 쿼리가 수행되었는지, 어떤 값이 바인딩 되었는지, 수행시간, 결과 튜플수 등에 대한 정보를 얻을 수 있다.


String connection_url = "jdbc:cubrid:localhost:53300:testdb:public::?";

Connection con =   DriverManager.getConnection(connection_url, null,   null);

String query = "select   * from foo where id = ?";

PreparedStatement pstmt = con.prepareStatement(query);

pstmt.setInt(1, 1);

ResultSet rs = pstmt.executeQuery();

while (rs.next())

{

// fetch result

}

rs.close();

pstmt.close();

con.close();


03/21   18:51:36.467 (0) CLIENT IP 127.0.0.1

03/21   18:51:36.473 (0) connect db cgkdb user public url   jdbc:cubrid:localhost:53300:cgkdb:public::?

03/21   18:51:36.476 (1) prepare 0 select * from foo where id = ?

03/21   18:51:36.477 (1) prepare srv_h_id 1

03/21   18:51:36.491 (1) execute srv_h_id 1 select * from foo where id = ?

03/21   18:51:36.491 (1) bind 1 : INT 1

03/21   18:51:36.529 (1) execute 0 tuple 1 time 0.055

03/21   18:51:36.529 (0) auto_commit

03/21   18:51:36.529 (0) auto_commit 0

03/21   18:51:36.529 (0) *** elapsed time 0.052

03/21   18:51:36.537 (0) con_close

03/21   18:51:36.537 (0) disconnect

세 번째 장점은 BROKER의 다양한 모드이다. 앞에서 간략한 설명이 있었지만 CUBRID는 HA를 지원한다. 데이터베이스 서버는 active-standby 구조를 가지며, active서버에 유입된 데이터는 standby서버로 복제되어 active 서버 장애 시 standby 서버로 자동 절체되어 서비스가 지속될 수 있도록 한다. 데이터의 입력, 삭제, 수정은 active 서버로 유입되어야 하고, 조회는 active, standby 서버에서 모두 가능하다. 애플리케이션은 용도에 따라 active 서버로 접근하기를 원하거나, standby 서버로 접근하기를 원할 수 있는데, 어떤 서버에 접근할지를 선택하는 모드가 필요할 수 있다.

BROKER는 read-write 모드와 read-only 모드가 있는데, read-write모드는 말 그대로 읽기, 쓰기를 위한 용도로서 active서버에 접속하기 위한 모드이다. read-only모드는 읽기를 위한 용도로서 standby 서버에 접속하기 위한 모드이다. BROKER가 read-only모드로 설정된 경우 standby 서버가 접속 가능한 상태이면 standby서버에 접속하고, standby 서버가 장애 상황인 경우에는 active 서버로 접속하게 된다. Active/standby 서버의 장애로 인해 구성이 변경된다고 하더라도, BROKER에서 read-write/read-only 모드에 따라 active/standby 서버를 찾아가므로 애플리케이션에서는 장애로 인한 서버 변경에 대해 고려할 필요가 없다.

read-only모드와 조금 다른 slave-only 모드도 제공 하는데, Slave-only 모드는 standby 서버에만 접속하는 모드를 의미한다. 만약 standby서버에 장애가 발생하여 active 서버 하나만 존재하는 경우에는 slave-only BROKER에 요청한 커넥션은 모두 에러를 반환하게 된다. 이 모드는 standby 서버가 있으면 접속을 하지만, active 서버만 있는 상황에서는 요청을 처리하지 않아도 되는 경우가 해당된다. 예를 들어 데이터 확인을 위한 용도로 서버에 접근할 때 서비스 영향을 고려하여 active 서버에는 접속하지 않아야 하는 경우이거나, 주기적으로 데이터를 수집해야 하는 상황에서 단기간 데이터를 수집하지 못하는 상황이 문제가 되지 않을 경우에는 standby 서버가 장애 상황인 경우 active 서버에 접속하지 않음으로써 서비스에 영향을 주지 않는 목적으로 사용될 수 있다.

Standby 서버의 장애 상황이 길어져서 active 서버에 접속해야 한다면 BROKER의 모드를 변경함으로써 애플리케이션의 설정 변경 없이 active 서버에 접속할 수 있게 된다. 브로커 모드에 따른 서버 접속을 그림으로 나타내면 다음과 같다.

cubrid-broker-modes.png

BROKER의 장단점에 대해 간략히 이야기해 보았는데, 마지막으로 어떤 형태의 발전이 가능할 지 몇 가지 예를 들어보고자 한다.

CAS for Oracle/MySQL

CAS for Oracle/MySQL은 cub_cas가 CUBRID에 연결하는 것을 ORACLE과 MySQL에 연결하여 드라이버의 요청을 처리하는 것으로, CUBRID 드라이버와 BROKER를 사용하고 DBMS는 Oracle 또는 MySQL이 되는 구조이다. 커넥션 관리에 대한 장점을 Oracle/MySQL에 대해 활용하기 위한 것이 가장 큰 목적이라고 볼 수 있다. 예를 들어 다음과 같은 환경을 가정해 보자. 하나의 웹 서버에서 특정 용도의 커넥션 풀에 2개의 커넥션이 만들어져 있고, 이 커넥션은 대부분 커넥션 풀에서 대기 상태이며, 이런 웹 서버 10개가 운영중이다.  이 경우 20개의 커넥션이 만들어지겠지만, 사용빈도가 높지 않을 경우 커넥션은 대부분 잠들어 있는 상태가 될 것이다. 관리용 커넥션 풀을 따로 만드는 등 용도별로 커넥션 풀을 따로 만들 경우 이렇게 잠들어 있는 커넥션은 더욱 많아질 것이다. 서비스 규모가 커져서 웹 서버 수가 늘어나게 된다면 잠들어 있는 커넥션 수도 비례하여 늘어나게 된다. 이 경우 BROKER에서 애플리케이션의 커넥션을 공유할 수 있으면 커넥션 수가 증가하는 것을 막을 수 있게 된다. 웹 서버에 설정한 커넥션 총합이 아닌 실제 DBMS 사용 부하에 맞게 cub_cas가 구동되도록 설정한다면 DBMS 서버 부담을 줄일 수 있다. 다음 그림은 CAS for Oracle/MySQL의 구조를 간략히 나타낸 것이다.

cubrid-cas-for-oracle.png

DB sharding

데이터베이스 규모가 커지면서 하나의 장비에서 관리할 수 있는 규모 이상의 데이터를 관리해야 하는 경우가 발생하게 된다. 이와 같은 경우 동일한 스키마를 가진 여러 개의 데이터베이스에 데이터를 분할하는 것이 필요하게 된다. 일반적으로 애플리케이션에서 처리할 경우에는 sharding key를 선정하고, 키 값으로 shard를 결정하는 룰을 정하여 데이터베이스 선택한 후 질의를 요청하는 형태가 될 것이다. 예를 들어 카페 데이터베이스의 경우 카페 ID가 shard 키가 되고, 해시 함수를 통해 shard를 결정한다. BROKER에서 이런 sharding 방법을 지원한다면, 애플리케이션에서는 질의를 요청할 때 sharding 키 값도 같이 전달하고, BROKER는 전달받은 sharding 키를 이용하여 shard를 결정한 후 선택된 shard에 연결하는 방법을 적용할 수 있다. Sharding을 BROKER에서 지원하게 되면 shard를 결정하기 위한 로직을 애플리케이션에서 구현할 필요가 없게되고, shard가 늘어나는 경우에 BROKER 설정만으로 추가된 BROKER를 활용할 수 있을 것이므로 애플리케이션에 주는 영향은 거의 없어지게 되는 장점을 가질 수 있다.

Load balancing

DB에 읽기 부하가 증가하는 경우 선택 가능한 대안 중 하나는 복제 서버를 늘려서 read 요청을 분산시키는 방법일 것이다. CUBRID도 HA를 통해 1:N 의 active-standby를 지원하며, read 분산을 위한 경우에는 별도의 replica를 추가로 둘 수 있는 방법을 제공한다. 단순하게 보면 n개의 slave를 가진다고 생각하면 된다. BROKER에서 load balancing을 할 경우 하나의 BROKER에서 여러 개의 복제 서버에 대해 부하를 판단하면서 동적으로 조정이 가능하게 될 것이다. 예를 들어 10개의 커넥션이 만들어져 있는 상황에서 5개는 복제서버 1로 연결되고, 나머지 5개는 복제서버2로 연결되었다고 가정해보자. 각각 5개의 연결이 만들어지는 시점에는 각 커넥션이 얼마나 사용될지 예측을 할 수 없게 되는데, 커넥션 풀이 First-In-First-Out 방식으로 동작하게 될 경우 커넥션 별 처리량은 편차가 커질 수 있고 복제 서버 별 처리량도 큰 차이가 발생할 수 있다. 동적으로 처리량을 판단하여 커넥션을 바꿔 줄 수 있게 된다면 처리량을 비슷하게 유지시킬 수 있게 되고, 복제 서버가 2개에서 3개로 증가되는 경우 BROKER에서 서버정보가 추가되었다는 설정 변경만으로 자동으로 부하를 고르게 배분시킬 수 있게 될 것이다.

DB sharding과 Load balancing의 공통점은 질의가 처리되는 시점에 수행할 대상이 되는 DB를 선택하는 것이 필요하다는 것인데, 이런 형태의 요청 처리에는 3-tier구조가 훨씬 유연하게 대응할 수 있을 것이다. DB sharding과 Load balancing기능은 현재 지원하지 않는 기능이다.

마치면서

지금까지 CUBRID의 BROKER에 대해 간략히 살펴보았다. 3-tier구조이기 때문에 갖는 단점도 분명히 존재하지만, 서버 환경이 복잡해질수록 커넥션에 대한 유연성을 확보해 주는 측면에서는 유용한 점이 많다고 생각한다. 또한 CAS for Oracle/MySQL 을 시작으로 구조적인 장점을 살려 독립적인 미들웨어로 발전하는 것이 큐브리드 브로커의 최종 모습이 아닐까 생각하고 있다.

NHN Business Platform, DBMS개발랩, 강철규.



comments powered by Disqus