1. PostgreSQL 성능이 갑자기 느려졌다면?
PostgreSQL을 운영하다 보면 어느 순간부터 쿼리 응답이 느려지거나, 특정 API가 지연되면서 전체 서비스 성능이 저하되는 상황을 겪게 됩니다. 문제는 대부분의 경우, 단순히 느려졌다는 현상은 인지하지만 정확한 원인을 빠르게 특정하지 못한다는 점입니다. 특히 최근에는 MSA 기반 아키텍처, 멀티 클라우드 환경, 다양한 애플리케이션 계층이 함께 쓰이면서 상황이 더 복잡해졌습니다. 성능 저하의 원인이 DB 내부인지, 애플리케이션인지, 인프라인지 구분하는 것 자체가 어려워진 것입니다.
이는 단순히 운영 경험의 부족 때문이 아닙니다. PostgreSQL 공식 문서 역시 쿼리 성능은 다양한 시스템 요소의 영향을 받을 수 있다고 설명합니다. 실제 운영 환경에서는 Planner 통계, WAL, VACUUM, I/O와 같은 요소들이 복합적으로 작용하며 성능 저하로 이어지는 경우가 많기 때문이죠.
이제는 단순히 ‘어떤 쿼리가 느린가’보다, ‘왜 특정 시점부터 전체 시스템이 느려졌는가’를 분석하는 역량이 중요해지고 있습니다. 이번 글에서는 PostgreSQL 성능 저하 발생 시 DBA가 반드시 점검해야 할 5가지 핵심 영역을 정리합니다.

