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

DB 인사이드 | PostgreSQL Replication - Slot

by EXEM 2023. 7. 26.

 

 

Replication Slot 탄생

PostgreSQL에서 안정적인 Replication 유지를 위한 WAL 파일의 관리는 항상 어려운 문제였습니다. Standby Server의 연결이 끊어진 상태에서 Main Server의 WAL 파일이 재사용(Overwrite) 되면 Replication 상태를 유지할 수 없으므로 다음과 같은 에러를 마주하게 됩니다.

LOG:  started streaming WAL from primary at 0/3000000 on timeline 1
FATAL:  could not receive data from WAL stream: 
ERROR:  requested WAL segment 000000010000000000000003 has already been removed

그리고 이러한 상황이 발생하기까지 적절한 조치를 취하지 못했다면, Replication을 지속하기 위해 Standby Server를 다시 구성해야 합니다. 물론, 이를 피하기 위해 운영자는 max_wal_size(PostgreSQL 9.5 버전 까지는 max_keep_segments) 파라미터를 조정하는 등, WAL 파일 보관 정책을 세워 관리했을 것입니다.

PostgreSQL 9.4 버전부터 도입된 Replication Slot은 Standby Server와의 연결이 끊어져도 필요한 WAL 파일이 Main Server에 유지되도록 하는 기능을 말합니다. Replication Slot을 통해 WAL 파일(또는 Record)이 Standby Server에 전달되면, WAL 파일을 삭제/재사용합니다.

 

Replication Slot 사용 장점

  • Slot을 사용한 Replication의 경우 Standby Server의 연결이 끊어진 경우에도 다시 연결이 될 때 필요한 WAL 파일을 유지할 수 있습니다.
  • Standby Server가 필요한 WAL 파일에 대한 보관을 자동으로 관리하기 때문에 운영자가 WAL 파일 보관 설정에 대한 고민을 할 필요가 없습니다.

Replication Slot 사용 단점

  • Standby Server가 연결이 끊긴 경우 WAL 파일이 재사용되지 않고 유지되어 Main Server의 Disk Full을 발생시킬 수 있습니다.(pg_wal Directory)
  • Replication Slot은 자동으로 삭제되지 않기 때문에 Replication을 유지하지 않을 경우 위와 같은 이유로 Replication Slot을 운영자가 삭제해야 합니다.

Replication Slot은 Physical Replication Slot과 Logical Replication Slot으로 구분됩니다.

 

 

Physical Replication Slot

Physical Replication Slot은 Streaming Replication에서 사용할 수 있습니다. Main Server에서 발생하는 변경 사항들은 WAL 파일에 기록됩니다. Physical Replication Slot은 WAL 파일에 기록된 내용을 읽어 Standby Server와 동기화를 유지할 수 있습니다.

 

Physical Replication Slot 생성

-- pg_create_physical_replication_slot Syntax
SELECT pg_create_physical_replication_slot( slot_name [, immediately_reserve , temporary ] ) ;

-- example
SELECT pg_create_physical_replication_slot( slot_name , immediately_reserve true , temporary true ) ;
SELECT pg_create_physical_replication_slot( 'pslot' ) ;
 pg_create_physical_replication_slot
-------------------------------------
 (pslot,)

 

Physical Replication Slot 확인

[postgres@main ~] ls -l /var/lib/pgsql/14/data/pg_replslot
drwx------ 2 postgres postgres 19 Feb 24 14:34 pslot
SELECT *
FROM   pg_replication_slots 
WHERE  slot_type = 'physical' ;
-[ RECORD 1 ]-------+---------
slot_name           | pslot
plugin              |
slot_type           | physical
datoid              |
database            |
temporary           | f
active              | t
active_pid          | 36491
xmin                |
catalog_xmin        |
restart_lsn         | 0/A000060
confirmed_flush_lsn |
wal_status          | reserved
safe_wal_size       |
two_phase           |

pg_replication_slots Catalog를 통해서 Replication Slot에 대한 정보를 확인할 수 있습니다. 위 예시에서 pslot은 restart_lsn컬럼을 통해 0/A000060 이전의 WAL 파일은 제거하지 않을 것임을 확인할 수 있습니다.

 

 

Logical Replication Slot

