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

PostgreSQL 아키텍처 개요

PostgreSQL은 왜 다르게 느껴지는가

MySQL을 주로 다루던 DBA가 PostgreSQL을 처음 보면 낯선 점이 있다. 테이블 이름은 소문자로만 다뤄지고, UPDATE 한 번에 테이블이 부풀어 오르고, 튜닝 파라미터도 다르다. 이 낯섦의 대부분은 PostgreSQL의 프로세스 구조와 메모리 모델에서 비롯된다. 아키텍처를 먼저 잡아두면 이후 내용이 훨씬 쉽게 연결된다.

클라이언트-서버 모델과 프로세스 방식

PostgreSQL은 멀티프로세스 구조다. MySQL이 커넥션마다 스레드를 생성하는 것과 달리, PostgreSQL은 각 커넥션에 독립적인 OS 프로세스(backend)를 포크한다. 프로세스 간 통신은 공유 메모리(Shared Memory)로 이뤄진다.

이 구조는 장단점이 뚜렷하다.

항목PostgreSQL (프로세스)MySQL (스레드)
격리성프로세스 크래시 시 다른 커넥션에 영향 없음스레드 충돌이 전체 프로세스에 영향
메모리커넥션당 work_mem 분리전역 버퍼 공유, 커넥션당 오버헤드 작음
대규모 커넥션수천 커넥션 시 포크 비용·메모리 문제 → pgBouncer 필요상대적으로 넓은 커넥션 수 지원

PostgreSQL에서 수천 개의 동시 커넥션을 열면 각각 ~5–10 MB 메모리를 차지하므로, 프로덕션에서는 PgBouncer 같은 커넥션 풀러를 앞에 두는 것이 기본이다.

핵심 프로세스 구조

postmaster (PID 1) ↓ 포크
backend
커넥션 1
backend
커넥션 2
backend
커넥션 N
WAL writer checkpointer bgwriter autovacuum
launcher
stats collector archiver
포스트마스터가 모든 프로세스를 생성·관리한다
  • postmaster: 인스턴스 시작 시 단 하나 실행. 공유 메모리 할당, 설정 파일 로드, 나머지 모든 프로세스를 포크한다. 클라이언트 연결 요청이 들어오면 새 backend를 포크해 위임한다.
  • backend: 클라이언트 커넥션 1개에 대응하는 프로세스. SQL 파싱 → 플래너 → 실행기 → 결과 반환을 담당한다. 커넥션이 끊기면 프로세스도 종료된다.
  • WAL writer: 공유 메모리의 WAL 버퍼를 pg_wal/ 파일로 주기적으로 플러시한다. backend가 직접 WAL을 디스크에 쓰는 부담을 줄인다.
  • checkpointer: checkpoint_timeout(기본 5분) 또는 max_wal_size 도달 시 dirty page 전체를 디스크에 플러시하고 WAL에 체크포인트 레코드를 기록한다. 크래시 복구 시 이 지점부터 재생한다.
  • bgwriter (Background Writer): 체크포인트 사이에 shared_buffers의 dirty page를 조금씩 미리 디스크에 내려 체크포인트 I/O 폭발을 완화한다.
  • autovacuum launcher: MVCC 구조상 쌓이는 dead tuple을 청소하는 autovacuum worker를 필요한 테이블에 주기적으로 띄운다.

메모리 구조

PostgreSQL의 메모리는 공유 메모리로컬 메모리 두 계층으로 나뉜다.

공유 메모리 (전체 프로세스 공유) shared_buffers
데이터 페이지 캐시
WAL buffers
커밋 전 WAL 임시 저장
Commit Log (pg_xact)
트랜잭션 상태 비트맵
로컬 메모리 (backend 별) work_mem
정렬·해시조인 버퍼
maintenance_work_mem
VACUUM·인덱스 빌드
temp buffers
임시 테이블
PostgreSQL 메모리 구조 — 공유 vs 로컬
파라미터역할주의사항
shared_buffers공유 데이터 페이지 캐시. RAM의 25 % 가 기본 출발점너무 크면 OS 페이지 캐시와 이중 캐싱 발생
work_mem커넥션당 정렬·해시조인에 사용하는 로컬 메모리커넥션 수 × 복잡한 쿼리 병렬도 × work_mem = 실제 소비량
maintenance_work_memVACUUM, 인덱스 빌드에 사용. 크게 잡으면 VACUUM이 빨라짐인덱스 병렬 빌드 시 worker 수만큼 곱해진다
wal_buffersWAL을 디스크에 쓰기 전 임시 저장 버퍼. 기본 1/32 of shared_buffers고쓰기 워크로드에서는 64 MB 정도로 올리면 효과적

디스크 레이아웃 (PGDATA)

PostgreSQL의 데이터 디렉터리($PGDATA)에는 다음이 있다.

$PGDATA/
├── base/          ← 데이터베이스별 테이블·인덱스 파일 (OID별 서브디렉터리)
├── pg_wal/        ← Write-Ahead Log 파일 (기본 16 MB 세그먼트)
├── pg_xact/       ← 트랜잭션 커밋 상태 (Commit Log, CLOG)
├── pg_tblspc/     ← 심볼릭 링크로 타블스페이스 연결
├── global/        ← 클러스터 전체 공유 시스템 카탈로그
├── postgresql.conf
└── pg_hba.conf

base/ 안에서 테이블은 OID 기반의 숫자 파일명으로 저장된다. 파일 크기가 1 GB를 넘으면 .1, .2 접미사로 자동 분할된다.

쿼리 실행 흐름

백엔드 프로세스가 쿼리를 받으면 다음 순서로 처리한다.

  1. 파서 — SQL 문자열 → 파스 트리
  2. 분석기(Analyzer) — 테이블·컬럼 존재 확인, 타입 해석
  3. 리라이터(Rewriter) — 뷰·룰을 실제 쿼리로 치환
  4. 플래너/옵티마이저 — 통계 정보(pg_statistic)를 참조해 최저 비용 실행 계획 선택
  5. 실행기(Executor) — 계획에 따라 실제로 페이지를 읽고 결과 반환

EXPLAIN ANALYZE는 5단계 실행기가 실제로 어느 노드에서 얼마나 걸렸는지 보여준다. MySQL의 EXPLAIN FORMAT=JSON에 해당한다.

DBA 체크리스트

  • ps aux | grep postgres 로 현재 프로세스 상태를 확인한다. idle in transaction 이 오래 머물면 락 경합의 원인이 된다.
  • pg_stat_bgwriter 뷰로 bgwriter와 checkpointer 활동을 모니터링한다.
  • 커넥션 수가 100 이상이라면 PgBouncer(트랜잭션 풀링) 도입을 검토한다.
  • shared_buffers 조정 후에는 반드시 인스턴스 재시작이 필요하다.

References

  • https://www.postgresql.org/docs/current/tutorial-arch.html
  • https://www.postgresql.org/docs/current/runtime-config-resource.html
  • https://www.enterprisedb.com/blog/postgres-internals-deep-dive-process-architecture
  • https://ahmed-n-abdeltwab.github.io/blog/2025/06/17/postgresql-architecture-processes.html
  • https://www.postgresql.fastware.com/blog/lets-get-back-to-basics-postgresql-memory-components