POSTGRESQL/단편

PostgreSQL, pg_rewind(Failback)

(주)비트나인 2024. 7. 2. 15:33

 

작성자: 서준섭 과장 ㅣDB R&D센터 l DB Tech 팀

1.pg_rewind 소개

1.1. pg_rewind란?

PostgreSQL 공식 문서에서 pg_rewind는 다음과 같이 소개하고 있습니다.

PostgreSQL 데이터 디렉터리를 자신으로부터 파생된 또 다른 데이터 디렉터리에게 맞춰 동기화합니다.

 

pg_rewind는 클러스터의 또 다른 복사본에 맞춰 PostgreSQL 클러스터를 동기화 해주는 도구입니다. 이는 클러스터의 타임라인이 분기된 경우에도 적용할 수 있습니다. 일반적인 시나리오는 장애 복구 후 이전의 마스터 서버를 새로운 마스터 서버의 스탠바이로 되돌려주는 것입니다. 

 

pg_rewind를 사용하면 대상 데이터 디렉터리가 원본 데이터 디렉터리의 베이스 백업본과 유사하게 만들어집니다. 다른 백업 방법과 다른 점은, pg_rewind는 변경된 블록만 복사하기 때문에 전체 파일을 복사할 필요가 없다는 것입니다. 또한, 대상 데이터 디렉터리에 없는 릴레이션 파일, 환경 설정 파일, WAL 파일 등은 그대로 복사됩니다. 데이터베이스가 크고 변경 사항이 크지 않는 경우, pg_rewind를 이용한 이 되감기 작업이 다른 구축 방법보다 빠릅니다. 

 

pg_rewind는 source 클러스터와 target 클러스터 간의 타임라인 이력을 조사하여 분기 지점(replication 중단 시점)을 찾아냅니다. 그리고 타겟 클러스터의 pg_wal 디렉터리에서 분기된 시점으로 되돌릴 수 있는 전체 WAL 파일을 찾아냅니다. 이 분기 지점은 source/target 클러스터 또는 두 클러스터 간의 공통된 이전 타임라인에서 찾을 수 있습니다. 

 

전형적인 장애 복구 시나리오에서 target 클러스터는 분기된 지 오래되지 않은 경우가 많습니다. 이 경우에는 문제가 없지만, target 클러스터가 분기된 이후에도 오랜 기간 운영되었다면, source 클러스터 측에서 오래된 WAL 파일이 없을 수 있습니다. 이런 경우에는 직접 해당 WAL 아카이브 파일을 pg_wal 디렉터리로 복사하거나, pg_rewind의 -c 옵션을 사용하여 복사할 수도 있습니다. pg_rewind 명령은 페일오버 용도로만 사용되는 것이 아니라, 대기 서버가 운영 서버로 전환되었다가 다시 대기 서버로 바뀌는 경우에도 사용될 수 있습니다. 

 

pg_rewind 수행 후, 타겟 서버는 데이터의 일관성을 유지하기 위해 WAL 재반영 작업이 필요합니다. 서버가 시작되면 아카이브 복구 모드로 진입하고, 원본 데이터베이스 서버의 마지막 체크포인트 이후에 소스 서버에서 생성된 모든 WAL을 재생합니다. 문제는 필요한 WAL 파일이 없는 경우입니다. 이 상황에서는 pg_rewind 명령으로 이 파일들을 복사할 수 없습니다. 이런 경우, target 서버가 실행될 때 target 데이터 디렉터리 내에 recovery.signal 파일을 만들고, postgresql.conf 파일에서 복구에 필요한 적절한 restore_command 설정을 할 수 있습니다. 이를 통해 target 서버가 필요한 WAL 파일을 복구할 수 있습니다. 

pg_rewind를 수행하기 위해서는 target 서버의 postgresql.conf 에서 다음과 같은 요구사항이 충족되어야 합니다:

1. wal_log_hints 옵션이 활성화되어 있어야 합니다. 

2. initdb로 클러스터를 초기화할 때 체크섬(checksum)이 활성화되어 있어야 합니다. 

