LLM WikiAccess-protected knowledge portal
← 스터디 홈
1편 · 약 22분

PostgreSQL 운영 아키텍처: process model, shared buffers, WAL

PostgreSQL이 멀티프로세스인 이유

PostgreSQL은 스레드 모델이 아닌 멀티프로세스 모델을 선택했다. 클라이언트 연결 하나마다 별도의 OS 프로세스(backend)가 생성된다. 이 설계는 한 프로세스가 크래시하더라도 다른 연결에 영향을 주지 않는 강한 격리성을 제공한다. 대신 연결 오버헤드가 크기 때문에, 프로덕션 환경에서는 반드시 PgBouncer 같은 커넥션 풀러를 앞에 두어야 한다.


프로세스 모델 전체 그림

Postmaster supervisor · shared memory 할당 · fork postgres (backend) client 1 — Parse→Plan→Exec postgres (backend) client 2 ... N번째 연결 max_connections 제한 공유 메모리 (Shared Memory) Shared Buffers 데이터 페이지 캐시 WAL Buffers 변경 로그 버퍼 CLOG Buffer 트랜잭션 커밋 상태 Lock Table heavyweight lock Proc Array 백그라운드 프로세스 checkpointer dirty→disk 정기 flush bgwriter trickle flush walwriter WAL buffer→disk autovacuum launcher + workers walsender 스트리밍 복제 archiver WAL 세그먼트 보관
PostgreSQL 프로세스 모델

Postmaster: 모든 것의 시작점

Postmaster는 PostgreSQL의 최상위 프로세스다. 주요 역할은 세 가지다.

  1. 공유 메모리 할당: 서버 시작 시 shared_buffers, WAL 버퍼, CLOG 버퍼, Lock Table 등을 포함하는 거대한 공유 메모리 세그먼트를 mmap으로 할당한다.
  2. 연결 수락 및 fork: 새 클라이언트 연결이 들어오면 fork()로 backend 프로세스를 생성한다. fork 비용이 크므로 프로덕션에서는 PgBouncer가 필수다.
  3. 백그라운드 프로세스 감시: checkpointer, bgwriter, walwriter, autovacuum launcher 등이 죽으면 재시작한다.

Postmaster 자체가 죽으면 전체 클러스터가 shutdown된다.

# 실행 중인 PostgreSQL 프로세스 확인
ps aux | grep postgres
# 출력 예:
# postgres  1234  0.0  ...  /usr/lib/postgresql/16/bin/postgres -D /var/lib/postgresql/16/main
# postgres  1235  0.0  ...  postgres: checkpointer
# postgres  1236  0.0  ...  postgres: background writer
# postgres  1237  0.0  ...  postgres: walwriter
# postgres  1238  0.0  ...  postgres: autovacuum launcher
# postgres  1239  0.0  ...  postgres: stats collector      ← PG14 이하에만 존재
# postgres  1240  0.0  ...  postgres: logical replication launcher
# postgres  2001  0.0  ...  postgres: app user mydb 10.0.0.5(54321) idle

백그라운드 프로세스 상세

checkpointer

모든 더티 버퍼(shared_buffers에서 수정됐으나 아직 디스크에 반영 안 된 페이지)를 디스크로 flush하고 WAL에 checkpoint record를 기록한다. 체크포인트 이후에는 이전 WAL 파일이 더 이상 복구에 필요 없으므로 재활용하거나 삭제할 수 있다.

체크포인트가 트리거되는 조건:

  • checkpoint_timeout(기본 5분) 경과
  • WAL 증가량이 max_wal_size(기본 1GB) 도달
  • 수동 CHECKPOINT 명령
-- 체크포인트 현황 조회 (PG16+에서는 pg_stat_checkpointer로 분리됨)
SELECT
  checkpoints_timed,       -- 주기(timeout) 트리거 체크포인트
  checkpoints_req,         -- 용량(max_wal_size) 트리거 체크포인트
  checkpoint_write_time,   -- ms
  checkpoint_sync_time,    -- ms (fsync 시간)
  buffers_checkpoint,      -- 체크포인트에서 쓴 버퍼 수
  buffers_clean,           -- bgwriter가 쓴 버퍼 수
  buffers_backend,         -- backend가 직접 쓴 버퍼 수 ← 이 값이 크면 bgwriter 부족
  maxwritten_clean         -- bgwriter가 한계에 걸린 횟수
FROM pg_stat_bgwriter;

checkpoints_reqcheckpoints_timed보다 많으면 WAL이 너무 빠르게 생성되고 있다는 신호다. max_wal_size를 늘려 체크포인트 빈도를 줄인다.

buffers_backend가 크면 bgwriter가 충분히 clean buffer를 공급하지 못하고 있다. bgwriter_lru_maxpagesbgwriter_delay를 조정한다.


bgwriter (Background Writer)

bgwriter는 체크포인트 사이에 더티 페이지를 미리 디스크로 내려보낸다. 체크포인트 때 한꺼번에 대량의 I/O가 몰리는 것을 방지하고, backend가 페이지 eviction 시 직접 I/O를 하지 않아도 되게 한다.

