2. DBMS이야기/01. PostgreSQL

pglogical 확장 모듈 소개

ioseph 2016. 11. 11. 17:02

1. PostgreSQL 논리적 디코딩 소개

http://postgresql.kr/docs/current/logicaldecoding.html


9.4 버전에서 새로 등장한 개념으로,

트랜잭션 로그를 출력 플러그인을 이용해서 사용자 정의 형태로 변환하는 기능을 말합니다.


기존 복제가 트랜잭션 처리에서 먼저 기록한(write-ahead) 내용을 다른 서버로 그대로 보내서 그것을 재실행하는 방식의 스트리밍 복제였다면, 논리적 디코딩을 이용하면 논리적 개념으로 데이터베이스 복제가 가능해 집니다.  이 말은 대상 데이터베이스가 똑 같은 OS에, 똑 같은 버전의 PostgreSQL 서버여야 할 필요가 없으며, 심지어 MySQL이나 기타 다른 데이터베이스, 더 나아가 굳이 데이터베이스가 아니어도 복제가 가능하다는 것을 의미합니다. 트랜잭션 로그를 분석해서 원하는 출력 양식으로 바꿀 수 있는 출력 플러그인만 있다면 말이지요.


2. 논리적 디코딩 출력 플러그인들

  • test_decoding
    배포판에 포함된 테스트 용도, SQL이나, pg_recvlogical 명령으로 확인 가능합니다.
  • decoder_raw
    https://github.com/michaelpq/pg_plugins/tree/master/decoder_raw
    트랜잭션 로그를 만들었던 DML로 출력
  • pglogical
    https://2ndquadrant.com/en/resources/pglogical/
    트리거 기반 테이블 단위 복제 솔루션인 slony를 대체할 테이블 단위 복제 솔루션, 멀티 마스터 기능은 아니고, 마스터에서 배포하고 슬레이브에서 구독하는 방식으로 단방향 복제 솔루션입니다.
  • decoding_json
    https://github.com/leptonix/decoding-json
    트랜잭션 로그를 분석해서 그 내용을 json 양식으로 만듭니다. 자료를 뽑을 때는 pg_recvlogical 명령을 사용해서 확인해 볼 수 있습니다.

 

3. 개념

논리적 디코딩 기능을 이용 하는 절차는 다음 순서로 진행합니다.

  1. 원본 서버의 환경 설정값을 확인합니다.
    replication 권한이 있는 사용자가 있어야 하고, (슈퍼유저와 분리하는 것이 안전합니다, - 대신에 아래 pglogical 용이라면, 이 사용자에게 pglogical 관련 객체들 사용할 수 있는 권한도 부여해야합니다. 테스트 용도로라면, 그냥 postgres 슈퍼 유저를 사용하세요)
    CREATE ROLE replicatest REPLICATION LOGIN;

    pg_hba.conf 파일에서 해당 사용자가 스트리밍 복제를 할 수 있도록 설정하고,
    host replication replicatest 192.168.0.10/32 trust

    postgresql.conf 파일에서 wal_level 값은 logical, max_replication_slots 값은 0 보다 크게, max_wal_senders 값도 0보다 크게 지정하고,

    필요하다면 변경한 설정값을 반영하기 위해서 서버 재실행합니다.

    (pglogical 기능 테스트라면 복제 내용을 재 실행할 대기서버 쪽에도, max_replaction_slots 값이 0 이상이어야 하더군요)

  2. 원본 서버 (배포 서버)에서 pg_create_logical_replication_slot() 함수를 이용해서 논리 복제 슬롯을 만듭니다.

    여기서 기억해야 할 것은 이 슬롯이 만들어지는 순간부터 트랜잭션 로그가 논리 복제 기능을 사용하는 응용프로그램(또는 다른 서버)에서 다 빼내가기 전까지 트랜잭션 로그를 지우지 않는다는 것입니다.
    사용하지 않는 복제 슬롯을 만들어두면, 결국 pg_xlog 쪽 WAL 파일 재활용을 하지 않게 되고, 결국 해당 디렉터리 가용 공간이 없어지게 되어 서비스 장애까지 이어질 수 있습니다. 주의해야 합니다.

    논리적 복제 슬롯을 만들 때, 매개변수로 출력 플러그인을 지정합니다.
    SELECT
    pg_create_logical_replication_slot('슬롯이름', '출력플러그인이름', '플러그인에서사용하는옵션이름', '옵션값')
    형태로 SQL 구문으로 지정할 수 있고,
    pg_recvlogical 명령같이 논리적 복제를 구현하는 응용프로그램측에서 스트리밍 복제 프로토콜을 이용 해당 슬롯을 만들 수도 있습니다.

  3. 논리 디코딩을 이용하는 응용프로그램(또는 서버)에서 원본 서버에서 발생한 DML에 대한 트랜잭션 로그를 사용합니다. 사용은 두 가지가 있는데, 하나는 그 로그를 꺼내오는 경우(get)고, 다른 하나는 그냥 두고 가져다 쓰는 경우(peek)입니다.

    응용프로그램이 트랜잭션 로그 디코딩을 요구하면, 출력 플러그인이 해당 복제 슬롯 정보를 확인하고, 해석해야할 트랜잭션 로그를 WAL 파일에서 찾아서 그것을 해당 플러그인 출력 양식에 맞춰 응용프로그램 쪽으로 보냅니다. 이때, get 방식으로 요청하면, 복제 슬롯 상태값에서 lsn(로그 시퀀스 번호)을 증가합니다.

  4. 원본 데이터베이스 서버가 기억하는 복제 슬롯 정보는 마지막 꺼내간 lsn 번호입니다. 즉, 그것을 사용하는 응용프로그램에서 그 자료가 제대로 쓰였는지는 전혀 모릅니다. 또한 이 상태는 checkpoint 작업으로 디스크에 영구 보관됩니다. checkpoint 전 서버 비정상 종료가 있었다면, 서버가 다시 시작되고, 다시 해당 슬롯을 사용하는 응용프로그램 트랜잭션 로그를 달라고 요청하면 이전에 이미 받았던 자료가 올 수도 있습니다.  즉 논리적 복제 슬롯을 이용하는 응용 프로그램이나, 서버는 반드시 이 부분의 중복 처리에 대한 정책을 세워야 합니다.

  5. 해당 복제 슬롯의 상황은 pg_replication_slots 뷰에서 이 슬롯을 스트리밍 리플리케이션 프로토콜로 사용한다면, pg_stat_replication 뷰에서 그 상황을 볼 수 있습니다.


