본문으로 건너뛰기

1.3 프로세스 모델

PostgreSQL은 프로세스 기반(process-per-connection) 아키텍처를 채택합니다. MySQL/InnoDB·MS SQL Server 같은 스레드 모델과 달리, 클라이언트 한 명마다 OS 레벨의 별도 프로세스가 생성됩니다. 그 위에 백그라운드 일을 담당하는 보조 프로세스 묶음이 항상 떠 있습니다. 본 절에서는 클러스터 한 개를 띄울 때 실제로 어떤 프로세스 가족이 만들어지는지 정리합니다.

프로세스 모델

    flowchart TD
  PM["<b>postmaster</b><br/>(parent, listener)"]

  PM --> CKPT["<b>checkpointer</b><br/>dirty page → disk"]
  PM --> BGW["<b>background writer</b><br/>dirty page 점진적 flush"]
  PM --> WALW["<b>WAL writer</b><br/>WAL buffer → pg_wal"]
  PM --> AVL["<b>autovacuum launcher</b>"]
  PM --> ARCH["<b>archiver</b><br/>WAL → archive (선택)"]
  PM --> LR["<b>logical replication launcher</b>"]

  AVL -.fork.-> AVW["autovacuum worker × N"]
  LR -.fork.-> LRW["logical replication worker × N"]

  PM -. 클라이언트 접속 .-> BE1["<b>backend</b> #1"]
  PM -. 클라이언트 접속 .-> BE2["<b>backend</b> #2"]
  PM -. 클라이언트 접속 .-> BEN["<b>backend</b> #N"]

  PM --> WS["<b>walsender</b><br/>standby로 WAL 스트림"]

  classDef root fill:#ede9fe,stroke:#6d28d9,color:#3b0764,stroke-width:2px
  classDef aux fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
  classDef be fill:#fef3c7,stroke:#b45309,color:#78350f
  classDef rep fill:#d1fae5,stroke:#047857,color:#064e3b
  class PM root
  class CKPT,BGW,WALW,AVL,ARCH,LR,AVW,LRW aux
  class BE1,BE2,BEN be
  class WS rep
  

postmaster — 부모 프로세스

클러스터를 기동하면 postgres라는 바이너리가 postmaster 역할로 시작합니다. 한 클러스터에는 postmaster가 정확히 한 개 있고, 모든 다른 프로세스의 부모입니다.

postmaster의 책임:

  • TCP 포트·Unix 도메인 소켓을 열고 listen()
  • 새 클라이언트 연결이 오면 fork()해서 backend 프로세스 생성
  • 보조 프로세스(checkpointer, WAL writer 등) 기동·감시·재시작
  • 자식 프로세스가 비정상 종료되면 클러스터 전체를 안전하게 재기동(crash recovery 트리거)

postmaster 자체는 SQL을 실행하지 않습니다. 감시·중재 역할만 합니다.

backend — 클라이언트 1명당 1개

PostgreSQL은 클라이언트 접속마다 postmaster가 fork()해서 backend 프로세스를 한 개 만든다. backend는 그 클라이언트의 모든 SQL을 처리하고, 연결이 끊기면 종료됩니다.

    sequenceDiagram
  participant Client
  participant Postmaster
  participant Backend
  Client->>Postmaster: TCP connect (포트 5432)
  Postmaster->>Postmaster: 인증 검사 (pg_hba.conf)
  Postmaster->>Backend: fork()
  Postmaster-->>Client: backend 핸드오프 완료
  Note over Client,Backend: 이후 클라이언트와 backend가<br/>postmaster 개입 없이 직접 통신
  Client->>Backend: SQL
  Backend-->>Client: 결과
  Client->>Backend: 연결 종료
  Backend->>Backend: exit
  

이 모델의 함의:

특성결과
한 연결 = 한 프로세스OS 프로세스가 무겁기 때문에 동시 접속이 많아지면 (수백~수천) 메모리·컨텍스트 스위칭 비용 급증
격리도 높음한 backend의 crash가 다른 backend에 직접 영향 주지 않음 — postmaster가 감시
커넥션 풀 사실상 필수운영에서는 pgBouncer·pgcat·pgpool 등을 앞에 둔다 (Part XIII 커넥션 풀링 참고)
왜 스레드 모델이 아닌가: 1990년대 후반 설계 당시 멀티스레드 안정성이 OS·libc마다 천차만별이었고, 메모리 격리가 강한 프로세스 모델이 더 안전했습니다. 이후 호환성·격리 이점이 유지되어 지금도 같은 모델을 씁니다.

보조 프로세스 (utility processes)

postmaster가 띄우는 보조 프로세스는 대부분 클러스터당 1개씩만 존재합니다.

프로세스역할
checkpointer주기적으로 체크포인트 수행. shared_buffers의 dirty page를 디스크에 flush, WAL을 안전하게 잘라낼 수 있는 시점 생성
background writer체크포인트와 별개로 dirty page를 점진적 flush. checkpointer 부담과 I/O 스파이크를 완화
WAL writerWAL buffer 내용을 pg_wal/의 세그먼트 파일로 flush. backend가 commit 시 동기 flush를 기다리는 시간을 줄임
autovacuum launcherautovacuum worker를 만들 시점을 결정. 통계 기반으로 vacuum/analyze 대상 데이터베이스를 결정한 뒤 worker를 fork
autovacuum workerlauncher가 띄우는 단기 워커. 실제 VACUUM/ANALYZE 작업 수행. 동시 실행 수는 autovacuum_max_workers로 제한
archiverarchive_mode = on일 때만 동작. 완료된 WAL 세그먼트를 archive_command로 외부 위치에 복사
logical replication launcher로지컬 복제 구독자 측에서 동작. 각 subscription마다 logical replication worker(apply worker)를 fork
logical replication workerpublication의 변경을 받아 로컬에 적용. 트랜잭션 단위 또는 streaming 단위로 작동
walsenderprimary 측에서 standby/구독자에게 WAL을 스트리밍. replication 연결 하나당 walsender 한 개
walreceiverstandby 측에서만 동작. primary의 walsender로부터 WAL을 받아 로컬 pg_wal/에 쓴다
startup process서버 기동 직후 또는 복구 중에만 존재. WAL을 재생해 일관된 상태로 만든 뒤 종료(primary) 또는 계속 재생(standby)

