본문 바로가기
엑셈 경쟁력/DB 인사이드

DB 인사이드 | PostgreSQL New Feature - 17 Release (4)

by exemtech 2025. 1. 16.

 

PostgreSQL New Feature는 시리즈로 구성됩니다.

  1. PostgreSQL 17 주요 기능 설명과 파라미터, 시스템 카탈로그 변화
  2. PostgreSQL 17 추가, 개선 기능
  3. PostgreSQL 17 Vacuum 성능 향상
  4. PostgreSQL 17 증분백업(Incremental Backup)

본 문서에서는 증분 백업 수행/복구하는 방법을 설명한 후, 증분 백업을 위한 WAL Summary 기능에 대한 내용을 기술합니다.

 

 

 

증분 백업(Incremental Backup)

증분 백업을 지원하지 않았던 PostgreSQL 16 까지는 데이터베이스 전체 백업을 기본으로 백업을 수행하였습니다. 따라서, 백업 수행할 때마다 데이터베이스 크기만큼의 백업 파일이 생성되었습니다. 또한, 백업 이후의 데이터를 복구하기 위하여 WAL 파일이 삭제되지 않게 아카이빙 해두고 이를 이용하여 복구를 수행할 수 있었습니다. 이러한 백업 과정은 시간이 많이 소요되고 필요한 스토리지 공간도 많이 필요하였습니다.

PostgreSQL 17부터는 증분 백업(Incremental Backup)을 지원하여 백업 과정의 시간과 스토리지 공간을 절약할 수 있습니다. 증분 백업을 위해서는 데이터베이스 전체 백업이 1회 필요하지만, 전체 백업 이후에는 마지막 백업 이후의 변경된 내용만 캡처하는 방식으로 백업이 수행됩니다. 즉, 전체 백업이든 다른 증분 백업이든 마지막 백업 이후에 수행된 변경 사항만 캡처합니다. 이는 변경 사항에 관계없이 전체 백업에 비해 백업 시간과 스토리지 소비를 크게 줄일 수 있습니다.

 

 

증분 백업을 위해 추가된 기능

pg_basebackup Utility 옵션 추가

증분 백업을 지원하기 위해 -i 옵션이 추가되었으며, 마지막 백업의 manifest file을 지정하여 수행합니다.

## pg_basebackup Utility에 추가된 옵션
-i, --incremental=OLDMANIFEST  take incremental backup

pg_combinebackup Utility 신규 추가

여러 증분 백업을 단일 백업으로 병합하기 위해 PostgreSQL 17에서 새롭게 추가된 Utility입니다.

pg_combinebackup [option] [backup_directory..]

 

주요 옵션 설명
-d, --debug 디버그 로그 출력
-n, --dry-run 병합을 실제로 수행하지 않고, 어떤 작업이 이루어질지 미리 확인
-o, --output=DIRECTORY 재구성된 데이터 디렉토리를 저장할 경로를 지정
backup_directory 병합할 증분 백업의 경로를 하나 이상 지정. 

시스템 파라미터 생성

파라미터 기본값 설명
summarize_wal off WAL Summary 기능 활성화 여부. on으로 설정되어야 증분 백업 가능
wal_summary_keep_time 14400 (10d) WAL Summary 파일 보관 주기 설정

 

 

 

증분 백업 수행

증분 백업 기능을 사용하기 위해서는 summarize_wal 시스템 파라미터를 활성화해야 합니다.

ALTER SYSTEM SET summarize_wal = on ;
SELECT pg_reload_conf() ;
 pg_reload_conf
----------------
 t

증분 백업을 수행하기 전 기본(전체) 백업을 먼저 생성합니다.

[postgres@pgdb ~]$ mkdir /home/postgres/backups
[postgres@pgdb ~]$ pg_basebackup -D /home/postgres/backups/base_backup -v
pg_basebackup: initiating base backup, waiting for checkpoint to complete
pg_basebackup: checkpoint completed
pg_basebackup: write-ahead log start point: 0/2000028 on timeline 1
pg_basebackup: starting background WAL receiver
pg_basebackup: created temporary replication slot "pg_basebackup_2847097"
pg_basebackup: write-ahead log end point: 0/2000158
pg_basebackup: waiting for background process to finish streaming ...
pg_basebackup: syncing data to disk ...
pg_basebackup: renaming backup_manifest.tmp to backup_manifest
pg_basebackup: base backup completed