Logical Replication Slot은 Physical Replication Slot과 달리 논리적 디코딩이라는 메커니즘을 통해 디코딩된 메시지를 통해 복제를 합니다. 논리적인 변경 사항을 기반으로 복제를 수행하므로 Physical Replication Slot보다 유연한 데이터 복제(특정 스키마, 테이블 등)가 가능합니다.

 

Logical Replication Slot 생성

-- pg_create_logical_replication_slot Syntax
SELECT pg_create_logical_replication_slot( slot_name , plugin [, temporary , two_phase ] ) ;

-- example
SELECT pg_create_logical_replication_slot( slot_name , pgoutput , temporary true , two_phase true ) ;
SELECT pg_create_logical_replication_slot( 'lslot' , 'pgoutput' ) ;
 pg_create_logical_replication_slot
------------------------------------
 (lslot,0/A000650)

Logical Replication Slot은 Physical Replication Slot과 달리 pgoutput 모듈을 사용하여 복제를 수행합니다. pgoutput 모듈은 PostgreSQL 10 버전부터 기본으로 제공되며, 변경 사항을 출력하는 데 사용됩니다. Logical Replication Slot 생성 시 현재 트랜잭션의 로그 위치가 반환되며(0/A000650), 이 위치부터 논리적으로 디코딩된 WAL Record가 Standby Server로 전송됩니다.

 

Logical Replication Slot 확인

SELECT *
FROM   pg_replication_slots
WHERE  slot_type = 'logical' ;
-[ RECORD 1 ]-------+----------------
slot_name           | lslot
plugin              | pgoutput
slot_type           | logical
datoid              | 14486
database            | postgres
temporary           | f
active              | t
active_pid          |
xmin                |
catalog_xmin        | 791
restart_lsn         | 4/55BC9E68
confirmed_flush_lsn | 4/55BC9EA0
wal_status          | reserved
safe_wal_size       |
two_phase           | f

 

Logical Replication Slot Plugin 종류

종류 설명
pgoutput Logical Replication에서 사용되는 표준 출력 Plugin
Database의 변경 사항을 이전형식(Binary)으로 제공
test_decoding Database의 변경 사항을 사람이 읽을 수 있는 형식으로 제공하여, 주로 테스트 및 개발목적으로 사용
wal2json Database의 변경 사항을 JSON 형식으로 제공(3rd-party Plugin)

 

 

Replication Slot의 위험성

Replication Slot은 서버 장애 상황에서도 Replication이 유지되기 위해 설계되었지만, Slot을 사용하는 응용프로그램의 상태는 알 수 없습니다. 따라서, 생성된 Slot이 사용되지 않는 경우도 있을 수 있습니다. 이러한 경우, 아직 반영되지 않은 WAL 파일을 계속 보관해야 하기 때문에 VACUUM에 의해서 정리되지 않습니다. 이러한 상태가 계속 유지될 경우 극단적으로 XID Wraparound를 방지하기 위해 PostgreSQL가 종료될 수 있으므로, 더 이상 필요하지 않은 Slot은 삭제해야 합니다.

 

Replication Slot 유무에 따른 pg_wal 사이즈 변화

테스트 테이블을 생성하고 WAL 파일들의 합이 1GB가 되도록 데이터를 입력합니다. 본 테스트에서는 약 17만건 데이터 입력 시 하나의 WAL파일(16MB)이 생성되며, 총 64개의 WAL 파일이 생성되도록 데이터를 입력합니다. (16MB * 64 = 1024MB)

-- 테이블생성
CREATE TABLE test01 ( c1 integer , c2 text ) ;

-- WAL 파일이 1GB 데이터 입력
INSERT INTO test01
SELECT generate_series( 1 , 170000 ) , md5( random()::text )
FROM   generate_series( 1 , 64 ) ;

실제로 pg_wal 디렉토리에 64개의 WAL 파일이 생성되었고 그 크기가 1009 MB인 것을 알 수 있습니다.

