본문으로 건너뛰기

4.2 WAL과 체크포인트

WAL(Write-Ahead Log)은 PostgreSQL의 내구성·복구·복제 모두의 기반입니다. 모든 데이터 변경은 데이터 페이지를 디스크에 쓰기 전에 WAL에 먼저 기록됩니다. crash 시 마지막 체크포인트 이후의 WAL을 재생해 일관된 상태를 복원합니다.

WAL 작성 → flush → 체크포인트 흐름​

WAL의 단위​

단위크기
WAL record가변. 변경된 행 1개, 페이지 1개 등 단위
WAL page8KB (--with-wal-blocksize)
WAL segment16MB 기본 (--wal-segsize로 변경)
LSN (Log Sequence Number)64-bit. WAL 안의 한 바이트 오프셋

LSN은 텍스트로 0/3000028 같은 형식. 0/3000028 < 0/3000050처럼 비교 가능합니다.

SELECT pg_current_wal_lsn();
-- pg_current_wal_lsn
-- --------------------
-- 0/3A1F8E0

트랜잭션 commit 시 동작​

핵심: COMMIT 응답은 WAL이 디스크에 안전히 들어간 시점에 보냅니다. 데이터 페이지는 아직 메모리에만 있어도 OK — crash가 나면 WAL 재생으로 복원되니까.

full_page_writes​

체크포인트 직후 첫 변경되는 페이지는 변경 부분이 아닌 페이지 전체를 WAL에 기록합니다.

이유는 torn page 방어입니다. OS나 디스크가 8KB 중 일부만 쓰고 죽으면 페이지가 망가지는데, 전체 페이지 이미지로 복구합니다.

SHOW full_page_writes; -- on (기본, 운영 권장)

trade-off: WAL 양이 늘어남(특히 체크포인트 직후). pgBackRest/Barman 같은 백업 도구도 full page를 기대합니다.

경고

full_page_writes = off는 운영 금지. 미세한 성능 이득을 위해 일관성을 포기하는 설정입니다. 일부 가이드에서 "특정 파일시스템에서만 OK"라고 하지만 운영 환경에서는 켜고 시작합니다.

체크포인트​

체크포인트는 "이 시점 이전의 변경은 모두 데이터 파일에 안전히 들어갔다"는 마커를 만들고, 그 시점 이전의 WAL을 안전하게 잘라낼 수 있게 합니다.

체크포인트 트리거​

트리거설정
시간 기반checkpoint_timeout 기본 5분
WAL 크기 기반max_wal_size 기본 1GB (PG 9.5+)
명시 명령CHECKPOINT; (슈퍼유저, 또는 pg_basebackup 등)
종료 시pg_ctl stop 또는 클러스터 정지

max_wal_size에 도달하기 전에 체크포인트가 일어나도록 checkpoint_timeout이 잡혀 있어야 합니다. 반대로 timeout 전에 max_wal_size에 먼저 도달하면 WAL 기반 checkpoint가 너무 자주 일어난다는 신호이므로 max_wal_size를 키웁니다.

checkpoint_completion_target​

체크포인트는 dirty page를 한꺼번에 fsync하지 않습니다. checkpoint_completion_target(기본 0.9, PostgreSQL 14+) 만큼 시간에 걸쳐 분산해 씁니다.

checkpoint_timeout = 5min
checkpoint_completion_target = 0.9
→ 체크포인트가 4.5분에 걸쳐 천천히 flush

이래서 체크포인트의 I/O가 한 점에 몰리지 않고 평탄해집니다.

체크포인트 진단​

SELECT * FROM pg_stat_checkpointer;

핵심 컬럼:

컬럼의미
num_timed시간 기반 체크포인트 횟수
num_requestedWAL 기반(요청) 체크포인트 횟수 — 이게 timed보다 크면 max_wal_size 키워야
write_time / sync_time누적 쓰기·sync 시간(ms)

PostgreSQL 17 기준입니다. 16 이하는 pg_stat_bgwriter의 checkpoints_timed, checkpoints_req, checkpoint_write_time, checkpoint_sync_time으로 봅니다.

권장 출발 설정:

checkpoint_timeout = 15min
max_wal_size = 8GB
checkpoint_completion_target = 0.9

쓰기 부하 큰 시스템은 max_wal_size를 더 키워(16~64GB) 체크포인트 빈도를 낮춥니다.

WAL 세그먼트와 pg_wal​

새 트랜잭션이 들어와 WAL이 늘어나면 16MB 세그먼트 파일이 생성됩니다.

파일 이름: <timeline_id>_<segment_high>_<segment_low> 24자리 16진수. 예: 00000001000000000000003A.

ls -lh $PGDATA/pg_wal/
# 00000001000000000000003A
# 00000001000000000000003B ...

보관 정책​

  • 체크포인트 시 더 이상 필요 없는 세그먼트는 재사용 또는 삭제
  • archive_mode = on이면 archive_command로 외부 복사 성공 후에만 삭제
  • replication slot이 잡고 있으면 삭제 불가 — 잊혀진 슬롯이 pg_wal/을 가득 채우는 사고가 흔하다

pg_wal이 차오를 때​

원인확인
archive_command 실패pg_stat_archiver의 last_failed_time
replication slot이 holdpg_replication_slots의 wal_status='extended'/'unreserved'/'lost'
매우 큰 트랜잭션이 commit 안 됨pg_stat_activity의 오래된 active xid
max_wal_size가 너무 작아 한 번에 잘림임시적 — 보통 자기 정상화
경고

pg_wal/을 수동으로 절대 지우지 말 것. 한 파일만 삭제해도 클러스터가 부팅 불가 상태가 됩니다. pg_wal은 checkpoint가 스스로 재활용하므로 원인(archive 실패, replication slot)을 해소합니다. archive 저장소 정리는 pg_archivecleanup이나 pgBackRest expire로 합니다.

동기 commit과 비동기 commit​

4.4에서 자세히 다루지만, 한 줄로 요약하면 이렇습니다.

  • synchronous_commit = on (기본): commit 응답 전 WAL fsync 완료 대기 — 안전, 약간 느림
  • synchronous_commit = off: commit 응답 즉시, WAL fsync는 백그라운드 — 빠르지만 직전 트랜잭션 손실 가능

정리​

  • 모든 변경은 데이터 파일 전에 WAL에 기록
  • COMMIT의 진짜 비용은 WAL fsync
  • full_page_writes = on이 표준 — torn page 방어
  • 체크포인트는 시간(checkpoint_timeout)·크기(max_wal_size)·명령으로 트리거
  • checkpoint_completion_target으로 체크포인트 I/O를 평탄화
  • pg_stat_bgwriter / pg_stat_checkpointer로 점검
  • pg_wal/은 archive·replication slot과 깊이 얽혀 있습니다. 수동 삭제 절대 금지