SQL 원문이나 비밀번호를 노출하지 않고 연결 슬롯, wait event·blocking PID, query ID 통계와 WAL 위치를 같은 시각에 비교해 연결 고갈·느린 실행·락 경합·복제 지연을 구분합니다.
PostgreSQL too many clientsremaining connection slotsPostgres slow querypg_blocking_pidsPostgreSQL lock waitpg_stat_replication lagWAL replay delaypg_stat_statements
환경PostgreSQL 14~18 · libpq service와 0600 패스워드 파일 · pg_monitor 또는 승인된 최소 통계 조회 권한
분류데이터베이스
검토일2026-09-04
진행5단계 · 조회 우선
SAFE OPERATING BOUNDARY
중단·복구 기준부터 확인하세요
STOP CONDITIONS
여기서는 멈추세요
PGSERVICE 대상·primary/standby 역할·장애 시각을 확정하지 못했다면 세션 취소·종료·replay 변경을 실행하지 않습니다.
PID의 query_id·application_name·transaction 소유자와 rollback 영향을 확인하지 못했다면 pg_cancel_backend 또는 pg_terminate_backend를 실행하지 않습니다.
비밀번호·connection URI·SQL literal·개인정보를 출력하거나 공유해야만 진단할 수 있는 상황이면 중단하고 DBA의 최소 권한·마스킹 수집 절차를 사용합니다.
복제 slot 삭제·standby promote·restart·max_connections 변경이 필요해 보이면 즉시 조치하지 말고 RPO/RTO와 failover runbook을 가진 복제 담당자에게 이관합니다.
ROLLBACK
복구 기준
application pool·timeout·release 변경은 기록한 이전 정상 설정과 표준 배포 절차로 되돌립니다. pg_cancel_backend로 취소된 query는 자동 복구되지 않으며 pg_terminate_backend는 열린 transaction을 rollback하므로 업무 idempotency와 외부 side effect를 확인합니다. replay resume은 데이터 변경을 되돌리는 명령이 아니며, 원래 승인된 지연 복제 목적이 있을 때만 담당자 확인 후 별도 절차로 pause를 복구합니다.
ESCALATION PACK
담당자에게 전달할 자료
동일 시각의 연결 상태별 집계, wait event, blocking PID·query_id와 max/reserved connection 설정
primary sent/write/flush/replay LSN·byte lag와 standby WAL receiver·receive/replay LSN·pause 상태
승인된 PID·조치자·조치 시각, 변경 전후 application error rate·업무 transaction 검증 결과
2026년 9월 4일 기준 PostgreSQL·Kubernetes·OpenSSL·NGINX·Certbot 공식 문서를 대조했습니다. 진단은 읽기 전용 명령과 최소 권한 계정으로 시작하고, 취소·세션 종료·설정 적용·reload는 대상·영향·복구 경로를 기록해 담당자가 승인한 경우에만 실행합니다. <...> 자리표시자는 검토된 리터럴 값으로 직접 치환하며 외부 입력으로 shell 명령을 조립하지 않습니다.
BEFORE YOU START
이런 증상에서 시작합니다
FATAL: remaining connection slots are reserved 또는 too many clients 오류가 발생함
요청 지연과 함께 Lock·LWLock·Client 계열 wait event가 늘어남
긴 transaction과 blocking PID가 확인되거나 동일 query ID 실행 시간이 급증함
standby의 replay LSN이 뒤처지고 읽기 복제본 데이터 반영이 늦음
CHECK THE BRANCH
놓치기 쉬운 원인 분기
01
연결 슬롯이 고갈된 경우
max_connections만 올리기 전에 상태별·DB별·사용자별·application_name별 연결 수와 pool 설정을 확인합니다. idle in transaction은 단순 idle과 달리 락과 vacuum 진행을 방해할 수 있습니다.
02
느린 쿼리 또는 락 경합인 경우
query 원문 대신 query_id, 실행 시간, wait event와 pg_blocking_pids를 사용해 범위를 좁힙니다. query_id는 compute_query_id 설정과 pg_stat_statements·query ID 계산 상태에 따라 NULL일 수 있으므로 NULL을 정상·이상 판정으로 단정하지 않습니다. pg_stat_statements가 사전에 활성화되지 않았다면 장애 중 재시작해 켜지 않습니다.
03
물리 복제 지연인 경우
sent·write·flush·replay LSN의 바이트 차이와 WAL receiver 상태를 함께 봅니다. write_lag·replay_lag는 idle 시 NULL이 될 수 있고 따라잡을 예상 시간을 뜻하지 않습니다.
04
여러 증상이 동시에 보이는 경우
장기 transaction이 WAL 보존과 락을 늘리고 과도한 연결이 CPU·I/O를 악화할 수 있습니다. 동일 시각 스냅샷을 남긴 뒤 최초 원인과 2차 증상을 분리합니다.
FOLLOW THE FLOW
순서대로 확인하기
1
조회시스템을 변경하지 않는 확인 단계
대상·역할과 연결·대기·복제 상태 관찰
비밀번호를 명령행이나 URI에 넣지 않습니다. 사전에 검토한 libpq service와 0600 패스워드 파일을 사용하고, SQL 원문을 조회하지 않는 집계부터 실행합니다.
psql "service=<PGSERVICE>" -X --set=ON_ERROR_STOP=1 --command "SELECT datname, usename, application_name, state, count(*) AS sessions, max(clock_timestamp() - state_change) AS oldest_state FROM pg_stat_activity WHERE pid <> pg_backend_pid() GROUP BY datname, usename, application_name, state ORDER BY sessions DESC;"
DB·role·application_name은 내부 식별정보입니다. 외부 공유본에서는 마스킹합니다.
대기 유형 집계
psql "service=<PGSERVICE>" -X --set=ON_ERROR_STOP=1 --command "SELECT coalesce(wait_event_type, 'CPU_or_not_waiting') AS wait_type, coalesce(wait_event, '-') AS wait_event, count(*) AS sessions FROM pg_stat_activity WHERE pid <> pg_backend_pid() GROUP BY wait_event_type, wait_event ORDER BY sessions DESC;"
primary 복제 연결 요약
psql "service=<PGSERVICE>" -X --set=ON_ERROR_STOP=1 --command "SELECT application_name, state, sync_state, sent_lsn, write_lsn, flush_lsn, replay_lsn, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn)) AS replay_byte_lag, write_lag, flush_lag, replay_lag, reply_time FROM pg_stat_replication ORDER BY application_name;"
결과 읽기
연결 수만 높으면 pool·누수 분기를, blocking PID와 Lock wait가 있으면 락 분기를, primary에서 replay LSN 차이가 크거나 standby receiver가 비정상이면 복제 분기를 우선합니다.
다음 판단
설정 상한과 PID 단위 대기·blocking 관계를 읽기 전용으로 확인합니다.
2
조회시스템을 변경하지 않는 확인 단계
연결·락·느린 query ID·WAL 구간 판별
각 분기는 필요한 명령만 실행합니다. 통계 view 권한이 부족하면 superuser 비밀번호를 공유받지 말고 DBA에게 최소 pg_monitor 또는 pg_read_all_stats 역할로 수집을 요청합니다.
연결 한도와 예약 슬롯
psql "service=<PGSERVICE>" -X --set=ON_ERROR_STOP=1 --command "SELECT current_setting('max_connections') AS max_connections, current_setting('superuser_reserved_connections') AS superuser_reserved, current_setting('reserved_connections', true) AS reserved_connections;"
waiting session과 blocker
psql "service=<PGSERVICE>" -X --set=ON_ERROR_STOP=1 --command "SELECT pid, datname, usename, application_name, state, wait_event_type, wait_event, clock_timestamp() - xact_start AS xact_age, clock_timestamp() - query_start AS query_age, query_id, pg_blocking_pids(pid) AS blocking_pids FROM pg_stat_activity WHERE pid <> pg_backend_pid() AND (wait_event_type IS NOT NULL OR state = 'idle in transaction') ORDER BY xact_start NULLS LAST;"
query 열은 의도적으로 조회하지 않습니다. PID·query_id와 업무 소유자 기록으로 정확한 세션을 확인합니다.
query ID별 누적 실행 통계
psql "service=<PGSERVICE>" -X --set=ON_ERROR_STOP=1 --command "SELECT queryid, calls, round(total_exec_time::numeric, 1) AS total_ms, round(mean_exec_time::numeric, 1) AS mean_ms, rows, shared_blks_read, temp_blks_written FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 20;"
blocking_pids가 비어 있고 유효한 query_id의 누적 시간만 크면 실행 계획·I/O 분기, blocker가 있으면 transaction 소유자 분기입니다. query_id가 NULL이면 compute_query_id·extension 상태와 다른 증거를 확인하고 임의로 같은 SQL을 묶지 않습니다. receive LSN은 전진하지만 replay LSN이 멈추면 replay·standby 부하를, 둘 다 멈추면 sender·network·slot을 봅니다.
다음 판단
취소·종료 또는 replay resume 전에 로그·timeout·pool·배포 변경 이력을 확보합니다.
3
주의서버 부하나 권한을 고려할 단계
민감 SQL 없이 원인 증거와 변경 전 상태 보존
로그에는 SQL literal과 개인정보가 포함될 수 있습니다. 제한된 터미널에서 오류 코드와 시각 주변만 확인하고, 외부 전달본은 SQL·client address·role을 마스킹합니다.
timeout·통계 설정
psql "service=<PGSERVICE>" -X --set=ON_ERROR_STOP=1 --command "SHOW statement_timeout; SHOW lock_timeout; SHOW idle_in_transaction_session_timeout; SHOW deadlock_timeout; SELECT extversion FROM pg_extension WHERE extname = 'pg_stat_statements';"
pool 상한 합계가 DB의 일반 연결 예산 안에 있고 timeout·재시도 폭주 방지가 검토된 release만 표준 배포 절차로 적용합니다.
결과 읽기
cancel은 query 취소, terminate는 session과 열린 transaction 종료, replay_resume은 standby apply 재개입니다. 서로 대체 관계가 아니며 원인에 맞지 않는 명령을 함께 실행하지 않습니다.
다음 판단
동일 조회를 반복해 연결 여유·wait·WAL 진전과 업무 정확성을 확인하고, 악화되면 승인된 이전 상태로 복구합니다.
5
변경데이터·서비스 상태가 달라질 수 있는 단계
연결·락·query ID·복제 회복 검증과 롤백 판단
변경 직후와 정상 트래픽 구간을 나눠 확인합니다. 숫자가 잠깐 좋아진 것만으로 종료하지 말고 application error rate와 transaction 정확성도 함께 검증합니다.
변경 단계입니다. 실행 전 대상 이름과 경로, 서비스 중단 영향, 복구 방법을 다시 확인하세요.
연결·대기 재검증
psql "service=<PGSERVICE>" -X --set=ON_ERROR_STOP=1 --command "SELECT state, wait_event_type, wait_event, count(*) FROM pg_stat_activity WHERE pid <> pg_backend_pid() GROUP BY state, wait_event_type, wait_event ORDER BY count(*) DESC;"
blocking 재검증
psql "service=<PGSERVICE>" -X --set=ON_ERROR_STOP=1 --command "SELECT pid, application_name, query_id, pg_blocking_pids(pid) AS blocking_pids, clock_timestamp() - query_start AS query_age FROM pg_stat_activity WHERE cardinality(pg_blocking_pids(pid)) > 0 ORDER BY query_start;"
primary WAL lag 재검증
psql "service=<PGSERVICE>" -X --set=ON_ERROR_STOP=1 --command "SELECT application_name, state, sync_state, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn)) AS replay_byte_lag, replay_lag, reply_time FROM pg_stat_replication ORDER BY application_name;"
새 연결이 안정적으로 수용되고 blocking chain이 해소되며 query ID latency와 WAL byte lag가 정상 기준으로 돌아와야 합니다. idle primary의 lag interval NULL은 단독 실패 신호가 아닙니다.
다음 판단
pool·release 변경은 이전 정상 버전으로 되돌립니다. 취소된 query는 자동 재실행하지 않고 업무 idempotency를 확인하며, 종료된 transaction은 rollback 여부를 확인해 보정합니다. replay를 다시 pause하는 것은 원래 승인 목적이 명확한 경우에만 별도 승인합니다.