WAL File, Data File에 이어서 Checkpoint에 대해서 알아보겠습니다.
Checkpoint는 그 목적과 별개로, 동작 과정 중 대량의 Data File I/O와 WAL File I/O를 동시에 발생시키므로 성능 부하와 I/O 관련 Wait Event 발생의 주요 원인이 되기도 합니다.
이번 글에서는 Checkpoint이 무엇인지를 먼저 살펴보고, 이어서 Checkpoint 동작 과정을 설명하겠습니다. 그 과정에서 Checkpoint 수행과 WAL File, Data File I/O의 연관 관계를 이해하고, 이를 바탕으로 I/O 부하를 분산시키기 위한 Checkpoint 최적화 방안을 살펴보겠습니다.
Checkpoint
Checkpoint는 Shared Buffer의 Dirty Page를 Data File에 Flush하고, 그 결과를 설명하는 Checkpoint WAL Record를 WAL에 Write&Flush 함으로써 WAL과 Data File 간의 일관성을 보장하는 기준 시점을 정의하는 매커니즘입니다.
이 기준 시점을 통해 PostgreSQL은 Crash Recovery나 Replication 과정에서 WAL Replay를 시작할 위치(LSN)를 명확하게 결정할 수 있습니다.
LSN(Log Sequence Number)
LSN은 WAL Record가 WAL에 기록된 순서를 나타내는 논리적 위치로, 각 Data Page에는 해당 Page에 마지막으로 적용된 WAL Record의 LSN이 저장되어 있습니다.
Checkpointer Process
Checkpointer Process는 주기적으로 Checkpoint를 수행하는 Background Process로, Shared Buffer에 존재하는 Dirty Page를 Data File에 Flush하고, 해당 시점을 설명하는 Checkpoint WAL Record를 WAL에 기록합니다.
이로 인해 Checkpoint 관련 I/O와 Wait Event는 Data File과 WAL File을 대상으로 발생하게 됩니다.