1-1. 슬로우 쿼리 및 실행 계획
가장 먼저 확인해야 할 영역은 어떤 쿼리에서 지연이 발생하고 있는지입니다. 성능 저하가 관찰되면, 우선 쿼리 자체의 성능 문제를 의심하고 해당 쿼리를 식별하는 것부터 시작해야 합니다. PostgreSQL에서는 다음과 같은 징후로 슬로우쿼리를 확인할 수 있습니다.
- 특정 쿼리의 실행 시간이 점진적으로 또는 갑자기 증가하는 경우
- 동일 쿼리가 대량 레코드를 대상으로 반복 실행되는 N+1 패턴이 나타나는 경우
- 실행 계획이 Index Scan에서 Seq Scan 등으로 비효율적으로 변경된 경우
- 불필요하게 큰 데이터 집합에 대한 Join 또는 Sort가 반복해서 발생하는 경우
특히
EXPLAIN ANALYZE 결과에서 아래 항목이 확인되면, 성능 저하의 직접적인 원인일 가능성이 높습니다. - 예상 비용 대비 실제 실행 시간이 과도하게 큰 경우
- Index Scan 대신 Seq Scan(테이블 전체 스캔)이 반복적으로 발생하는 경우
실무에서는 어떤 쿼리가 느린지보다 왜 특정 시점부터 느려졌는지를 찾는 것이 훨씬 어렵습니다.
특정 배포 이후인지, 트래픽 증가 시점인지, 특정 시간대에만 발생하는지 등과 같은 맥락까지 함께 살펴봐야 하기 때문입니다. 또한 통계 정보(Statistics)가 실제 데이터 분포를 제대로 반영하지 못하면, 옵티마이저가 비효율적인 실행 계획을 선택하는 경우도 빈번합니다. 이 경우 동일 쿼리라도 특정 시점부터 급격히 느려질 수 있기 때문에, 실행 계획의 변화 이력까지 함께 추적하는 접근이 필요합니다.
1-2. 락(Lock) 및 트랜잭션 병목
PostgreSQL 성능 저하의 대표적인 원인 중 하나는 Lock 경합입니다. Lock이 적절히 해소되지 않으면 단일 트랜잭션 지연이 전체 서비스 지연으로 확산될 수 있으므로, 다음과 같은 상황을 우선적으로 점검해야 합니다.
- 장시간 실행 중인 트랜잭션
- 특정 테이블에 대한 Lock 대기 증가
- Deadlock 발생 여부
운영 환경에서는 하나의 트랜잭션이 Lock을 장시간 점유하면, 해당 리소스를 대기하는 쿼리들이 연쇄적으로 지연되어 서비스 전체에 영향을 줄 수 있습니다.
PostgreSQL에서는 현재 시점의 Lock 상태를 다음과 같은 뷰로 확인할 수 있습니다.
다만 이런 뷰들은 현재 상태만 보여줄 뿐, 과거에 어떤 Lock이 있었는지 추적하기는 어렵다는 한계가 있습니다. 그리고 단순히 'Lock이 존재하는가' 보다 더 중요한 것은 Blocking 관계입니다. 어떤 세션이 어떤 Lock을 점유하고 있고, 그로 인해 어떤 쿼리들이 대기 중인지까지 파악해야 실제 병목 지점을 정확히 찾을 수 있습니다.
따라서 장기적인 Lock 분석을 위해서는 별도의 로깅 정책이나 모니터링 솔루션을 통해 Lock 이력과 Blocking 관계를 함께 보존하는 접근이 필요합니다.
1-3. 커넥션 및 세션 상태
DB 성능 저하 상황에서 간과하기 쉬운 부분이 Connection 관리입니다. 커넥션 수가 임계치에 근접하거나 세션이 비정상적으로 누적되면 쿼리 성능과 무관하게 응답 지연이 발생할 수 있으며, 다음 항목을 우선적으로 점검합니다.
- max_connections 대비 현재 세션 수
- 커넥션 풀 사용 여부 (pgBouncer 등)
- idle session 과다 여부
- 커넥션 누수(Connection leak) 여부
커넥션이 임계치에 도달하면 신규 요청이 대기 상태로 전환되어 응답 지연이 발생하고, 장애로 이어질 수 있습니다. 특히 MSA 환경에서는 서비스 수 증가와 함께 예상보다 빠르게 커넥션이 고갈되는 패턴이 자주 나타납니다.
또한 커넥션 반환 지연이나 불필요하게 긴 트랜잭션 유지와 같이 애플리케이션 레벨에서 커넥션을 비효율적으로 사용하는 경우도 자주 발견되는 원인입니다. 따라서 단순 DB 지표만 보는 것을 넘어, 애플리케이션의 커넥션 사용 패턴까지 함께 살펴봐야 합니다.
1-4. I/O 및 스토리지 병목
쿼리와 인덱스에 문제가 없는데도 성능이 느리다면, I/O 및 스토리지 레벨의 병목을 의심해야 합니다. PostgreSQL의 성능은 디스크 I/O에 크게 의존하기 때문에, 다음 지표를 우선적으로 확인합니다.
- Disk I/O 대기 시간
- Checkpoint 발생 빈도
- WAL(Write-Ahead Log) 쓰기 지연
- Checkpoint 주기가 부적절하면 write burst가 발생합니다.
Checkpoint 간격이 너무 길면 한 번에 많은 양의 dirty buffer를 몰아 쓰면서 I/O 스파이크가 일어나고, 반대로 너무 잦으면 WAL 생성량이 폭증해 디스크 부하가 가중됩니다.
- WAL flush가 지연되면 트랜잭션 commit이 지연됩니다.
PostgreSQL은 commit 시 WAL을 디스크에 안전하게 기록한 후에야 응답을 반환하기 때문에, WAL 디스크의 쓰기 속도가 사실상 모든 쓰기 트랜잭션의 상한선을 결정합니다.
이런 증상은 DB 내부 문제가 아니라 인프라 레벨 병목일 가능성이 높습니다.
클라우드 환경에서는 스토리지 등급과 IOPS·throughput 제한에 따라 성능 차이가 큽니다. 예를 들어 AWS EBS의 경우, 스토리지 타입(gp3, io2 등)에 따라 허용 IOPS가 다르며, 일부 타입은 일정 크레딧이 소진되면 성능이 급격히 저하되는 구조입니다. 이는 운영 중 원인을 특정하기 어려운 함정 중 하나입니다. 따라서 지표만 볼 것이 아니라, 현재 워크로드가 인프라 한계에 닿았는지도 함께 살펴야 합니다.
1-5. VACUUM/AUTOVACUUM 상태
I/O 지표까지 정상인데도 시간이 지날수록 쿼리가 느려진다면, VACUUM 상태를 점검해야 합니다. PostgreSQL은 MVCC(Multi-Version Concurrency Control) 구조 특성상, 삭제·수정된 데이터가 즉시 제거되지 않고 Dead Tuple로 남습니다. 이를 정리하는 것이 VACUUM이며, 관리가 제대로 이루어지지 않으면 성능 저하로 직결됩니다. 다음 항목을 우선적으로 점검합니다.
- Dead Tuple 증가 여부
- AUTOVACUUM 지연 또는 미실행
- Table/Index bloat 증가
이 상태가 지속되면 인덱스 효율이 떨어지고, 같은 데이터를 읽는 데도 더 많은 페이지를 스캔하게 되면서 쿼리 성능이 전반적으로 저하됩니다. 특히 트래픽이 많은 테이블일수록 VACUUM 정책이 성능에 직접적인 영향을 미칩니다. 중요한 것은 AUTOVACUUM이 단순히 실행되는지 여부가 아닙니다. 현재 워크로드에 맞는 시점과 속도로 동작하고 있는가가 핵심입니다. 설정 값이 실제 환경에 맞지 않으면 VACUUM 수행이 지연되고, 누적된 bloat가 장기적인 성능 저하의 원인이 됩니다.
또한, AUTOVACUUM이 장기간 정상 동작하지 않으면 트랜잭션 ID 한계에 도달해 DB가 멈출 수 있는 상황(wraparound)까지 발생할 수 있으므로 성능뿐 아니라 안정성 측면에서도 중요한 항목입니다.
2. 한눈에 보는 PostgreSQL 성능 저하 점검 체크리스트
지금까지 살펴본 다섯 가지 점검 영역을 표로 정리하면 다음과 같습니다.
점검 항목 | 확인 사항 | 주요 증상 | 우선 대응 방법 |
슬로우 쿼리/실행 계획 | 실행 시간 증가, Seq Scan 발생, 실행 계획 변경 | 특정 기능만 느려짐. 응답 시간 급증 | EXPLAIN ANALYZE 확인, 인덱스 점검, 통계 갱신 |
Lock/트랜잭션 | Lock 대기, idle in transcation, Deadlock | 전체 서비스 지연. 특정 API 정체 | Blocking 세션 확인, 장기 트렌잭션 종료 |
커넥션/세션 | max_connections 근접, idle session 증가, 커넥션 누수 | 요청 대기, 간헐적 장애, 신규 접속 거부 | 커넥션 풀 적용, 세션 관리 |
I/O, 스토리지 | iowait 증가, WAL 지연, Checkpoint 주기 부적절 | 전반적인 성능 저하 | 디스크 성능 확인, 스토리지 등급·IOPS 점검 |
VACUUM/AUTOVACUUM | Dead Tuple 증가, bloat 발생 | 점진적인 성능 저하, 심각 시 DB 정지(wraparound) | AUTOVACUUM 설정 조정, 수동 VACUUM 수행 |
이러한 점검 항목들은 각각 독립적으로 보이지만, 실제 장애 상황에서는 여러 영역이 동시에 또는 연쇄적으로 나타나는 경우가 많습니다. 그리고 이 모든 항목을 실시간으로, 과거 이력까지 함께 추적하는 것은 수작업만으로는 한계가 있습니다.
3. 통합 모니터링이 필요한 이유
지금까지 살펴본 5가지 항목은 DBA에게 익숙한 기본 점검 포인트입니다. 하지만 실제 운영 환경에서는 아래와 같은 한계에 부딪힙니다.
- 쿼리는 느린데 → Lock 때문인지 I/O 때문인지 구분 어려움
- Lock은 확인되는데 → 어떤 서비스 요청 때문인지 추적 어려움
- 성능 저하 시점은 아는데 → 배포/트래픽과 연결이 안 됨
즉, 지표는 보이지만 원인은 보이지 않는 상태가 됩니다.
이러한 한계를 보완하려면 통합 관점의 성능 분석이 필요합니다 체크리스트는 개별 항목 확인에는 유용하지만, 실제 운영 환경에서는 이 지표들이 상호 영향을 주고받습니다. 따라서 단일 지표 중심의 모니터링이 아니라 원인 중심의 통합 분석 체계가 필요합니다.

