DB의 시점복구를 위해서 가장 중요한 것은 무엇일까요?
fullbackup과 fullbackup본 이후 ~ 복구하려는 시점까지의 archive log입니다.
MySQL에서는 binary log가 되겠고, Oracle, PostgreSQL에서는 archive log가 되겠네요.
이번 글에서는 PostgreSQL 13버전에서의 archive logging 설정 방법과 시점복구 방법에 대해서 알아보겠습니다.
특히 PostgreSQL 12버전부터 DB 복구를 위한 커맨드가 그 전 버전과 약간 달라진 점 참고 부탁드립니다.

WAL 이란

PostgreSQL에서는 DML,vaccum 등 데이터를 변경하거나 작업이력이 남는 커맨드에 대해 WAL(Write Ahead Logging) 파일을 남깁니다.
이 WAL 파일을 replay 함으로써 유실된 데이터를 복구할 수 있게 되는데요
이러한 WAL 파일은 엔진 디렉토리 내의 pg_wal 디렉토리에 저장되고 각 파일의 크기는 16MB입니다.
그리고 pg_wal 디렉토리는 무한정 계속 커지는 게 아니라
max_wal_size , min_wal_size 파라미터로 pg_wal에 저장할 wal 파일의 개수를 제어하게 되는데요
아래처럼 설정되어있다면 pg_wal의 크기가 1GB가 넘는다면 오래된 WAL파일은 지우고 최근파일만 남기게 됩니다.

max_wal_size = 1GB
min_wal_size = 80MB

그렇기 때문에 WAL 파일을 Archiving 해서 따로 저장해놓는 것이 중요합니다.
갑자기 WAL 파일이 많이 생성되고 위 제한에 따라 파일이 유실되어 시점복구를 못하게 되면 큰일이니까요.
아카이빙 설정은 아래와 같습니다.

Archiving 설정

$ vi postgresql.conf
archive_mode = on
archive_command = 'cp %p /home/psql/arch/testdb/%f'             
#restore_command = 'cp /home/psql/arch/testdb/%f %p'

여기서 %p는 WAL파일이 있는 디렉토리, %f는 옮길 WAL 파일의 이름으로 자동 세팅되며
archive_mode 를 변경할 땐 DB재시작이 필요합니다.

  • restore_command는 기존 PostgreSQL 11버전까지는 recovery.conf 파일에 들어가던 설정입니다.
    PostgreSQL 12 부터는 recovery.conf 파일을 더이상 사용하지 않으면서 postgresql.conf 파일에서 설정합니다.

다음은 트랜잭션을 수행하여 WAL 로그 생성 및 아카이빙 되는 것을 확인해보겠습니다.

testdb=# call insert_test_data(1000);

CALL

### pg_wal 디렉토리

[postgres@df91de86eb7d pg_wal]$ pwd
/home/psql/engn/PGSQL/pg_wal
[postgres@df91de86eb7d pg_wal]$ ls -ltr
total 180228
-rw------- 1 postgres postgres 16777216 Oct 10 01:56 000000010000000100000064
-rw------- 1 postgres postgres 16777216 Oct 10 01:56 000000010000000100000063
-rw------- 1 postgres postgres 16777216 Oct 10 01:57 000000010000000100000062
-rw------- 1 postgres postgres 16777216 Oct 10 01:59 000000010000000100000065
-rw------- 1 postgres postgres 16777216 Oct 10 01:59 000000010000000100000066
-rw------- 1 postgres postgres 16777216 Oct 10 02:00 000000010000000100000067
-rw------- 1 postgres postgres 16777216 Oct 10 02:28 00000001000000010000005D
-rw------- 1 postgres postgres 16777216 Oct 10 02:29 00000001000000010000005E
-rw------- 1 postgres postgres 16777216 Oct 10 02:29 00000001000000010000005F
-rw------- 1 postgres postgres 16777216 Oct 10 02:30 000000010000000100000060
drwx------ 2 postgres postgres     4096 Oct 10 02:30 archive_status
-rw------- 1 postgres postgres 16777216 Oct 10 02:30 000000010000000100000061   <==== 현재 쓰고 있는 WAL 파일 

[postgres@df91de86eb7d pg_wal]$ ps -ef | grep archiver
postgres 15305 15298  0 01:54 ?        00:00:00 postgres: archiver   last was 00000001000000010000005F
==> archiver 백그라운드 프로세스가 archiving 작업을 수행함

[postgres@df91de86eb7d pg_wal]$ cd archive_status/
[postgres@df91de86eb7d archive_status]$ ls -ltr
total 0
-rw------- 1 postgres postgres 0 Oct 10 02:29 00000001000000010000005E.done   <=== archive 디렉토리로 넘어간 파일들
-rw------- 1 postgres postgres 0 Oct 10 02:29 00000001000000010000005F.done 
-rw------- 1 postgres postgres 0 Oct 10 02:30 000000010000000100000060.done



### archive 디렉토리
[postgres@df91de86eb7d testdb]$ pwd
/home/psql/arch/testdb
[postgres@df91de86eb7d testdb]$ ls -ltr

-rw------- 1 postgres postgres 16777216 Oct 10 02:29 00000001000000010000005E  
-rw------- 1 postgres postgres 16777216 Oct 10 02:29 00000001000000010000005F
-rw------- 1 postgres postgres 16777216 Oct 10 02:30 000000010000000100000060

=> 위 pg_wal/archive_status 에서 .done 처리된 파일들이 넘어와있음 

시점복구

  • 백업
$ pg_basebackup -U postgres -h localhost --progress -D testdb
  • 복구 시점 확인
testdb=# call insert_test_data(1000);
CALL
testdb=#

### 복구 시점
testdb=# select pg_switch_wal();
 pg_switch_wal
---------------
 1/614ABDD0
(1 row)

### 복구 시점 이후 추가 데이터 인입
testdb=# call insert_test_data(1000);
CALL
testdb=#

=> fullbackup + ~ 1/614ABDD0 까지의 archive log가 필요한 상태

  • 복구
### 기존 DB 삭제 
$ pg_ctl -D /home/psql/engn/PGSQL/ stop
$ rm -rf psql/

### 백업본 copy
$ cp -r backup/* psql/

$ vi postgresql.conf 
#restore_command = 'cp /home/psql/arch/testdb/%f %p'
recovery_target_lsn = '1/614ABDD0'


$ touch psql/recovery.signal
$ pg_ctl -D /home/psql/engn/PGSQL/ start
=> 복구 모드로 DB를 띄움


postgres=# call insert_test_data(1);
ERROR:  cannot execute INSERT in a read-only transaction


postgres=# SELECT pg_wal_replay_resume();
 pg_wal_replay_resume
----------------------

(1 row)

postgres=# call insert_test_data(1);
CALL
=> 복구모드로 띄운 DB는 readonly 상태임, 복구 모드 종료를 위해 위 커맨드 수행