## pg_wal 디렉토리 확인
[postgres@main ~] ls /var/lib/pgsql/14/data/pg_wal
000000010000000000000023  000000010000000000000030  00000001000000000000003D  00000001000000000000004A  000000010000000000000057
000000010000000000000024  000000010000000000000031  00000001000000000000003E  00000001000000000000004B  000000010000000000000058
000000010000000000000025  000000010000000000000032  00000001000000000000003F  00000001000000000000004C  000000010000000000000059
000000010000000000000026  000000010000000000000033  000000010000000000000040  00000001000000000000004D  00000001000000000000005A
000000010000000000000027  000000010000000000000034  000000010000000000000041  00000001000000000000004E  00000001000000000000005B
000000010000000000000028  000000010000000000000035  000000010000000000000042  00000001000000000000004F  00000001000000000000005C
000000010000000000000029  000000010000000000000036  000000010000000000000043  000000010000000000000050  00000001000000000000005D
00000001000000000000002A  000000010000000000000037  000000010000000000000044  000000010000000000000051  00000001000000000000005E
00000001000000000000002B  000000010000000000000038  000000010000000000000045  000000010000000000000052  00000001000000000000005F
00000001000000000000002C  000000010000000000000039  000000010000000000000046  000000010000000000000053  000000010000000000000060
00000001000000000000002D  00000001000000000000003A  000000010000000000000047  000000010000000000000054  000000010000000000000061
00000001000000000000002E  00000001000000000000003B  000000010000000000000048  000000010000000000000055  000000010000000000000062
00000001000000000000002F  00000001000000000000003C  000000010000000000000049  000000010000000000000056  archive_status

## pg_wal 디렉토리 사이즈 확인
[postgres@main ~] du -sh /var/lib/pgsql/14/data/pg_wal
1009M     /var/lib/pgsql/14/data/pg_wal

Main Server에서 생성된 WAL 파일의 내용을 Standby Server에서 전부 수신했다면, Main Server의 pg_wal 디렉토리는 max_wal_size에 설정된 1GB 안에서 WAL 파일이 재사용됩니다.

📢 max_wal_size Parameter의 기본값은 1GB입니다.
## pg_wal 디렉토리 확인
[postgres@main ~] ls /var/lib/pgsql/14/data/pg_wal
000000010000000000000086  000000010000000000000093  0000000100000000000000A0  0000000100000000000000AD  0000000100000000000000BA
000000010000000000000087  000000010000000000000094  0000000100000000000000A1  0000000100000000000000AE  0000000100000000000000BB
000000010000000000000088  000000010000000000000095  0000000100000000000000A2  0000000100000000000000AF  0000000100000000000000BC
000000010000000000000089  000000010000000000000096  0000000100000000000000A3  0000000100000000000000B0  0000000100000000000000BD
00000001000000000000008A  000000010000000000000097  0000000100000000000000A4  0000000100000000000000B1  0000000100000000000000BE
00000001000000000000008B  000000010000000000000098  0000000100000000000000A5  0000000100000000000000B2  0000000100000000000000BF
00000001000000000000008C  000000010000000000000099  0000000100000000000000A6  0000000100000000000000B3  0000000100000000000000C0
00000001000000000000008D  00000001000000000000009A  0000000100000000000000A7  0000000100000000000000B4  0000000100000000000000C1
00000001000000000000008E  00000001000000000000009B  0000000100000000000000A8  0000000100000000000000B5  0000000100000000000000C2
00000001000000000000008F  00000001000000000000009C  0000000100000000000000A9  0000000100000000000000B6  0000000100000000000000C3
000000010000000000000090  00000001000000000000009D  0000000100000000000000AA  0000000100000000000000B7  0000000100000000000000C4
000000010000000000000091  00000001000000000000009E  0000000100000000000000AB  0000000100000000000000B8  0000000100000000000000C5
000000010000000000000092  00000001000000000000009F  0000000100000000000000AC  0000000100000000000000B9  archive_status

Standby Server 종료

Standby Server가 종료된 상태에서 Main Server에 대량의 트랜잭션이 발생했다고 가정해 보겠습니다. 이때 Replication Slot의 존재 유무에 따라 pg_wal 디렉토리의 크기는 전혀 다른 모습을 보입니다.

Replication Slot을 사용하지 않을 경우 Main Server는 Standby Server의 WAL 파일 수신 여부를 고려하지 않으므로, max_wal_size로 설정된 크기를 유지하기 위해 기존의 WAL 파일을 재사용(Overwrite)합니다.

