PostgreSQL

PostgreSQL: Disk I/O 및 WAL 흐름

dewstream 2025. 6. 11. 08:00
728x90

※ PostgreSQL: Disk I/O and WAL Flow.

 

안녕하세요. 듀스트림입니다.

 

기능적인 요소도 중요하지만 엔진 파인 튜닝을 하기 위해서는 내부에서 어떻게 흘러가는지 아는 것도 중요합니다.

 

오늘의 포스팅은 PostgreSQL에서 데이터를 어떻게 처리하는지에 대한 내용입니다.


PostgreSQL의 디스크 I/O 및 트랜잭션 로그 처리 구조는 shared_buffers, WAL buffers, OS page cache 그리고 디스크 계층 간의 정확한 흐름과 역할 분담을 기반으로 구성됩니다.

1. PostgreSQL의 트랜잭션 처리 및 WAL 쓰기 흐름

1. 클라이언트 SQL 요청 처리

  • 클라이언트로부터 SQL 쿼리가 PostgreSQL 백엔드 프로세스로 전달됩니다.

 

2. 데이터 페이지 접근 (shared_buffers)

  • 백엔드는 쿼리 실행을 위해 테이블/인덱스 블록을 shared_buffers에 로딩하고, 필요한 경우 이를 수정합니다.

 

3. WAL 레코드 생성 및 WAL Buffer 기록

  • 변경된 트랜잭션 내용을 Write-Ahead Logging(WAL) 포맷으로 변환해 WAL Buffer에 기록합니다.

 

4. WAL Buffer → 파일 시스템 기록 (write)

  • WAL Writer 또는 백엔드 프로세스는 write() 시스템 콜을 통해 WAL 데이터를 pg_wal/ 디렉토리의 WAL 파일로 씁니다. 이때는 실제 디스크가 아니라 OS 페이지 캐시에 저장됩니다.

 

5. 디스크 동기화 (flush)

  • XLogFlush()가 fsync() 또는 fdatasync()를 호출해 WAL이 디스크에 안전하게 저장(동기화)됩니다. (durability 보장을 위한 단계)
    • 관련 소스 코드: XLogFlush()
    • 조건: synchronous_commit = on 또는 remote_write일 경우 WAL이 디스크에 flush(동기화)됩니다.
      (remote_write는 streaming replication 환경에서만 의미 있으며, standalone 환경에서도 기본값인 on 상태에서는 WAL flush가 항상 발생)

 

6. 커밋 확정 및 응답

  • WAL flush 완료 후 클라이언트에 커밋 완료 응답이 전송됩니다.

2. 복구 및 스트리밍 레플리케이션 시의 WAL Replay 흐름

PostgreSQL은 시스템 재시작(Crash Recovery), PITR 또는 Streaming-Replication 중 Standby에서 다음과 같은 방식으로 WAL을 처리합니다.

 

1.WAL 파일 읽기

  • PostgreSQL은 Crash Recovery, PITR, Streaming-Replication 시 pg_wal/(pg_xlog/) 또는 아카이브에서 WAL 파일을 읽어와 복구를 시작합니다.

 

2. WAL record 분석 및 적용 대상 확인

  • WAL 레코드는 각 타입(XLOG_HEAP_INSERT, XLOG_BTREE_SPLIT 등)에 따라 데이터 파일, FSM, VM, 인덱스 등에 적용됩니다.
  • StartupXLOG() → xlog_redo() → 각 작업별 Redo 루틴 호출
WAL 레코드 타입 적용 대상
XLOG_HEAP_INSERT Relation 파일
XLOG_FSM_CLEANUP FSM(Free Space Map)
XLOG_BTREE_SPLIT 인덱스(B-Tree)
XLOG_VACUUM VM(Visibility Map)

 

 

3. WAL Redo (Apply)

 

  • 복구 모드(Crash Recovery, PITR, Standby)에서는 WAL 레코드가 데이터 파일에 직접 적용되며, shared_buffers를 거치지 않습니다. (이는 복구 성능과 일관성 보장을 위한 설계입니다.)
  • WAL Redo 동작 특성
    • 직접 디스크 쓰기: 버퍼 캐시를 우회하여 데이터 파일(물리적 저장소)에 직접 적용됩니다. 
    • 원자성 보장: 각 WAL 레코드는 해당 데이터 블록의 LSN과 비교하여 필요할 때만 적용되고, 적용 후 LSN이 갱신되어 중복 적용을 방지합니다.
    • synchronous_commit 설정에 따라 커밋된 트랜잭션의 WAL이 디스크에 안전하게 기록된 후 복구가 진행되므로, 복구 시점까지의 데이터 일관성이 보장됩니다.

 

4. 디스크 동기화 및 checkpoint 갱신

  • 복구가 완료되면 PostgreSQL은 새로운 체크포인트를 생성하고, pg_control 파일을 갱신하여 복구가 끝났음을 표시합니다.

 

