콘텐츠로 이동

PostgreSQL: Asynchronous Commit

출처: PostgreSQL 공식 문서 - 28.4. Asynchronous Commit


개요

Asynchronous Commit(비동기 커밋)은 트랜잭션이 더 빠르게 완료될 수 있도록 허용하는 옵션입니다. 단, 데이터베이스가 크래시되는 경우 가장 최근 트랜잭션 일부가 손실될 수 있다는 비용이 따릅니다. 많은 애플리케이션에서 이는 수용 가능한 트레이드오프입니다.


동기(Synchronous) 커밋 vs 비동기(Asynchronous) 커밋

동기 커밋 (기본 동작)

트랜잭션 커밋은 일반적으로 동기(synchronous) 방식으로 동작합니다.

  • 서버는 트랜잭션의 WAL(Write-Ahead Log) 레코드가 영구 스토리지에 플러시(flush)될 때까지 대기한 후, 클라이언트에 성공 응답을 반환합니다.
  • 따라서 클라이언트는 커밋된 것으로 보고된 트랜잭션이 직후에 서버 크래시가 발생하더라도 보존됨이 보장됩니다.
  • 그러나 짧은 트랜잭션의 경우, 이 대기 지연이 전체 트랜잭션 시간의 주요 구성 요소가 됩니다.

비동기 커밋

비동기 커밋 모드를 선택하면, 서버는 트랜잭션이 논리적으로 완료되는 즉시 성공 응답을 반환합니다. 이는 생성된 WAL 레코드가 실제로 디스크에 기록되기 전에 이루어집니다.

  • 이 방식은 소규모 트랜잭션에서 처리량(throughput)을 크게 향상시킬 수 있습니다.

비동기 커밋의 위험성: 데이터 손실 (데이터 손상 아님)

비동기 커밋 사용 시의 위험은 데이터 손상(corruption)이 아닌 데이터 손실(loss)입니다.

  • 데이터베이스가 크래시되면, 마지막으로 플러시된 WAL 레코드까지 재실행(replay)하여 복구합니다.
  • 데이터베이스는 자기 일관적(self-consistent) 상태로 복원되지만, 아직 디스크에 플러시되지 않은 트랜잭션은 해당 상태에 반영되지 않습니다.
  • 결과적으로 마지막 몇 건의 트랜잭션이 손실됩니다.

일관성 보장

트랜잭션은 커밋 순서대로 재실행되므로 불일관성이 발생하지 않습니다.

예시: 트랜잭션 B가 이전 트랜잭션 A의 효과에 의존하는 변경을 했을 경우, A의 효과가 손실되면서 B의 효과만 보존되는 상황은 발생하지 않습니다.


비동기 커밋을 사용하면 안 되는 경우

비동기 커밋은 다음과 같은 경우에 사용해서는 안 됩니다:

  • 클라이언트가 트랜잭션이 기억될 것이라는 가정하에 외부 동작을 취하는 경우
  • 예: ATM의 현금 인출을 기록하는 트랜잭션에는 비동기 커밋을 절대 사용해서는 안 됩니다.
  • 반면, 이벤트 로깅 같은 많은 시나리오에서는 이러한 강력한 보장이 필요하지 않습니다.

동기/비동기 커밋 혼용

사용자는 각 트랜잭션의 커밋 모드를 선택할 수 있으므로, 동기 및 비동기 커밋 트랜잭션을 동시에 실행할 수 있습니다. 이를 통해 성능과 트랜잭션 내구성의 확실성 사이에서 유연한 트레이드오프를 허용합니다.

설정 방법

커밋 모드는 사용자 설정 가능한 파라미터 synchronous_commit 으로 제어됩니다.

  • 설정 가능한 방법: postgresql.conf, 세션 레벨, 트랜잭션 레벨 등
  • 특정 트랜잭션에만 비동기 커밋을 적용하려면:
    SET LOCAL synchronous_commit TO OFF;
    

관련 파라미터

synchronous_commit

트랜잭션 커밋 방식을 제어하는 핵심 파라미터입니다. 트랜잭션 커밋이 시작될 때의 synchronous_commit 값에 따라 해당 트랜잭션의 커밋 모드가 결정됩니다.

wal_writer_delay

WAL 라이터(WAL writer)가 기록되지 않은 WAL 레코드를 디스크에 플러시하는 주기(기본값: 200ms)를 제어합니다.

PostgreSQL은 WAL 레코드를 생성할 때 처음엔 공유 메모리의 WAL 버퍼에만 씁니다. 이것을 실제 디스크에 내려쓰는(flush) 작업을 전담하는 백그라운드 프로세스가 WAL writer이며, wal_writer_delay는 이 프로세스가 얼마나 자주 깨어나 flush를 수행할지를 결정합니다.

위험 구간이 3배인 이유