또한 full_page_writes 값은 on으로 활성화되어 있어야 합니다. 이 옵션들은 모두 기본값으로 비활성화되어 있습니다. 이러한 설정이 완료되어야 pg_rewind를 성공적으로 수행할 수 있습니다. 

 

[postgres@204b0624983f ~]$ $PGHOME/pg_rewind --help
pg_rewind resynchronizes a PostgreSQL cluster with another copy of the cluster.

Usage:
  pg_rewind [OPTION]...

Options:
  -c, --restore-target-wal       use restore_command in target configuration to
                                 retrieve WAL files from archives
  -D, --target-pgdata=DIRECTORY  existing data directory to modify
      --source-pgdata=DIRECTORY  source data directory to synchronize with
      --source-server=CONNSTR    source server to synchronize with
  -n, --dry-run                  stop before modifying anything
  -N, --no-sync                  do not wait for changes to be written
                                 safely to disk
  -P, --progress                 write progress messages
  -R, --write-recovery-conf      write configuration for replication
                                 (requires --source-server)
      --config-file=FILENAME     use specified main server configuration
                                 file when running target cluster
      --debug                    write a lot of debug messages
      --no-ensure-shutdown       do not automatically fix unclean shutdown
  -V, --version                  output version information, then exit
  -?, --help                     show this help, then exit

Report bugs to <pgsql-bugs@lists.postgresql.org>.
PostgreSQL home page: <https://www.postgresql.org/>

 

1.2 wal_log_hints 파라미터의 역할

wal_log_hints 옵션을 on으로 설정하면 PostgreSQL의 Write-Ahead Logging (WAL) 메커니즘에 변화가 발생합니다. 이 옵션이 활성화되면, 데이터베이스는 힌트 비트(hint bits)를 업데이트할 때마다 WAL에 해당 로그를 기록하게 됩니다. 힌트 비트는 데이터베이스 인덱스 및 기타 메타데이터의 정합성을 유지하는 데 사용됩니다. 이 옵션이 중요한 이유는 다음과 같습니다: 

 

hint bits란?
PostgreSQL의 MVCC 메커니즘은 많은 유용한 기능을 제공하지만 구현 과정에서 몇 가지 혼란스러운 부작용이 발생할 수 있습니다. 그 중 하나는 힌트 비트(hint bits) 처리와 관련된 것입니다. 

MVCC는 힌트 비트를 사용하여 데이터의 가시성과 정합성을 관리합니다. 힌트 비트는 커밋되거나 중단된 트랜잭션에 의해 튜플이 생성 및/또는 삭제된 것을 표시하는 데 사용됩니다. 이러한 힌트 비트가 설정되지 않은 튜플의 가시성을 결정하려면 pg_clog 및 pg_subtrans를 참조해야 하므로 비용이 많이 드는 검사 작업이 필요합니다. 반면에 튜플에 비트가 설정된 경우, 해당 상태가 알려져 있어 현재 스냅샷에서 쉽게 계산할 수 있습니다.

문제는 이러한 힌트 비트 처리 과정에서 데이터베이스 테이블에 많은 쓰기 작업이 발생할 수 있다는 점입니다. 즉, 사용자가 데이터베이스 테이블을 단순히 읽기만 하는 경우에도, 힌트 비트 관리를 위한 쓰기 작업이 필요할 수 있습니다. 이는 MVCC 메커니즘의 부작용으로, 데이터베이스 성능에 부정적인 영향을 미칠 수 있습니다. 



wal_log_hints 설정으로 인한 변화

WAL 기록 증가:

  • 힌트 비트(hint bits)는 MVCC(Multi-Version Concurrency Control) 구현의 일환으로 사용되며, 레코드의 상태를 추적하는데 사용됩니다. 
  • 힌트 비트 업데이트는 주로 커밋된 트랜잭션을 표시하거나 오래된 트랜잭션의 상태를 표시하는 데 사용됩니다.
  • wal_log_hints를 on으로 설정하면, 이러한 힌트 비트 변경 사항이 WAL에 기록됩니다. 
  • 결과적으로 WAL 로그의 크기가 증가할 수 있습니다.