4. pglogical

2ndQuadrant 사에서 만든 PostgreSQL 확장 모듈입니다.

이 글은 https://2ndquadrant.com/en/resources/pglogical/pglogical-installation-instructions/ 페이지 기준으로 몇가지 작업을 하면서 정리한 것입니다.


이 확장 모듈은 트리거 기반 복제 솔루션인 slony 의 대안으로 개발되었습니다.  왜냐하면, slony가 나온 뒤 PostgreSQL 서버는 background worker, logical decoding 등 많은 기능이 추가되어 이 기능들을 잘 쓰면 테이블 단위 복제가 가능할 것이다는 생각이 들었기 때문입니다.


기본 개념은 운영 서버에서 배포하고, 슬레이브 서버에서 구독하는 방식입니다. 이 배포와 구독은 pglogical 모듈을 설치하고, 재실행하면 생기는 background worker 프로세스가 관리하고, 내용 전달은 pglogical_output 이라는 모듈에서 제공하는 논리적 디코딩 출력 플러그인을 사용해서 스트리밍 복제 프로토콜을 이용합니다.


즉, 배포와 구독 서버간 스트리밍 복제가 가능하도록 설정이 먼저 되어 있어야 합니다. 물론 이전의 스트리밍 복제 대기 서버를 구축하는 것 처럼 pg_basebackup을 이용해서 운영서버 전체를 백업하고 재구축할 필요는 없습니다. 그저 깡통 데이터베이스에 필요한 테이블만 있으면 됩니다.


 

