Bren Newman | MS SQL 서버 개발팀 수석 프로그램 매니저
마이크로소프트의 SQL 서버 고객개발지원 팀은 양방향 트랜잭션 복제의 강점에 대해서 보여주고 있다.
분산 데이터베이스 시스템을 관리하면서 빨리 동기화 될 필요가 있는 것들 외에 해볼만한 일들을 많이 볼 수 있다. 트랜잭션 복제와 통합 복제는 효과적인 복제방법으로 잘 알려져 있지만, 제약 사항들과 성능상의 한계가 있을 수 있다. 만약 이런 복제 방식의 제약 사항들 때문에 실망한다면, 다른 대안(양방향 트랜잭션 복제)을 찾고자 할지도 모른다. 어떤 경우에는, 이런 복제 방식 구현을 통해 매우 빨리, 지리적으로 분산된, 여러 곳을 업데이트할 수 있는 방안을 제공한다. (몇몇 장애물들은 뛰어넘어도 상관없다면). 마이크로소프트의 SQL 서버 고객개발지원 팀은 고객들이 재무, 주식거래, 통신과 같은 시장에서 양방향 트랜잭션 복제의 아키텍트(architect)를 구성하도록 도와준다. 이런 고객들의 대부분은 장애조치 혹은 재해 복구 사이트로 빠르고 쉽게 옮길 수 있는 방안으로 데이터베이스를 생성하는 방법을 사용한다.
SQL 서버는 SQL 서버 7.0 이후에 빠르고 강력한 트랜잭션 복제를 지원하고 있다. 그러나, 부적절한 문서와 기능에 대한 낮은 이해도 때문에 이를 알고 있거나 이용하고 있는 DBA들은 몇 명 되지 않는다. SQL 서버 7.0 온라인 설명서(BOL)는 양방향 트랜잭션 복제에 대해서 부족한 부분을 가지고 있다. 게다가 마이크로소프트는 유감스럽게도 SQL 서버 2000 초기 BOL에서도 생략했다. 마이크로소프트는 최근의 SQL 서버 2000 BOL 버전에 몇 개의 문서를 포함시켜 놓았으며, http://www.microsoft.com/sql/techinfo/p ··· oks.asp. 에서 다운로드 받을 수 있다.
[웹 리스트 1](http://www.adminmag.com의 자료실에서 다운로드 받을 수 있음)은 4개의 복제 방법인 양방향 트랜잭션, 트랜잭션 지연, 트랜잭션 즉시 업데이트, 병합 (bi-directional transactional, transactional queued, transactional immediate updating, merge )의 특징을 비교하고 있다. 대부분의 DBA들은 기록된 트랜잭션들을 하나의 소스 즉 게시자(Publisher)에서 일반적으로 수정하지 않고 읽기만 가능한 대상서버(예, 보고서버(reporting server))인 구독자(Subscriber)에게 배포하기위해 트랜잭션 복제를 사용하다. DBA들은 집계 읽기 성능을 향상시키기 위해서 전형적으로 이런 유형의 복제를 사용한다. MS SQL 서버 7.0에서 소개되었던, 즉시 업데이트 구독(immediate updating subscription)에서 트랜잭션 구독자는 구독자(Subscriber)에 있는 데이터를 수정할 수 있다. 그러나, 구독자는 게시자(Publisher)에 네트워크 연결만을 통해 데이터를 수정할 수 있다(즉, 구독자(Subscriber)는 반드시 on-line 상태이어야 함). SQL 서버 2000에서 지연 업데이트 구독자(queued updating subscriber)는 게시자(Publisher)에 연결되지 않은 상태에서도 구독자(Subscriber)의 데이터를 수정할 수 있다. 이 두 방법들은 사용자 테이블에 컬럼을 추가해야 한다. 게다가 이 방법들은 텍스트 혹은 이미지 컬럼의 업데이트와 자동 생성된 트리거를 통한 데이터 변경내용 추적 기능을 지원하지 않는다.
비록 병합 복제(SQL 서버 7.0에서 소개됨.)에 텍스트와 이미지 컬럼에 대한 수정을 할 수 있더라도 트리거를 사용하고 16bytes의 전세계적으로 유일한 uniqueidentifier 데이터 형식의 복제별 컬럼(즉, GUID)을 테이블들에 추가 해야 한다. 이런 트리거들과 컬럼들을 추가하면 몇몇 애플리케이션들에 문제가 야기될 수 있으므로 많은 DBA들은 관례적으로 난색을 표하게 된다. 마이크로소프트는 특히 오프라인의 모바일 사용자들(예, 영업 자동화 애플리케이션의 사용자), 충돌에 대한 추적, 매우 유연한 분할 및 필터링 기능과 같은 운영 능력에 적절하게 병합 복제를 설계했다. 그러나 이런 이익을 위해서 트랜잭션 일관성의 손실, GUID컬럼과 트리거 추가, 그 밖의 메타데이터(예를 들면 병합 생성 트리거는 데이터 변경 사항들과 발생 가능한 충돌들을 확인할 수 있는 추적 테이블에 있다.)의 저장 공간을 감안해야 한다.
높은 성능, 새로운 컬럼을 추가하지 않고 동기화할 때, 업데이트 가능성과 트랜잭션 일관성 둘을 유지할 수 있는 비-트리거기반 방식을 원한다면, 양방향 트랜잭션 복제가 해결책이 될 수 있다. 이름에서 암시하듯이, 양방향 트랜잭션 복제 에이전트는 트랜잭션 복제 에이전트(로그 판독기 에이전트(Logreader Agent)와 배포 에이전트(Distribution Agent))를 사용하여 두개의 서버 간의 복제를 구현한다. 로그 판독기 에이전트는 데이터베이스 로그에서 게시물(article)들을 복제할 트랜잭션을 찾고, 대응되는 SQL INSERT, UPDATE 혹은DELETE 구문을 생성하며, 이 구문들을 배포 데이터베이스에 트랜잭션 단위로 작성한다. 배포 에이전트는 배포자 서버 데이터베이스에서 복제할 트랜잭션을 찾아서 구독 데이터베이스에 작성한다. 이런 유형의 복제에서, 게시자(Publisher)는 또한 구독자(Subscriber)이고, 구독자(Subscriber)는 또한 동일한 데이터의 게시자(Publisher)이기도 하다. 예를 들면 [그림 1]에서 보듯이, LONDON 서버는 Customer_Publication_A로 Customer 테이블을 게시하고 CAPETOWN 서버 은 LONDON 서버 의 Customer_Publication_A에서 구독한다. 또한, CAPETOWN 서버 은 Customer_Publication_B 로 동일한 Customer 테이블을 재게시하고 LONDON 서버 은 Customer_Publication_B를 구독한다. 이 방법은 Customer 테이블에 복제별 컬럼을 추가하거나 변경 내용들을 추적하기 위해 둘 중 하나의 서버에 트리거를 추가하지 않고 구성할 수 있다는 것을 명심해야 한다.
필자가 설명한 관계가 둘 중 하나의 서버에서 발생한 변경이 서버들 사이에서 끊임 없이 데이터를 발생 시킬 수 있기 때문에 정상적으로 작동되지 않을 수 있다는 것에 대해 생각해 봤을지도 모르겠다. 만약 사용자가 LONDON 서버에서 Customer 레코드를 수정했다면, LONDON 로그 판독기 에이전트는 트랜잭션을 검출하여, LONDON서버의 배포 데이터 베이스에 트랜잭션을 저장한다. CAPETOWN서버에 속한 배포 에이전트는 LONDON서버에 있는 배포 데이터베이스에서 트랜잭션을 가지고 온 후 CAPETOWN 서버의 Customer 테이블에 해당 내용을 저장한다. 그러나 게시 데이터베이스 로그를 모니터링 하고 있는 CAPETOWN 서버의 로그 판독기 에이전트는 또한 트랜잭션을 찾아서 CAPETOWN서버의 배포 데이터베이스에 저장할 것 이다. LONDON서버의 배포 에이전트는 CAPETOWN 서버의 배포 데이터베이스에서 트랜잭션을 찾아서 LONDON서버에 재적용시킬 것이다. 둘 중 어느 서버도 최초에 변경된 서버가 어느 것인지 알지 못하기 때문에, 두 서버 모두 계속적으로 업데이트를 전파시키게 된다. 복제를 하기 원했었으면, 매트릭스 3 (The Matix Revolutions)에서의 스미스 요원이 필요했었다. 고맙게도 그 대안은 상대적으로 간단하다. 구독자(Subscriber)에게 구독(subscription)을 추가하기 위해 저장 프로시저 sp_addsubscription 을 사용할 때, 저장프로시저에 @loopback_detection 매개변수의 값을 ‘true’ (@loopback_detection=’true’) 추가할 필요가 있다( sp_addsubscription에 대한 좀 더 많은 정보는 http://support.microsoft.com/default .aspx?scid=kb;en-us;820675&Product=sql.에서 Microsoft 의 기사 중 “HOW TO: Implement Bidirectional Transactional Replication” 참조). 이 매개변수를 추가하면 배포 에이전트가 최초의 개시자(Publisher)에서 재배달을 하지 않도록 하기 위해 각 트랜잭션을 제대로 확인하게 된다. 충돌뿐 아니라 레코드와 컬럼 수정이 제대로 이루어지기 위해 복잡한 코딩이 필요한 트리거 기반의 병합 복제와는 달리 로그 기반의 트랜잭션 복제 방식은 상대적으로 간단하다. 양방향 트랜잭션 복제는 게시자 (Publisher)로 하여금 트랜잭션들을 읽어서 구독자 (Subscriber)에게 빠른 속도(초당 수천개의 구문처리)와 짧은 대기시간(하나의 서버에서 변경사항이 발생한 시간과 다른 서버에서 그 내용이 반영되는 시간사이의 간격)으로 트랜잭션들을 적용시킨다. 물론 이런 속도나 대기시간은 네트워크와 디스크 읽기/쓰기 대기시간과 수정되고 있는 레코드의 크기에 따라 차이가 있을 수 있다. New York과 Los Angeles처럼 멀리 떨어져 있음에도 불구하고 빠르고 안정적인 네트워크상의 서버들은 단 몇 초(가능한 4초 이하) 동안 처리할 수 있었다. 게다가 병합 복제와는 달리, 트랜잭션 복제는 저장 프로시저의 실행을 복제할 수 있다. 많은 수의 레코드들을 수정하려고 할 때 저장 프로시저 복제를 사용하고자 할 수 있다. 네트워크를 통해서 처리된 레코드들을 옮기는 것 대신 게시자(Publisher)가 실행시키는 프로시저의 실행 구문만을 복제하는 것이다. 다음의 구문을 예로 들 수 있다.
따라서, I/O, 네트워크, CPU 시간들을 많이 감소 시킬 수 있다.
적절한 방식은?
양방향 트랜잭션 복제를 사용하면 스키마를 바꾸지 않고 성능향상은 얻게 된다고 생각할 수 있다. 그러나 이런 유형의 복제에 대한 비용과 단점들에 대해서 평가해야만 한다. 양방향 트랜잭션 복제가 실제 환경에 적절하다고 판단하기 전에 복잡한 초기 설정, 어려운 충동 관리, 제한적인 확장성에 대해서 검토할 필요가 있다.
복잡한 설치과정. 양방향 트랜잭션 복제가 완벽하게 UI 마법사를 지원하기 않기 때문에 복제 설치 스크립트를 직접 수정하거나 만들어야 한다. 그리고 구독자(Subscriber)의 초기화는 복잡해 질 수 있다. 예를 들면 [그림 1]에서 보듯이 두 개의 서버 사이에서 데이터베이스의 양방향 트랜잭션 복제를 설정하기 위한 방법 중 하나는 다음의 단계를 거친다.
* LONDON 서버에서 데이터베이스를 분리시키고, CAPETOWN 서버에 연결시킴.
* LONDON 서버와 CAPETOWN 서버에 트랜잭션 게시(transactional publication)를 만들기 위해서 게시(Publication) 복제 만들기 마법사 (Create Publication Replication Wizard)를 사용.
* 복제하고자 하는 데이터를 이미 가지고 있기 때문에 LONDON 서버에서 게시를 구독하고 있을 때 CAPETOWN 서버에서 no-sync 옵션을 사용.
* 불행히도 구독할 때, 구독추가 복제 마법사(Replication Add Subscription Wizard)를 사용하여 매개변수 중 @loopbak_detection = ‘true’를 추가할 수 없다. 그래서 구독(subscription)에 대한 복제 스크립트를 생성하고 저장하기 위해 CAPETOWN 서버에서 스크립트 생성 마법사(Generate Script Wizard)를 사용해야 한다. CAPETOWN에서 구독을 취소하고 생성하고 저장한 복제 스크립트를 수정하여 sp_addsubscription 프로시저를 호출하기 위해 @loopback_detection=’true’를 추가한 후 CAPETOWN 서버에서 스크립트 실행.
* LONDON 서버에 대해 올바른 서버와 데이터베이스 이름을 추가하기 위해 복제 스크립트 재수정 후, LONDON 서버에서 스크립트 실행.
양방향 트랜잭션 복제를 설정하는 것이 쉽지 않았다.
복잡한 충돌 관리
고려해야 할 다음 사항은 충돌관리로 좀더 어려운 부분이다. 충돌은 두 개의 서버들이 동기화 되지 않은 데이터에 대한 동일한 레코드를 수정할 때 발생할 수 있다. 복제의 다른 업데이트 할 수 있는 구독자(Subscriber) 유형들과는 달리, 양방향 복제는 즉각적인 충돌관리 대안을 제공하지 않는다. 대신에 장비들로 해결할 수 있다. 충돌을 처리하기 위해 병합 복제는 복잡한 충돌(예, 레코드 레벨 과 컬럼 레벌 충돌)들을 관리하기 위한 여러 특징들을 가지고 있고, T-SQL이나 다른 프로그래밍 언어로 정의된 사용자 정의 충돌 처리가 가능하도록 개발자와 설계자들에게 뛰어난 유연성을 제공한다. 즉시 업데이트 복제 구독자들은 2단계 커밋(2PC, 2-Phasce commit)을 수행하기 위해서 DTC(Distributed Transaction Coordinator)를 사용함으로써 충돌을 피하게 된다. 그러나 이런 기술은 온라인 연결 환경에서만 가능하다. 비록 지연 업데이트 방안이 오프라인 데이터 변경을 지원하지만 지연 업데이트는 제한적인 충돌처리 모델을 지원한다. 즉, 구독자(Subscriber)이든 게시자 (Publisher)이든 변경은 전체 트랜잭션 일괄작업보다 우선권을 갖게 될 것이다. 게다가 앞에서 언급했듯이 지연 업데이트이든 즉시 업데이트이든 둘 다 텍스트나 이미지 데이터 형식 에 대한 수정은 불가능 하다. 반면에 병합 복제와 양방향 트랜잭션 복제는 가능하다.
충돌을 예상하지 않고 있을 때나 응용프로그램 제약(예, 사용자들이 데이터 소유권에 따라 데이터를 수정할 수 있음)을 사용하여 충돌을 피할 수 있을 때나 게시(publication)가 필터링 되었을 때 혹은 한번에 오직 하나의 서버만이 데이터를 수정할 때 양방향 트랜잭션 복제는 최적으로 작동된다. (마지막 상태는 고 가용성 혹은 장애조치 시나리오를 종종 보게 된다.) 양방향 트랜잭션 복제에서 여러 방법으로 충돌을 피할 수 있다.
많은 데이터베이스 기본 키(primary key)로 IDENTITY 데이터 형식을 사용하여 설계된다. 양방향 트랜잭션 복제환경에서 이런 유형의 테이블 설계를 효과적으로 사용하고 중복 키 (duplicate key)와 같은 충돌을 비하기 위해서는 설정과정에서 두 단계를 거쳐야 한다. 첫 번째로 IDENTITY 컬럼이 NOT FOR REPLICATION 속성을 갖도록 정의해야 한다. 두 번째는 설계 시 서버 모두에 독립적인 레코드 삽입이 고려되어야 한다면 각 IDENTITY 컬럼은 다른 숫자로 시작될 필요가 있다. 예를 들어 LONDON 서버의 Customer 테이블은 IDENTITY 값은 0으로 시작하고 CAPTETOWN 서버의 Customer 테이블은 IDENTITY 값이 1,000,000으로 시작하도록 조건을 지정할 수 있다. DBCC CHECKIDENT RESEED 구문으로 사용하거나 최초의 설정과 동기화 단계 이후에 복제되는 서버들에게 구문들을 보내기 위해서 sp_addscriptexec 을 사용하여 이런 조건을 설정할 수 있다.
다른 방법으로 기본 키(primary key)로 UNIQUEIDENTIFIER를 사용하여 테이블을 설계하면 중복 키(duplicate key)를 피할 수 있다. 그러나, 이 방법은 전체적으로 필요한 저장공간이 늘어나게 되고 모든 사람들은 C677EF69 -8645-4C1D-8816-7E629F8B1AA0(SQL 서버가 데이터 형식에 맞게 생성해준 번호임)와 같이 어의 없는 고객번호를 가지는 것을 싫어한다.
양방향 트랜잭션 복제에 발생된 충돌에 대한 추적은 또 하나의 어려운 문제일 수 있다. 특히 업데이트 충돌은 하나의 레코드의 특정 컬럼에 대한 가장 마지막 업데이트가 이전의 결과에 번복되기 때문에 추적하기 어렵다. 예를 들면 레코드가 LONDON 서버에서 업데이트 되어지면 대상 서버인 CAPETOWN에 도착할 때, 이미 그 레코드가 삭제되어 있을 때 충돌이 발생한다. 이런 상황에서 트랜잭션 복제는 기본적으로 에러를 발생시키고 프로세서를 중지시킨다. 이때 관리자는 배포 데이터베이스에서 복제 시스템 테이블들의 레코드를 삭제하거나 수정할 수 있는 기능(그렇게 하면 “시스템 테이블을 수정하지 말아라”라는 경고 발생) 혹은 구독(subscription)을 다시 초기화시키는 기능을 포함하여 여러 개의 옵션을 설정할 수 있다. 그러나 다시 초기화 시키는 것은 연결 유형에 따라 시간이 한없이 걸릴 수 있다. 또 다른 대안은 Skiperrors %error_number% 매개변수를 추가하여 배포 에이전트를 재 실행 시키는 것이다. 충돌의 위험을 좀 더 줄이기 위해서 Region_ID와 같은 컬럼에 대한 게시(publication)를 필터링 할 수 있다. 이런 필터링으로 LONDON 서버는 Region_ID=1인 데이터만 게시할 수 있고, LONDON 서버는 Regioen_ID=2인 데이터만 게시하고 있는 CAPETOWN 서버를 구독할 수 있다.
충돌에 대한 추적과 관리에 대한 방법이 나뉘어 있지 않은 다음과 같은 방법들 중 하나를 사용할 수 있다. 사용자 정의 코드를 포함시키기 위해서 복제 시 생성된 저장프로시저의 내용 수정하기, 복제될 테이블에 트리거를 추가하거나 추적 컬럼 추가하기(예, 범용적인 datetime, uniqueidentifier, priority 데이터 형식의 컬럼들), 수정되어진 레코드의 현재와 이전 값들 모두 사용가능 하도록 XCALL 옵션 선택하기, 혹은 위의 방법들을 조합하여 사용하기에 대해서 생각해 볼 수 있다. 이런 방법들에 대한 좀 더 자세한 정보와 예제 코드에 대해서는 mk:@MSITStore:C:\Program%20Files\Microsoft%20SQL%20Server\80\Tools\Books\replprog .chm::/rp_replsamp_3ve6.htm.에서 BOL 주제 중 “양방향 복제(Bidirectional Replication)을 참고하기 바란다. 충돌처리는 양방향 트랜잭션 복제의 유일한 약점이다. 제한적인 확장성. 양방향 트랜잭션 복제에 대해서 검토할 때 두개 이상의 서버를 추가하여 양방향 설계를 확장할 계획이 있는지 없는지에 대해 고려해야 한다. 분할을 하기 위해 필터를 추가하는 것은 게시(publication)가 양방향 복제를 여러 개의 서버들로 확장 시킬 수 있게 할 의도가 있다. 그러나 복잡도와 관리비용의 급격한 증가, 잠재적인 성능 저하들을 가져올 수 있다. 성능은 필터의 복잡도에 영향을 받는다. 최적의 성능과 지원을 위해서 복제 시스템을 항상 하나의 중앙 허브를 갖고 항상 두 대의 서버와 연결되도록 설계하기 바란다. 예를 들면 [그림 2]에서 보는 것과 같이 LONDON 서버는 CAPETOWN 서버와 NEWYORK 서버와 함께 양방향 복제를 할 수 있지만 CAPETOWN서버와 NEWYORK 서버는 서로 직접 복제를 할 수는 없다. 필터하지 않는 이번 설계 예제에서 모든 변경 사항은 모든 서버들에게 영향을 준다. CAPETOWN 서버에 이루어진 변경 사항은 결국 NEWYORK 서버에 반영되지만 항상 중앙의LONDON 서버를 통해서 반영된다. 필요하다면 특정 위치가 서버에 속한 레코드들만 수정하도록 하기 위해서 사용자 애플리케이션에 간단한 비즈니스 규칙들을 구현 할 수 있다. 이런 설계는 애플리케이션에서 사용자가 모든 데이터를 조회하지만 로컬 레코드들만 수정하도록 할 수 있는 좋은 방법이다. 그러나 사용자가 종종 각 서버에서 수없이 많은 변경 작업을 한다면 서버들 사이에서 처리되고 있는 많은 양의 트랜잭션들로 인해 네트워크 부하가 급증할 수 있기 때문에 필터하지 않는 이번 설계에 대해 스케일 아웃(sacle out) 방식으로 확장하는 것은 힘들 수 있다.
성능 향상 시키기
양방향 트랜잭션 복제를 사용할 때, 로그 판독기와 각 게시 데이터 베이스와 구독 데이터 베이스에 대한 배포 에이전트의 PollingInterval 매개변수를 낮춤으로써 서버들 간의 대기 시간을 좀 더 줄일 수 있다. 예를 들면 PollingInterval을 2로 설정하면 에이전트는 복제할 트랜잭션이 없는 상태가 된 후 최대 2초 동안 “sleep”상태로 될 것이다. 그래서 배포 에이전트는 로그 판독기가 배포 데이터베이스에 작성하는 트랜잭션 처리 후 최대 2초간 대기 할 수 있다. 그리고 로그 판독기 에이전트는 로그에 커밋된 트랜잭션 처리 후 최대 2초간 대기하게 될 것이다.
SQL 서버 다음 버전이 SQL 서버 2005에서 트랜잭션 복제는 다중 양방향 구독자들에 대한 즉각적인 지원을 제공함으로써 현재 양방향성을 보완할 수 있는 피어-투-피어(Peer-to-Peer) 특징을 갖게 된다. [그림 2]의 예를 살펴보면 피어-투-피어(Peer-to-Peer)의 향상된 역순환 탐지 알고리즘은 CAPETOEN이 LONDON을 거치지 않고 직접 NEWYORK과 양방향 복제를 할 수 있게 된다. 비록 좋은 확장성(데이터 변경 양에 의존됨, 10에서 20개의 피어-투-피어(Peer-to-Peer) 서버를 지원 할 수 있음.)과 향상된 UI 통합과 지원이 가능하지만 충돌관리에 대해 즉각적인 지원은 여전히 힘들 것이다.
양방향 트랜잭션 복제는 예외적으로 배달 속도(delivery rates)와 매우 낮은 대기 시간, 텍스트와 이미지 데이터 수정 지원, 트랜잭션의 일관성 유지를 제공하고, 추가적인 칼럼들 혹은 트리거들은 필요하지 않다. 그러나 고급 충돌 관리와 병합 복제에서 제공되는 강력한 기능의 동적 필터의 기능은 부족하다. 원하는 적절한 방법인가? 높은 성능을 원한다면 양방향 트랜잭션 복제는 적절하지 않다.
출처 : Windows&.NetMagazine[2004년8월호]
마이크로소프트의 SQL 서버 고객개발지원 팀은 양방향 트랜잭션 복제의 강점에 대해서 보여주고 있다.
분산 데이터베이스 시스템을 관리하면서 빨리 동기화 될 필요가 있는 것들 외에 해볼만한 일들을 많이 볼 수 있다. 트랜잭션 복제와 통합 복제는 효과적인 복제방법으로 잘 알려져 있지만, 제약 사항들과 성능상의 한계가 있을 수 있다. 만약 이런 복제 방식의 제약 사항들 때문에 실망한다면, 다른 대안(양방향 트랜잭션 복제)을 찾고자 할지도 모른다. 어떤 경우에는, 이런 복제 방식 구현을 통해 매우 빨리, 지리적으로 분산된, 여러 곳을 업데이트할 수 있는 방안을 제공한다. (몇몇 장애물들은 뛰어넘어도 상관없다면). 마이크로소프트의 SQL 서버 고객개발지원 팀은 고객들이 재무, 주식거래, 통신과 같은 시장에서 양방향 트랜잭션 복제의 아키텍트(architect)를 구성하도록 도와준다. 이런 고객들의 대부분은 장애조치 혹은 재해 복구 사이트로 빠르고 쉽게 옮길 수 있는 방안으로 데이터베이스를 생성하는 방법을 사용한다.
SQL 서버는 SQL 서버 7.0 이후에 빠르고 강력한 트랜잭션 복제를 지원하고 있다. 그러나, 부적절한 문서와 기능에 대한 낮은 이해도 때문에 이를 알고 있거나 이용하고 있는 DBA들은 몇 명 되지 않는다. SQL 서버 7.0 온라인 설명서(BOL)는 양방향 트랜잭션 복제에 대해서 부족한 부분을 가지고 있다. 게다가 마이크로소프트는 유감스럽게도 SQL 서버 2000 초기 BOL에서도 생략했다. 마이크로소프트는 최근의 SQL 서버 2000 BOL 버전에 몇 개의 문서를 포함시켜 놓았으며, http://www.microsoft.com/sql/techinfo/p ··· oks.asp. 에서 다운로드 받을 수 있다.
[웹 리스트 1](http://www.adminmag.com의 자료실에서 다운로드 받을 수 있음)은 4개의 복제 방법인 양방향 트랜잭션, 트랜잭션 지연, 트랜잭션 즉시 업데이트, 병합 (bi-directional transactional, transactional queued, transactional immediate updating, merge )의 특징을 비교하고 있다. 대부분의 DBA들은 기록된 트랜잭션들을 하나의 소스 즉 게시자(Publisher)에서 일반적으로 수정하지 않고 읽기만 가능한 대상서버(예, 보고서버(reporting server))인 구독자(Subscriber)에게 배포하기위해 트랜잭션 복제를 사용하다. DBA들은 집계 읽기 성능을 향상시키기 위해서 전형적으로 이런 유형의 복제를 사용한다. MS SQL 서버 7.0에서 소개되었던, 즉시 업데이트 구독(immediate updating subscription)에서 트랜잭션 구독자는 구독자(Subscriber)에 있는 데이터를 수정할 수 있다. 그러나, 구독자는 게시자(Publisher)에 네트워크 연결만을 통해 데이터를 수정할 수 있다(즉, 구독자(Subscriber)는 반드시 on-line 상태이어야 함). SQL 서버 2000에서 지연 업데이트 구독자(queued updating subscriber)는 게시자(Publisher)에 연결되지 않은 상태에서도 구독자(Subscriber)의 데이터를 수정할 수 있다. 이 두 방법들은 사용자 테이블에 컬럼을 추가해야 한다. 게다가 이 방법들은 텍스트 혹은 이미지 컬럼의 업데이트와 자동 생성된 트리거를 통한 데이터 변경내용 추적 기능을 지원하지 않는다.
비록 병합 복제(SQL 서버 7.0에서 소개됨.)에 텍스트와 이미지 컬럼에 대한 수정을 할 수 있더라도 트리거를 사용하고 16bytes의 전세계적으로 유일한 uniqueidentifier 데이터 형식의 복제별 컬럼(즉, GUID)을 테이블들에 추가 해야 한다. 이런 트리거들과 컬럼들을 추가하면 몇몇 애플리케이션들에 문제가 야기될 수 있으므로 많은 DBA들은 관례적으로 난색을 표하게 된다. 마이크로소프트는 특히 오프라인의 모바일 사용자들(예, 영업 자동화 애플리케이션의 사용자), 충돌에 대한 추적, 매우 유연한 분할 및 필터링 기능과 같은 운영 능력에 적절하게 병합 복제를 설계했다. 그러나 이런 이익을 위해서 트랜잭션 일관성의 손실, GUID컬럼과 트리거 추가, 그 밖의 메타데이터(예를 들면 병합 생성 트리거는 데이터 변경 사항들과 발생 가능한 충돌들을 확인할 수 있는 추적 테이블에 있다.)의 저장 공간을 감안해야 한다.
높은 성능, 새로운 컬럼을 추가하지 않고 동기화할 때, 업데이트 가능성과 트랜잭션 일관성 둘을 유지할 수 있는 비-트리거기반 방식을 원한다면, 양방향 트랜잭션 복제가 해결책이 될 수 있다. 이름에서 암시하듯이, 양방향 트랜잭션 복제 에이전트는 트랜잭션 복제 에이전트(로그 판독기 에이전트(Logreader Agent)와 배포 에이전트(Distribution Agent))를 사용하여 두개의 서버 간의 복제를 구현한다. 로그 판독기 에이전트는 데이터베이스 로그에서 게시물(article)들을 복제할 트랜잭션을 찾고, 대응되는 SQL INSERT, UPDATE 혹은DELETE 구문을 생성하며, 이 구문들을 배포 데이터베이스에 트랜잭션 단위로 작성한다. 배포 에이전트는 배포자 서버 데이터베이스에서 복제할 트랜잭션을 찾아서 구독 데이터베이스에 작성한다. 이런 유형의 복제에서, 게시자(Publisher)는 또한 구독자(Subscriber)이고, 구독자(Subscriber)는 또한 동일한 데이터의 게시자(Publisher)이기도 하다. 예를 들면 [그림 1]에서 보듯이, LONDON 서버는 Customer_Publication_A로 Customer 테이블을 게시하고 CAPETOWN 서버 은 LONDON 서버 의 Customer_Publication_A에서 구독한다. 또한, CAPETOWN 서버 은 Customer_Publication_B 로 동일한 Customer 테이블을 재게시하고 LONDON 서버 은 Customer_Publication_B를 구독한다. 이 방법은 Customer 테이블에 복제별 컬럼을 추가하거나 변경 내용들을 추적하기 위해 둘 중 하나의 서버에 트리거를 추가하지 않고 구성할 수 있다는 것을 명심해야 한다.

[그림 1] 양방향 트랜잭션 복제 서버간의 관계
필자가 설명한 관계가 둘 중 하나의 서버에서 발생한 변경이 서버들 사이에서 끊임 없이 데이터를 발생 시킬 수 있기 때문에 정상적으로 작동되지 않을 수 있다는 것에 대해 생각해 봤을지도 모르겠다. 만약 사용자가 LONDON 서버에서 Customer 레코드를 수정했다면, LONDON 로그 판독기 에이전트는 트랜잭션을 검출하여, LONDON서버의 배포 데이터 베이스에 트랜잭션을 저장한다. CAPETOWN서버에 속한 배포 에이전트는 LONDON서버에 있는 배포 데이터베이스에서 트랜잭션을 가지고 온 후 CAPETOWN 서버의 Customer 테이블에 해당 내용을 저장한다. 그러나 게시 데이터베이스 로그를 모니터링 하고 있는 CAPETOWN 서버의 로그 판독기 에이전트는 또한 트랜잭션을 찾아서 CAPETOWN서버의 배포 데이터베이스에 저장할 것 이다. LONDON서버의 배포 에이전트는 CAPETOWN 서버의 배포 데이터베이스에서 트랜잭션을 찾아서 LONDON서버에 재적용시킬 것이다. 둘 중 어느 서버도 최초에 변경된 서버가 어느 것인지 알지 못하기 때문에, 두 서버 모두 계속적으로 업데이트를 전파시키게 된다. 복제를 하기 원했었으면, 매트릭스 3 (The Matix Revolutions)에서의 스미스 요원이 필요했었다. 고맙게도 그 대안은 상대적으로 간단하다. 구독자(Subscriber)에게 구독(subscription)을 추가하기 위해 저장 프로시저 sp_addsubscription 을 사용할 때, 저장프로시저에 @loopback_detection 매개변수의 값을 ‘true’ (@loopback_detection=’true’) 추가할 필요가 있다( sp_addsubscription에 대한 좀 더 많은 정보는 http://support.microsoft.com/default .aspx?scid=kb;en-us;820675&Product=sql.에서 Microsoft 의 기사 중 “HOW TO: Implement Bidirectional Transactional Replication” 참조). 이 매개변수를 추가하면 배포 에이전트가 최초의 개시자(Publisher)에서 재배달을 하지 않도록 하기 위해 각 트랜잭션을 제대로 확인하게 된다. 충돌뿐 아니라 레코드와 컬럼 수정이 제대로 이루어지기 위해 복잡한 코딩이 필요한 트리거 기반의 병합 복제와는 달리 로그 기반의 트랜잭션 복제 방식은 상대적으로 간단하다. 양방향 트랜잭션 복제는 게시자 (Publisher)로 하여금 트랜잭션들을 읽어서 구독자 (Subscriber)에게 빠른 속도(초당 수천개의 구문처리)와 짧은 대기시간(하나의 서버에서 변경사항이 발생한 시간과 다른 서버에서 그 내용이 반영되는 시간사이의 간격)으로 트랜잭션들을 적용시킨다. 물론 이런 속도나 대기시간은 네트워크와 디스크 읽기/쓰기 대기시간과 수정되고 있는 레코드의 크기에 따라 차이가 있을 수 있다. New York과 Los Angeles처럼 멀리 떨어져 있음에도 불구하고 빠르고 안정적인 네트워크상의 서버들은 단 몇 초(가능한 4초 이하) 동안 처리할 수 있었다. 게다가 병합 복제와는 달리, 트랜잭션 복제는 저장 프로시저의 실행을 복제할 수 있다. 많은 수의 레코드들을 수정하려고 할 때 저장 프로시저 복제를 사용하고자 할 수 있다. 네트워크를 통해서 처리된 레코드들을 옮기는 것 대신 게시자(Publisher)가 실행시키는 프로시저의 실행 구문만을 복제하는 것이다. 다음의 구문을 예로 들 수 있다.
따라서, I/O, 네트워크, CPU 시간들을 많이 감소 시킬 수 있다.
적절한 방식은?
양방향 트랜잭션 복제를 사용하면 스키마를 바꾸지 않고 성능향상은 얻게 된다고 생각할 수 있다. 그러나 이런 유형의 복제에 대한 비용과 단점들에 대해서 평가해야만 한다. 양방향 트랜잭션 복제가 실제 환경에 적절하다고 판단하기 전에 복잡한 초기 설정, 어려운 충동 관리, 제한적인 확장성에 대해서 검토할 필요가 있다.
복잡한 설치과정. 양방향 트랜잭션 복제가 완벽하게 UI 마법사를 지원하기 않기 때문에 복제 설치 스크립트를 직접 수정하거나 만들어야 한다. 그리고 구독자(Subscriber)의 초기화는 복잡해 질 수 있다. 예를 들면 [그림 1]에서 보듯이 두 개의 서버 사이에서 데이터베이스의 양방향 트랜잭션 복제를 설정하기 위한 방법 중 하나는 다음의 단계를 거친다.
* LONDON 서버에서 데이터베이스를 분리시키고, CAPETOWN 서버에 연결시킴.
* LONDON 서버와 CAPETOWN 서버에 트랜잭션 게시(transactional publication)를 만들기 위해서 게시(Publication) 복제 만들기 마법사 (Create Publication Replication Wizard)를 사용.
* 복제하고자 하는 데이터를 이미 가지고 있기 때문에 LONDON 서버에서 게시를 구독하고 있을 때 CAPETOWN 서버에서 no-sync 옵션을 사용.
* 불행히도 구독할 때, 구독추가 복제 마법사(Replication Add Subscription Wizard)를 사용하여 매개변수 중 @loopbak_detection = ‘true’를 추가할 수 없다. 그래서 구독(subscription)에 대한 복제 스크립트를 생성하고 저장하기 위해 CAPETOWN 서버에서 스크립트 생성 마법사(Generate Script Wizard)를 사용해야 한다. CAPETOWN에서 구독을 취소하고 생성하고 저장한 복제 스크립트를 수정하여 sp_addsubscription 프로시저를 호출하기 위해 @loopback_detection=’true’를 추가한 후 CAPETOWN 서버에서 스크립트 실행.
* LONDON 서버에 대해 올바른 서버와 데이터베이스 이름을 추가하기 위해 복제 스크립트 재수정 후, LONDON 서버에서 스크립트 실행.
양방향 트랜잭션 복제를 설정하는 것이 쉽지 않았다.
복잡한 충돌 관리
고려해야 할 다음 사항은 충돌관리로 좀더 어려운 부분이다. 충돌은 두 개의 서버들이 동기화 되지 않은 데이터에 대한 동일한 레코드를 수정할 때 발생할 수 있다. 복제의 다른 업데이트 할 수 있는 구독자(Subscriber) 유형들과는 달리, 양방향 복제는 즉각적인 충돌관리 대안을 제공하지 않는다. 대신에 장비들로 해결할 수 있다. 충돌을 처리하기 위해 병합 복제는 복잡한 충돌(예, 레코드 레벨 과 컬럼 레벌 충돌)들을 관리하기 위한 여러 특징들을 가지고 있고, T-SQL이나 다른 프로그래밍 언어로 정의된 사용자 정의 충돌 처리가 가능하도록 개발자와 설계자들에게 뛰어난 유연성을 제공한다. 즉시 업데이트 복제 구독자들은 2단계 커밋(2PC, 2-Phasce commit)을 수행하기 위해서 DTC(Distributed Transaction Coordinator)를 사용함으로써 충돌을 피하게 된다. 그러나 이런 기술은 온라인 연결 환경에서만 가능하다. 비록 지연 업데이트 방안이 오프라인 데이터 변경을 지원하지만 지연 업데이트는 제한적인 충돌처리 모델을 지원한다. 즉, 구독자(Subscriber)이든 게시자 (Publisher)이든 변경은 전체 트랜잭션 일괄작업보다 우선권을 갖게 될 것이다. 게다가 앞에서 언급했듯이 지연 업데이트이든 즉시 업데이트이든 둘 다 텍스트나 이미지 데이터 형식 에 대한 수정은 불가능 하다. 반면에 병합 복제와 양방향 트랜잭션 복제는 가능하다.
충돌을 예상하지 않고 있을 때나 응용프로그램 제약(예, 사용자들이 데이터 소유권에 따라 데이터를 수정할 수 있음)을 사용하여 충돌을 피할 수 있을 때나 게시(publication)가 필터링 되었을 때 혹은 한번에 오직 하나의 서버만이 데이터를 수정할 때 양방향 트랜잭션 복제는 최적으로 작동된다. (마지막 상태는 고 가용성 혹은 장애조치 시나리오를 종종 보게 된다.) 양방향 트랜잭션 복제에서 여러 방법으로 충돌을 피할 수 있다.
많은 데이터베이스 기본 키(primary key)로 IDENTITY 데이터 형식을 사용하여 설계된다. 양방향 트랜잭션 복제환경에서 이런 유형의 테이블 설계를 효과적으로 사용하고 중복 키 (duplicate key)와 같은 충돌을 비하기 위해서는 설정과정에서 두 단계를 거쳐야 한다. 첫 번째로 IDENTITY 컬럼이 NOT FOR REPLICATION 속성을 갖도록 정의해야 한다. 두 번째는 설계 시 서버 모두에 독립적인 레코드 삽입이 고려되어야 한다면 각 IDENTITY 컬럼은 다른 숫자로 시작될 필요가 있다. 예를 들어 LONDON 서버의 Customer 테이블은 IDENTITY 값은 0으로 시작하고 CAPTETOWN 서버의 Customer 테이블은 IDENTITY 값이 1,000,000으로 시작하도록 조건을 지정할 수 있다. DBCC CHECKIDENT RESEED 구문으로 사용하거나 최초의 설정과 동기화 단계 이후에 복제되는 서버들에게 구문들을 보내기 위해서 sp_addscriptexec 을 사용하여 이런 조건을 설정할 수 있다.
다른 방법으로 기본 키(primary key)로 UNIQUEIDENTIFIER를 사용하여 테이블을 설계하면 중복 키(duplicate key)를 피할 수 있다. 그러나, 이 방법은 전체적으로 필요한 저장공간이 늘어나게 되고 모든 사람들은 C677EF69 -8645-4C1D-8816-7E629F8B1AA0(SQL 서버가 데이터 형식에 맞게 생성해준 번호임)와 같이 어의 없는 고객번호를 가지는 것을 싫어한다.
양방향 트랜잭션 복제에 발생된 충돌에 대한 추적은 또 하나의 어려운 문제일 수 있다. 특히 업데이트 충돌은 하나의 레코드의 특정 컬럼에 대한 가장 마지막 업데이트가 이전의 결과에 번복되기 때문에 추적하기 어렵다. 예를 들면 레코드가 LONDON 서버에서 업데이트 되어지면 대상 서버인 CAPETOWN에 도착할 때, 이미 그 레코드가 삭제되어 있을 때 충돌이 발생한다. 이런 상황에서 트랜잭션 복제는 기본적으로 에러를 발생시키고 프로세서를 중지시킨다. 이때 관리자는 배포 데이터베이스에서 복제 시스템 테이블들의 레코드를 삭제하거나 수정할 수 있는 기능(그렇게 하면 “시스템 테이블을 수정하지 말아라”라는 경고 발생) 혹은 구독(subscription)을 다시 초기화시키는 기능을 포함하여 여러 개의 옵션을 설정할 수 있다. 그러나 다시 초기화 시키는 것은 연결 유형에 따라 시간이 한없이 걸릴 수 있다. 또 다른 대안은 Skiperrors %error_number% 매개변수를 추가하여 배포 에이전트를 재 실행 시키는 것이다. 충돌의 위험을 좀 더 줄이기 위해서 Region_ID와 같은 컬럼에 대한 게시(publication)를 필터링 할 수 있다. 이런 필터링으로 LONDON 서버는 Region_ID=1인 데이터만 게시할 수 있고, LONDON 서버는 Regioen_ID=2인 데이터만 게시하고 있는 CAPETOWN 서버를 구독할 수 있다.
충돌에 대한 추적과 관리에 대한 방법이 나뉘어 있지 않은 다음과 같은 방법들 중 하나를 사용할 수 있다. 사용자 정의 코드를 포함시키기 위해서 복제 시 생성된 저장프로시저의 내용 수정하기, 복제될 테이블에 트리거를 추가하거나 추적 컬럼 추가하기(예, 범용적인 datetime, uniqueidentifier, priority 데이터 형식의 컬럼들), 수정되어진 레코드의 현재와 이전 값들 모두 사용가능 하도록 XCALL 옵션 선택하기, 혹은 위의 방법들을 조합하여 사용하기에 대해서 생각해 볼 수 있다. 이런 방법들에 대한 좀 더 자세한 정보와 예제 코드에 대해서는 mk:@MSITStore:C:\Program%20Files\Microsoft%20SQL%20Server\80\Tools\Books\replprog .chm::/rp_replsamp_3ve6.htm.에서 BOL 주제 중 “양방향 복제(Bidirectional Replication)을 참고하기 바란다. 충돌처리는 양방향 트랜잭션 복제의 유일한 약점이다. 제한적인 확장성. 양방향 트랜잭션 복제에 대해서 검토할 때 두개 이상의 서버를 추가하여 양방향 설계를 확장할 계획이 있는지 없는지에 대해 고려해야 한다. 분할을 하기 위해 필터를 추가하는 것은 게시(publication)가 양방향 복제를 여러 개의 서버들로 확장 시킬 수 있게 할 의도가 있다. 그러나 복잡도와 관리비용의 급격한 증가, 잠재적인 성능 저하들을 가져올 수 있다. 성능은 필터의 복잡도에 영향을 받는다. 최적의 성능과 지원을 위해서 복제 시스템을 항상 하나의 중앙 허브를 갖고 항상 두 대의 서버와 연결되도록 설계하기 바란다. 예를 들면 [그림 2]에서 보는 것과 같이 LONDON 서버는 CAPETOWN 서버와 NEWYORK 서버와 함께 양방향 복제를 할 수 있지만 CAPETOWN서버와 NEWYORK 서버는 서로 직접 복제를 할 수는 없다. 필터하지 않는 이번 설계 예제에서 모든 변경 사항은 모든 서버들에게 영향을 준다. CAPETOWN 서버에 이루어진 변경 사항은 결국 NEWYORK 서버에 반영되지만 항상 중앙의LONDON 서버를 통해서 반영된다. 필요하다면 특정 위치가 서버에 속한 레코드들만 수정하도록 하기 위해서 사용자 애플리케이션에 간단한 비즈니스 규칙들을 구현 할 수 있다. 이런 설계는 애플리케이션에서 사용자가 모든 데이터를 조회하지만 로컬 레코드들만 수정하도록 할 수 있는 좋은 방법이다. 그러나 사용자가 종종 각 서버에서 수없이 많은 변경 작업을 한다면 서버들 사이에서 처리되고 있는 많은 양의 트랜잭션들로 인해 네트워크 부하가 급증할 수 있기 때문에 필터하지 않는 이번 설계에 대해 스케일 아웃(sacle out) 방식으로 확장하는 것은 힘들 수 있다.

[그림 2] 양방향 설계에 서버 추가하기
성능 향상 시키기
양방향 트랜잭션 복제를 사용할 때, 로그 판독기와 각 게시 데이터 베이스와 구독 데이터 베이스에 대한 배포 에이전트의 PollingInterval 매개변수를 낮춤으로써 서버들 간의 대기 시간을 좀 더 줄일 수 있다. 예를 들면 PollingInterval을 2로 설정하면 에이전트는 복제할 트랜잭션이 없는 상태가 된 후 최대 2초 동안 “sleep”상태로 될 것이다. 그래서 배포 에이전트는 로그 판독기가 배포 데이터베이스에 작성하는 트랜잭션 처리 후 최대 2초간 대기 할 수 있다. 그리고 로그 판독기 에이전트는 로그에 커밋된 트랜잭션 처리 후 최대 2초간 대기하게 될 것이다.
SQL 서버 다음 버전이 SQL 서버 2005에서 트랜잭션 복제는 다중 양방향 구독자들에 대한 즉각적인 지원을 제공함으로써 현재 양방향성을 보완할 수 있는 피어-투-피어(Peer-to-Peer) 특징을 갖게 된다. [그림 2]의 예를 살펴보면 피어-투-피어(Peer-to-Peer)의 향상된 역순환 탐지 알고리즘은 CAPETOEN이 LONDON을 거치지 않고 직접 NEWYORK과 양방향 복제를 할 수 있게 된다. 비록 좋은 확장성(데이터 변경 양에 의존됨, 10에서 20개의 피어-투-피어(Peer-to-Peer) 서버를 지원 할 수 있음.)과 향상된 UI 통합과 지원이 가능하지만 충돌관리에 대해 즉각적인 지원은 여전히 힘들 것이다.
양방향 트랜잭션 복제는 예외적으로 배달 속도(delivery rates)와 매우 낮은 대기 시간, 텍스트와 이미지 데이터 수정 지원, 트랜잭션의 일관성 유지를 제공하고, 추가적인 칼럼들 혹은 트리거들은 필요하지 않다. 그러나 고급 충돌 관리와 병합 복제에서 제공되는 강력한 기능의 동적 필터의 기능은 부족하다. 원하는 적절한 방법인가? 높은 성능을 원한다면 양방향 트랜잭션 복제는 적절하지 않다.
출처 : Windows&.NetMagazine[2004년8월호]
"MSSQL" 카테고리의 다른 글
- SQL Server 2000에서 Top N 쿼리의 숨겨진 기능 (5)2007/05/21
- XML을 사용하여 SQL Server 데이터 표시 (0)2007/05/21
- 과도한 동기화 (0)2007/05/21
- 검색 제한자 깊이보기 (0)2007/05/21
- 뷰를 실체화하기 (0)2007/05/21

수안이의 컴퓨터 연구실



Leave your greetings.