PostgreSQL WAL 팽창 관리: 4단계로 디스크 공간 회복
Source: Dev.to
작년, 한 클라이언트 프로젝트에서 PostgreSQL 데이터베이스 서버의 디스크 사용량이 급격히 급증한 것을 발견했습니다. 해당 시스템은 중요한 프로덕션 ERP를 운영 중이었고, 디스크 사용률이 90%를 넘는 것은 심각한 경보 신호였습니다. 가장 먼저 확인한 곳은 물론 pg_wal 디렉터리였습니다. 예상대로 이 디렉터리는 기가바이트는 물론 테라바이트에 달하는 WAL(Write‑Ahead Log) 파일들로 가득 차 있었습니다.
이 상황은 PostgreSQL을 사용하는 많은 시스템 관리자나 개발자들이 겪는 전형적인 시나리오입니다. WAL 팽창은 눈에 잘 띄지 않게 디스크 공간을 소모하지만, 적절한 조치를 취하지 않으면 큰 문제로 이어질 수 있는 교활한 문제입니다. 이 글에서는 제가 이 문제를 어떻게 감지했는지와 디스크 공간을 회복하기 위해 적용한 4가지 기본 단계별 전략을 설명합니다.
PostgreSQL에서 데이터 무결성과 내구성을 보장하는 핵심 메커니즘 중 하나가 바로 WAL, 즉 Write‑Ahead Log 시스템입니다. 모든 데이터 수정(INSERT, UPDATE, DELETE)은 먼저 WAL 파일에 기록된 뒤 메인 데이터 파일에 적용됩니다. 이는 장애 발생 시 데이터베이스가 마지막 일관된 상태로 복구될 수 있음을 보장합니다. 일반적으로 WAL 파일은 일정 기간이 지나거나 체크포인트가 발생하면 재활용됩니다.
하지만 몇몇 상황에서는 이 재활용 과정이 방해받아 WAL 파일이 디스크에 계속 쌓이게 됩니다. 이를 WAL 팽창이라고 부릅니다. 제가 주로 마주하는 주요 원인은 다음과 같습니다.
- 장기 실행 트랜잭션: 열린 트랜잭션이 있으면 해당 트랜잭션이 시작된 시점으로 되돌아갈 필요가 있기 때문에 WAL 세그먼트를 재활용하지 못합니다. 한 제조업체 ERP에서 야간 보고 프로세스가 8~10시간 동안 열려 있었던 적이 있어 WAL 팽창을 일으켰습니다.
- 복제 슬롯(Replication Slots): 물리 복제를 사용하는 시스템에서는 복제 슬롯이 복제 서버가 따라잡아야 할 WAL 세그먼트를 보관합니다. 복제 서버가 오랫동안 오프라인 상태이거나 슬롯 관리가 제대로 이루어지지 않으면, 이 슬롯이 다량의 WAL 파일을 계속 보관하게 됩니다. 내부 은행 플랫폼에서 테스트 환경의 복제 서버 슬롯이 몇 달 동안 방치돼 기본 서버에 2TB가 넘는 WAL이 축적된 사례가 있었습니다.
- archive_mode 설정:
archive_mode가 활성화돼 있으면 WAL 파일이 지정된 위치로 아카이브됩니다. 이는 재해 복구 시 필수적이지만, 아카이브 과정이 실패하거나 대상 저장소가 가득 차면 WAL 파일이pg_wal디렉터리에 계속 쌓입니다. 클라우드 백업 서비스에 접근 문제가 있었을 때 비슷한 상황을 겪었습니다. - wal_keep_size(또는 wal_keep_segments) 값: 이 파라미터는 PostgreSQL이
pg_wal디렉터리에 유지할 WAL 파일 수를 결정합니다. 값이 과도하게 크거나 기본값보다 많이 필요할 경우, 불필요하게 많은 WAL 파일이 보관될 수 있습니다.
이러한 상황을 조기에 감지하고 개입하는 것은 디스크 사용률이 100%에 도달해 데이터베이스 서비스가 중단되는 것을 방지하는 데 필수적입니다.
WAL 팽창은 보통 디스크 가득 경보와 함께 나타나지만, 사전 예방이 언제나 더 좋습니다. 저는 여러 가지 방법과 지표를 활용해 문제를 미리 탐지합니다.
가장 간단한 첫 번째 확인 방법은 pg_wal 디렉터리의 크기를 살펴보는 것입니다. SSH로 서버에 접속한 뒤 다음 명령을 실행합니다.
sudo du -sh /var/lib/postgresql/16/main/pg_wal
Note: 디렉터리 경로는 PostgreSQL 버전 및 설치 방식에 따라 다를 수 있습니다. 제 시스템에서는
/var/lib/postgresql/16/main이었습니다.
이 명령은 pg_wal 디렉터리 전체 크기를 보여줍니다. 만약 예상보다 훨씬 큰(예: 보통 1~2GB여야 할 시스템에서 50GB) 경우 즉시 더 깊은 조사를 시작합니다.
데이터베이스 내부에서도 현재 활성 WAL LSN(로그 시퀀스 번호) 위치와 아카이브 상태를 확인할 수 있습니다.
SELECT pg_current_wal_lsn(),
pg_wal_lsn_diff(pg_current_wal_lsn(), '0/0') AS wal_bytes_since_start;
이 쿼리는 현재 WAL 위치와 데이터베이스가 시작된 이후 기록된 WAL 양을 반환합니다. 하지만 핵심은 pg_wal 디렉터리의 WAL 파일이 왜 재활용되지 못하는지를 파악하는 것입니다. 이를 위해 복제 슬롯과 장기 실행 트랜잭션을 확인해야 합니다.
💡 Proactive Monitoring
제 개인 사이드 프로젝트 인프라에서는 pg_wal 디렉터리 크기를 정기적으로(크론 잡이나 Prometheus exporter 활용) 모니터링합니다. 크기가 특정 임계값(예: 10GB)을 초과하면 즉시 알림을 받아 디스크가 가득 차기 전에 개입할 수 있습니다.
또한 PostgreSQL 로그(보통 journalctl -u postgresql 로 접근)에서 disk full이나 WAL segment 같은 키워드를 스캔하면 문제 원인을 파악하는 데 도움이 됩니다. 때때로 아카이브 명령 실패 메시지나 특정 복제 슬롯이 진행되지 않는 경고를 볼 수 있습니다. 예를 들어 2026년 4월 28일에 본 로그 메시지는 다음과 같습니다.
LOG: could not archive WAL file "000000010000001E000000D0": No space left on device.
이는 아카이브 대상이 가득 찼음을 명확히 보여줍니다.
복제 슬롯은 PostgreSQL 물리 복제의 핵심 메커니즘입니다. 슬롯은 특정 복제 서버가 따라잡아야 할 WAL LSN을 보관합니다. 슬롯이 활성화돼 있으면 PostgreSQL은 해당 LSN 이후의 WAL 파일을 삭제하거나 재활용하지 않습니다. 이는 복제 서버가 오프라인이 되더라도 나중에 다시 온라인될 때 중단된 지점부터 복구할 수 있게 하기 위함입니다. 그러나 사용되지 않거나 잊혀진 슬롯은 pg_wal 디렉터리를 크게 부풀릴 수 있습니다.
복제 슬롯을 확인하려면 기본 데이터베이스에서 다음 쿼리를 실행합니다.
SELECT slot_name,
plugin,
slot_type,
active,
restart_lsn,
confirmed_flush_lsn
FROM pg_replication_slots;
이 쿼리 결과는 활성(active = true) 슬롯과 비활성(active = false) 슬롯 모두를 보여줍니다. 특히 active 컬럼이 f(false)인 슬롯에 주목해야 합니다. 이러한 슬롯은 복제 서버가 연결되지 않았거나 더 이상 존재하지 않을 가능성을 나타냅니다. restart_lsn 컬럼은 해당 슬롯이 보관하고 있는 가장 오래된 WAL LSN을 보여주며, 얼마나 오래된 WAL 파일이 유지되고 있는지 가늠할 수 있게 해줍니다.
⚠️ Be Careful!
복제 슬롯을 삭제하면 해당 슬롯을 사용하던 복제 서버가 기본 서버와의 동기화를 잃게 됩니다. 이 경우 복제 서버는 pg_basebackup 등을 이용해 처음부터 다시 구축해야 할 수도 있습니다. 따라서 슬롯을 삭제하기 전에 어떤 복제 서버가 해당 슬롯을 사용 중인지, 현재 상태가 어떤지 반드시 확인하십시오.
불필요하고 비활성인 슬롯을 확인했다면 다음 명령으로 삭제합니다.
SELECT pg_drop_replication_slot('slot_name_here');
예를 들어, 테스트 서버에 있던 test_replica_slot이라는 오래된 복제 슬롯이 남아 있고 해당 서버가 더 이상 존재하지 않을 경우, 아래와 같이 실행합니다.
SELECT pg_drop_replication_slot('test_replica_slot');
이 작업을 수행하면 PostgreSQL이 자동으로 사용되지 않는 WAL 세그먼트 재활용을 시작하고, 디스크 공간이 점차 회복됩니다.
저는 이 단계를 가장 먼저 확인하는 단계로 여기며, 보통 가장 빠른 결과를 얻을 수 있습니다. 한 클라이언트 프로젝트에서 기본 서버의 pg_wal 디렉터리가 오래된 개발 환경 복제 슬롯 때문에 300GB까지 늘어났던 적이 있습니다. 슬롯을 삭제한 뒤 약 15분 만에 디스크 사용량이 정상 수준으로 돌아왔습니다.
archive_mode 및 wal_keep_size 설정
WAL 팽창 관리의 두 번째 중요한 단계는 PostgreSQL의 WAL 아카이브 및 보존 정책을 제어하는 파라미터들을 올바르게 설정하는 것입니다. 이 파라미터들은 postgresql.conf 파일에 있으며, 시스템이 얼마나 오래, 혹은 얼마나 많은 WAL 데이터를 유지할지를 결정합니다.
archive_mode
archive_mode 파라미터는 WAL 파일을 아카이브할지 여부를 제어합니다. 값은 off, on, always 중 하나가 될 수 있습니다.
off: WAL 아카이브가 비활성화됩니다.on:archive_command를 사용해 WAL 파일을 아카이브합니다.always:on과 동일하게 동작하면서, 크래시 복구 중에도 아카이브를 수행합니다.
archive_mode가 on 또는 always로 설정되고 archive_command가 … (본문이 여기서 끊겼습니다)