핵심 파라미터:

파라미터기본값역할
bgwriter_delay200ms라운드 간격
bgwriter_lru_maxpages100라운드당 최대 flush 페이지
bgwriter_lru_multiplier2.0예상 수요의 몇 배를 clean해 둘지

walwriter

WAL 버퍼(shared memory)를 실제 WAL 파일(pg_wal/)로 주기적으로 fsync한다. backend가 COMMIT 시점에 직접 flush하지 않아도 될 경우 walwriter가 대신 처리한다.

wal_writer_delay(기본 200ms)마다 깨어나 WAL 버퍼를 flush한다. 동기 복제 환경에서는 COMMIT이 walwriter를 즉시 깨운다.


autovacuum launcher / worker

PostgreSQL은 MVCC 구조상 삭제/업데이트된 행이 즉시 제거되지 않고 "dead tuple"로 남는다. autovacuum이 이를 정리하지 않으면 테이블이 무한히 부풀어오르고(bloat), xid wraparound라는 치명적 장애로 이어진다.

  • launcher: autovacuum_naptime(기본 1분)마다 깨어나 pg_stat_all_tables를 확인해 dead tuple 임계값 초과 테이블을 찾는다.
  • worker: 실제 vacuum/analyze 수행. 최대 autovacuum_max_workers(기본 3)개 동시 실행.
-- autovacuum 대상이 될 가능성이 높은 테이블 조회
SELECT
  schemaname, relname,
  n_dead_tup,
  n_live_tup,
  ROUND(n_dead_tup::numeric / NULLIF(n_live_tup + n_dead_tup, 0) * 100, 1) AS dead_ratio_pct,
  last_autovacuum,
  last_autoanalyze
FROM pg_stat_user_tables
WHERE n_dead_tup > 1000
ORDER BY n_dead_tup DESC
LIMIT 20;

walsender / walreceiver

스트리밍 복제를 담당한다. Primary에서 walsender가 WAL 스트림을 replica의 walreceiver로 전송한다. 논리 복제(logical replication) 시에도 walsender가 사용된다.


공유 메모리 구성요소

Shared Buffers

PostgreSQL의 데이터 페이지 캐시다. 모든 backend와 백그라운드 프로세스가 이 공간을 공유한다.

  • 기본값: 128MB (너무 작음)
  • 권장값: 서버 RAM의 25~35%
  • 상한: 40% 이상은 OS 페이지 캐시 공간을 빼앗아 역효과가 날 수 있음
-- 현재 shared_buffers 확인
SHOW shared_buffers;

-- pg_buffercache로 버퍼 사용 현황 조회 (확장 필요)
CREATE EXTENSION IF NOT EXISTS pg_buffercache;

SELECT
  c.relname,
  COUNT(*) AS buffers,
  ROUND(COUNT(*) * 8192 / 1024.0 / 1024.0, 1) AS mb
FROM pg_buffercache b
JOIN pg_class c ON b.relfilenode = pg_relation_filenode(c.oid)
WHERE b.isdirty = false
GROUP BY c.relname
ORDER BY buffers DESC
LIMIT 20;

WAL Buffers

WAL 레코드를 WAL 파일에 쓰기 전에 임시 보관하는 버퍼다.

  • 기본값: -1 (shared_buffers의 1/32, 최대 16MB)
  • 권장값: 대부분 -1 그대로 사용하거나 명시적으로 16MB 설정
SHOW wal_buffers;

CLOG (Commit Log) Buffer

각 트랜잭션의 커밋/롤백/진행 중 상태를 비트맵으로 저장한다. MVCC visibility check 시 사용된다. 별도 파라미터 없이 자동 관리된다.

Lock Table

모든 heavyweight lock 정보를 저장한다. max_locks_per_transaction(기본 64) × max_connections로 크기가 결정된다. 매우 많은 객체를 동시에 잠그는 배치 작업은 이 값을 높여야 한다.


WAL 쓰기 경로

DML 실행
UPDATE/INSERT
WAL record 생성
WAL Buffer에 기록
Shared Buffer 수정
페이지 dirty 표시
COMMIT
WAL flush
walwriter / backend가
pg_wal/ 에 fsync
클라이언트 응답
내구성 보장 완료
↓ (비동기)
bgwriter
trickle flush
Dirty page → disk
데이터 파일 갱신
↓ (주기적)
Checkpoint
checkpointer
모든 dirty page flush
checkpoint record → WAL
이전 WAL 재활용
복구 기준점 이동
WAL 쓰기 경로와 Checkpoint

핵심 내구성 원리: COMMIT 시 WAL이 디스크에 fsync되면 트랜잭션은 내구적이다. 데이터 파일은 나중에 써도 된다. 크래시 후 복구 시 WAL을 재생(replay)해 데이터 파일을 최신 상태로 맞춘다.