Checkpoint 동작 과정
Checkpoint는 단일 동작이 아닌, 여러 단계의 작업 흐름으로 구성됩니다.
이 작업은 기본적으로 Checkpointer Process가 수행하지만, 필요 시 Backend 또는 Startup Process가 직접 Checkpoint 작업을 수행할 수 있습니다.
- CHECKPOINT Command 실행 시 - Backend Process
- Crash Recovery 중 - Startup Process
1. Checkpoint 발생 조건
Checkpoint는 다음 조건 중 하나가 만족되면 Checkpoint를 수행합니다.
- max_wal_size 초과 - WAL 조건
- checkpoint_timeout 경과 - TIME 조건
- CHECKPOINT Command 수행
- Crash Recovery 중 Startup Process에 의한 강제 실행
- max_wal_size(Default, 1GB) : WAL이 확장될 수 있는 최대 크기입니다. 이 값 이상의 WAL Record가 누적되면 Checkpoint가 발생됩니다.
- checkpoint_timeout(Default, 300) : 자동으로 발생하는 Checkpoint 간 최대 시간 간격으로, Checkpoint 발생 주기를 결정합니다.
2. Checkpoint 시작 지점 고정
Checkpoint가 시작되면, 먼저 해당 시점의 LSN 정보를 고정(Redo LSN, Redo Point)합니다.
Redo LSN 이전의 변경사항은 반드시 Data File에 반영되어야 하며, 이 시점을 기준으로 어떤 Dirty Page를 Flush 해야 하는지가 결정됩니다.
3. Shared Buffer Dirty Page Flush
Checkpointer는 Shared Buffer 내 Dirty Page 중
page LSN ≤ redo LSN인 페이지를 대상으로 Data File에 Write+fsync(Flush)를 수행합니다.Checkpointer는 BG Writer와 같이 Shared Buffer의 Dirty Page를 Data File에 Write하는 역할을 수행하지만 fsync(Flush) 방식에 차이가 존재합니다. BG Writer와 달리 Write 즉시 fsync(Flush)를 수행하므로 비교적 큰 I/O 부하를 유발합니다.
4. Checkpoint WAL Record 기록
Dirty Page Flush가 완료되면, 현재 Checkpoint 상태를 설명하는 Checkpoint WAL Record가 생성됩니다.
이러한 과정을 거쳐 Checkpoint가 완료되면 redo LSN 이전의 변경 사항은 이미 Data File에 반영되어 있으며, 그 이후의 변경 사항은 WAL을 통해 안전하게 복구할 수 있습니다.
CHECKPOINT 동작 패턴
Checkpoint 동작은 관련 파라미터 설정에 따라 서로 다른 패턴을 보입니다.
max_wal_size와 checkpoint_timeout은 Checkpoint 발생 시점에 영향을 주는 설정이며, checkpoint_completion_target은 Checkpoint 수행 과정에서 발생하는 I/O 부하를 일정 시간에 걸쳐 분산하는 역할을 합니다.
먼저 max_wal_size와 checkpoint_timeout 설정에 따라 아래와 같이 동일한 Insert 작업을 실행했을 때 Checkpoint 발생 패턴을 확인해보겠습니다.
[CASE1] max_wal_size=3GB, checkpoint_timeout=30s(checkpoint_completion_target=0.9) [CASE2] max_wal_size=512MB, checkpoint_timeout=1800s(checkpoint_completion_target=0.9)
[CASE1] max_wal_size=3GB, checkpoint_timeout=30s
WAL 크기에 의한 Checkpoint 발생 영향을 최소화하기 위해 max_wal_size를 크게 설정하고, checkpoint_timeout을 짧게 조정한 후, PostgreSQL Log를 통해 Checkpoint 발생 이력을 확인한 결과입니다.
로그를 보면 모든 Checkpoint가
checkpoint starting: time으로 시작하는 것을 확인할 수 있습니다. 이는 Checkpoint가 WAL 크기 조건이 아닌, checkpoint_timeout을 초과해 발생한 것을 의미합니다.이러한 결과는, max_wal_size를
3GB로 크게 설정한 상황이었기에 WAL이 누적되어 임계값에 도달하기 전에 checkpoint_timeout으로 설정한 30초 주기가 먼저 만족되었기 때문입니다.즉, 이와 같은 설정에서는 Checkpoint가 주로 TIME 조건에 의해 발생되며, WAL 크기에 따른 Checkpoint 발생 영향은 거의 나타나지 않았습니다.
[CASE2] max_wal_size=512MB, checkpoint_timeout=1800s
이번에는 반대로 checkpoint_timeout을 크게 설정하고 max_wal_size를 작은 값으로 조정한 후, Checkpoint 발생 이력을 확인해보면 다음과 같은 결과를 확인할 수 있습니다.
CASE2에서는 Checkpoint가 모두
checkpoint starting: wal로 시작합니다. 이는 WAL 발생 누적량에 의해 Checkpoint가 발생했음을 의미합니다. checkpoint_timeout을 1800초(30분)로 크게 설정함으로써 TIME 조건의 영향은 최소화하였고, max_wal_size로 설정된 512MB 크기에 빠르게 WAL 누적량이 도달했기 때문입니다.CASE1과 CASE2를 비교해보면 max_wal_size와 checkpoint_timeout 설정에 따라 Checkpoint 발생 조건이 명확하게 분리되어 동작하는 패턴을 볼 수 있습니다.
max_wal_size를 크게 설정하면 TIME 조건이 Checkpoint 발생에 영향을 주고, 반대로 checkpoint_timeout을 크게 설정하면 WAL 조건이 Checkpoint 발생을 결정합니다.
이어서 Checkpoint 발생 시점은 고정한 상태에서 I/O 분산 패턴을 비교해보겠습니다.
[CASE3] checkpoint_completion_target=0.1(max_wal_size=3GB, checkpoint_timeout=90) [CASE4] checkpoint_completion_target=0.9(max_wal_size=3GB, checkpoint_timeout=90)
checkpoint_completion_target(Default, 0.9)
Checkpoint 동안 Dirty Page를 처리하는데 소요된 시간을 Checkpoint 간격 대비 비율로 지정합니다.
즉, checkpoint_timeout 경과로 인한 Checkpoint가 발생하면
checkpoint_timeout*checkpoint_completion_target으로 계산된 시간 내에 Dirty Page Flush가 완료되도록 작업을 수행합니다. 따라서 checkpoint_completion_target이 클수록 더 긴 시간을 사용하므로 I/O 부하가 분산됩니다.CASE3과 CASE4는 Checkpoint 발생 시점은 동일하게 유지한 채, checkpoint_completion_target 값에 따라 Checkpoint I/O가 어떻게 집중/분산되는지를 비교하기 위한 설정입니다.
Checkpoint 발생 시점에 영향을 주는 파라미터는 max_wal_size=
3GB, checkpoint_timeout=90 로 고정합니다. 이는 WAL 조건의 개입을 최소화하고, TIME 조건에 의한 Checkpoint 발생을 일정하게 유지하기 위함입니다.[CASE3] checkpoint_completion_target=0.1
checkpoint_completion_target =
0.1로 설정한 경우, 짧은 시간(checkpoint_timeout*checkpoint_completion_target=90s*0.1=9s) 안에 집중적으로 수행되면서 Dirty Page Flush가 한꺼번에 발생하는 패턴을 확인할 수 있습니다.위 로그에서
checkpoint complete: 라인에 기록된 내용 중에서 write와 sync 항목은 각각 Write와 Flush 작업에 소요된 시간을 의미합니다.Time | write (s) | sync (s) | total (s) |
11:36:35.032 KST | 8.207 | 0.004 | 8.220 |
11:38:05.173 KST | 8.839 | 0.039 | 8.932 |
11:39:35.126 KST | 8.321 | 0.023 | 8.368 |
11:41:05.105 KST | 8.726 | 0.058 | 8.802 |
11:42:35.131 KST | 8.374 | 0.060 | 8.452 |
모든 Checkpoint에서 write+sync 시간이 약 8~9초 수준으로 일정하게 나타나며, 짧은 시간에 수행되는 패턴을 보입니다. 순간적으로 Checkpoint I/O가 집중되는 형태임을 확인할 수 있습니다.
[CASE4] checkpoint_completion_target=0.9
checkpoint_completion_target = 0.9로 설정한 경우, Checkpoint 작업이 짧은 시간에 집중되지 않고 (
checkpoint_timeout*checkpoint_completion_target=90s*0.9=81s) 다음 Checkpoint까지 분산되는 패턴을 보입니다.이번에도
checkpoint complete: 라인에 기록된 write와 sync 항목을 아래와 같이 정리해서 살펴보겠습니다.Time | write (s) | sync (s) | total (s) |
11:16:47.112 KST | 80.984 | 0.007 | 81.057 |
11:17:56.664 KST | 60.370 | 0.164 | 60.543 |
11:19:47.146 KST | 80.313 | 0.076 | 80.411 |
11:21:17.116 KST | 80.143 | 0.045 | 80.209 |
11:22:47.137 KST | 80.770 | 0.089 | 80.879 |
11:24:17.148 KST | 80.794 | 0.055 | 80.870 |
11:25:47.080 KST | 80.575 | 0.003 | 80.617 |
대부분의 Checkpoint에서 write+sync 시간이 약 81초 내외로 길게 유지되며, 짧은 시간에 몰리지 않고 긴 시간에 걸쳐 분산되어 수행되는 만큼 특정 시점에 몰리지 않는 형태를 확인할 수 있습니다.
마무리
- Checkpointer Process는 주기적으로 Checkpoint를 수행하여 Shared Buffer의 Dirty Page를 Data File에 Flush하고, Checkpoint WAL Record를 WAL에 기록한다.
- max_wal_size와 checkpoint_timeout 설정에 따라 Checkpoint 발생의 기준이 WAL 조건 또는 TIME 조건으로 달라지며, 이는 PostgreSQL 로그를 통해 명확히 확인할 수 있다.
- checkpoint_completion_target 값은 Checkpoint 발생 시점에는 영향을 주지 않지만, Checkpoint 수행 과정에서 발생하는 I/O 작업을 다음 Checkpoint까지 어느 정도 분산할 지를 조절하는 역할을 한다.
- Checkpoint 수행 중 발생하는 I/O와 Wait Event는 Data File과 WAL File을 대상으로 한 Write와 Flush 작업과 직접적으로 연관된다.
함께 보면 좋은 아티클