비동기 커밋을 사용할 경우, 위험 구간(risk window)의 최대 지속 시간은 wal_writer_delay3배입니다. WAL writer가 매 주기마다 무조건 flush하지 않고, 조건(flush 대상 데이터량, 마지막 flush 이후 경과 시간 등)을 충족해야만 실제 disk flush를 수행하도록 설계되어 있기 때문입니다.

[타임라인 예시 — wal_writer_delay = 200ms]

T=0ms   : 비동기 트랜잭션 커밋 → 클라이언트에 "성공" 응답 반환
          (WAL 레코드는 아직 메모리 버퍼에만 존재)

T=200ms : WAL writer 깨어남 → 조건 미충족으로 disk flush 건너뜀
          (OS 버퍼에만 write)

T=400ms : WAL writer 깨어남 → 또 조건 미충족으로 건너뜀

T=600ms : WAL writer 깨어남 → 조건 충족 → 실제 disk flush 실행
          (이 시점부터 비로소 데이터 손실 위험 해소)

즉 최악의 경우, 커밋 직후 크래시가 발생하면 최대 wal_writer_delay × 3 = 600ms 동안 커밋된 트랜잭션이 손실될 수 있습니다.

commit_delay

이름 때문에 비동기 커밋과 혼동되기 쉽지만, 실제로는 동기 커밋 방식입니다. 비동기 커밋 중에는 commit_delay가 무시됩니다.

핵심 아이디어: flush는 비싸다, 같이 쓰자

디스크 flush(fsync)는 비용이 큰 작업입니다. 트랜잭션마다 개별적으로 flush를 요청하면 낭비가 심해집니다. commit_delay는 flush 직전에 짧게 대기하여, 그 시간 동안 다른 트랜잭션들을 모아 단 한 번의 flush로 여러 트랜잭션을 한꺼번에 커밋하는 전략입니다.

[commit_delay 없을 때]

  트랜잭션 A → flush → 완료
  트랜잭션 B → flush → 완료   ← flush 3번 발생 (비용 × 3)
  트랜잭션 C → flush → 완료

[commit_delay 있을 때]

  트랜잭션 A ─┐
  트랜잭션 B ─┼─ (commit_delay 동안 대기) → 단일 flush → 모두 완료
  트랜잭션 C ─┘                              ← flush 1번 (비용 × 1/3)

commit_delay는 비동기 커밋과 달리 데이터 손실 위험이 없습니다. flush가 완료된 후에야 클라이언트에 성공을 반환하는 동기 커밋이기 때문입니다. 단지 flush 시점을 살짝 늦춰 "동승자"를 모으는 것뿐입니다.

참고로 commit_delay는 활성 트랜잭션 수가 commit_siblings 이상일 때만 발동합니다. 트랜잭션이 혼자뿐이라면 기다려봤자 의미가 없기 때문입니다.

wal_writer_delay vs commit_delay 비교

wal_writer_delay commit_delay
커밋 방식 비동기 커밋에서 유효 동기 커밋에서만 유효
데이터 손실 위험 있음 (위험 구간 존재) 없음
목적 WAL flush 주기 제어 여러 트랜잭션의 flush 비용 분산
대기 주체 WAL writer 백그라운드 루프 커밋 직전 명시적 대기

fsync와의 차이

비동기 커밋은 fsync = off와는 다른 동작을 합니다.

구분 비동기 커밋 (synchronous_commit = off) fsync = off
적용 범위 개별 트랜잭션 단위 서버 전체 설정
위험 데이터 손실 가능성 (손상 없음) 시스템 크래시 시 데이터 손상 가능성
성능 향상 상당한 수준 fsync off와 유사한 수준
  • fsync = off는 PostgreSQL 내의 디스크 동기화 시도를 모두 비활성화하므로, 하드웨어/OS 크래시 시 데이터베이스가 임의로 손상될 수 있습니다.
  • 비동기 커밋은 데이터 손상 위험 없이 fsync off에 근접한 성능 향상을 제공합니다.

주의 사항 (Caution)

Immediate-mode 종료는 서버 크래시와 동일하며, 플러시되지 않은 비동기 커밋의 손실을 초래합니다.

또한, 2단계 커밋(two-phase commit)을 지원하는 명령(예: PREPARE TRANSACTION)과 DROP TABLE 같은 특정 유틸리티 명령은 synchronous_commit 설정에 관계없이 항상 동기적으로 커밋됩니다. 이는 서버 파일 시스템과 데이터베이스 논리 상태 간의 일관성을 보장하기 위함입니다.


요약

항목 내용
목적 트랜잭션 처리량 향상 (특히 소규모 트랜잭션)
위험 크래시 시 최근 트랜잭션 일부 손실 가능
위험 없는 것 데이터 손상 (corruption)
핵심 파라미터 synchronous_commit
위험 구간 최대 시간 wal_writer_delay × 3
적합한 사용 사례 이벤트 로깅, 고빈도 소규모 트랜잭션 등 손실이 허용되는 경우
부적합한 사용 사례 금융 거래, 외부 동작이 수반되는 중요한 트랜잭션

참고: PostgreSQL 공식 문서 - Asynchronous Commit