※ synchronous_commit = remote_apply일 경우 Standby에서 Redo(apply)가 완료되어야 primary에서 커밋이 완료됩니다.

 

 

+ Flush 메커니즘

  • Normal 모드
상황 처리 방식 관련 함수 설명
버퍼를 "Dirty" 상태로 표시 shared_buffers 내 마킹 MarkBufferDirty() 실제 쓰기 트리거 X
LRU 관리 대상
주기적 플러시 shared_buffers → OS 캐시 FlushBuffer() 백그라운드 writer/checkpointer 주기적 실행
Checkpoint 버퍼 → OS 캐시 (비동기) BufferSync()FlushBuffer() 모든 더티 버퍼 일괄 처리
OS 캐시 → 디스크 (동기) smgrimmedsync() 파일 단위 fsync() 수행
  • Recovery 모드
상황 처리 방식 관련 함수 설명
WAL Redo 직접 디스크 쓰기 smgrwrite() → mdwritev() 버퍼 풀 무시
벡터 I/O로 다중 블록 처리
체크포인트 물리적 동기화 생략 - 복구 완료 시 최종 sync만 수행

 

 

++호출 계층

  • 일반 쓰기 경로
MarkBufferDirty()  
  → (체크포인트 발생 시)  
    BufferSync()  
      → FlushBuffer()  
        → smgrwrite()  
          → mdwrite()  # 단일 블록  
            → pg_fsync()  # 필요 시
  • Recovery 모드 경로
StartupXLOG()  
  → XLogReadRecord()  
    → heap_redo()/btree_redo()  
      → smgrwrite()  
        → mdwritev()  # 다중 블록  
          → pg_fsync()  # 최종 sync

 

 

+++ Normal vs Recovery 모드 차이

항목 일반 모드 복구 모드
버퍼 풀 사용
I/O 단위 페이지(8KB) 블록 그룹(벡터 I/O)
동기화 주체 checkpointer Startup 프로세스

3. OS Page Cache와 PostgreSQL의 역할 구분

계층 내용
Shared Buffers PostgreSQL 내부의 블록 캐시(8KB 페이지 단위), 데이터 페이지 I/O의 첫 진입점, LRU 알고리즘으로 페이지 관리
WAL Buffers 트랜잭션 변경을 로그 형식으로 기록하는 내부 버퍼 (Default: 16MB), 주기적으로 OS Page Cache에 기록
OS Page Cache PostgreSQL이 write() 호출로 암묵적 사용(직접 제어 x), fsync() 없이도 커널이 주기적으로 디스크에 동기화
Disk fsync() 호출 시 데이터가 최종 반영되는 영구 저장소
  • PostgreSQL은 OS page cache를 직접 제어하진 않으며, write()를 통해 암묵적으로 커널 캐시에 기록하고 fsync()로 이를 디스크에 반영합니다.

4. 디스크 I/O 및 WAL 관련 튜닝 가이드

파라미터 설명 권장값/튜닝 포인트
shared_buffers PostgreSQL이 자체적으로 관리하는 메모리 캐시 전체 RAM의 25~40%. 너무 크면 OS 캐시 영역 부족 위험
effective_cache_size Planner에게 OS 캐시 추정치 제공 (쿼리 비용 계산에 영향) 전체 RAM의 70~80% 수준으로 설정
wal_writer_delay WAL Writer가 flush 주기를 얼마나 지연할지 WAL 발생량 많다면 낮추는 것도 고려 (Default: 200ms)
synchronous_commit 커밋이 flush를 기다릴지 여부 (on, remote_write, remote_apply) 장애 내구성 vs 성능 trade-off
checkpoint_timeout checkpoint 간격 제어 너무 짧으면 디스크 쓰기 증가, 너무 길면 crash recovery 시간 증가 (Default: 5min)
checkpoint_completion_target checkpoint 분산률 제어 (0~1) 0.7~0.9 설정 시 I/O spike 완화 가능 (Default: 0.5)
bgwriter_lru_maxpages bgwriter가 dirty page를 얼마나 flush할지 WAL pressure가 높을 경우 증가 고려
wal_compression WAL 크기 줄이기 위한 압축 on 설정 시 I/O 및 archive 저장 공간 절감 가능
wal_buffers WAL 버퍼 크기 특별한 튜닝은 대량 트랜잭션 발생 시 고려

오늘은 여기까지~

 

 

728x90

'PostgreSQL' 카테고리의 다른 글

PostgreSQL: Lock  (0) 2025.06.24
PostgreSQL: credcheck  (0) 2025.06.17
PostgreSQL: Streaming Replication  (0) 2025.06.10
PostgreSQL: ROLE  (4) 2025.06.09
PostgreSQL: 병렬 INSERT가 되지 않는 이유  (1) 2025.06.08