이전 글에서 Replication 동작 원리와 동기화 제어 방식에 대해서 알아보았습니다. 이번에는 Replication 구성 환경에서 발생할 수 있는 Wait Event에 대해서 살펴보겠습니다.
Replication은 WAL을 기반으로 동작하고 I/O 성능에 크게 좌우되기 때문에, WAL 및 I/O 관련 Wait Event 역시 매우 밀접한 관계에 있습니다. 하지만 WAL 및 I/O 관련 Wait Event는 앞선 PWI 시리즈에서 이미 다루었기에, 본 문서에서는 Replication 환경에서만 발생하는 IPC:SyncRep, Client:WalSenderWaitForWal / Client:WalSenderWriteDataWait,IPC:RecoveryConflictSnapshot에 대해 중점적으로 살펴보겠습니다.
IPC:SyncRep
Main Server에서 발생하는 Wait Event로, 동기화를 위해 Standby Server로 부터 응답을 기다리는 동안 발생하는 Wait Event입니다.
Streaming Replication이 Synchronous 방식으로 구성되었을 때 주로 발생하며 동기화 수준을 제어하는 synchronous_commit 과 synchronous_standby_names 파라미터 설정에 따라 Wait Event 발생 경향이 달라질 수 있습니다.
주요 발생 원인
Standby Server의 WAL 처리 속도가 저하될수록 동기화에 더 오랜 시간이 소요되어 Wait Event 발생이 증가합니다. 따라서 Standby Server에서의 WAL 수신 및 처리 성능과 관련된 부분들이 중요한 요소가 됩니다.
- Main↔Standby 간 네트워크 연결이 지연되거나 불안정하면 Main Server의 WAL 발생량과 상관없이 동기화가 지연될 수 있습니다. 이처럼 네트워크로 인한 응답 지연이 해당 Wait Event 발생을 증가시킬 수 있습니다.
- Standby Server의 리소스 부족이나 I/O 성능이 저하되는 경우에도 WAL이 빠르게 처리되지 못해 동기화 작업에 부하가 생기게 됩니다.
- 성능이나 사용 목적이 고려되지 않은 채로 지나치게 높은 동기화 수준을 설정하면 이로 인해 불필요한 Wait Event 부하가 발생할 수 있습니다.
해결 방안
- synchronous_commit 및 synchronous_standby_names를 통해 동기화 대상 및 수준을 최적화하여 부하를 줄일 수 있습니다. 빠른 응답이 가능한 Server를 주요 동기화 대상으로 선정하고 중요도가 낮은 Server는 동기화 수준을 비교적 낮게 설정함으로써 Wait Event 발생을 줄일 수 있습니다.
- 네트워크나 디스크 성능 개선과 같은 Replication 구성 환경을 점검하여 Wait Event 발생을 줄일 수 있습니다.
- Main↔Standby 간 네트워크 상태를 모니터링하여 지연 및 패킷 손실이 발생하는지 확인합니다.
- SSD를 사용하거나 WAL 전용 디스크 할당을 통해 Standby Server의 I/O 성능을 개선하면 Wait Event에 의한 성능 저하를 해결할 수 있습니다.
Client:WalSenderWaitForWal / Client:WalSenderWriteData
두 Wait Event는 모두 Main Server에서 발생하는 Wait Event로 WAL Sender 프로세스가 WAL 전송 처리 과정에서 대기할 때 발생합니다.
Client:WalSenderWaitForWal은 Main Server에서 새로운 WAL이 생성되기를 대기할 때, Client:WalSenderWriteData는 Standby Server에 있는 WAL Receiver의 응답을 대기할 때 발생합니다.
이 Wait Event들은 Streaming Replication 보다는 Logical Replication에서 비교적 자주 발생합니다.
그 이유는 Streaming Replication에서는 WAL Sender가 WAL을 전송하는 역할만 수행하는 반면, Logical Replication에서는 WAL Sender가 WAL Decoding까지 수행하기 때문입니다. 그로 인해 Logical Replication에서는 WAL Sender 프로세스 단계에 머무는 시간이 길어 Wait Event가 더 자주 관찰됩니다.
주요 발생 원인
설명한 두 가지 Wait Event는 WAL 생성 및 전송 과정에서 자연스럽게 발생하기도 하지만 WAL 처리가 지연되었을 때에도 발생합니다.
- 트랜잭션 수행이 적어 새로운 WAL이 거의 생성되지 않는 경우 Client:WalSenderWaitForWal이 발생할 수 있습니다. (정상적인 Idle 상태)
- WAL Sender는 Main Server에서 Flush까지 완료된 WAL만을 전송할 수 있습니다. 따라서 Main Server에서 WAL Write 또는 Flush가 지연되면 WAL Sender가 전송 가능한 WAL을 확인할 수 없으므로 Client:WalSenderWaitForWal이 발생하게 됩니다.
- Main↔Standby 간 네트워크가 불안정하거나 Standby Server의 일시적인 리소스 병목 또는 장애가 발생하면 WAL 전송이 지연되어 Client:WalSenderWriteData가 발생합니다.
- Standby Server에서 Long-running Query가 실행 중이면 WAL 적용이 지연되어 WAL Receiver가 신호를 빠르게 반환할 수 없습니다. 따라서 Long Query가 실행되는 동안 Main Server의 WAL Sender는 Client:WalSenderWriteData를 대기하게 됩니다.
해결 방안
두 Wait Event는 주로 함께 발생하지만, Client:WalSenderWaitForWal만 단독으로 발생하기도 합니다. 해당 Wait Event들의 주요 해결방안은 다음과 같습니다.
- Main Server의 WAL Write 및 Flush 지연을 해소하여 Wait Event 발생을 줄일 수 있습니다.
- wal_writer_delay (Default:
200ms) 값을 조정하여 WAL Writer의 동작 주기를 최적화합니다. - 디스크 I/O 병목을 점검하고 필요시 WAL 전용 디스크를 구성합니다.
- wal_buffers를 증가시켜 WAL Flush 효율을 개선합니다.
지연 요소를 해소하면 전송 가능한 WAL을 WAL Sender가 쉽게 확보하여 대기 없이 전송할 수 있습니다. 이를 위해 다음과 같은 방법을 고려해볼 수 있습니다.
- Main↔Standby 간 WAL 전송 지연 여부를 확인하여 문제를 진단할 수 있습니다.
다음과 같이 pg_stat_replication 뷰를 조회하면 문제 발생 구간을 파악할 수 있습니다.
- sent_lsn과 현재 LSN(
pg_current_wal_lsn())의 차이가 크면 Main Server에서 WAL 전송이 지연되고 있는 것입니다. sent_lsn은 WAL 전송 성공 후 갱신되므로 전송 이전 단계에서 문제가 생기면 갱신되지 않습니다.
이 경우 Main Server의 네트워크 사용률과 패킷 전송률 혹은 CPU, 메모리 부족 등 리소스 상태를 확인하여 문제 원인을 유추해볼 수 있습니다.
IPC:RecoveryConflictSnapshot
해당 Wait Event는 Standby Server에서만 발생하며, Startup 프로세스가 WAL을 수신하여 Replay 하는 도중에 충돌(Conflict)이 발생하여 해소되기를 대기할 때 발생합니다.
주요 발생 원인
이 Wait Event는 주로 Hot Standby(hot_standby=
on)로 구성된 환경에서 Main Server의 Vacuum 작업이 Standby Server의 작업과 충돌할 때 발생합니다.- Standby Server에서 Long-running Query가 실행되는 동안 Main Server에서 동일 테이블에 대해 Vacuum을 수행하면 충돌이 발생합니다. Query가 참조하는 Tuple을 Vacuum이 제거하려고 하면서 Conflict가 발생하게 됩니다.
특히 Main Server에서 Autovacuum이 자주 실행되면 Conflict 발생 빈도가 더 높아져 Wait Event가 증가할 수 있습니다.
- Hot Standby 관련 파라미터 설정에 따라 Conflict 발생 빈도와 Query 취소 여부가 달라지므로 부적절한 설정은 Wait Event 발생에 영향을 미칩니다.
파라미터 | Default | 설명 |
hot_standby | on | Standby Server에서 Query 실행 가능 여부 |
hot_standby_feedback | off | Standby Query 정보를 Main Server로 전송 여부 |
max_standby_streaming_delay | 30000ms | Streaming WAL 처리 중 Snapshot 충돌 대기 시간 |
max_standby_archiving_delay | 30000ms | Archiving WAL 처리 중 Snapshot 충돌 대기 시간 |
해결 방안
Hot Standby 환경의 특성과 운영 시 우선순위를 고려하여 다음과 같은 방법으로 해결할 수 있습니다.
- Main Server의 Vacuum 작업을 조정하거나 Standby Server의 Query 패턴 및 설정을 조정하여 Conflict 발생을 줄일 수 있습니다.
- Autovacuum 설정을 점검하여 실행 빈도나 기준치를 완화하거나 부하가 낮은 시간대에 Manual Vacuum을 수행합니다. 혹은 테이블별로 Autovacuum 트리거 조건을 달리하여 불필요한 Vacuum 실행을 줄입니다.
- Standby Server에서 실행 중인 Query를 주기적으로 모니터링하여 Scan 범위를 줄이거나 Query Timeout을 설정하여 Long-running Query로 인한 Conflict 빈도를 감소시킵니다.
- Hot Standby 관련 파라미터를 조정하여 운영 환경에 맞게 최적화할 수 있습니다.
- Standby Server의 Query 수행이 중요한 경우 hot_standby_feedback=
on으로 설정합니다. - Main Server의 Vacuum 수행이 중요한 경우 hot_standby_feedback=
off로 설정합니다. - hot_standby_feedback=
off로 설정하면서 Standby Server 작업도 일정 수준 보장하고 싶다면 max_standby_streaming_delay와 max_standby_archive_delay 값을 조정해볼 수 있습니다.
해당 설정에서는 Standby Server의 Query 수행 정보가 Main Server로 전송됩니다. 단, 신호를 수신한 Main Server는 Query 대상 데이터에 대한 조작을 할 수 없습니다. 따라서 Standby Server의 Query는 Conflict에 의한 중단 없이 실행될 수 있습니다.
이 경우, Standby Server의 작업과 무관하게 Main Server의 Vacuum 작업이 정상 동작 가능합니다.하지만 Standby Server에서는 Conflict 발생에 따른 작업 중단이 발생할 수 있으며 Wait Event 발생 빈도가 늘어날 수 있습니다.
hot_standby_feedback=
on 설정 시 Main Server에서의 Vacuum 성능이 저하될 수 있습니다.설정 시 Standby Server의 Query 수행을 보장하지만 Main Server에서 xmin horizon 갱신이 지연됩니다. xmin horizon은 Vacuum이 정리할 수 있는 가장 오래된 트랜잭션 ID를 의미하므로 이 값이 갱신되지 않으면 많은 양의 Dead Tuple이 정리되지 못하고 누적됩니다.
해당 설정 값을 늘리면 설정된 시간만큼 Standby 작업이 종료되기를 대기하여, 해당 시간 내에 완료되는 작업은 보장할 수 있습니다.
CASE STUDY
테스트를 통해 Replication 관련 파라미터 설정에 따른 Wait Event 발생 내역을 확인해보겠습니다.
각 Case에서 Replication을 위한 Main Server 설정은 다음과 같습니다.
CASE | Replication 구성 방식 | Replication 파라미터 설정(Main Server) |
1 | Streaming Replication | synchronous_standby_names= standby1 |
2 | Streaming Replication | synchronous_standby_names = standby1
hot_standby=on |
- CASE1에서는 synchronous_standby_names=
standby1로 설정되어 동기화 동작을 수행할 대상 Server가 명시되었습니다. 따라서 synchronous_commit 설정에 따라 다양한 방식의 동기화 제어가 가능합니다
- CASE2에서는 hot_standby=
on으로 설정되어 Standby Server에서의 읽기 작업이 가능합니다.
[CASE1] synchronous_commit 조정
Main Server에서 synchronous_commit 값을
on과 local로 변경하며 IPC:SyncRep 발생 빈도를 비교해보겠습니다. (synchronous_commit = on설정과의 비교 대상으로 off가 아닌 local을 선정한 것은 최소한의 WAL Flush는 보장하기 위해서 입니다.)- synchronous_commit=
on: Standby Server에서의 WAL Flush 완료를 대기한 후 Main Server에서 Commit
- synchronous_commit=
local: Main Server에서의 WAL Flush가 완료될 때까지만 대기 후 Commit
위와 같이 10,000건의 데이터에 대한 Insert/Commit Command를 반복 수행하고 pg_wait_sampling_profile을 활용하여 Wait Event 통계 정보를 확인해보겠습니다.
1) synchronous_commit=
local- IO:WALWrite와 IO:WALSync Wait Event가 다량 발생하였는데, 이는 WAL 생성 작업 중 WAL Write 및 Flush에 부하가 생겼음을 의미합니다.
- IPC:SyncRep은 발생하지 않습니다. synchronous_commit =
local로 설정하였기 때문에 Commit 시 Standby Server의 응답을 대기하지 않았음을 의미합니다.
2) synchronous_commit=
on- IO:WALWrite와 IO:WALSync가 발생했으며 그 수치가 이전 결과와 유사합니다.
즉, WAL 생성량은 synchronous_commit 설정과 무관함을 유추할 수 있습니다.
- IPC:SyncRep이
15888건으로 다량 발생했습니다.
synchronous_commit =
on으로 설정하면 매번 Commit을 수행할 때마다 Standby Server의 응답 (WAL Flush 완료까지 대기)을 대기해야 하므로, 해당 대기 상황이 Wait Event로 관측되었습니다.테스트 결과, 오직 synchronous_commit을
on으로 설정했을 때에만 IPC:SyncRep이 발생합니다.별도로 해당 과정의 소요시간을 측정한 결과,synchronous_commit=
local에서는 840초였지만 synchronous_commit=on에서는 1,137초로 크게 증가했습니다. 이는 synchronous_commit=on 인 경우, 동기화 수준이 더 높게 설정되어 Commit을 완료하기까지 더 많은 시간이 소요되었기 때문입니다.테스트에서 확인한 IPC:SyncRep은 파라미터 설정을
on 이 아닌 Standby Server와의 동기화를 필요로 하는 다른 설정(remote_write, remote_apply)을 적용했을 때에도 발생할 수 있습니다. 따라서 Wait Event 발생을 줄이기 위해서는 synchronous_commit 설정을 최적화하는 것이 중요합니다.Main Server에서의 성능을 최우선으로 한다면 synchronous_commit을
local 또는 off, Replication 안정성을 더 중요시 한다면 on 또는 remote_write, remote_apply로 설정할 수 있습니다.[CASE2] hot_standby_feedback 조정
Standby Server를 Hot Standby로 구성한 후, hot_standby_feedback 값을 변경하며 IPC:RecoveryConflictSnapshot 발생을 비교해보겠습니다.
- hot_standby_feedback=
off, max_standby_streaming_delay=300s
- hot_standby_feedback=
on
위와 같이 Main Server에서는 50,000건의 데이터에 대한 Update/Commit Command를 반복 수행하고 Standby Server에서는 동일한 테이블에 대해 Long Query를 수행합니다.
테스트 결과는 pg_wait_sampling_profile과 Replication 관련 다양한 모니터링 방법을 통해 확인해보겠습니다.
1) hot_standby_feedback=
off, max_standby_streaming_delay=300sㅤ | Main (Update/Commit) | Standby (Long Query) |
소요 시간 | 1,011초 | 300초 후 Cancel |
Main Server에서 Update/Commit Command 는 1,011초 소요되었고 Standby Server에서 Long Query는 300초간 수행되다가 실행이 취소되었습니다. Long Query가 300초 동안만 수행된 이유는 max_standby_streaming_delay 설정으로 인해 지정된 시간(300초)만큼 Conflict 상황 해소를 대기했기 때문입니다.
Standby Server에서 pg_wait_sampling_profile을 조회하여 Wait Event 발생량을 확인합니다.
- IPC:RecoveryConflictSnapshot이 매우 높게 발생했습니다. Standby Server에서 Long Query 수행 중 (Recovery) Conflict가 빈번하게 발생했음을 의미합니다.
Conflict의 발생은 Standby Server에서 PostgreSQL Log를 통해서도 확인할 수 있습니다. 아래와 같이 Cancel된 Query 정보 및 시점 정보를 확인 가능합니다.
Main Server에서 pg_stat_replication을 조회하면 Conflict 발생에 따른 Replication Lag 변화를 확인할 수 있습니다.
- 테스트에서 replay_lag는 최대
00:04:00.818363까지 증가합니다.
- 이는 max_standby_streaming_delay에 설정한 값(300s)과 동일하며, Long Query가 중단되기까지 약 300초 간 WAL 적용이 불가능한, Replication 지연 상태가 발생했음을 나타냅니다.
2) hot_standby_feedback=
onㅤ | Main (Update/Commit) | Standby (Long Query) |
소요 시간 | 1,016초 | 2,113초 (정상 수행 완료) |
Main Server의 작업 소요 시간은 1,016초로 이전과 유사하며, Standby Server에서는 Long Query가 중단되지 않고 2,113초 동안 실행 후 완료되었습니다.
- hot_standby_feedback 설정이 Main Server의 작업에
는영향을 미치지 않기 때문에 작업 소요시간이 유사하게 확인됩니다.
- Standby Server에서는 hot_standby_feedback =
on설정으로 인해 Long Query 관련 정보를 Main Server로 전송합니다. Main Server는 해당 정보를 이용해 Conflict 상황을 미연에 방지하므로, Query가 중단되지 않고 완료됩니다.
Standby Server에서 pg_wait_sampling_profile 조회 결과를 확인합니다.
- IPC:RecoveryConflictSnapshot이 발생하지 않았습니다.
Long Query를 수행하는 동안 어떠한 Conflict도 발생하지 않았음을 의미합니다.
- IO:WALSync, IO:BufFileRead, Client:ClientRead 발생이 증가했습니다.
Long Query가 취소되지 않고 완료되었기 때문에 Query 실행과 관련된 Wait Event가 증가하였습니다.
Main Server에서 pg_stat_replication을 조회하여 Replication Lag를 확인합니다.
- replay_lag는 최대
00:00:00.004312로 기록되며 매우 작은 수준으로 유지됩니다.
Main Server에서 Long Query 정보를 수신한 후, 해당 Query가 참조하는 데이터에 대한 조작은 제한(지연)됩니다. 따라서 Main↔Standby 간 특별한 제약 없이 정상적으로 동기화가 수행되며 Replication Lag가 낮게 유지됩니다.
pg_stat_all_tables를 조회하여 Vacuum 진행 상황을 확인할 수도 있습니다.
다음은 각 CASE에서 Vacuum에 의해 처리되지 않은 Dead Tuple 양을 의미하는 n_dead_tup의 최댓값을 조회한 결과입니다.
- hot_standby_feedback=
off, max_standby_streaming_delay=300s
- hot_standby_feedback=
on
hot_standby_feedback=
on으로 설정한 경우, n_dead_tup이 더 크게 나타납니다.이는 Long Query 신호 수신으로 인해 대상 데이터에 대한 Vacuum이 지연되어 정리되지 못한 Dead Tuple이 증가했다는 것을 의미합니다.
hot_standby_feedback=
on으로 설정하면 Standby Server의 Long Query가 취소되지 않고 안정적으로 수행가능하다는 장점이 있습니다. 하지만 이로 인해 Main Server에서 xmin horizon 갱신이 지연되어 Vacuum 성능이 저하될 수 있습니다.반면
off로 설정하면 Standby Server에서 실행 중인 Query가 Conflict로 인해 취소될 위험이 높아집니다.또한, off 상황에서 max_standby_streaming_delay를 너무 크게 설정하면 Query는 일부 보장되지만 Replication 지연이 누적될 수 있고, 너무 짧게 설정하면 Query가 빈번하게 취소될 수 있습니다.결국 운영 환경의 우선순위에 따라 파라미터를 조정해야 합니다. Standby Server를 주로 조회 용도로 사용하여 읽기 작업의 중요도가 높다면 hot_standby_feedback=
on으로 설정하는 것이 적합합니다. 반대로 Main Server의 성능 유지와 Vacuum 성능이 더 중요하다면 hot_standby_feedback=off를 유지하고 max_standby_streaming_delay 를 적절히 제한하여 필요시 Query를 취소하도록 구성하는 것이 바람직합니다.마무리
- IPC:SyncRep은 Main Server에서 Standby Server의 동기화 응답을 대기할 때 발생하며 synchronous_commit 설정과 Standby Server의 WAL 처리 성능과 관련된다. synchronous_commit을
local또는off로 설정하면 Standby 응답 대기 없이 Commit이 완료되어 Wait Event가 발생하지 않지만,on또는remote_write,remote_apply로 설정하면 Standby의 응답을 기다려야 하므로 관련 Wait Event 발생이 증가한다.
- Client:WalSenderWaitForWal과 Client:WalSenderWriteData는 Main Server의 WAL Sender 프로세스가 각각 새로운 WAL 생성과 WAL Receiver의 응답을 대기할 때 발생한다.
- IPC:RecoveryConflictSnapshot은 Main Server의 작업과 Standby Server의 WAL 적용이 충돌할 때 주로 발생한다. hot_standby_feedback 설정에 따라 Conflict 발생 빈도와 처리 방식이 달라지며, 이는 Query의 안정성과 Main Server의 Vacuum 성능 사이의 Trade-off 관계를 고려하여 조정해야 한다.
- Replication 관련 Wait Event는 PostgreSQL에서 제공되는 다양한 뷰를 활용하여 종합적으로 분석하는 것이 중요하다.
함께 보면 좋은 아티클
