LabHub

PostgreSQL 복제와 승격 · 복제의 구조 · 이론

복제는 WAL 을 흘려보내는 일이다

LabHub 에서 이어서 보기

한 줄 요약

PostgreSQL 복제는 WAL(선행 기록 로그)을 대기 서버로 보내고 그대로 재생하는 것이다. 그래서 대기 서버는 주 서버의 바이트 단위 복사본이 되고, 그 대신 서로 다른 버전을 섞을 수 없다.

왜 이게 필요했나

데이터베이스가 죽으면 서비스가 죽는다. 백업이 있어도 복구에 몇 시간이 걸린다. 복제는 미리 만들어 둔 복사본을 두어 그 시간을 분 단위로 줄인다.

PostgreSQL 이 하는 일은 단순하다. 모든 변경은 먼저 WAL 에 적힌다(그래야 크래시에서 복구할 수 있다). 복제는 그 WAL 을 네트워크로 흘려보내고, 대기 서버가 자기 데이터에 그대로 재생한다.

여기서 두 가지가 따라 나온다.

어떻게 동작하나

세우는 순서는 네 단계다.

1) 주 서버 설정        wal_level=replica, max_wal_senders, 복제 계정2) 복제 슬롯 생성      pg_create_physical_replication_slot('s1')3) 베이스백업          pg_basebackup -X stream -S s1 -R4) 대기 서버 기동      standby.signal 이 있으면 자동으로 recovery 모드

pg_basebackup-Rstandby.signal 파일과 primary_conninfo 설정을 자동으로 만들어 준다. 그 파일 하나가 "너는 대기 서버다" 라는 표시다.

복제 슬롯이 하는 일

슬롯이 없으면 주 서버는 대기 서버가 어디까지 받았는지 모른다. 그래서 WAL 을 자기 기준으로 재활용하고, 대기 서버가 잠깐 끊겼다 돌아오면 필요한 WAL 이 이미 사라져 있다. 그러면 복제가 깨지고 베이스백업부터 다시 해야 한다.

슬롯은 "이 대기 서버가 받아 갈 때까지 WAL 을 지우지 마라" 는 표식이다. 대신 대기 서버가 영영 안 돌아오면 WAL 이 무한히 쌓여 디스크가 찬다. 그래서 max_slot_wal_keep_size 로 상한을 둔다 — 상한을 넘으면 슬롯을 포기하고 디스크를 지킨다. 어느 쪽을 잃을지 미리 정하는 설정이다.

동기냐 비동기냐

기본은 비동기다. 주 서버는 WAL 을 보내 놓고 커밋을 즉시 반환한다. 빠르지만, 주 서버가 갑자기 죽으면 아직 안 간 트랜잭션이 사라진다.

synchronous_commit = onsynchronous_standby_names 를 설정하면 동기가 된다. 대기 서버가 WAL 을 받았다고 답할 때까지 커밋이 기다린다. 유실이 없는 대신 커밋 지연이 네트워크 왕복만큼 늘고, 대기 서버가 죽으면 주 서버의 쓰기가 멈춘다.

그래서 동기 복제는 대기 서버를 둘 이상 두고 ANY 1 (s1, s2) 처럼 "둘 중 하나만 답하면 된다" 로 쓰는 것이 정석이다. 하나만 두고 동기로 걸면 가용성이 오히려 내려간다.

흔한 착각

"복제가 백업이다" — 아니다. DROP TABLE 을 실수로 치면 그 명령이 복제되어 대기 서버에서도 테이블이 사라진다. 복제는 하드웨어 장애를 막고, 백업은 사람의 실수를 막는다. 둘 다 필요하다.

"대기 서버를 읽기 부하 분산에 쓰면 공짜" — 대기 서버에서 긴 쿼리가 돌면 WAL 재생이 그 쿼리와 충돌해 지연되거나 쿼리가 취소된다(hot_standby_feedback 로 완화하지만 이번엔 주 서버의 청소가 밀린다). 공짜는 없다.

실무에서 진짜 중요한 것

지연을 보는 법을 알아야 한다.

-- 주 서버에서select client_addr, state, sent_lsn, replay_lsn,       write_lag, flush_lag, replay_lag  from pg_stat_replication;

statestreaming 이 아니면 붙지 않은 것이다. replay_lag 이 계속 커지면 대기 서버가 따라잡지 못하고 있다 — 디스크가 느리거나, 대기 서버에서 긴 쿼리가 돌고 있다.