26.2. 로그 전달 Log-Shipping 대기 서버

26.2.1. 계획
26.2.2. 대기 서버 동작방식
26.2.3. 대기 서버 구축을 위한 운영 서버 준비작업
26.2.4. 대기 서버 구축하기
26.2.5. 스트리밍 복제
26.2.6. 복제 슬롯
26.2.7. 순차적 복제
26.2.8. 동기식 복제
26.2.9. 대기 서버에서 아카이브 연속성 보장

운영 서버에서 만드는 트랜잭션 로그 조각을 정기적으로 대기 서버로 옮기고, 그것을 적용시켜, 운영 서버가 장애로 멈추게 되면, 대기 서버를 운영해서, 가용성을 향상할 수 있다. 이 기능을 warm standby, 또는 log shipping 기능이라고 한다.

이 복제 방식은 먼저, 운영 서버와 대기 서버가 모두 실행 중이어야한다. 하지만, 두 서버 쌍방간의 연결 상태는 다른 복제 방식보다 나빠도 괜찮다. 운영 서버는 아카이브 모드로 운영 되어야하며, 운영 중에 생기는 다 쓴 WAL 세그먼트 파일(스위칭된 트랜잭션 로그 파일)을 차례대로 대기 서버로 보내고, 대기 서버는 복구 모드 전용(복구가 끝나도 다음 복구 파일이 있으면 계속 복구 작업을 하는 상태)으로 실행된다. 이 방식을 이용하면, 데이터베이스 테이블들을 수정해야할 필요가 없다. 또한 다른 복제 방식에 비해 관리 작업 비용도 적으며, 복제 작업이 운영 서버 쪽으로 끼치는 영향도 다른 복제 방식보다 적다.

이 방식의 구현 방법은 간단하다. 운영 서버에서 다 쓴 WAL 파일을 다른 서버로 운송(shipping) 하는 것 뿐이다. PostgreSQL에서는 그 로그 옮기는 작업은 한 번에 하나의 로그 파일을 옮길 수 있도록 구현되어 있다. WAL 파일(16MB)을 옮기는 작업은 데이터베이스 서버 밖에서 관리자가 정의한 방식으로 진행 되기 때문에, 같은 사이트 내로 옮겨도 되고, 전혀 다른 시스템 쪽으로 보내도 되고, 여러 시스템으로 한꺼번에 보내도 된다. 이 부분은 전적으로 관리자에게 맡긴다. 단지 고려해야할 사항은 파일이 전송될 때의 전송량 때문에 운영 서버에 영향을 줄 수도 있다. 이 부분이 염려되면, 전송 속도를 제한 할 수 있는 방법도 고려해야할 것이다. 아니면, 레코드 기반 로그 전달 방식 (스트리밍 복제)을 고려할 수 도 있다. (26.2.5절 참조)

로그 전달 방식은 비동기식임을 기억해야 한다. 다시 말하면, 전송하는 WAL 내용은 이미 커밋된 자료이기 때문에, 그 자료가 대기 서버로 미쳐 전달 되기전에 운영 서버가 멈춰버리면, 그 자료는 손실 된다. 자료 손실량을 줄이는 방법으로 archive_timeout 환경설정값을 몇 초 정도로 짧게 지정해 자주 사용하는 WAL 파일을 바꾸고, 그것을 전송하면, 되겠지만, 그 파일의 크기가 16MB이기 때문에, 잦은 WAL 파일 전송 작업으로 네트워크 사용량이 증가할 것이다. 스트리밍 복제 방식(26.2.5절 참조)을 이용하면, 이 손실 되는 자료량을 최소화 할 수 있다.

(물론 위에서 언급한 한계점이 있기는 하지만,) 대기 서버로 넘어 오는 WAL 파일이 제때에 잘 넘어 온다면, 운영 서버가 중지 되어 대기 서버가 그 역할을 맡기까지 서비스가 중지되는 시각은 극히 짧다. 이렇게 가용성을 향상 시킨다. 베이스 백업 자료를 준비하고, 지금까지 보관해둔 WAL 파일을 가지고, 서버를 복구 하는 방법은 이 방식보다 꽤 많은 서비스 중지 시간이 필요할 것이다. 서버가 복구 되는 기술적인 방식은 동일하지만, 가용성 입장에서는 차이가 난다. warm standby 방식으로 구현되면, 대기 서버 쪽에서는 어떤 쿼리도 사용할 수 없다. 대기 서버 쪽에서 읽기 전용 쿼리를 사용하려면, hot standby 방식으로 구축해야한다. 이 부분은 26.5절에서 자세히 설명한다.

26.2.1. 계획

일반적으로 데이터베이스 시스템을 구축할 때, 운영 서버와 대기 서버를 함께 구축한다면, 그 두 서버 환경은 최대한 비슷한 환경으로 구축하는 것이 제일 좋다. 물론 하드웨어 사양은 틀려도 크게 문제가 되지 않으나, 테이블스페이스 문제를 고려했을 때만 보아도, 운영 서버에서 CREATE TABLESPACE 명령으로 테이블스페이스를 조작할 일이 생겼을 때, 대기 서버에서도 똑 같은 작업이 정상적으로 진행되려면, 운영 서버와 같은 디렉터리 구조를 가지고 있어야한다. 물론 운영체제 전체를 운영하는 입장에서 본다면, 하드웨어와 운영체제도 똑 같은 사양이면, 보다 쉽게 운영 할 수 있을 것이다. 하드웨어 머신 아키텍쳐는 같아야한다. 로그 전달 방식의 복제를 사용할 경우, 운영 서버가 64bit이다면, 거기서 만든 로그 파일은 32bit 대기 서버에서는 사용할 수 없다.

