이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- 어제 빠르던 쿼리가 오늘 갑자기 느려지는 이유는 무엇인가?
- Plan regression을 의심할 때 어떤 증거를 모아야 하는가?
- 운영에서 실행 계획 변화에 어떻게 대응해야 하는가?
개요
Plan regression은 같은 SQL 또는 비슷한 SQL이 이전보다 나쁜 실행 계획을 선택하면서 성능이 급격히 나빠지는 현상이다. 원인은 코드 변경일 수도 있지만 데이터 증가, 통계 갱신, 인덱스 추가/삭제, DB 버전 변경, 파라미터 분포 변화일 수도 있다.
백엔드 개발자에게 중요한 점은 “쿼리를 안 바꿨는데 왜 느려졌지?”라는 상황이 실제로 가능하다는 것이다.
원리
Optimizer는 통계와 비용 모델로 계획을 선택한다. 따라서 다음 변화가 계획을 바꿀 수 있다.
- 테이블 행 수 증가
- 특정 값에 데이터가 몰림
- 통계 갱신 또는 통계 부정확
- 인덱스 생성 또는 제거
- 파라미터 값 분포 변화
- DB 설정 변경
- DB 버전 업그레이드
예를 들어 status = 'FAILED' 조건이 예전에는 전체의 0.1%였는데 장애 이후 실패 주문이 30%가 되었다면, 같은 인덱스의 효율이 달라질 수 있다.
이때 중요한 점은 “좋던 인덱스가 갑자기 나빠졌다”가 아니라 “데이터 사건이 인덱스의 선택도를 바꿨다”는 해석이다. 외부 결제 장애, 이벤트 트래픽, 대형 고객 유입, batch 실패처럼 비즈니스 사건이 곧 optimizer 입력값을 바꾼다.
증거 수집
Plan regression을 의심하면 다음을 비교한다.
- 느려지기 전후 SQL
- 바인딩 값
- 실행 계획
- 예상 row와 실제 row
- 통계 갱신 시점
- 인덱스 변경 이력
- 배포 이력
- 데이터 증가량과 분포 변화
- DB 설정 또는 버전 변경
운영에서 예전 실행 계획을 남기지 않았다면 비교가 어렵다. 중요한 API는 배포 전후 실행 계획을 PR이나 문서에 남기는 습관이 도움이 된다.
최소한 다음 형태로 남기면 회귀 분석이 쉬워진다.
API: GET /orders/pending
SQL fingerprint: orders status + created_at asc + limit
bind sample: status=PENDING, limit=100
plan captured: 2026-06-29 18:00 KST
data scale: orders 42M, PENDING 50k
index: idx_orders_status_created
p95: 180ms이 기록은 장애 중 “무엇이 바뀌었는가”를 찾는 기준선이 된다.
기준선은 너무 거창할 필요가 없다. 핵심 목록 API, 결제/주문 상태 조회, 관리자 batch 쿼리처럼 장애 영향이 큰 SQL부터 남긴다. 저장할 때는 SQL 전체보다 fingerprint, 대표 bind, 데이터 규모, 사용 인덱스, p95/p99, 캡처 시점을 함께 남기는 것이 나중에 더 유용하다.
예시 상황
다음 API가 있다고 하자.
SELECT id, status, created_at
FROM orders
WHERE status = 'PENDING'
ORDER BY created_at ASC
LIMIT 100;평소에는 PENDING 주문이 적어 빠르다. 그런데 외부 결제 장애로 PENDING 주문이 폭증하면 같은 SQL이 훨씬 많은 행을 읽는다. 이때 단순히 인덱스가 있는지보다 상태 분포 변화와 처리 지연 원인을 봐야 한다.
CREATE INDEX idx_orders_status_created
ON orders (status, created_at);인덱스가 있어도 처리하지 못한 PENDING이 계속 쌓이면 API와 배치가 함께 느려질 수 있다. 근본 해결은 장애 복구, 백로그 처리, 배치 처리량 조절, 사용자 API 제한을 함께 봐야 한다.
운영 대응
장애 중 대응은 조심스럽게 나눈다.
- 문제가 특정 SQL인지 DB 전체인지 확인한다.
- 최근 배포와 배치 작업을 멈출 수 있는지 본다.
- 안전하게 취소 가능한 long query가 있는지 확인한다.
- 인덱스 추가가 정말 즉시 가능한지 DBA와 확인한다.
- 필요하면 기능 단위 rate limit이나 read replica 우회를 검토한다.
Plan regression은 성급한 힌트나 강제 인덱스로 덮기보다 통계, 인덱스, 쿼리 구조, 데이터 분포를 함께 고치는 편이 낫다. DB별 optimizer hint는 마지막 수단으로 제한한다.
장애 중에는 다음 순서로 본다.
- 특정 SQL/파라미터만 느린지 확인한다.
- 데이터 분포나 백로그가 갑자기 바뀌었는지 확인한다.
- 통계 갱신, 인덱스 변경, 배포, DB 설정 변경 이력을 비교한다.
- 임시 완화로 트래픽, batch, 기능 플래그, read path를 조정한다.
- 근본 해결로 쿼리 구조, 인덱스, 통계 전략, 데이터 처리량을 수정한다.
힌트나 강제 인덱스는 특정 시점의 데이터에는 맞아도 다음 분포 변화에서 독이 될 수 있다. 남겨야 한다면 만료 조건과 제거 기준을 함께 문서화한다.
운영 대응에서 ANALYZE, 통계 갱신, 인덱스 추가는 모두 변경 작업이다. 통계 갱신은 보통 안전해 보이지만 큰 테이블에서는 I/O를 만들고, 계획을 다시 바꿀 수 있다. 장애 중에는 “무엇을 바꾸는가”와 “바꾼 뒤 어떤 지표가 좋아져야 하는가”를 기록한다.
인프라/DBA와의 접점
인프라/DBA에게 전달할 정보는 구체적이어야 한다.
2026-06-30 13:00 이후 GET /orders/pending p95가 200ms에서 4s로 증가했습니다.
SQL은 status='PENDING' + created_at asc + limit 100입니다.
PENDING 행 수가 5만에서 900만으로 증가했습니다.
최근 배포는 없고, 결제 외부사 장애가 있었습니다.
현재 실행 계획과 이전 계획 비교가 필요합니다.이 정도 정보가 있으면 DBA는 통계, 인덱스, DB 부하를 더 빠르게 확인할 수 있다. 반대로 “DB가 갑자기 이상합니다”만 전달하면 서로 시간을 잃는다.
실전 팁
- 핵심 API의 대표 SQL과 실행 계획을 정기적으로 남긴다.
- 배포 전후 p95/p99와 slow query를 비교한다.
- 데이터 분포가 바뀌는 이벤트, 장애, 배치를 성능 변화와 연결한다.
- optimizer hint는 팀 규칙 없이 남발하지 않는다.
- plan regression은 쿼리만이 아니라 운영 사건의 결과일 수 있다.
- 핵심 SQL은 배포 전후 plan과 p95/p99를 가볍게라도 비교한다.
- 힌트나 강제 인덱스는 임시 조치인지 영구 정책인지 명시하고 제거 조건을 둔다.
- 데이터 분포를 바꾸는 비즈니스 사건을 성능 타임라인에 함께 표시한다.
ANALYZE, 인덱스 추가, hint 적용 같은 조치는 변경 전후 plan과 p95/p99를 남긴다.- plan baseline은 모든 SQL이 아니라 핵심 API와 batch부터 작게 시작한다.
위험 신호!
- 쿼리를 바꾸지 않았으니 DB 문제일 리 없다고 단정한다.
- 통계와 데이터 분포를 보지 않고 인덱스만 추가한다.
- 장애 중 임시로 넣은 optimizer hint가 영구 운영 코드가 된다.
- 실행 계획 기록이 없어 성능 회귀를 비교할 수 없다.
- 특정 고객이나 특정 상태값만 느린데 전체 시스템 문제로 대응한다.
- optimizer hint를 넣은 이유와 제거 기준이 문서에 없다.
- 외부 장애나 batch 실패로 데이터 백로그가 쌓였는데 인덱스 문제로만 해석한다.
- 통계 갱신이나 인덱스 추가 후 어떤 지표가 개선되어야 하는지 정하지 않는다.
확인 질문
- Plan regression이 발생할 수 있는 대표 원인은 무엇인가?
- 데이터 분포 변화, 통계 변화, 인덱스 변경, DB 설정이나 버전 변경, 파라미터 값 변화 등이다.
- 실행 계획 회귀를 분석할 때 전후 비교해야 할 것은 무엇인가?
- SQL, 바인딩 값, 실행 계획, 예상/실제 row, 통계 시점, 인덱스 이력, 배포 이력, 데이터 분포다.
- DBA와 협업할 때 왜 비즈니스 사건 정보를 함께 전달해야 하는가?
- 외부 장애, 이벤트, 배치처럼 데이터 분포와 호출량을 바꾸는 사건이 실행 계획과 DB 부하를 바꿀 수 있기 때문이다.
- plan regression 대응에서 optimizer hint를 조심해야 하는 이유는 무엇인가?
- 특정 데이터 분포와 시점에는 도움이 되어도 통계, 데이터량, 파라미터 분포가 바뀌면 더 나쁜 계획을 고정할 수 있기 때문이다.
- Plan regression 대응에서 변경 전후 지표를 남겨야 하는 이유는 무엇인가?
- 통계 갱신, 인덱스 추가, hint 적용이 실제로 plan과 p95/p99를 개선했는지 확인하고, 부작용이 생기면 되돌릴 근거를 만들기 위해서다.