디스크 I/O 증가:

  • WAL 로그의 크기 증가로 인해 디스크 I/O가 증가할 수 있습니다. 
  • 이는 시스템의 성능에 영향을 미칠 수 있으며, 특히 많은 업데이트가 발생하는 워크로드에서 두드러질 수 있습니다. 

데이터 무결성 향상:

  • wal_log_hints가 on으로 설정되면, 클러스터의 데이터를 다른 노드로 이동하거나 복구하는 과정에서 데이터 무결성을 유지하는 데 도움이 됩니다.
  • 이는 주로 고가용성 환경에서 중요한 고려 사항입니다. 

 

pg_rewind 시 wal_log_hints가 필요한 이유

pg_rewind는 PostgreSQL 클러스터를 특정 시점으로 되돌리기 위해 사용되는 도구입니다. 주로 장애 복구 상황이나 스탠바이 서버를 프라이머리 서버로 전환한 후 원래 프라이머리 서버를 다시 동기화할 때 사용됩니다.

 

pg_rewind가 제대로 작동하려면 두 서버 간의 차이를 식별하고 필요한 변경 사항을 동기화해야 합니다. wal_log_hints가 필요한 이유는 다음과 같습니다:

블록 수준 차이 검출:

  • pg_rewind는 블록 수준에서 변경된 데이터를 식별합니다. WAL에 힌트 비트 변경 사항이 기록되지 않으면, pg_rewind는 정확한 변경 내역을 파악할 수 없습니다. 이는 데이터 일관성에 문제를 야기할 수 있습니다.

안전한 동기화:

  • 힌트 비트의 변경 사항이 WAL에 기록되면, pg_rewind는 이러한 변경 사항을 인식하고 안전하게 동기화할 수 있습니다. 이는 클러스터의 무결성을 유지하고 데이터 손실을 방지하는 데 필수적입니다.

따라서, wal_log_hints 설정을 ‘on’으로 하는 것은 pg_rewind의 올바른 작동을 보장하고 데이터 일관성과 무결성을 유지하는 데 중요한 역할을 합니다. 이를 통해 PostgreSQL 클러스터의 안정성과 신뢰성을 높일 수 있습니다.

 

2. pg_rewind 목적

pg_rewind의 목적은 끊어진 Replication을 현재 운영중인 Primary DB와 Data Cluster를 동기화하고 Replication을 재연결하는 것입니다. 

 

새롭운 Replication을 구성하는 간단한 방법으로 pg_basebackup을 고려할 수 있습니다. 그렇다면 pg_rewind가 pg_basebackup과 어떤 차이점과 장점이 있는지 살펴볼 필요가 있습니다. pg_basebackup의 경우 Data Cluster를 완전히 삭제하고 현재 운영 중인 Primary DB의 Data Cluster를 처음부터 물리적으로 복제합니다. 

 

반면 pg_rewind는 source DB가 분기된 시점부터 target DB와의 차이점을 블록 단위로 확인하고 변경된 부분만 추가합니다. 따라서 데이터 용량이 큰 데이터베이스에서 끊어진 Replication을 재구성하는 데 소요되는 시간은 pg_rewind가 월등히 빠르다는 장점이 있습니다. 마찬가지로 pg_rewind를 수행하는 경우 pg_basebackup에 비해 빠른 속도로 failback이 가능하다는 장점이 있습니다.

 

3. pg_rewind 사용

 

과정 primary secondary
primary 기본 설정 listen_addresses = '*'
archive_mode = on
archive_command = 'test ! -f /archive/%f && cp %p /archive/%f'
logging_collector = on
log_filename = 'postgresql-%Y-%m-%d.log'

wal_level = replica
synchronous_commit = local

wal_keep_size = 32
synchronous_standby_names = '*'
promote_trigger_file = '/home/agensql/data/trigger.signal'
hot_standby = on
 
primary 기본 설정 host all all 0.0.0.0/0 trust
host replication repluser 0.0.0.0/0 trust
 
primary 기동 ag_ctl start  
repluser 생성 및 권한 부여 create user repluser with replication password '1234' login;
grant all privileges on database agensdb to repluser;
 