## Replication Slot을 사용하지 않는 Replication 구성
## pg_wal 디렉토리 확인
[postgres@main ~] ls /var/lib/pgsql/14/data/pg_wal
0000000100000000000000C8  0000000100000000000000D5  0000000100000000000000E2  0000000100000000000000EF  0000000100000000000000FC
0000000100000000000000C9  0000000100000000000000D6  0000000100000000000000E3  0000000100000000000000F0  0000000100000000000000FD
0000000100000000000000CA  0000000100000000000000D7  0000000100000000000000E4  0000000100000000000000F1  0000000100000000000000FE
0000000100000000000000CB  0000000100000000000000D8  0000000100000000000000E5  0000000100000000000000F2  0000000100000000000000FF
0000000100000000000000CC  0000000100000000000000D9  0000000100000000000000E6  0000000100000000000000F3  000000010000000100000000
0000000100000000000000CD  0000000100000000000000DA  0000000100000000000000E7  0000000100000000000000F4  000000010000000100000001
0000000100000000000000CE  0000000100000000000000DB  0000000100000000000000E8  0000000100000000000000F5  000000010000000100000002
0000000100000000000000CF  0000000100000000000000DC  0000000100000000000000E9  0000000100000000000000F6  000000010000000100000003
0000000100000000000000D0  0000000100000000000000DD  0000000100000000000000EA  0000000100000000000000F7  000000010000000100000004
0000000100000000000000D1  0000000100000000000000DE  0000000100000000000000EB  0000000100000000000000F8  000000010000000100000005
0000000100000000000000D2  0000000100000000000000DF  0000000100000000000000EC  0000000100000000000000F9  000000010000000100000006
0000000100000000000000D3  0000000100000000000000E0  0000000100000000000000ED  0000000100000000000000FA  000000010000000100000007
0000000100000000000000D4  0000000100000000000000E1  0000000100000000000000EE  0000000100000000000000FB  archive_status

## pg_wal 디렉토리 사이즈 확인
[postgres@main ~] du -sh /var/lib/pgsql/14/data/pg_wal
1009M     /var/lib/pgsql/14/data/pg_wal

반면, Replication Slot을 사용 중이라면 Standby Server의 WAL 파일 수신 여부를 체크한 후, 수신이 불가한 상태라면 max_wal_size크기 이상으로 pg_wal 공간이 증가할 수 있습니다. 이는 Standby Server가 WAL 파일을 수신할 때까지 파일을 보관하기 때문인데, 별다른 조치가 없다면 Main Server의 Disk Full 혹은 XID Wraparound 방지를 위한 PostgreSQL 종료등의 문제가 발생할 수 있습니다.