작업 방법은 다음과 같습니다.


  1. 제일 먼저 할 작업은 운영 서버는 배포 역할을 할 수 있도록, 대기 서버는 구독 역할을 할 수 있도록 스트리밍 복제 환경을 만들어야 합니다. 자세한 설명은 윗 페이지 참조

  2. pglogical 이라는 확장 모듈을 설치합니다.
    github (https://github.com/2ndQuadrant/pglogical) 에서 소스를 받아서 직접 컴파일을 하거나, 위 홈페이지에서 해당 패키지를 설치하거나, 자기 환경에 맞게 설치합니다.

  3. psql 창에서 CREATE EXTENSION pglogical; CREATE EXTESION pglogical_output; 두 모듈을 설치합니다.

  4. 각 서버(노드)는 pglogical 모듈을 이용해서 논리적 복제를 사용하겠다는 정보로, 제일 먼저 각 노드별 노드 등록을 합니다. pglogical.create_node() 함수 이용. 이 작업은 각각의 노드에서 해야 합니다. 즉, 접속 dsn 값은 자기 자신의 서버로 접속하는 정보를 입력합니다.

  5. 배포 서버 쪽에서는 배포할 세트를 만들고, 그 세트 안에 배포할 객체들을 등록합니다. 현재는 테이블과 시퀀스입니다. pglogical.create_replication_set(), pglogical.replication_set_add_sequence(), pglogical.replication_set_add_table() 함수를 이용합니다.  확장 모듈을 설치하면, 기본으로 사용할 수 있도록 default 라는 세트가 이미 등록되어 있기 때문에, 특별한 경우가 아니면, 그냥 default 세트에 원하는 객체들을 포함시기는 작업 (repliaction_set_add_*) 만 하면 됩니다.

  6. 구독 서버는 위에서 작업한 자신의 노드 등록과 함께, 구독 등록도 해야 합니다. pglogical.create_subscription() 함수 이용. 이 때 기억해야 할 것은 논리적 스트리밍 복제 슬롯이 만들어지는 시점 - 트랜잭션 로그를 보내는 시작 lsn 값이 지정되는 시점 - 은 구독 서버가 구독 작업을 시작하는 시점이라는 점입니다. 즉, 구독 이후부터 발생한 변경 사항이 구독 서버에 적용 될 것이라는 점입니다.
    (해당 테이블을 아에 그대로 복제하겠다면, pglogical.alter_subscription_resynchronize_table() 함수를 이용합니다.)

    또 하나 기억해야 할 것은 구독 시작 이후 그 구독 서버를 더 이상 사용하지 않아  서버가 중지된 상태여도, 명시적으로 구독 중지(pglogical.drop_subscription() 함수 이용) 처리를 하지 않았다면, 구독 신청으로 만들어진 논리적 복제 슬롯은 삭제 되지 않는다는 점입니다. 이 말은 해당 슬롯의 마지막 lsn 뒤 부터는 모든 트랜잭션 로그를 보관하겠다는 것을 의미합니다. 결국 pg_xlog 쪽 가용 공간이 점점 줄어 나중에는 서버 장애로까지 발전할 수도 있다는 것입니다.


4.1 pglogical 한계

논리적 복제를 이용하기 때문에, 논리적 복제 한계가 그대로 있습니다. 자세한 이야기는 이 글 맨 앞에 언급한 사용 설명서를 참조하세요.


CREATE EXTENSION 명령이 데이터베이스 단위로 이루워짐으로 결국 이 복제 작업도 데이터베이스 단위일 수 밖에 없으며, 여러 데이터베이스를 여러 객체를 복제하는 상황이라면, 그 만큼의 스트리밍 복제 환경이 구축되어야 합니다.


또한 복제 작업은 background worker 프로세스로 진행 됨으로 슈퍼 유저 권한으로 작업이 진행된다는 점입니다. 슈퍼 유저 권한을 부여하고 싶지 않다면, pglogical 스키마 안에 있는 모든 객체를 사용할 수 있는 권한을 스트리밍 복제 작업하는 계정에게 부여해야 합니다. 


그 외 여러 한계점들에 대해서는 2ndQuadrant 사 홈페이지를 참조하세요.


5. 마치며

이상으로 논리적 복제와 그것을 이용하는 pglogical 확장 모듈을 이용한 테이블 단위 복제에 대해서 살펴봤습니다.  실무 환경에서 이 복제 정책을 도입 할 때는 꽤 많은 다양한 예외 상황을 고려해야 합니다.  구독 서버에서 해당 자료가 변경 된 경우와, 그 테이블이 복잡한 관계성을 유지하고 있는 경우(트리거, 참조키, 상속)에서 자료 동기화 유연성을 어떻게 확보할지, 그리고, 구독 서버의 장애시 배포 서버의 트랜잭션 로그 관리 등 꼼꼼하게 여러
상황을 따져봐야 할 것입니다.


하지만, 좀 더 유연하게 생각하면, 파티션 테이블을 만들고, 하위 테이블 들에 대해서 서로 이 pglogical로 묶으면 부하 분산용 다중 마스터 환경도 구현 가능할 듯합니다. 참신한 아이디어만 있다면, 충분히 유용하게 사용될 수 있는 모듈임에는 분명한 듯합니다.


- posted by 김상기