기본(전체) 백업 후 데이터를 생성한 후 증분 백업을 수행합니다. 증분 백업 수행 시 주의해야 할 사항은 가장 마지막 백업의 backup_manifest를 사용해야 합니다. 이 단계에서 가장 마지막 백업은 기본(전체) 백업으로 base_backup의 backup_manifest를 지정해야 합니다.

CREATE TABLE test01 AS SELECT 1 AS c1 , md5( i::text ) AS c2 FROM generate_series( 1, 1000000 ) AS i ;
SELECT 1000000
[postgres@pgdb ~]$ pg_basebackup --incremental=/home/postgres/backups/base_backup/backup_manifest -D /home/postgres/backups/incremental_backup01 -v
pg_basebackup: initiating base backup, waiting for checkpoint to complete
pg_basebackup: checkpoint completed
pg_basebackup: write-ahead log start point: 0/9000028 on timeline 1
pg_basebackup: starting background WAL receiver
pg_basebackup: created temporary replication slot "pg_basebackup_2847731"
pg_basebackup: write-ahead log end point: 0/90AED20
pg_basebackup: waiting for background process to finish streaming ...
pg_basebackup: syncing data to disk ...
pg_basebackup: renaming backup_manifest.tmp to backup_manifest
pg_basebackup: base backup completed

다시 데이터를 추가한 후 증분 백업을 수행합니다. 증분 백업 수행 시 주의해야 할 사항은 가장 마지막 백업의 backup_manifest를 사용해야 합니다. 이 단계에서 가장 마지막 백업은 첫 번째 증분 백업으로 incremental_backup01의 backup_manifest를 지정해야 합니다.

INSERT INTO test01 SELECT 2 , i FROM generate_series( 1 , 1000000 ) AS i ;
INSERT 0 1000000
[postgres@pgdb ~]$ pg_basebackup --incremental=/home/postgres/backups/incremental_backup01/backup_manifest -D /home/postgres/backups/incremental_backup02 -v
pg_basebackup: initiating base backup, waiting for checkpoint to complete
pg_basebackup: checkpoint completed
pg_basebackup: write-ahead log start point: 0/F000028 on timeline 1
pg_basebackup: starting background WAL receiver
pg_basebackup: created temporary replication slot "pg_basebackup_2851007"
pg_basebackup: write-ahead log end point: 0/F05CA78
pg_basebackup: waiting for background process to finish streaming ...
pg_basebackup: syncing data to disk ...
pg_basebackup: renaming backup_manifest.tmp to backup_manifest
pg_basebackup: base backup completed

 

 

증분 백업 복원

증분 백업 수행 단계에서 한 번의 기본(전체) 백업과 두 번의 증분 백업을 수행하였습니다. 이 백업들을 복원하고 복원된 데이터 디렉터리로 데이터베이스를 시작한 후 앞서 입력한 테스트 데이터가 존재하는지 확인합니다.

PostgreSQL 17에서 새롭게 생긴 pg_combinebackup Utility를 사용하여 모든 증분 백업을 결합하여 복원할 수 있습니다. 앞서 생성한 증분 백업을 pg_combinebackup Utility로 결합하여 /home/postgres/pg17.combine_backup 디렉터리로 복원합니다.

[postgres@pgdb ~]$ pg_combinebackup --output=/home/postgres/pg17.combine_backup /home/postgres/backups/base_backup /home/postgres/backups/incremental_backup01 /home/postgres/backups/incremental_backup02
[postgres@pgdb ~]$ ls -al /home/postgres/pg17.combine_backup
backup_label     global        pg_hba.conf    pg_multixact  pg_serial     pg_stat_tmp  pg_twophase  pg_xact
backup_manifest  pg_commit_ts  pg_ident.conf  pg_notify     pg_snapshots  pg_subtrans  PG_VERSION   postgresql.auto.conf
base             pg_dynshmem   pg_logical     pg_replslot   pg_stat       pg_tblspc    pg_wal       postgresql.conf

