PostgreSQL 프로세스 아키텍처: 서버 시작부터 종료까지
저자: Srinath Reddy
2025년 9월 3일
PostgreSQL 서버가 시작된 뒤 종료될 때까지 내부 프로세스가 어떻게 생성되고 협력하는지 살펴봅니다. postmaster와 ServerLoop를 중심으로 백엔드 프로세스, 백그라운드 워커, 복제 프로세스, 시그널 처리 및 종료 모드의 동작 흐름을 단계별로 설명합니다.
PostgreSQL 프로세스 아키텍처
pg_ctl을 사용해 postgres를 시작하면 가장 먼저 실행되는 것은 postmaster입니다. postmaster는 이후 실행되는 대부분의 프로세스에서 부모 프로세스 역할을 합니다. postmaster는 main() 함수를 실행하고 그 안에서 PostmasterMain을 호출하며, 이때 다음과 같은 주요 작업이 진행됩니다.
- GUC(Global User Configuration) 옵션 초기화
- postgres 실행 파일의 명령줄 인자 파싱 및 관련 GUC 설정
- 데이터 디렉터리, lock 파일, control 파일 확인 및 datadir lock 파일 생성(해당 파일에는 postmaster 상태와 pid가 기록되어 하나의 클러스터에 하나의 서버만 실행되도록 보장)
- 공유 라이브러리 로드 및 필요한 공유 메모리 크기 계산
- 해당 크기만큼 공유 메모리 세그먼트 할당
다음으로 핵심 루프인 ServerLoop가 시작됩니다. 이 루프는 계속 실행되며, 루프가 종료되면 서버도 내려갑니다.
ServerLoop: PostgreSQL 서버의 핵심 이벤트 루프
ServerLoop는 시그널 핸들러가 설정한 Latch 또는 소켓에 대기 중인 새로운 연결 요청을 기다리는 무한 이벤트 루프입니다. 이 루프에서는 다음 작업이 진행됩니다.
- 새로운 자식 프로세스가 시작되고 종료된 프로세스가 정리됩니다.
- 백엔드에서 오는 요청과 시그널이 처리됩니다.
- 필요한 프로세스가 실행 중인지 확인하고, 부족하면 IO workers나 백그라운드 프로세스를 새로 시작합니다(
LaunchMissingBackgroundProcesses).
postmaster의 자식 프로세스
postmaster의 주요 자식 프로세스는 다음과 같습니다.
- IO workers: 페이지를 비동기적으로 읽어 일반 백엔드의 부하를 줄임
- Checkpointer: 오래된 WAL 정리 및 공유 버퍼의 dirty 페이지를 디스크에 기록
- Background writer: 공유 버퍼의 dirty 페이지를 주기적으로 기록해 백엔드의 부하 감소
- Startup process: 마스터에서는 복구 완료 시까지 WAL을 재생, 리플리카에서는 지속적으로 WAL을 재생
- WAL writer: WAL 버퍼에서 디스크로 플러시
- Autovacuum launcher: 주기적으로 autovacuum-worker 실행
이외에도 요청에 따라 다양한 프로세스가 fork되며, 각각 별도 블로그에서 다룰 만한 주제입니다.
PostgreSQL은 시점에 따라 자식 프로세스를 다르게 생성합니다.
- 서버 시작 시 기본 프로세스 생성
- 이후 필요 시:
- Backend 프로세스(클라이언트 연결 처리)
- Background workers(확장에서 제공하는 사용자 정의 코드 실행)
대부분의 자식 프로세스는 postmaster_child_launch 함수로 생성되며, BackendType enum에 따라 해당 프로세스의 main 함수가 호출됩니다.
클라이언트 연결을 처리하는 백엔드 프로세스
기본적으로 postmaster는 listen_addresses(기본값: localhost)에서 클라이언트 연결 요청을 기다립니다. psql 클라이언트가 libpq를 통해 postmaster에 연결 요청을 보내면, postmaster는 새로운 백엔드 프로세스를 fork하여 해당 클라이언트의 쿼리를 처리하게 합니다.
- 각 psql 클라이언트마다 서버에 새로운 자식 프로세스가 생성됨
- 새 백엔드 프로세스는 공유 메모리에 접근 가능(예: BufferBlocks)
- 쿼리 실행 시 필요한 페이지가 공유 버퍼에 없으면 디스크에서 읽어옴
- 버퍼가 가득 차면 백엔드가 직접 dirty 페이지를 디스크에 기록한 후 새 페이지를 로드
이 과정에서 checkpointer와 background writer가 백엔드의 부하를 줄여 쿼리 실행 속도를 개선합니다.
복제에서 백엔드 프로세스가 walsender로 전환되는 과정
복제(replication) 상황에서는 특별한 동작이 발생합니다.
- 리플리카 노드가 시작되면 startup process가 walreceiver 프로세스를 실행
- walreceiver가 libpq로 primary postmaster에 replication 메시지를 보내 연결 요청
- postmaster는 이를 일반 백엔드처럼 처리하지만, startup packet에 “replication”이 있으면 해당 백엔드를 walsender로 변환
- 물리적 복제에서는 WAL을 그대로 전송하고, 논리적 복제에서는 WAL을 해석해 전송
즉, walsender도 일반 백엔드 프로세스로 시작합니다.
확장 코드를 실행하는 백그라운드 워커
백그라운드 워커는 확장에서 제공하는 사용자 정의 코드를 실행하는 프로세스입니다.
- 확장이
shared_preload_libraries에 지정되면_PG_init()이 호출되고,RegisterBackgroundWorker로 postmaster의 리스트에 추가 - postmaster의 ServerLoop에서
maybe_start_bgworkers가 호출되면 실행 - 서버 실행 중에도
RegisterDynamicBackgroundWorker로 추가 가능
예를 들어 논리적 복제 런처(logical replication launcher)는 서버 시작 시 함께 실행되는 백그라운드 워커입니다.
postmaster의 시그널 처리
postmaster는 다음과 같은 시그널을 처리합니다.
| 시그널 | 핸들러 | 처리 함수 |
|---|---|---|
| SIGHUP | handle_pm_reload_request_signal | process_pm_reload_request |
| SIGINT | handle_pm_shutdown_request_signal | process_pm_shutdown_request |
| SIGQUIT | handle_pm_shutdown_request_signal | process_pm_shutdown_request |
| SIGTERM | handle_pm_shutdown_request_signal | process_pm_shutdown_request |
| SIGUSR1 | handle_pm_pmsignal_signal | process_pm_pmsignal |
| SIGCHLD | handle_pm_child_exit_signal | process_pm_child_exit |
대표적인 처리 예시는 다음과 같습니다.
SIGHUP:postgresql.conf재로드SIGUSR1: 백엔드 요청, promote/logrotate 처리
PostgreSQL 종료 모드(Shutdown Modes)
PostgreSQL에는 3가지 종료 모드가 있습니다.
| 시그널 | 모드 | 설명 |
|---|---|---|
| SIGTERM | Smart | 모든 클라이언트 종료 후 종료 |
| SIGINT | Fast | 즉시 종료(기본값) |
| SIGQUIT | Immediate | 즉시 종료(복구 필요) |
- Smart: 기존 연결이 모두 종료될 때까지 기다림, 신규 연결 차단
- Fast: 클라이언트 종료 대기 없이 백엔드 종료
- Immediate: 모든 프로세스 강제 종료, 재시작 시 복구 필요
체크포인트 및 walsender 종료, IO workers 종료 등이 순차적으로 진행되며, syslogger는 마지막에 종료됩니다.
자식 프로세스 종료를 처리하는 리퍼(Reaper)
리퍼는 자식 프로세스가 종료될 때 postmaster가 이를 처리하는 방식입니다.
- 자식 프로세스 종료 → postmaster가
SIGCHLD수신 handle_pm_child_exit_signal실행 →pending_pm_child_exit = true설정- ServerLoop에서 이를 감지해
process_pm_child_exit()호출 → 자원 정리
비정상 종료 시 HandleChildCrash API가 모든 프로세스에 종료 신호를 보내고, 서버 재시작 및 crash recovery가 진행됩니다.
PostgreSQL 프로세스 아키텍처 핵심 요약
PostgreSQL의 프로세스 아키텍처는 postmaster와 다양한 자식 프로세스가 협력하는 구조입니다.
- 클라이언트 쿼리 처리: 백엔드 프로세스
- 성능 최적화: checkpointer, background writer
- 복제: walreceiver, walsender
- 확장 기능: 백그라운드 워커
- 안정성: 시그널 및 종료 모드
이 모든 프로세스가 맞물려 PostgreSQL이 안정적으로 작동합니다.
PostgreSQL 프로세스 아키텍처 FAQ
PostgreSQL에서 postmaster는 어떤 역할을 하나요?
postmaster는 PostgreSQL 서버가 시작될 때 먼저 실행되며, 대부분의 자식 프로세스에서 부모 프로세스 역할을 합니다. 설정과 데이터 디렉터리를 확인하고 공유 메모리를 할당한 뒤 ServerLoop를 실행합니다.
ServerLoop에서는 어떤 작업이 처리되나요?
ServerLoop는 연결 요청과 시그널을 기다리는 핵심 이벤트 루프입니다. 새 자식 프로세스를 시작하고 종료된 프로세스를 정리하며, 필요한 백그라운드 프로세스가 실행 중인지 확인합니다.
PostgreSQL은 클라이언트 연결마다 프로세스를 생성하나요?
네. psql 같은 클라이언트가 libpq를 통해 연결을 요청하면 postmaster가 새로운 백엔드 프로세스를 fork하여 해당 클라이언트의 쿼리를 처리합니다.
walreceiver와 walsender는 어떻게 동작하나요?
리플리카의 startup process가 walreceiver를 실행하고, walreceiver는 primary postmaster에 복제 연결을 요청합니다. primary의 일반 백엔드는 startup packet의 “replication”을 확인한 뒤 walsender로 전환됩니다.
PostgreSQL의 Smart, Fast, Immediate 종료 모드는 어떻게 다른가요?
Smart는 기존 연결이 끝날 때까지 기다리고, Fast는 클라이언트 종료를 기다리지 않고 백엔드를 종료합니다. Immediate는 모든 프로세스를 강제로 종료하므로 재시작할 때 복구가 필요합니다.
자식 프로세스가 비정상 종료되면 어떻게 되나요?
postmaster는 SIGCHLD를 받아 종료된 자식 프로세스의 자원을 정리합니다. 비정상 종료 시에는 HandleChildCrash API가 모든 프로세스에 종료 신호를 보내고 서버 재시작과 crash recovery가 진행됩니다.