Postgres Internals Deep Dive: 프로세스 아키텍처
저자: Srinath Reddy
2025년 9월 3일
서론
PostgreSQL이 시작될 때부터 종료될 때까지 내부에서 어떤 일이 일어나며, 프로세스들이 어떻게 함께 동작하는지 궁금했던 적이 있나요? 이 블로그는 그 과정을 단계별로 안내합니다.
프로세스 아키텍처
pg_ctl
을 사용해 postgres를 시작하면, 가장 먼저 실행되는 것은 postmaster로, 이후 대부분의 프로세스의 부모 프로세스 역할을 합니다.
postmaster는 main()
함수를 실행하고, 그 안에서 PostmasterMain
을 호출하는데 여기서 중요한 작업들이 진행됩니다.
- GUC(Global User Configuration) 옵션 초기화
- postgres 실행 파일의 명령줄 인자 파싱 및 관련 GUC 설정
- 데이터 디렉터리, lock 파일, control 파일 확인 및 datadir lock 파일 생성 (해당 파일에는 postmaster 상태와 pid가 기록되어, 하나의 클러스터에 하나의 서버만 실행되도록 보장)
- 공유 라이브러리 로드 및 필요한 공유 메모리 크기 계산
- 해당 크기만큼 공유 메모리 세그먼트 할당
그 다음은 핵심 루프인 ServerLoop가 시작됩니다. 이 루프는 결코 끝나지 않으며, 종료된다면 서버도 내려갑니다.
ServerLoop (생명의 루프)
이는 무한 이벤트 루프로, 시그널 핸들러에 의해 설정된 Latch나 소켓에 대기 중인 새로운 연결 요청을 기다립니다.
이 루프에서:
- 새로운 자식 프로세스가 시작되고, 죽은 프로세스는 정리됩니다.
- 백엔드에서 오는 요청/시그널이 처리됩니다.
- 필요한 프로세스가 실행 중인지 확인하고, 부족하면 IO workers나 백그라운드 프로세스를 새로 시작합니다 (
LaunchMissingBackgroundProcesses
).
자식 프로세스의 등장
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가 도와주어 쿼리 실행 속도를 개선합니다.
변신하는 백엔드 프로세스
복제(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는 다양한 시그널을 처리합니다.
시그널 | 핸들러 | 처리 함수 |
---|---|---|
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 처리
종료 모드 (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의 프로세스 아키텍처는 postmaster와 다양한 자식 프로세스들의 협력 구조입니다.
- 클라이언트 쿼리 처리: 백엔드 프로세스
- 성능 최적화: checkpointer, background writer
- 복제: walreceiver, walsender
- 확장 기능: 백그라운드 워커
- 안정성: 시그널 및 종료 모드
이 모든 것이 맞물려 PostgreSQL이 안정적으로 작동합니다.