일반적으로 로그 전달 방식 복제는 메이저 버전이 서로 다른 PostgreSQL 서버 간은 구현이 불가능하다. 디스크 자료 저장 포멧이 바뀔 경우에, PostgreSQL 개발 그룹에서는 메이저 버전을 바꾸어 릴리즈하는 것이 정책이기 때문이다. 물론 마이너 버전의 서로 틀린 경우는 이 복제 환경을 구축해도 잘 작동할 것이다. 이렇게 서로 마이너 버전이 틀린 경우 해결 하기 힘든 예상치 못한 문제가 발생할 수도 있기 때문에, 가능하다면, 운영 서버와 대기 서버의 버전을 최대한 같은 것을 사용하길 권장한다. 마이너 버전 업데이트가 필요하다면, 이 작업의 가장 좋은 순서는 먼저 대기 서버 쪽을 업데이트해서, 옛 버전의 WAL 파일을 잘 적용할 수 있는지 부터 확인하고, 운영 서버를 업데이트한다.

26.2.2. 대기 서버 동작방식

서버가 실행 될 때, 그 데이터 디렉터리 안에, standby.signal 파일이 있으면 대기 서버로 작동한다.

서버가 대기 모드로 실행되면, 서버는 마스터 서버에서 받는 WAL 파일을 계속해서 자신의 서버에 반영하는 작업만 한다. 대기 서버는 운영 서버가 보내는 파일을 보관해 두는 디렉터리에 새 WAL 파일이 있는지 확인해서, 새 WAL 파일을 반영하는 방식(warm standby)도 있고 (restore_command 참조), TCP 연결 방식을 이용해서 운영 서버와 직접 연결하고, 커밋된 트랜잭션을 즉시 대기 서버로 반영하는 방식(스트리밍 복제)도 있다. 또 내부적으로 보면, 대기 서버의 pg_wal 디렉터리에 있는 WAL 파일을 참조하는 경우도 있다. 이 경우는 스트리밍 복제 환경에서 운영 서버에서 보낸 트랜잭션을 미쳐 반영하기 전에 대기 서버가 중지 되었다가 다시 실행될 경우에 일어난다. 물론 관리자가 직접 pg_wal 디렉터리에 직접 WAL 파일을 두고 서버를 실행할 수도 있다.

대기 서버가 실행되면, 제일 먼저 restore_command 설정값에 지정된 명령어를 진행한다. 적용 해야할 WAL 세그먼트 파일이 아카이브 디렉터리에 있는지 확인하고 있으면 차례대로 적용한다. 이 작업이 끝나고 더 이상 적용할 파일이 없으면 restore_command 설정값에 지정된 명령은 실패로 끝나야한다. 그러면, 복구 환경이 스트리밍 복제 방식이라면, 운영 서버 쪽으로 접속 해서, 운영 서버가 데이터베이스 연결을 통해 보내는 트랜잭션들을 자신의 pg_wal 디렉터리 내 해당 WAL 로그 파일에 추가한다. 다음부터는 서버가 알아서 해당 작업을 수행하고, 커밋 되었다고 기록하고, 체크포인트 작업을 하는 등 일련의 자료 변경 작업을 수행한다. 반면, 복구 환경이 warm standy 방식이라면, restore_command 설정값에 지정된 명령이 실패로 끝나면, 원하는 WAL 세그먼트 파일이 새롭게 생길 때까지 기다렸다가 생기면, 다시 작업을 하고, 또 더 이상 없으면 실패로 끝나면서 대기하는 작업을 반복한다. 이 반복 작업은 서버가 중지되거나, 대기 작업을 이제 그만 하고 스스로 독립된 운영 서버 역할을 하라고 알려주는 트리거 파일이 생기기 전까지 계속 반복된다.

대기 모드는 pg_ctl promote 명령을 실행하거나, pg_promote() 함수를 호출 하거나, 트리거 파일(promote_trigger_file 설정값에 지정한 파일)이 생기면 끝난다. 물론 적용해야할 WAL 파일이, 아카이브 디렉터리에 아직 있거나, pg_wal 디렉터리 내에 있다면, 이것들을 모두 적용하고 대기 모드를 끝낸다. 이때 스트리밍 복제를 위한 운영 서버와의 연결도 더 이상 재시도하지 않는다.

26.2.3. 대기 서버 구축을 위한 운영 서버 준비작업

첫번째 작업은 대기 서버로 보낼 WAL 세그먼트 파일을 그냥 버리지 않고, 달리 처리하는 설정을 해야한다. 이 방법에 대한 자세한 이야기는 25.3절에서 하고 있다. 그냥 버리지 않고 다르게 처리 한다는 것은 그 WAL 세그먼트 파일이 운영 서버가 중지되어도 대기 서버가 사용할 수 있는 위치로 옮겨 놓는다는 것을 의미한다. 이 방법에는 여러 방법이 있을 수 있다. 단순히 네트워크 드라이브에 복사를 하는 방법도 있을 수 있고, FTP를 이용해서, 대기 서버의 원하는 디렉터리로 업로드 하는 방법도 있고, 백업용 테이프 드라이브 쪽으로 보내는 방법도 있을 것이다. 중요한 것은 운영 서버를 더이상 사용할 수 없을 때도 대기 서버는 그 WAL 세그먼트 파일들을 사용할 수 있어야한다는 점이다.

스트리밍 복제 방식을 사용하려면, 대기 서버에서 요청하는 데이터베이스 접속을 허용하도록 설정하는 작업을 해야한다. 총 세가지 작업인데, 첫번째는 접속 계정의 권한을 복제 기능을 사용할 수 있도록 해야하고, 두번째는 pg_hba.conf 파일에 대기 서버가 운영되는 호스트가 등록되어야하고, 그곳에 데이터베이스 항목은 replication으로 지정되어야하며, 세번째는 서버 환경 설정에서 max_wal_senders 값이 대기 서버의 연결수 만큼은 지정되어야한다.

대기 서버를 실행하려면, 먼저 운영서버의 베이스 백업을 준비해야한다. 이것에 대한 자세한 이야기는 25.3.2절에서 다룬다.

26.2.4. 대기 서버 구축하기

대기 서버를 구축하려면, 먼저 운영서버의 베이스 백업 자료를 복원해야한다(25.3.4절 참조). 다음 대기 서버의 데이터 클러스터 디렉터리 안에 standby.signal 파일 파일을 만든다. restore_command 설정으로 아카이브된 WAL 조각 파일들을 대기 서버로 복사하는 명령을 restore_command 설정값으로 지정한다. 고가용성을 고려해서, 대기 서버가 여럿 있는 경우, 다른 대기 서버들이 failover 로 새롭게 운영 되는 주서버의 타임라인을 따르도록 recovery_target_timeline 설정값을 latest 로 설정할 수도 있다. (기본설정값이다.)