복원된 디렉터리에 존재하는 postgresql.conf 파일은 백업 대상의 postgresql.conf와 동일하게 구성됩니다. 복원된 디렉터리를 데이터 디렉터리로 사용하는 데이터베이스를 시작하기 위해서 postgresql.conf에서 Port를 변경한 후 데이터베이스를 시작합니다.

[postgres@pgdb ~]$ vi /home/postgres/pg17.combine_backup/postgresql.conf
## 중복되지 않는 Port로 변경
## 본 테스트에서는 5432 → 5433으로 변경

[postgres@pgdb ~]$ pg_ctl -D /home/postgres/pg17.combine_backup -l logfile start
waiting for server to start.... done
server started

데이터베이스에 접속하여 데이터를 확인합니다. 앞서 테스트 데이터는 test01 테이블에 총 2,000,000 row가 저장되어 있습니다. c1=1인 데이터는 1,000,000 row, c1=2인 데이터는 1,000,000 row가 저장되어 있습니다.

[postgres@pgdb ~]$ psql -p 5433 -U postgres
psql (17.2)
Type "help" for help.

postgres=# SELECT c1 , COUNT(*) FROM test01 GROUP BY c1 ;
 c1 |  count
----+---------
  1 | 1000000
  2 | 1000000
(2 rows)

 

 

Trouble Shooting

summarize_wal 파라미터를 활성화하지 않은 경우

summarize_wal 파라미터를 활성화하지 않은 경우 기본 백업은 가능하지만, 증분 백업은 불가능합니다.

[postgres@pgdb ~]$ pg_basebackup -D /home/postgres/backups/base_backup
[postgres@pgdb ~]$ pg_basebackup --incremental=/home/postgres/backups/base_backup/backup_manifest -D /home/postgres/backups/incremental_backup01
pg_basebackup: error: could not initiate base backup: ERROR:  incremental backups cannot be taken unless WAL summarization is enabled
pg_basebackup: removing data directory "/home/postgres/backups/incremental_backup01"

summarize_wal=on 설정 후 증분 백업을 다시 수행하면 아래와 같은 에러 메시지가 발생합니다.

ALTER SYSTEM SET summarize_wal=on ;
SELECT pg_reload_conf() ;
[postgres@pgdb ~]$ pg_basebackup --incremental=/home/postgres/backups/base_backup/backup_manifest -D /home/postgres/backups/incremental_backup01
WARNING:  aborting backup due to backend exiting before pg_backup_stop was called
pg_basebackup: error: could not initiate base backup: ERROR:  WAL summaries are required on timeline 1 from 0/2000028 to 0/9000028, but the summaries for that timeline and LSN range are incomplete
DETAIL:  The first unsummarized LSN in this range is 0/2000028.
pg_basebackup: removing data directory "/home/postgres/backups/incremental_backup01"

기본 백업을 수행한 후 summarize_wal을 활성화하였기 때문에 증분 백업이 실패하게 됩니다. 따라서 summarize_wal을 활성화 한 이후 새로 기본 백업을 수행한 후 증분 백업을 수행해야 정상적으로 증분 백업이 가능합니다.