엑셈의 풀스택 옵저버빌리티 ‘exemONE(엑셈원)’은 다음과 같은 방식으로 모니터링을 지원합니다.
- 쿼리 → 트랜잭션 → 서비스 흐름 연결
어떤 사용자 요청이 어떤 SQL을 실행했고, 해당 SQL이 왜 느려졌는지까지 추적 가능합니다.
- Lock, 세션, I/O를 다각도로 분석
특정 시점의 병목 원인을 하나의 화면에서 직관적으로 파악할 수 있습니다.
- 지능형 분석 및 자동화
사용자 정의 분석, AI 기반 예측을 통해 능동적인 판단과 자동화 대응이 가능합니다.
4. PostgreSQL 성능 문제는 대응이 아닌 ‘분석’의 영역
지금까지 살펴본 것처럼, PostgreSQL 성능 저하는 단일 원인이 아닌, 여러 요인이 복합적으로 작용하는 경우가 대부분입니다. 따라서, 개별 지표 확인에서 나아가 전체 흐름을 연결해서 보는 것이 중요합니다.
서비스 규모가 커질수록 수작업 기반 점검만으로는 한계가 명확해집니다. 이제는 모니터링 → 원인 분석 → 이력 기반의 인사이트 확보로 접근 방식을 전환해야 하는 시점입니다.
쿼리, Lock, 세션, 인프라 지표를 통합적으로 분석할 수 있는 체계를 통해 장애 대응 시간을 줄여보세요.
👉🏻 exemONE 살펴보기
출처
함께 보면 좋은 아티클