synchronous_commit = off로 설정하면 COMMIT 후 WAL flush를 비동기로 처리해 지연을 줄일 수 있다. 하지만 크래시 시 최근 커밋 일부가 유실될 수 있다. 데이터 손실이 허용 안 되는 테이블에는 절대 사용하면 안 된다.


핵심 파라미터 튜닝 가이드

-- postgresql.conf 주요 파라미터 현재 값 조회
SELECT name, setting, unit, short_desc
FROM pg_settings
WHERE name IN (
  'shared_buffers', 'wal_buffers',
  'checkpoint_timeout', 'max_wal_size', 'min_wal_size',
  'checkpoint_completion_target',
  'bgwriter_delay', 'bgwriter_lru_maxpages',
  'max_connections', 'work_mem', 'maintenance_work_mem'
)
ORDER BY name;
파라미터기본값권장값설명
shared_buffers128MBRAM × 25~35%데이터 페이지 캐시
wal_buffers-1 (auto)16MB 또는 -1WAL 메모리 버퍼
checkpoint_timeout5min10~15min주기 체크포인트 간격
max_wal_size1GB4~8GB (쓰기 많은 서버)체크포인트 트리거 WAL 크기
min_wal_size80MB512MB~1GBWAL 파일 재활용 최소 크기
checkpoint_completion_target0.90.9체크포인트 I/O 분산 비율
bgwriter_delay200ms50~200msbgwriter 라운드 간격
bgwriter_lru_maxpages100200~500 (쓰기 많은 서버)라운드당 최대 flush 페이지

checkpoint_completion_target = 0.9는 체크포인트 I/O를 다음 체크포인트까지 남은 시간의 90% 동안 분산하겠다는 의미다. I/O 스파이크를 방지하는 중요한 파라미터다.


통계 뷰와 모니터링

-- checkpointer/bgwriter 활동 현황
SELECT
  checkpoints_timed,
  checkpoints_req,
  ROUND(checkpoint_write_time / 1000.0, 1) AS write_sec,
  ROUND(checkpoint_sync_time / 1000.0, 1) AS sync_sec,
  buffers_checkpoint,
  buffers_clean,
  buffers_backend,       -- 클수록 bgwriter 부족 신호
  maxwritten_clean,      -- bgwriter 한계 도달 횟수
  buffers_alloc          -- 새로 할당된 버퍼 수
FROM pg_stat_bgwriter;

-- WAL 활동 현황 (PG14+)
SELECT
  wal_records,
  wal_fpi,               -- full page image 수
  wal_bytes,
  wal_buffers_full,      -- WAL 버퍼 꽉 찬 횟수 (0이어야 좋음)
  wal_write,
  wal_sync,
  wal_write_time,
  wal_sync_time
FROM pg_stat_wal;

-- 현재 백그라운드 프로세스 목록
SELECT pid, backend_type, state, query
FROM pg_stat_activity
WHERE backend_type != 'client backend'
ORDER BY backend_type;

wal_buffers_full이 증가하면 WAL 버퍼가 너무 작은 것이다. wal_buffers를 16MB 이상으로 늘린다.


PG14 → PG15 통계 수집 방식 변화

PG14까지는 별도의 stats collector 프로세스가 pg_stat_activity 등의 통계를 수집했다. PG15부터는 통계가 공유 메모리에 직접 기록되어 stats collector 프로세스가 사라졌다. 덕분에 프로세스 간 IPC 지연이 없어지고 통계의 실시간성이 향상됐다.


References

  • PostgreSQL Documentation, "Chapter 19: Server Configuration": https://www.postgresql.org/docs/current/runtime-config.html
  • PostgreSQL Documentation, "pg_stat_bgwriter": https://www.postgresql.org/docs/current/monitoring-stats.html#MONITORING-PG-STAT-BGWRITER-VIEW
  • PostgreSQL Documentation, "pg_stat_wal": https://www.postgresql.org/docs/current/monitoring-stats.html#MONITORING-PG-STAT-WAL-VIEW
  • Highgo Software, "An Overview of PostgreSQL Backend Architecture": https://www.highgo.ca/2020/06/18/an-overview-of-postgresql-backend-architecture/
  • EDB Blog, "Tuning shared_buffers and wal_buffers": https://www.enterprisedb.com/blog/tuning-sharedbuffers-and-walbuffers
  • AWS Blog, "Determining the optimal value for shared_buffers": https://aws.amazon.com/blogs/database/determining-the-optimal-value-for-shared_buffers-using-the-pg_buffercache-extension-in-postgresql/
  • postgresql.fastware.com, "Let's get back to basics - PostgreSQL memory components": https://www.postgresql.fastware.com/blog/lets-get-back-to-basics-postgresql-memory-components
  • postgresql.fastware.com, "Understanding the importance of shared_buffers, work_mem, and wal_buffers": https://www.postgresql.fastware.com/pzone/2024-06-understanding-shared-buffers-work-mem-and-wal-buffers-in-postgresql
  • Mydbops, "PostgreSQL Performance Tuning Best Practices 2025": https://www.mydbops.com/blog/postgresql-parameter-tuning-best-practices