Replication 초기 구성   pg_basebackup -h 192.168.54.184 -D $PGDATA -U repluser -p 5333 -v -P -R --wal-method=stream
  ag_ctl start
recovery 모드 확인   select pg_is_in_recovery();
wal_reciever 확인 select * from pg_stat_replication;  
Primary 장애 상황 ag_ctl stop  
Secondary Promote   [방법 2 가지]
1. pg_ctl promote -D $PGDATA
2. vi $PGDATA/postgresql.conf
promote_trigger_file = '/home/agens/data/trigger.signal'
pg_ctl reload / select pg_reload_conf();
touch /home/agens/data/trigger.signal
DDL 발생   create table test(a int);
insert into test select * from generate_series(1,1000);
과정   Single DB
설정 추가 vi postgresql.conf
wal_log_hints = on
vi postgresql.conf
wal_log_hints = on
재기동 및 설정 등록 ag_ctl start
ag_ctl stop
pg_ctl reload
pg_rewind(sync) pg_rewind --source-server='host=192.168.54.83 port=5333 user=agens dbname=postgres' --target-pgdata=$PGDATA -P -R  
  rm $PGDATA/postgresql.auto.conf  
설정 추가 vi postgresql.conf
primary_conninfo='user=repluser password=1234 host=192.168.54.83 port=5333'
 
standby.signal 추가 touch $PGDATA/standby.signal  
재기동 ag_ctl start  
과정 new_secondary new_primary
recovery 모드 확인 select pg_is_in_recovery();  
wal_receiver 확인   select * from pg_stat_replication;

 

3.1. pg_rewind를 이용한 failback

과정 Primary Standby
초기 Replication 구성   $PGHOME/pg_basebackup -h 172.18.0.2 -D $PGDATA -U repluser -p 5432 -v -P -R --wal-method=stream
  $PGHOME/pg_ctl start
select * from pg_stat_replication; select pg_is_in_recovery();
Primary 서버 장애 $PGHOME/pg_ctl stop  
Standby 서버 Read only 확인   create table test123(id int);
ERROR: cannot execute CREATE TABLE in a read-only transaction
Standby 서버 Promote   [postgres@204b0624983f ~]$ $PGHOME/pg_ctl promote
waiting for server to promote.... done
server promoted
과정 DB off Single (Promote)
테이블 생성 및 데이터 삽입   CREATE TABLE user_activity_log2 (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL,
activity_type VARCHAR(50) NOT NULL,
activity_timestamp TIMESTAMP NOT NULL,
activity_details TEXT
);
  DO $$
DECLARE
start_id INT := 1;
end_id INT := 1000000; -- Adjust this to reach ~1GB data
user_id INT;
activity_types TEXT[] := ARRAY['login', 'logout', 'purchase', 'view', 'click'];
activity_details TEXT;
activity_timestamp TIMESTAMP;
BEGIN
FOR i IN start_id..end_id LOOP
user_id := (i % 10000) + 1; -- Simulating 10,000 unique users
activity_timestamp := NOW() - (random() * INTERVAL '1000 days');
activity_details := md5(random()::text);

INSERT INTO user_activity_log2 (user_id, activity_type, activity_timestamp, activity_details)
VALUES (user_id, activity_types[(random() * 4 + 1)::INT], activity_timestamp, activity_details);
END LOOP;
END $$;
wal_log_hints 설정 vi postgresql.conf
wal_log_hints = on
 
DB 재기동 ag_ctl start
ag_ctl stop
 
pg_rewind 수행 $PGHOME/pg_rewind --source-server='host=172.18.0.3 port=5432 user=postgres dbname=postgres' --target-pgdata=$PGDATA -P -R
pg_rewind: connected to server
pg_rewind: servers diverged at WAL location 0/630000A0 on timeline 2
pg_rewind: rewinding from last common checkpoint at 0/63000028 on timeline 2
pg_rewind: reading source file list
pg_rewind: reading target file list
pg_rewind: reading WAL in target
pg_rewind: need to copy 537 MB (total source directory size is 1071 MB)
550000/550000 kB (100%) copied
pg_rewind: creating backup label and updating control file
pg_rewind: syncing target data directory
pg_rewind: Done!
 