사라진 stats collector

PostgreSQL 14까지는 stats collector라는 별도 프로세스가 UDP로 통계를 모았습니다. v15부터 이 프로세스는 사라지고 cumulative statistics system이 shared memory 기반으로 동작합니다. backend가 자기 통계를 shared memory에 직접 쓰고, 종료 시점에 pg_stat/ 디렉토리에 영구화합니다. v15+에서 ps 출력에 stats collector가 보이지 않는 이유입니다.

실제로 보기 — pspg_stat_activity

기동된 클러스터의 프로세스 가족은 OS 도구로 볼 수 있습니다.

# Linux
ps -ef --forest | grep '[p]ostgres'

전형적인 출력 (PostgreSQL 17, 클라이언트 2명 접속 중, standby 1대 연결):

postgres  1024     1   /usr/pgsql-17/bin/postgres -D /var/lib/pgsql/17/data
postgres  1025  1024     \_ postgres: logger
postgres  1027  1024     \_ postgres: checkpointer
postgres  1028  1024     \_ postgres: background writer
postgres  1029  1024     \_ postgres: walwriter
postgres  1030  1024     \_ postgres: autovacuum launcher
postgres  1031  1024     \_ postgres: logical replication launcher
postgres  2001  1024     \_ postgres: app_user app_main 10.0.0.5(43210) idle
postgres  2002  1024     \_ postgres: app_user app_main 10.0.0.6(43211) SELECT
postgres  2100  1024     \_ postgres: walsender repl_user 10.0.0.10(5432) streaming 0/3A001F8

backend의 process title에는 <user> <db> <client_ip>(<port>) <state> 형식으로 현재 상태가 박혀 있습니다. ps만 봐도 누가 무얼 하고 있는지 대략 파악됩니다.

SQL 레벨에서는 pg_stat_activity 뷰가 같은 정보를 카탈로그로 제공합니다.

SELECT pid, backend_type, datname, usename, client_addr, state, query
  FROM pg_stat_activity
 ORDER BY pid;

backend_type 컬럼은 위 표의 프로세스 종류를 그대로 노출한다 — 'client backend', 'checkpointer', 'autovacuum worker', 'walsender', 'logical replication worker' 등.

프로세스 수 제한 파라미터

기본값 기준으로 한 클러스터의 총 프로세스 수는 다음 설정의 합에 가깝습니다.

설정기본값의미
max_connections100동시 client backend 수 상한
superuser_reserved_connections3슈퍼유저 예약분
max_worker_processes8autovacuum·logical replication·parallel query·확장 worker 총합 상한
autovacuum_max_workers3동시 autovacuum worker 수
max_wal_senders10동시 walsender 수 (복제·로지컬 구독자)
max_logical_replication_workers4logical replication worker 수
max_parallel_workers8parallel query 워커 풀

운영 환경에서 동시 접속 1000명을 받겠다면 max_connections = 1000처럼 키워야 하지만, OS 프로세스가 천 개로 늘면 커널 스케줄러·메모리 압박이 커집니다. 운영 권장은 max_connections를 200~500 정도로 제한하고, 그 앞에 pgBouncer 같은 풀러를 두는 것입니다.

max_connections·max_worker_processes·max_wal_senders 등은 shared memory 크기에 영향을 주므로 변경 시 클러스터 재시작이 필요합니다. ALTER SYSTEM으로 값만 바꾸고 reload하면 적용되지 않습니다.

crash 시 동작

backend 중 하나가 SIGSEGV 등으로 비정상 종료되면, postmaster는 클러스터 전체를 강제로 셧다운한 뒤 자동 재기동합니다. 이유는 안전성 — 죽은 backend가 shared memory를 망가뜨렸을 가능성을 배제할 수 없기 때문입니다. 재기동 시 startup process가 WAL을 재생해 일관된 상태를 복원합니다.

운영자가 알아 둘 것:

  • 한 클라이언트의 query가 backend crash를 유발하면 다른 모든 클라이언트도 끊긴다
  • 끊긴 직후 클러스터는 recovery 상태가 되며 수 초~수십 초 동안 새 연결을 받지 못할 수 있다
  • 자주 발생하면 원인(특정 확장, 메모리 부족, 손상된 데이터)을 추적해야 한다 — 그냥 무시하면 안 된다

정리

  • PostgreSQL은 프로세스 기반 모델 — 클라이언트 1명당 backend 프로세스 1개
  • postmaster가 부모. 보조 프로세스(checkpointer, WAL writer, autovacuum launcher 등)를 띄우고 감시
  • v15부터 stats collector가 사라지고 cumulative statistics system이 shared memory로 동작
  • 동시 접속이 많은 시스템은 pgBouncer 같은 풀러가 사실상 필수
  • pg_stat_activity.backend_type으로 SQL에서 프로세스 종류 식별 가능
  • 한 backend의 crash는 클러스터 전체를 자동 재기동시킨다

다음 절(1.4)에서는 이 프로세스들이 공유하는 메모리 영역(shared_buffers, work_mem, WAL buffer)을 봅니다.