## Replication Slot을 사용하는 Replication 구성
## pg_wal 디렉토리 확인
[postgres@main ~] ls /var/lib/pgsql/14/data/pg_wal
000000010000000000000063  000000010000000000000083  0000000100000000000000A3  0000000100000000000000C3  0000000100000000000000E3
000000010000000000000064  000000010000000000000084  0000000100000000000000A4  0000000100000000000000C4  0000000100000000000000E4
000000010000000000000065  000000010000000000000085  0000000100000000000000A5  0000000100000000000000C5  0000000100000000000000E5
000000010000000000000066  000000010000000000000086  0000000100000000000000A6  0000000100000000000000C6  0000000100000000000000E6
000000010000000000000067  000000010000000000000087  0000000100000000000000A7  0000000100000000000000C7  0000000100000000000000E7
000000010000000000000068  000000010000000000000088  0000000100000000000000A8  0000000100000000000000C8  0000000100000000000000E8
000000010000000000000069  000000010000000000000089  0000000100000000000000A9  0000000100000000000000C9  0000000100000000000000E9
00000001000000000000006A  00000001000000000000008A  0000000100000000000000AA  0000000100000000000000CA  0000000100000000000000EA
00000001000000000000006B  00000001000000000000008B  0000000100000000000000AB  0000000100000000000000CB  0000000100000000000000EB
00000001000000000000006C  00000001000000000000008C  0000000100000000000000AC  0000000100000000000000CC  0000000100000000000000EC
00000001000000000000006D  00000001000000000000008D  0000000100000000000000AD  0000000100000000000000CD  0000000100000000000000ED
00000001000000000000006E  00000001000000000000008E  0000000100000000000000AE  0000000100000000000000CE  0000000100000000000000EE
00000001000000000000006F  00000001000000000000008F  0000000100000000000000AF  0000000100000000000000CF  0000000100000000000000EF
000000010000000000000070  000000010000000000000090  0000000100000000000000B0  0000000100000000000000D0  0000000100000000000000F0
000000010000000000000071  000000010000000000000091  0000000100000000000000B1  0000000100000000000000D1  0000000100000000000000F1
000000010000000000000072  000000010000000000000092  0000000100000000000000B2  0000000100000000000000D2  0000000100000000000000F2
000000010000000000000073  000000010000000000000093  0000000100000000000000B3  0000000100000000000000D3  0000000100000000000000F3
000000010000000000000074  000000010000000000000094  0000000100000000000000B4  0000000100000000000000D4  0000000100000000000000F4
000000010000000000000075  000000010000000000000095  0000000100000000000000B5  0000000100000000000000D5  0000000100000000000000F5
000000010000000000000076  000000010000000000000096  0000000100000000000000B6  0000000100000000000000D6  0000000100000000000000F6
000000010000000000000077  000000010000000000000097  0000000100000000000000B7  0000000100000000000000D7  0000000100000000000000F7
000000010000000000000078  000000010000000000000098  0000000100000000000000B8  0000000100000000000000D8  0000000100000000000000F8
000000010000000000000079  000000010000000000000099  0000000100000000000000B9  0000000100000000000000D9  0000000100000000000000F9
00000001000000000000007A  00000001000000000000009A  0000000100000000000000BA  0000000100000000000000DA  0000000100000000000000FA
00000001000000000000007B  00000001000000000000009B  0000000100000000000000BB  0000000100000000000000DB  0000000100000000000000FB
00000001000000000000007C  00000001000000000000009C  0000000100000000000000BC  0000000100000000000000DC  0000000100000000000000FC
00000001000000000000007D  00000001000000000000009D  0000000100000000000000BD  0000000100000000000000DD  0000000100000000000000FD
00000001000000000000007E  00000001000000000000009E  0000000100000000000000BE  0000000100000000000000DE  0000000100000000000000FE
00000001000000000000007F  00000001000000000000009F  0000000100000000000000BF  0000000100000000000000DF  archive_status
000000010000000000000080  0000000100000000000000A0  0000000100000000000000C0  0000000100000000000000E0
000000010000000000000081  0000000100000000000000A1  0000000100000000000000C1  0000000100000000000000E1
000000010000000000000082  0000000100000000000000A2  0000000100000000000000C2  0000000100000000000000E2

## pg_wal 디렉토리 사이즈 확인
[postgres@main ~] du -sh /var/lib/pgsql/14/data/pg_wal
2.5G     /var/lib/pgsql/14/data/pg_wal

 

 

Replication Slot Parameter

PostgreSQL Replication Slot을 사용하기 위해서는 max_replication_slotswal_level Parameter를 확인해야 합니다.

  • max_replication_slots Parameter는 PostgreSQL이 사용할 수 있는 Replication Slot의 개수를 지정할 수 있으며, 최소 Standby Server 개수만큼 지정해야 합니다.
  • Main Server의 내용을 복제하는데 pg_basebackup 등을 사용할 경우 Standby Server 수 + 1로 설정해야 합니다.
  • wal_level Parameter는 WAL 파일에 기록되는 양을 제어하는 파라미터로 기본값 replica이며 Physical Replication Slot을 사용합니다. logical로 사용하는 경우 Logical Replication Slot을 사용합니다.
📢 max_replication_slots은 PostgreSQL 10 버전부터 기본값이 10입니다. PostgreSQL 9.6 버전까지는 기본값이 0입니다.

max_replication_slots < 1로 설정되어 있을 경우 Replication Slot은 생성되지 않습니다.

SHOW max_replication_slots;
 max_replication_slots
-----------------------
 0

SELECT pg_create_physical_replication_slot( 'physical_slot' ) ;
ERROR:  replication slots can only be used if max_replication_slots > 0

 

 

 

 

기획 및 글 | DB기술기획팀

이미지 제작 | 브랜드디자인팀

 

 

댓글