Development

PostgreSQL 락 타임아웃 장애를 30분 안에 진정시키는 실무 대응 플레이북

Mike발행일 2026년 3월 11일읽는 시간 약 3

💡 Key Takeaways

  • 락 대기는 쿼리 튜닝 이전에 차단 세션 식별과 트랜잭션 경계 정리가 우선입니다.
  • 읽기/쓰기 경로를 분리하고 timeout 계층을 나누면 장애 확산을 빠르게 멈출 수 있습니다.
  • 사후 점검은 SQL 한두 개가 아니라 배포 절차와 운영 알람 기준까지 함께 고쳐야 효과가 유지됩니다.

PostgreSQL 락 타임아웃 장애를 30분 안에 진정시키는 실무 대응 플레이북

1. 문제 정의: 느린 쿼리가 아니라 "대기열이 쌓이는 구조"를 먼저 본다

월요일 오전 10시 20분, 결제 상태를 갱신하는 배치가 평소보다 3배 오래 걸리기 시작했고 API p95가 600ms에서 4.8초까지 뛰었습니다. 처음엔 인덱스 누락을 의심했지만, pg_stat_activity를 열어보니 실행 중인 쿼리보다 wait_event_type = Lock 세션이 급격히 늘고 있었습니다. 이때 팀이 자주 하는 실수는 슬로우 쿼리 로그만 붙잡고 개별 SQL을 최적화하려는 겁니다. 실제 현장에서는 락 대기가 길어지면 커넥션 풀이 먼저 마르고, 애플리케이션 재시도 로직이 다시 DB를 두드리면서 장애가 가속됩니다. 그래서 저는 “어떤 쿼리가 느린가”보다 “어떤 트랜잭션이 누구를 막고 있는가”를 먼저 확인합니다. 장애 시간에는 완벽한 원인 분석보다 확산 차단이 중요합니다. 락 트리를 빠르게 파악하고 쓰기 경로를 임시로 감속해 대기열 증가율을 꺾어야 이후 조치가 먹힙니다.

2. 판단 기준: 차단 세션, 대기 시간 분포, 커넥션 소진 속도를 같은 화면에서 본다

운영 판단은 세 가지 숫자로 고정해두면 흔들리지 않습니다. 첫째, 차단 세션 수입니다. pg_blocking_pids 기준으로 1~2개 세션이 다수를 막고 있으면 개별 세션 종료만으로도 즉시 회복될 가능성이 큽니다. 둘째, 대기 시간 분포입니다. 평균값보다 90퍼센타일 락 대기 시간이 5초를 넘는지 봐야 합니다. 평균은 짧은 요청이 가려버리기 쉽기 때문입니다. 셋째, 커넥션 풀 여유율입니다. 애플리케이션 풀의 idle 비율이 15% 아래로 내려가면 재시도 트래픽이 붙으면서 DB가 스스로 회복하기 어려워집니다. 저희 팀은 한 번 이 지표를 놓쳐서, DB CPU는 40%인데도 API는 거의 타임아웃 상태가 18분 지속됐습니다. 이후 대시보드에서 락 대기, 차단 세션, 풀 idle을 한 패널에 묶고, 세 지표 중 두 개 이상 임계치를 넘으면 즉시 장애 채널로 승격하도록 바꿨습니다. 이 기준을 문서화한 뒤엔 “조금 더 지켜보자”라는 애매한 시간이 크게 줄었습니다.

3. 실행 절차: 10분 완화, 20분 안정화, 24시간 내 재발 방지 항목 확정

대응은 시간 구간별로 나누는 게 가장 실용적이었습니다. 0~10분 구간에서는 차단 세션을 식별해 종료 후보를 정하고, 쓰기 폭주를 만드는 배치나 대량 업데이트를 일시 중지합니다. 동시에 API 레이어에서 재시도 횟수를 한 단계 낮춰 DB 재진입을 줄입니다. 10~20분 구간에서는 장기 트랜잭션 원인을 찾습니다. 보통 ORM 기본 트랜잭션 범위가 넓거나, 외부 API 호출을 트랜잭션 안에서 기다리는 코드가 문제였습니다. 저희는 주문 확정 로직에서 결제 검증 HTTP 호출이 트랜잭션 내부에 있던 것을 분리해 평균 락 점유 시간을 1.9초에서 220ms로 줄였습니다. 20~30분 구간에서는 read replica 우회, 특정 기능의 쓰기 제한, 작업 큐 소비 속도 하향처럼 서비스 품질을 부분 희생하는 완화책을 적용합니다. 핵심은 “정상화처럼 보이는 순간”에 바로 종료하지 않는 것입니다. 최소 15분 동안 락 대기 재상승이 없는지 확인해야 진짜 안정화로 볼 수 있습니다.

4. 운영 체크리스트: 쿼리 개선만으로 끝내지 말고 배포/알람 규칙까지 수정한다

사후 조치에서 가장 효과가 큰 건 기술 수정과 운영 규칙 수정을 함께 묶는 일입니다. 기술 측면에서는 긴 트랜잭션이 발생하는 코드 경로를 태그로 남기고, 배포 전 부하 리허설에서 해당 경로 락 대기 시간을 반드시 측정합니다. 운영 측면에서는 장애 알람 조건을 CPU 중심에서 대기 중심으로 옮겨야 합니다. 저희는 “DB CPU 80%” 알람보다 “락 대기 p90 5초 초과 3분 지속” 알람이 실제 사고를 훨씬 빨리 잡았습니다. 또 PR 체크리스트에 "트랜잭션 내부 외부 I/O 존재 여부" 항목을 추가하자 신규 기능에서 같은 유형의 사고가 줄었습니다. 마지막으로 롤백 기준도 숫자로 고정해야 합니다. 락 대기 p90이 10분 내 두 번 재상승하면 기능 롤백을 우선하고, 쿼리 튜닝 실험은 다음 배포 창으로 미룹니다. 이렇게 해두면 장애 중에도 팀이 같은 판단 체계로 움직이고, 복구 후 회고가 개인 책임 공방이 아니라 시스템 개선 작업으로 이어집니다.

함께 읽으면 좋은 글

Development

Kubernetes HPA 스래싱 진정 플레이북: 점심 피크 장애를 3일 만에 멈춘 운영 절차

2026-03-14
Development

CI 시크릿 만료 사고 대응 플레이북: 배포 중단 40분에서 8분으로 줄인 운영 절차

2026-03-13

본문 작성/검수 원칙은 편집 원칙에서 확인할 수 있습니다.

← 목록으로 돌아가기