참고

여기서 설명하는 구축 방법을 사용할 때는, pg_standby나 기타 다른 복제 툴들을 사용하면 안된다. restore_command 설정값으로 지정하는 명령은 작업할 파일이 없을 경우 즉시 종료될 수 있는 것을 사용해야한다. 서버 쪽에서 이 명령의 재실행 처리를 관리하기 때문이다. pg_standby 명령을 사용하려면, 26.4절을 참조하라.

스트리밍 복제 기능을 사용하려면, primary_conninfo 설정값을 지정해야한다. 이 값은 libpq 라이브러리에서 사용하는 데이터베이스 연결 문자열이다. 이 값에는 운영서버의 호스트 이름(또는 IP 주소)은 반드시 지정해야하며, 그 외 접속에 필요한 정보를 지정한다. 운영 서버에서 데이터베이스 접속을 계정 비밀번호를 입력해야한다면, 이곳에 그 비밀번호를 지정하거나, 대기 서버를 실행하는 시스템 계정의 홈 디렉터리에 .pgpass 파일에 지정하면 된다.

만일 대기 서버가 고가용성을 고려해서 구축되는 경우라면, 대기 서버의 서버 환경도 운영 서버와 똑같이 아카이브 모드로 운영되어야하며, 서버 접근 인증 설정도 운영 서버와 같아야한다. 왜냐하며, 운영 서버 장애가 발생하면 이 대기 서버가 운영 서버 역할을 해야하기 때문이다.

운영 서버에서 넘겨 받은 WAL 파일을 사용한다면, archive_cleanup_command 설정으로 더 이상 필요없는, 이미 대기 서버에 반영이 완료된 파일을 지워서, 디스크 공간을 확보 할 수도 있다. 이 설정값으로 pg_archivecleanup 명령을 이용하면, 보다 쉽게 이 작업을 할 수 있다. 이 명령에 대한 자세한 설명은 pg_archivecleanup 명령 도움말을 참조하라. 반면, 대기 서버가 운영 서버의 백업 용도로 구성한다면, 운영 서버의 베이스 백업 뒤로 생긴 모든 WAL 세그먼트 파일을 지우지 않는 것이 좋다. 설령 그 파일이 대기 서버로 모두 반영되었다 하더라도, 어떤 방식으로 다시 복원 작업을 할지 모르기 때문에 모두 남겨두라. 물론 베이스 백업이 바뀌었다면, 이 베이스 백업 이전 로그 파일들은 기록 보관용적인 자료가 아니라면 삭제해도 무방할 것이다.

윗 내용을 참고해서, 대기 서버로 구축하기 위한 설정은 다음과 같다:

primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass options=''-c wal_sender_timeout=5000'''
restore_command = 'cp /path/to/archive/%f %p'
archive_cleanup_command = 'pg_archivecleanup /path/to/archive %r'

대기 서버는 다중으로 구성할 수 있지만, 스트리밍 복제 방식을 이용한다면, 운영 서버의 max_wal_senders 환경 설정값으로 이 대기 서버 모두가 동시에 접속 할 수 있도록 충분한 연결을 확보해야한다.

26.2.5. 스트리밍 복제

스트리밍 복제는 WAL 세그먼트 파일 전달 방식보다 운영 서버의 자료 상태를 거의 실시간으로 동기화 한다. 대기 서버는 운영 서버로 접속해서, WAL 레코드 단위로 그 정보를 가져오고, 그것을 반영하기 때문이다. WAL 세그먼트 파일 전달 방식을 이용하면, 운영 서버가 그 로그 파일을 다 채워서 다른 로그 파일을 쓰겠다고 하는 시점에 대기 서버로 옮겨지기 때문에, 그 동안은 대기 서버와 운영 서버 사이의 자료 불일치가 일어난다.

스트리밍 복제 방식은 기본적으로 비동기식으로 이루워진다. (26.2.8절 참조) 그래서, 운영 서버와 대기 서버간의 자료 불일치가 약간 있을 수는 있다. 하지만 대기 서버와 운영 서버 사이의 네트워크 환경이 양호하고, 대기 서버의 성능이 좋다면, 파일 기반 로그 전달 방식과는 비교도 안될 만큼 1초 미만의 지연시간이 생긴다. 운영 서버와 대기 서버가 같은 네트워크 대역대 안에 있다면, 거의 실시간으로 동기화 된다. 스트리밍 복제 환경에서는 archive_timeout 설정값을 짧게 해서, 자료 손실을 줄이겠다는 설정이 필요 없다.

파일 기반 로그 전달 방식을 함께 사용하지 않고, 단지 스트리밍 복제 기능만 사용한다면, 운영 서버에서 반드시 wal_keep_size 환경설정값을 지정해서, 아직 대기 서버로 반영되지 못한 WAL 파일을 남겨두는 최대 개수를 지정해 주어야한다. 운영 서버에는 WAL 파일을 재활용하기 때문에, 대기 서버의 중지 시간이 길어져, 이 대기 서버로 반영되지 못한 WAL 파일이 계속 쌓이면 사용할 수 있는 디스크 공간이 점점 줄어들 것이다. 또한 대기 서버의 중지 시간이 아주 길어졌고, 운영 서버쪽에서는 더 이상 로그를 보관할 수 없어 재활용 해 버렸다면, 더 이상 동기화 할 자료를 찾지 못하기 때문에 다시 대기 서버를 구축 해야한다. 만일 운영 서버 쪽에서 WAL 세그먼트 로그 파일을 대기 서버 쪽으로 보내는 설정을 해 두었고, 대기 서버 쪽에서 그 파일을 사용할 수 있는 환경이라면, wal_keep_segments 설정은 굳이 필요가 없다. 대기 서버 쪽으로 처리되어야할 WAL 로그 파일들은 로그 전달 설정을 통해서 이미 대기 서버 쪽으로 옮겨졌기 때문에, 충분히 운영 서버의 자료와 동기화 할 수 있을 것이다.

이런 이유로, 스트리밍 복제 기능을 이용 할 때도, 먼저 대기 서버에서 파일 기반 로그 전달 기능도 함께 설정 하는 것이 좋다. 자세한 것은 26.2절에서 설명하고 있다. 파일 기반 로그 전달 방식에서 스트리밍 복제 방식으로 바뀌려면, primary_conninfo 설정값에 접속할 운영 서버의 정보를 지정하면 된다. 운영 서버에서는 대기 서버가 접속할 수 있도록 listen_addresses 환경설정값이 바뀌어야하며, pg_hba.conf 파일에 대기 서버에서의 접속을 허용할 있도록 지정하며, 그 때 사용할 데이터베이스 이름은 replication 으로 지정한다. 자세한 사항은 26.2.5.1절 을 참조하라.

또한, 운영 서버 입장에서 클라이언트의 TCP 연결이 끊겼을 경우, 빠르게 그 소켓을 정리해서, TCP 연결을 원할하게 하기 위해, tcp_keepalives_idle, tcp_keepalives_interval, tcp_keepalives_count 설정값을 조정할 수도 있다.

또한 대기 서버의 최대 동시 연결수도 지정해야한다. (max_wal_senders 환경설정값 항목을 참조).

대기서버는 먼저 운영 서버에서 넘겨 받은 WAL 세그먼트 파일을 모두 자신의 서버에 적용하면, primary_conninfo 설정값에 지정한 운영 서버로 접속한다. 이 접속이 성공하면, 대기 서버는 walreceiver 프로세스를 만들어 운영 서버의 walsender 프로세스에서 보내는 트랜잭션 정보를 하나씩 받아서 자신의 서버에 적용한다.

26.2.5.1. 인증

WAL 스트리밍 정보는 보안상 슈퍼유저나 인증된 데이터베이스 사용자만 사용할 수 있어야한다. 이 권한을 부여하려면, REPLICATION 권한 또는 슈퍼유저 권한을 부여한다. 그래서, 대기 서버에서 운영 서버로 접속하는 데이터베이스 사용자는 LOGIN 권한과, REPLICATION 권한을 반드시 지정해야한다. 이 리플리케이션 전용 사용자는 운영 서버의 자료를 조작할 수 없도록 SUPERUSER 권한이 없는 별개의 사용자를 만들어 사용하는 것이 보안상 안전하다.

스트리밍 복제 기능을 이용하기 위한 운영 서버의 클라이언트 인증은 pg_hba.conf 파일에서 해당 데이터베이스 이름으로 replication 이라는 예약어를 지정해야한다. 예를 들어서, 대기 서버의 IP가 192.168.1.100 이라면, 운영 서버의 pg_hba.conf 파일은 다음과 같은 형식으로 지정한다:

# 아래 "foo" 는 스트리밍 복제를 위해 대기 서버가 사용할 운영 서버로 접속할
# 데이터베이스 사용자 이름이다. 인증 방식은 md5 비밀번호 인증을 사용한다.
# 그 대기 서버의 IP는 192.168.1.100 딱 하나다.
#
# TYPE  DATABASE        USER            ADDRESS                 METHOD
host    replication     foo             192.168.1.100/32        md5

대기 서버 입장에서 접속할 운영 서버 접속 정보는 primary_conninfo 설정에서 접속할 데이터베이스 사용자의 비밀번호를 지정해도 되고, 대기 서버를 실행하는 시스템 사용자의 홈 디렉터리에 있는 ~/.pgpass 파일에서 지정해도 된다. (이 때 데이터베이스 이름으로 replication 예약어를 사용한다.) 예를 들어, 운영 서버의 IP는 192.168.1.50, 포트는 5432, 데이터베이스 사용자는 foo, 그 사용자의 비밀번호는 foopass인 경우, 대기 서버에서 사용하는 postgresql.conf 파일에 다음과 같이 지정한다:

# 이 대기 서버는 호스트 IP 192.168.1.50, 포트 5432 운영 서버로
# "foo" 사용자, "foopass" 비밀번호로 접속한다.
primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass'

26.2.5.2. 모니터링

복제 환경의 모니터링 가운데 가장 중요한 사항은 운영 서버에서 만들어내는 WAL 레코드들이 얼마나 잘 대기 서버로 반영 되고 있느냐를 살펴보는 것이다. 이 작업은 운영 서버의 현재 WAL 쓸 위치와, 대기 서버의 마지막 처리된 WAL 쓸 위치 차이를 계산하는 방식을 이용하면 된다. 이 작업을 위해서, 운영 서버에서는 pg_current_wal_lsn, 대기 서버에서는 pg_last_wal_receive_lsn 두 함수를 사용한다. (이 함수의 구체적인 사용법은 표 9.85, 표 9.86에서 다룬다.) 대기 서버에서 마지막 받은 WAL 위치는 OS의 프로세스를 확인하는 ps 명령으로 출력되는 WAL receiver 프로세스의 이름에서도 확인할 수 있다. (프로세스 모니터링에 대한 자세한 이야기는 27.1절을 참조)

또한 운영 서버에서 사용하는 WAL sender 프로세스의 정보는 pg_stat_replication 뷰로 확인할 수 있다. 이 뷰에서 pg_current_wal_lsn 값과, sent_lsn 값이 차이가 많이 나면, 운영 서버가 많이 바쁜 상황이고, sent_lsn 값과, pg_last_wal_receive_lsn 값이 차이가 많이 나면, 대기 서버가 많이 바쁘거나, 네트워크 상황이 좋지 않은 경우다.

On a hot standby, the status of the WAL receiver process can be retrieved via the pg_stat_wal_receiver view. A large difference between pg_last_wal_replay_lsn and the view's flushed_lsn indicates that WAL is being received faster than it can be replayed.

26.2.6. 복제 슬롯

대기 서버가 운영 서버로부터 복제 해야 할 내용을 미처 다 복제 하기도 전에 운영 서버가 자신의 트랜잭션 로그를 버리는 문제를 막을 수 있는 방법으로 복제 슬롯을 이용하면 된다. 또한 대기 서버와 운영 서버간 연결이 끊겨서 복구 충돌이 발생하는 상황에서도 운영 서버의 자료가 지워지는 것을 방지 할 때 이 기능을 이용할 수 있다.

복제 슬롯을 사용하지 않는 방법으로, wal_keep_size 환경 설정값에 지정된 크기 만큼 보관하는 방법과 archive_command 설정으로 WAL 조각 파일을 따로 보관하는 방법이 있다. 그런데, 이 방법은 남겨 놓은 WAL 조각들보다 더 오래된 것들을 필요로 하는 경우가 종종 생긴다. 이런 문제를 피해가는 방법으로 복제 슬롯을 사용해서 pg_wal 디렉터리 내에 대기 서버로 보내지 못한 WAL 조각 파일을 남겨 놓을 수는 있으나, 계속 남겨 놓게 되면 또 문제가 생긴다. 이 때는 max_slot_wal_keep_size 설정으로 남겨 놓을 최대 크기를 제한 할 수 있다.

In lieu of using replication slots, it is possible to prevent the removal of old WAL segments using wal_keep_size, or by storing the segments in an archive using archive_command. However, these methods often result in retaining more WAL segments than required, whereas replication slots retain only the number of segments known to be needed. On the other hand, replication slots can retain so many WAL segments that they fill up the space allocated for pg_wal; max_slot_wal_keep_size limits the size of WAL files retained by replication slots.

비슷하게, 이전 버전에서도 제공하고 있던 hot_standby_feedback 또는 vacuum_defer_cleanup_age 설정 값을 이용해서 vacuum 작업으로 인한 관계된 자료가 지워지는 문제점을 막을 수는 있었지만, 대기 서버 단절 시간이 길어 질 때는 방법이 없었다. 복제 슬롯은 이런 단점을 극복했다.

26.2.6.1. 복제 슬롯 조회 및 관리

각각의 복제 슬롯에는 이름을 부여한다. 이름은 소문자, 숫자, 밑줄(_) 조합으로 구성한다.

운영 서버에서 사용하고 있는 복제 슬롯 관련 정보는 pg_replication_slots 뷰에서 제공한다.

슬롯은 스트리밍 복제 프로토콜(52.4절 참조) 과 SQL 함수(9.27.6절 참조) 로 만들거나 삭제 할 수 있다.

26.2.6.2. 설정 예제

먼저 운영 서버에서 아래와 같은 슬롯을 하나 만든다:

postgres=# SELECT * FROM pg_create_physical_replication_slot('node_a_slot');
  slot_name  | lsn
-------------+-----
 node_a_slot |

postgres=# SELECT slot_name, slot_type, active FROM pg_replication_slots;
  slot_name  | slot_type | active 
-------------+-----------+--------
 node_a_slot | physical  | f
(1 row)

다음은 대기 서버 환경설정으로 아래와 같이 그 슬롯 이름을 primary_slot_name 설정 값으로 지정한다:

primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass'
primary_slot_name = 'node_a_slot'

26.2.7. 순차적 복제

순차적 복제 cascading replication 기능은 대기 서버가 마치 이어 달리기처럼 다른 대기 서버의 스트리밍 복제를 할 수 있도록 하는 것이다. 이 기능은 스트리밍 복제에 필요한 운영 서버의 부하를 최소화 할 수 있다.

대기 서버가 트랜잭션 로그를 받음과 동시에 또 다른 대기 서버로 보낼 수 있음을 의미한다. 이렇게 전달하는 방식을 순차적 복제라고 하며, 운영 서버에서 복제 정보를 받아오는 서버를 업스트림 서버라고 하고, 그 서버로부터 정보를 받아가는 서버를 다운스트림 서버라고 한다. 순차적 복제에서 다운스트림 서버수는 제한이 없다. 마치 기존 하나의 운영 서버에 여러 대의 대기 서버를 연결해서 복제를 구현 하는 것 같다.

순차적 복제에서는 운영서버에 만든 트랜잭션 로그 레코드를 전달하는 것뿐만 아니라, 아카이빙 모드로 보관된 로그들도 함께 전달한다. 그래서 업스트림 서버와의 연결이 끊기더라도, 자신의 트랜잭션 로그는 다운스트림 서버 쪽으로 모두 보낸다. (하위 노드 입장에서 보면 자신의 상위 노드가 대기 서버에서 운영 서버로 전환되어 새로운 트랜잭션 로그를 만들어도 여전히 복제를 할 수 있음을 뜻한다. - 옮긴이)

순차적 복제 기능은 현재 비동기식으로 구현되었다. 동기식 복제 설정(26.2.8절 참조)을 하더라도 순차적 복제 기능을 이용한다면, 그 설정은 무시된다.

대기 서버의 hot_standby_feedback 설정은 순차적 복제 환경에서도 그대로 작동 한다.

업스트림 대기 서버가 새로운 운영 서버가 된다면, 다운스트림 대기 서버의 recovery_target_timeline 설정값이 'latest' (기본값)이면 새 운영 서버로부터 복제를 계속한다.

순차적 복제 기능을 사용하려면, 업스트림 서버 쪽에서는 기본적인 복제 기능을 위해 운영 서버 쪽에서 설정했던 것을 그대로 설정하며 (max_wal_senders, hot_standby 설정값 조정하고, 호스트 기반 인증 설정을 한다), 다운스트림 대기 서버는 기존 대기서버 설정과 같이 하면서 primary_conninfo 설정에서 업스트림 대기 서버 정보를 지정하면 된다.

26.2.8. 동기식 복제

PostgreSQL 스트리밍 복제 기능은 기본이 비동기식이다. 이 말은 운영 서버가 장애로 멈추어, 대기 서버가 운영 서버로 역할 전환 작업(failover, 장애처리)을 할 때, 운영 서버와 대기 서버 사이 자료 동기화에 지연이 있었다면, 그 만큼의 자료를 잃어버린다는 뜻이다.

동기식 복제 방식은 하나의 트랜잭션을 하나 이상의 대기 서버에 반영하고, 그 결과를 운영 서버가 확인하는 방식이다. 이 방식은 트랜잭션 커밋으로 제공하는 기본 자료 안정성을 더 확장한 것이다. 이것을 컴퓨터 과학 이론에서는 이중 안전 복제라 하고, synchronous_commit 설정값이 remote_write 인 경우는 그룹 안전, 또는 단일 안전 복제라한다.

동기식 복제 기능을 이용하면, 운영 서버, 대기 서버 모두 트랜젹션이 트랜잭션 로그 파일에 기록되었을 경우에만 정상처리 되었다고 판단한다. 이렇게 하면, 운영 서버가 중지 되어도 대기 서버에서 자료 손실이 일어나지 않는다. 이렇게 해서, 자료의 안정성을 제공하지만, 대기 서버의 작업 완료 응답을 확인하는 작업까지 포함되어 운영 서버의 복제 기능을 이용하는 비용이 상대적으로 늘어난다. 직렬화 된 트랜잭션 전달 작업은 현재 전달할 트랜잭션의 응답을 받아야 다음 작업을 진행하기 때문에, 최소한 그 만큼의 지연시간이 생길 수 밖에 없다.

읽기 전용 트랜잭션과, 트랜잭션 롤백은 대기 서버의 응답을 받지 않는다. 최 상위 레벨의 트랜잭션에 대해서 응답을 확인하지, 그 하위 레벨 트랜잭션인 서브트랜잭션에 대해서는 응답을 확인하지 않는다. 대량 자료 등록이나, 인덱스 생성 작업 같이 시간이 많이 걸리는 트랜잭션에 대해서도 대기 서버의 마지막 커밋 메시지를 확인하지 않는다. 이중 커밋(2PC)에 대해서는 모든 작업에 대해서 확인한다.

A synchronous standby can be a physical replication standby or a logical replication subscriber. It can also be any other physical or logical WAL replication stream consumer that knows how to send the appropriate feedback messages. Besides the built-in physical and logical replication systems, this includes special programs such as pg_receivewal and pg_recvlogical as well as some third-party replication systems and custom programs. Check the respective documentation for details on synchronous replication support.

26.2.8.1. 기본 환경설정

이미 복제 기능을 사용하고 있는데, 동기식 복제 방식으로 변경하려면, synchronous_standby_names 설정값을 비워두지 않으면 된다. synchronous_commit 설정값도 on이어야하지만, 이 값이 기본값이 때문에 실질적으로는 윗 환경설정 파라미터만 지정하면 된다. (19.5.1절19.6.2절 참조) 동기식 복제 사용하는 대기 서버를 지정하면, 그 대기 서버로 보내는 모든 커밋 작업은 대기 서버에서도 커밋 작업을 완료했다는 응답을 기다린다. 이 응답을 확인 해야 운영 서버의 다음 커밋 작업을 진행한다. synchronous_standby_names 설정값은 쉼표 구분 여러 개의 대기 서버 이름을 지정할 수 있으며, 여기 등록되지 않은 대기 서버는 동기식 복제를 하지 않는다. synchronous_commit 설정은 환경 설정 파일에서 변경할 수 있을 뿐만 아니라, 각 트랜잭션에 대한 각각의 내구성 보장 방법을 개별적으로 설정할 수 있도록 개별 사용자나, 특정 데이터베이스나, 아니면, 특정 응용 프로그램에서 이 값을 필요할 때 변경할 수 있다.

커밋 레코드가 운영 서버 WAL 세그먼트 파일에 기록된 뒤에, 그 레코드를 대기 서버로 보낸다. 대기 서버는 그 레코드를 자신의 WAL 세그먼트 파일에 기록한 뒤에, wal_receiver_status_interval 환경설정 값이 0으로 지정되어 있지 않으면, 운영 서버로 작업을 완료했다고 메시지를 보낸다. synchronous_commit 설정값이 remote_apply로 지정한 경우, 대기서버는 커밋 트랜잭션을 적용해서, 해당 트랜잭션이 완료되었음을 알리는 내용을 운영 서버 쪽으로 보낸다. 운영 서버에서 synchronous_standby_names 설정값으로 지정한 동기식 대기서버 우선순위 목록에서 동기화 대상 대기 서버로 선정되면, 그 대기 서버에서 보내는 응답 메시지는 다른 동기식 대기 서버에서 보내는 응답 메시지와 함께 고려해서, 운영 서버가 커밋 트랜잭션의 반영이 완료되었음을 확인하는데 사용된다. 여기서 언급한 환경설정 매개 변수들이 대기 서버의 동기화 방식에 대한 설정에 관계된 것들이다. 동기식 복제 기능 설정을 할 때는 어느 환경 변수는 대기 서버에서 설정하고, 어느 환경 변수는 운영 서버에서 설정하는지 잘 파악하고 있어야한다. 또한, 대기 서버로 등록되는 서버들은 반드시 운영 서버와 직접 연결된 서버들이여야한다. 대기 서버의 하위 대기 서버는 동기식 복제를 사용할 수 없다.

synchronous_commit 설정값을 remote_write로 하면, 대기 서버의 OS 차원에서 해당 자료가 디스크에 기록되었다는 것까지만 확인하다. 실재로 OS가 디스크 버퍼 내용을 물리적으로 디스크에 기록했다는 것에 대해서는 확인하지 않는다. 이 설정은 on으로 설정했을 때 보다는 자료를 안전하게 동기화 하지는 못한다. PostgreSQL 서버 장애가 아니라, OS 장애가 생겼을 경우에는 대기서버 측 자료 손실이 발생할 수도 있기 때문이다. 하지만, 동기화 작업 시간은 그 만큼 줄어들기 때문에, 특수 환경에서는 유용하게 사용될 수도 있다. 시스템 전체적인 입장에서 본다면, 자료가 손실 되는 경우는 운영서버와, 대기 서버 모두 동시에 장애가 발생될 경우에만 일어나기 때문에다.

synchronous_commit 설정값이 remote_apply인 경우는 대기 서버에서도 해당 트랜잭션이 모두 반영되어 사용자가 반영된 트랜잭션을 볼 수 있는 상태가 되었음을 대기 서버가 알릴 때까지 운영 서버는 기다린다. 부하 분산 환경을 구축하는 경우, 자료 정합성을 유지해야 하는 경우에 사용된다.

동기식 복제 기능을 이용하는 경우 빠른 서버 중지 명령이 실행되면, 운영 서버가 정상적인 빠른 서버 중지 작업을 진행하지만, 비동기식 복제 기능을 이용하는 경우는 대기 서버로 보내야할 WAL 레코드를 연결 되어 있는 모든 대기 서버로 모두 보내고 서버가 중지된다.

26.2.8.2. 다중 동기식 대기 서버

Synchronous replication supports one or more synchronous standby servers; transactions will wait until all the standby servers which are considered as synchronous confirm receipt of their data. The number of synchronous standbys that transactions must wait for replies from is specified in synchronous_standby_names. This parameter also specifies a list of standby names and the method (FIRST and ANY) to choose synchronous standbys from the listed ones.

The method FIRST specifies a priority-based synchronous replication and makes transaction commits wait until their WAL records are replicated to the requested number of synchronous standbys chosen based on their priorities. The standbys whose names appear earlier in the list are given higher priority and will be considered as synchronous. Other standby servers appearing later in this list represent potential synchronous standbys. If any of the current synchronous standbys disconnects for whatever reason, it will be replaced immediately with the next-highest-priority standby.

An example of synchronous_standby_names for a priority-based multiple synchronous standbys is:

synchronous_standby_names = 'FIRST 2 (s1, s2, s3)'

In this example, if four standby servers s1, s2, s3 and s4 are running, the two standbys s1 and s2 will be chosen as synchronous standbys because their names appear early in the list of standby names. s3 is a potential synchronous standby and will take over the role of synchronous standby when either of s1 or s2 fails. s4 is an asynchronous standby since its name is not in the list.

The method ANY specifies a quorum-based synchronous replication and makes transaction commits wait until their WAL records are replicated to at least the requested number of synchronous standbys in the list.

An example of synchronous_standby_names for a quorum-based multiple synchronous standbys is:

synchronous_standby_names = 'ANY 2 (s1, s2, s3)'

In this example, if four standby servers s1, s2, s3 and s4 are running, transaction commits will wait for replies from at least any two standbys of s1, s2 and s3. s4 is an asynchronous standby since its name is not in the list.

The synchronous states of standby servers can be viewed using the pg_stat_replication view.

26.2.8.3. 성능을 고려한 계획

동기식 복제 기능을 이용할 때는 세심한 계획이 필요하다. 응용 프로그램들이 제 성능을 잘 낼 수 있도록 대기 서버들을 구축해야한다. 대기 서버 작업 완료 확인을 위한 지연 작업이 시스템 자원을 거의 사용하지는 않지만, 그 만큼 트랜잭션의 잠금 현상이 일어난다. 결과적으로 세심한 계획 없이 무작정 구축한 동기식 복제 환경은 많은 클라이언트 접속이 있을 경우 심각하게 반응 시간이 느려지기도 한다.

PostgreSQL에서는 응용 프로그램 개발자가 복제 작업에서의 커밋 트랜잭션 작업에 대한 운영 서버의 확인 수준을 결정할 수 있도록 제공한다. 이 수준 조정 작업은 시스템 전체를 대상으로 할 수도 있고, 사용자 단위, 데이터베이스 연결 세션 단위, 심지어 개별 트랜잭션 단위로도 할 수 있다.

예를 들어 한 서비스에서 10% 정도는 고객의 중요한 세부 정보 변경 작업이고, 90%는 사용자가 채팅 정보와 같은 장애가 발생되어 자료 손실이 생겨도 치명적인 문제가 발생하지 않는 자료에 대한 작업으로 운영된다고 하자.

이 때, 이 10% 작업에 대해서만 동기식 방식을 이용하고, 나머지는 비동기식으로 사용해서, 운영 서버의 성능을 개선할 수 있다. 이것을 응용 프로그램측에서 지정해서, 특정 업무 상태에서만 동기식 복제를 이용할 수 있다.

이와 함께 고려해야할 부분은 운영 서버와 대기 서버 사이 복제를 위해 사용되는 네트워크 사용량이다. 이 부분을 잘못 계산하면, 운영 서버가 운영을 위해 사용해야할 네트워크 자원을 복제 작업 비용으로 써버리는 경우가 생기고, 이것은 곧바로 성능 저하를 발생시킨다.

26.2.8.4. 고가용성을 고려한 계획

운영 서버에서 synchronous_commit 설정을 on, remote_apply, remote_write 로 지정했고, synchronous_standby_names 설정값으로 지정한 동기식 대기 서버 수와 그 서버 이름으로 등록한 서버가 있는 경우, 그 동기화 될 대기 서버 모두 해당 트랜잭션이 반영 되어야 운영 서버는 트랜잭션이 완료되었음 응용프로그램에게 알린다. 즉 대기 서버 중 하나라도 장애로 응답을 하지 않는다면, 운영 서버의 해당 트랜잭션은 영원히 완료되지 않는다.

고가용성을 구현하는 가장 좋은 방법은 요구되는 모든 동기식 대기 서버가 장애로 멈추지 않게 만드는 것이다. 운영 서버의 환경설정에서 synchronous_standby_names 변수의 값으로 동기시 방식을 이용할 대기 서버들을 나열해서 이것을 구현할 수 있다.

In a priority-based synchronous replication, the standbys whose names appear earlier in the list will be used as synchronous standbys. Standbys listed after these will take over the role of synchronous standby if one of current ones should fail.

In a quorum-based synchronous replication, all the standbys appearing in the list will be used as candidates for synchronous standbys. Even if one of them should fail, the other standbys will keep performing the role of candidates of synchronous standby.

대기 서버가 처음으로 실행되어 운영 서버로 접속하는 시점에는 대기 서버는 아직까지는 운영 서버와 동기화가 되지 않았을 것이다. 이 때, 대기 서버는 운영 서버 동기화를 위해, 더 이상 동기화를 할 필요가 없는 상태가 될 때까지 계속 스트리밍 방식으로 자료를 동기화 할 것이다. 이런 일련의 상태를 따라잡기 catchup 상태라고 한다. 이 상태의 기간은 대기 서버와 운영 서버의 자료 동기화 간격(차이가 나는 WAL 레코드 수)을 좁혀서, 최종적으로는 동기화 간격을 없게 만드는 시간이다. 달리 말하면, 운영 서버에서는 반영되었으나, 대기 서버에는 아직 반영되지 않은 WAL 레코드들을 모두 처리하는 시간이다. 이 따라잡기 시간은 대기 서버가 얼마 동안 중지해 있었고, 그 사이 얼마나 많은 자료 변경 트랜잭션이 일어났냐에 따라 결정된다. 이 작업의 상태는 pg_stat_replication 뷰를 통해서 살펴볼 수 있다.

운영 서버에서는 커밋되었고, 운영 서버가 대기 서버의 커밋 완료 응답을 기다리고 있는 중에 운영 서버가 재실행된다면, 운영 서버가 재실행 복구 작업 시, 그 트랜잭션은 대기 서버에서 커밋 완료 되었었다는 응답을 받았다고 간주하고, 복구 작업을 진행한다. 이것은 모든 대기 서버가 그 커밋을 완료했는지 알길이 없기 때문이다. 하지만, 복구가 완료되어 운영서버가 정상적으로 실행되었고, 어떤 대기 서버는 그 작업을 아직 하지 않았다면, 그 작업부터 진행할 것이기 때문에, 자료 손실은 없을 것이다. 응용 프로그램 입장에서는 이런 상황이 발생했다면, - 커밋 명령이 성공적으로 끝나기 전에 데이터베이스 연결이 끊겼다면 - 재연결시 반드시 해당 작업으로 자료가 모든 동기식 대기 서버에 정상적으로 반영되었는지 확인해야할 필요가 있다.

synchronous_standby_names 환경 변수값에 등록된 모든 대기 서버들이 모두 커밋 완료 응답을 보내지 않아, 운영 서버의 커밋 작업이 멈춰 있는 상태라면, 이 변수의 값에서 동기화 될 서버 개수를 줄이거나, 동기식 복제를 포기하는 설정(빈문자열)으로 바꾸고 환경 설정을 다시 적용해서 운영 서버를 정상화 할 수 있다.

운영 서버와 대기 서버와의 연결이 끊겼고, 대기 서버를 운영 서버로 사용해야할 상황이라면, 대기 서버들 가운데 운영 서버와 동기화가 잘 된 서버를 운영 서버로 선택하는 것은 관리자의 몫이다. (동기식 복제를 하는 대기 서버를 운영 서버로 채택하는 것이 제일 안전할 것이다.)

대기 서버의 트랜잭션 완료 응답을 기다리고 있는 중에, 새로운 대기 서버를 만들어야할 상황이라면, pg_start_backup(), pg_stop_backup() 함수를 사용해서, 일반 베이스 백업을 만드는 방식으로 만들면 되나, 이 때 이 작업을 하는 세션은 반드시, synchronous_commit = off 환경으로 작업을 진행해야한다. 그렇지 않으면, 윗 함수들 조차 대기 서버의 응답을 기다리게 되어 동기화 하고 있는 대기 서버의 문제가 풀릴 때까지 무한 대기 상태가 되어버린다.

26.2.9. 대기 서버에서 아카이브 연속성 보장

연속된 트랜잭션 로그 조각들을 따로 보관하는 작업은 두 가지 상황을 고려해 볼 수 있다. 하나는 운영 서버에서만 따로 보관하는 작업(아카이빙)을 하는 경우이고, 다른 하나는 대기 서버도 자체적으로 동시에 그 작업을 중복해서 하는 것이다. 대기 서버에서도 아카이브 작업을 하려면, 대기 서버의 archive_mode 설정값을 always로 지정한다. 이 환경이면, 대기 서버 쪽으로 트랜잭션 로그 조각 파일이 전달 되거나, 스트리밍 복제 방식으로 트랜잭션 로그들이 전달 된 경우 모두 적정한 트랜잭션 로그 따로 보관하기 작업을 진행한다. 이렇게 트랜잭션 로그 조각 파일을 중복해서 보관하는 것은 쉽게 지정할 수 있으나, 운영 서버와 대기 서버에 각각 저장된 같은 이름의 파일 내용도 똑같다고는 보장할 수 없다. 그래서 완전한 트랜잭션 로그 조각 파일의 연속성을 보장하려면, archive_command 설정값을 보다 세밀하게 신경 써야 한다. 첫번째는 일단 같은 이름의 파일이 있는 경우 덮어쓰는 것은 막아야 하며, 두번째 이런 작업에 대한 예외처리가 정상적인 작업이었음을 서버 쪽으로 알려주어야 한다(이 작업의 쉘 반환 값이 0여야 한다).

archive_mode 설정값이 on으로 지정하면, 대기 서버로 실행되고 있는 경우나, 운영 전 복구 모드인 경우 트랜잭션 로그 아카이브 작업은 하지 않는다. 대기 서버가 운영 서버로 전환되면, 그때부터 아카이브 작업을 시작한다. 하지만, 따로 설정하지 않았다면, WAL 파일을 아카이브 하지 않고, 타임라인 내역 파일도 스스로 만들지 않는다. 이 때문에, 연속된 트랜잭션 로그 파일을 따로 보관하려면, 이전 운영 서버가 종료되는 시점의 모든 트랜잭션 로그가 따로 보관 되어야 한다 (아카이브 되어야 함). 스트리밍 복제 환경에서 운영 서버의 아카이브 작업이 완료되기 전에, 대기 서버가 운영 서버로 전환되면, 트랜잭션 로그의 아카이브 작업은 연속성을 잃게 될 수도 있다. 물론, 서버가 시작할 때 트랜잭션 로그를 재실행해야 하는 복구 모드 작업이 없다면, archive_mode 설정이 on 이나, always 모두 같은 트랜잭션 로그 아카이브 연속성을 보장한다.