postgresql.auto.conf 삭제 rm $PGDATA/postgresql.auto.conf  
복제 대상 설정 vi postgresql.conf
primary_conninfo='user=repluser password=1234 host=172.18.0.3 port=5432'
 
DB 기동 $PGHOME/pg_ctl start  
과정 new_standby new_primary
Replication 확인 select pg_is_in_recovery(); select * from pg_stat_replication;
pid | usesysid | usename | application_name | client_addr | client_hostname | client_port | backend_start | backend_xmin | state | sent_lsn | write_lsn | flush_lsn | replay_lsn |
write_lag | flush_lag | replay_lag | sync_priority | sync_state | reply_time
-------+----------+----------+------------------+-------------+-----------------+-------------+-------------------------------+--------------+-----------+------------+------------+------------+------------+
-----------+-----------+------------+---------------+------------+-------------------------------
97179 | 24576 | repluser | walreceiver | 172.18.0.2 | | 50820 | 2024-06-17 05:39:39.883996+00 | | streaming | 0/900633D8 | 0/900633D8 | 0/900633D8 | 0/900633D8 |
| | | 1 | sync | 2024-06-17 05:39:50.838498+00
(1 row)
wal_log_hints 설정   vi postgresql.conf
wal_log_hints = on
DB 종료   $PGHOME/pg_ctl stop
구 Primary promote $PGHOME/pg_ctl promote  
과정 new_primary DB off
pg_rewind 수행   $PGHOME/pg_rewind --source-server='host=172.18.0.2 port=5432 user=postgres dbname=postgres' --target-pgdata=$PGDATA -P -R
복제 대상 설정   vi postgresql.conf
primary_conninfo='user=repluser password=1234 host=172.18.0.2 port=5432'
DB 기동   $PGHOME/pg_ctl start
Replication 확인 select * from pg_stat_replication;
pid | usesysid | usename | application_name | client_addr | client_hostname | client_port | backend_start | backend_xmin | state | sent_lsn | write_lsn | flush_lsn | repl
ay_lsn | write_lag | flush_lag | replay_lag | sync_priority | sync_state | reply_time
-------+----------+----------+------------------+-------------+-----------------+-------------+-------------------------------+--------------+-----------+------------+------------+------------+-----
-------+-----------+-----------+------------+---------------+------------+-------------------------------
53519 | 24576 | repluser | walreceiver | 172.18.0.3 | | 57092 | 2024-06-17 05:56:49.833254+00 | | streaming | 0/91005F98 | 0/91005F98 | 0/91005F98 | 0/91
005F98 | | | | 1 | sync | 2024-06-17 05:58:50.991647+00
(1 row)
select pg_is_in_recovery();


4. pg_rewind 마무리

결론적으로, pg_rewind는 PostgreSQL 클러스터의 장애 복구 및 스탠바이 서버 전환에 유용한 도구입니다. 변경된 블록만을 복사하여 효율적으로 데이터를 동기화하며, 타임라인 분기 후에도 적용할 수 있어 유연합니다. 이를 통해 빠른 시간 내에 클러스터를 복구하거나 스탠바이 서버로 전환할 수 있습니다.

 

단, pg_rewind를 성공적으로 수행하기 위해서는 타겟 서버의 postgresql.conf 파일에서 필요한 설정이 충족되어야 하며, 필요한 WAL 파일을 확보해야 합니다. 특히, 복구 모드에서 WAL 파일이 없을 경우에는 별도로 아카이브 파일을 복사하거나 restore_command 설정을 통해 복구할 수 있습니다. 

 

따라서, pg_rewind는 신속하고 효율적인 복구 및 서버 전환을 지원하는 중요한 도구로, 적절한 설정과 파일 관리가 필수적입니다. 이러한 점들을 고려하여 pg_rewind를 활용하면 PostgreSQL 클러스터의 안정성과 가용성을 크게 향상시킬 수 있습니다.