[postgres@pgdb ~]$ rm -rf /home/postgres/backups/*
[postgres@pgdb ~]$ pg_basebackup -D /home/postgres/backups/base_backup
[postgres@pgdb ~]$ pg_basebackup --incremental=/home/postgres/backups/base_backup/backup_manifest -D /home/postgres/backups/incremental_backup01

 

pg_combinebackup 수행 시 백업 디렉터리 나열을 순서대로 하지 않은 경우

pg_combinebackup 수행 시 백업 디렉터리 나열 순서는 백업 순서대로 나열해야 합니다. 백업 순서대로 백업 디렉터리를 나열하지 않은 경우 pg_combinebackup은 실패하여 에러가 발생하며 증분 백업 복원도 되지 않습니다.

기본 백업이 선두에 나열되지 않은 경우 (증분백업1 → 증분백업2 → 기본백업 순으로 나열)

[postgres@pgdb ~]$ pg_combinebackup --output=/home/postgres/pg17.combine_backup /home/postgres/backups/incremental_backup01 /home/postgres/backups/incremental_backup02 /home/postgres/backups/base_backup
pg_combinebackup: error: backup at "/home/postgres/backups/base_backup" is a full backup, but only the first backup should be a full backup

증분 백업이 순서대로 나열되지 않은 경우 (기본백업 → 증분백업2 → 증분백업1 순으로 나열)

[postgres@pgdb ~]$ pg_combinebackup --output=/home/postgres/pg17.combine_backup /home/postgres/backups/base_backup /home/postgres/backups/incremental_backup02 /home/postgres/backups/incremental_backup01
pg_combinebackup: error: backup at "/home/postgres/backups/incremental_backup02" starts at LSN 0/11000028, but expected 0/2000028

 

 

 

WAL Summary 기능

PostgreSQL 17에서 새롭게 생긴 walsummarizer 프로세스는 WAL과 관련된 새로운 Background 프로세스로 WAL 로그를 효율적으로 요약하고 통계를 수집하는 역할을 수행합니다.

 

WAL Summary 관련 파라미터

파라미터 카테고리 기본값 설명
summarize_wal Write-Ahead Log > Summarization off WAL Summary 기능 활성화 여부
wal_summary_keep_time Write-Ahead Log > Summarization 14400 (10d) WAL Summary 파일 보관 주기 설정

WAL Summary 기능 활성화를 위해서는 wal_level 파라미터를 replica 또는 logical로 설정해야 하고, PostgreSQL 17에서 새로 생긴 summarize_wal 파라미터를 on으로 활성화해야(기본값:off) 합니다.

ALTER SYSTEM SET summarize_wal = on ;
SELECT pg_reload_conf();
 pg_reload_conf
----------------
 t

WAL Summary 기능이 활성화되면, walsummarizer 프로세스가 실행되어 pg_wal/summaries 디렉터리에 WAL Summary가 저장됩니다.

[postgres@pgdb ~]$ ps -ef | grep postgres
postgres 4144298       1  0 Jan06 ?        00:00:00 /home/postgres/pg17/bin/postgres -D /home/postgres/pg17.data
postgres 4144299 4144298  0 Jan06 ?        00:00:00 postgres: checkpointer
postgres 4144300 4144298  0 Jan06 ?        00:00:00 postgres: background writer
postgres 4144302 4144298  0 Jan06 ?        00:00:00 postgres: walwriter
postgres 4144303 4144298  0 Jan06 ?        00:00:00 postgres: logical replication launcher
postgres  144870 4144298  0 11:08 ?        00:00:00 postgres: walsummarizer
[postgres@pgdb ~]$ ls -al $PGDATA/pg_wal
-rw-------  1 postgres postgres 16777216 Jan  9 15:44 000000010000000000000001
drwx------  2 postgres postgres        6 Jan  9 15:42 archive_status
drwx------  2 postgres postgres     4096 Jan  9 15:43 summaries
[postgres@pgdb ~]$ ls -al $PGDATA/pg_wal/summaries
-rw------- 1 postgres postgres 4808 Jan  9 15:43 00000001000000000100002800000000010B1D38.summary
-rw------- 1 postgres postgres 4934 Jan  9 15:43 0000000100000000010B1D3800000000014CF250.summary
-rw------- 1 postgres postgres   56 Jan  9 15:43 0000000100000000014CF25000000000014CF350.summary
-rw------- 1 postgres postgres  230 Jan  9 15:43 0000000100000000014CF35000000000014D5D38.summary
-rw------- 1 postgres postgres   56 Jan  9 15:43 0000000100000000014D5D3800000000014D5E38.summary
-rw------- 1 postgres postgres   88 Jan  9 15:43 0000000100000000014D5E3800000000014D6188.summary

WAL Summary 파일은 PostgreSQL 17에서 새로 생긴 wal_summary_keep_time 파라미터에 의해 유지됩니다. wal_summary_keep_time 파라미터의 기본값은 14400분(10일)으로 WAL Summary 파일은 설정 값에 의해 10일이 지나면 자동으로 제거됩니다.

테스트를 위해 테이블 생성 및 데이터를 입력한 후 강제로 WAL Summary 파일을 생성한 후 WAL Summary의 내용을 확인합니다.

CREATE TABLE test01 AS SELECT i , md5( i::text ) FROM generate_series( 1 , 1000 ) i ;
CHEKCPOINT ;
[postgres@pgdb ~]$ ls -al $PGDATA/pg_wal/summaries
-rw------- 1 postgres postgres 4808 Jan  9 15:43 00000001000000000100002800000000010B1D38.summary
-rw------- 1 postgres postgres 4934 Jan  9 15:43 0000000100000000010B1D3800000000014CF250.summary
-rw------- 1 postgres postgres   56 Jan  9 15:43 0000000100000000014CF25000000000014CF350.summary
-rw------- 1 postgres postgres  230 Jan  9 15:43 0000000100000000014CF35000000000014D5D38.summary
-rw------- 1 postgres postgres   56 Jan  9 15:43 0000000100000000014D5D3800000000014D5E38.summary
-rw------- 1 postgres postgres   88 Jan  9 15:43 0000000100000000014D5E3800000000014D6188.summary
-rw------- 1 postgres postgres  590 Jan  9 15:53 0000000100000000014D6188000000000150A9E0.summary

CHECKPOINT를 발생시켜 WAL Summary 파일을 생성된 것을 확인할 수 있습니다. WAL Summary 파일의 내용은 편집기로 확인할 수 없고, PostgreSQL 17에서 새롭게 생성된 pg_walsummary Utility를 통해 확인할 수 있습니다.

[postgres@pgdb ~]$ pg_walsummary $PGDATA/pg_wal/summaries/0000000100000000014D6188000000000150A9E0.summary
... 생략 ...
TS 1663, DB 5, REL 16384, FORK main: limit 0
TS 1663, DB 5, REL 16384, FORK main: blocks 0..8
TS 1663, DB 5, REL 16387, FORK main: limit 0
TS 1663, DB 5, REL 16388, FORK main: limit 0
TS 1663, DB 5, REL 16388, FORK main: block 0

pg_walsummary 결과를 보면 REL 16384가 test01 테이블이고, blocks 0.. 8을 통해 9개의 Block이 변경되었음을 확인할 수 있습니다. 이러한 내용은 아래 WAL Summary 함수 설명 시 확인할 수 있습니다.

WAL Summary 파일 내용을 확인하기 위해서 아래와 같은 함수를 제공합니다.

함수 설명
pg_available_wal_summaries() 각 WAL Summary 파일의 시작 LSN과 종료 LSN 반환
pg_wal_summary_contents( tli , start_lsn , end_lsn ) 지정한 LSN 사이의 업데이트 정보 반환
pg_get_wal_summarizer_state() WAL Summary 생성 상태 반환

 

pg_available_wal_summaries()

-- 각 WAL Summary 파일의 시작 LSN과 종료 LSN 확인
SELECT * FROM pg_available_wal_summaries() ;
 tli | start_lsn |  end_lsn
-----+-----------+-----------
   1 | 0/1000028 | 0/10B1D38
   1 | 0/10B1D38 | 0/14CF250
   1 | 0/14CF250 | 0/14CF350
   1 | 0/14CF350 | 0/14D5D38
   1 | 0/14D5D38 | 0/14D5E38
   1 | 0/14D5E38 | 0/14D6188
   1 | 0/14D6188 | 0/150A9E0

WAL Summary 파일의 시작 LSN과 종료 LSN은 함수를 수행하지 않아도 WAL Summary 파일명에서 유추할 수 있습니다.

 

pg_wal_summary_contents( tli , start_lsn , end_lsn )

-- 지정한 LSN 사이의 업데이트 정보 확인
SELECT * FROM pg_wal_summary_contents( 1 , '0/14D6188' , '0/150A9E0' ) ;
 relfilenode | reltablespace | reldatabase | relforknumber | relblocknumber | is_limit_block
-------------+---------------+-------------+---------------+----------------+----------------
 ... 생략 ...
       16384 |          1663 |           5 |             0 |              0 | t
       16384 |          1663 |           5 |             0 |              0 | f
       16384 |          1663 |           5 |             0 |              1 | f
       16384 |          1663 |           5 |             0 |              2 | f
       16384 |          1663 |           5 |             0 |              3 | f
       16384 |          1663 |           5 |             0 |              4 | f
       16384 |          1663 |           5 |             0 |              5 | f
       16384 |          1663 |           5 |             0 |              6 | f
       16384 |          1663 |           5 |             0 |              7 | f
       16384 |          1663 |           5 |             0 |              8 | f
       16387 |          1663 |           5 |             0 |              0 | t
       16388 |          1663 |           5 |             0 |              0 | t
       16388 |          1663 |           5 |             0 |              0 | f

 

컬럼 설명
relfilenode Relation 파일 번호 (pg_class의 relfilenode 컬럼을 통해 Relation 이름 확인 가능)
reltablespace Relation의 테이블스페이스 oid (pg_tablespace의 oid 컬럼을 통해 테이블스페이스 이름 확인 가능)
reldatabase Relation의 데이터베이스 oid (pg_database의 oid 컬럼을 통해 데이터베이스 이름 확인 가능)
relforknumber Relation에서의 Fork 번호
relblocknumber Relation에서의 Page 번호 (위 예시에서는 relfilenode = 16384인 Relation에 9개 블록이 수정되었음을 의미)
is_limit_block f : WAL 레코드에 의해 수정이 있었음을 표시
t and relblocknumber > 0 : Relation Fork가 WAL 레코드 범위 내에서 relblocknumber로 지정된 길이로 Truncate 되었음을 표시
t and relblocknumber = 0 : Relation Fork가 WAL 레코드 범위 내에서 생성되거나 삭제 되었음을 표시

 

Relation 이름 확인 (relfilenode)

SELECT oid , relfilenode , relname
FROM   pg_class
WHERE  relfilenode = 16384 ;
  oid  | relfilenode | relname
-------+-------------+---------
 16384 |       16384 | test01

테이블스페이스 이름 확인 (reltablespace)

SELECT oid , spcname
FROM   pg_tablespace 
WHERE  oid = 1663 ;
 oid  |  spcname
------+------------
 1663 | pg_default

데이터베이스 이름 확인 (reldatabase)

SELECT oid , datname
FROM   pg_database
WHERE  oid = 5 ;
 oid | datname
-----+----------
   5 | postgres

수정된 블록 개수 확인 (relblocknumber)

-- relblocknumber 확인
SELECT pg_relation_filepath( relname::regclass )
FROM   pg_class 
WHERE  relfilenode = 16384 ;

 pg_relation_filepath
----------------------
 base/5/16384
## Relation 파일 목록 확인
[postgres@pgdb ~]$ ls -al $PGDATA/base/5/16384*
-rw------- 1 postgres postgres  73728 Jan  9 16:02 /home/postgres/pg17.data/base/5/16384
-rw------- 1 postgres postgres  24576 Jan  9 16:02 /home/postgres/pg17.data/base/5/16384_fsm
-rw------- 1 postgres postgres   8192 Jan  9 16:02 /home/postgres/pg17.data/base/5/16384_vm
-- Replation 블록 개수 확인
SELECT 73728 / 8192 ;
 ?column?
----------
        9

 

pg_get_wal_summarizer_state()

-- WAL Summary 생성 상태 확인
SELECT * FROM pg_get_wal_summarizer_state() ;
 summarized_tli | summarized_lsn | pending_lsn | summarizer_pid
----------------+----------------+-------------+----------------
              1 | 0/21819E0      | 0/2181AE8   |         546673

 

컬럼 설명
summarized_tli walsummarizer 프로세스가 실행된 적이 없는 경우 : 0
walsummarizer 프로세스가 실행된 경우 : 디스크에 기록된 마지막 WAL Summary 파일의 TLI(Timeline) 표시
summarized_lsn walsummarizer 프로세스가 실행된 적이 없는 경우 : 0/0
walsummarizer 프로세스가 실행된 경우 : 디스크에 기록된 마지막 WAL Summary 파일의 종료 LSN 표시
pending_lsn walsummarizer 프로세스가 실행 중이 아닌 경우 : 0/0
walsummarizer 프로세스가 실행 중인 경우 : 마지막 레코드의 종료 LSN 표시. (summarized_lsn 보다 크거나 같음)
summarizer_pid walsummarizer 프로세스가 실행 중이 아닌 경우 : NULL
walsummarizer 프로세스가 실행 중인 경우 : walsummarizer 프로세스의 PID

 

 

 

 

 

 

 

 

 

기획 및 글 | 플랫폼기술연구팀

댓글