Issue 41 · Cover story · postmortem

인덱스 하나 잘못 지워서
4시간 멈춘 이야기

복합 인덱스 하나를 지웠고, 30분 뒤 조회 API 응답이 8초를 넘겼습니다. 롤백까지 90분이 더 걸렸습니다.

어둠 속에 떠 있는 형광 초록빛 격자 다이아몬드
gallery-01.webp1600×900 · 16:9 · 기사 상세 히어로 Glowing acid lime #77DE3F wireframe grid folded into a diamond shape floating in a dark misty void, sharp neon lines with soft atmospheric bloom, retro-futurist 3D perspective, high contrast against near-black background

6월 9일 화요일 오전 10시 4분, 저희는 프로덕션 데이터베이스에서 복합 인덱스 하나를 제거했습니다. 지난 90일간 쿼리 통계에 한 번도 잡히지 않은 인덱스였고, 쓰기 지연을 줄이려는 정기 정리 작업의 일부였습니다. 변경은 사전에 리뷰를 거쳤고 스테이징에서 이틀간 문제없이 돌아갔습니다.

10시 38분, 조회 API의 p99 응답이 320ms에서 8.4초로 뛰었습니다. 알림은 정상적으로 울렸지만 처음 20분 동안 저희는 인덱스를 의심하지 않았습니다. 같은 시각 배포된 다른 변경이 두 건 있었기 때문입니다.

왜 통계에 안 잡혔나

문제의 인덱스는 created_atstatus를 묶은 것이었는데, 평소에는 옵티마이저가 다른 단일 인덱스를 골랐습니다. 그런데 특정 조건 — 상태값이 편중되고 기간이 90일을 넘는 조회 — 에서만 이 복합 인덱스로 실행 계획이 바뀝니다. 그 조건은 월별 정산 배치가 도는 화요일 오전에만 발생했습니다.

쿼리 통계 수집 주기가 주 단위 롤업이었던 것도 겹쳤습니다. 주당 몇 시간만 쓰이는 인덱스는 반올림 과정에서 사용률 0으로 표시됐습니다.

-- 문제가 된 조회. status 편중 + 90일 초과 구간에서만
-- 복합 인덱스로 실행 계획이 바뀐다.
SELECT id, status, created_at
  FROM orders
 WHERE status IN ('settled', 'partial')
   AND created_at >= now() - interval '120 days'
 ORDER BY created_at DESC
 LIMIT 500;

롤백이 왜 90분이나 걸렸나

인덱스를 다시 만드는 데 걸린 시간은 22분이었습니다. 나머지 68분은 실행 계획 캐시 때문이었습니다. 인덱스가 복구된 뒤에도 커넥션 풀에 남아 있던 준비된 구문이 예전 계획을 그대로 쓰고 있었습니다. 결국 애플리케이션을 순차 재시작해야 했습니다.

인덱스를 지우는 변경은 되돌리기 쉽다고 생각했습니다. 되돌리기 쉬운 것과 빠르게 복구되는 것은 다른 문제였습니다.

바꾼 것

첫째, 인덱스 사용 통계를 시간 단위로 수집하도록 바꿨습니다. 둘째, 인덱스 제거는 바로 지우지 않고 INVISIBLE 상태로 2주를 두는 절차를 만들었습니다. 셋째, 스테이징 데이터셋에 정산 배치 시나리오를 포함시켰습니다.

바꾸지 않은 것도 있습니다. 배포 창을 오전에서 옮기자는 제안은 받아들이지 않았습니다. 사람이 깨어 있는 시간에 배포하는 편이 낫다고 판단했습니다.

Timeline — 4시간 12분

어두운 종이 면이 각지게 접히며 만드는 그림자
gallery-03.webp800×600 · 4:3 · 타임라인 이미지 Abstract dark folded planes forming sharp angular ridges, single low grazing light creating deep gradients between facets, near-black charcoal palette with a faint lime #77DE3F edge, minimal sculptural composition
검은 바탕에 촘촘히 솟은 원기둥 돌기의 접사
gallery-04.webp800×600 · 4:3 · 타임라인 이미지 Macro shot of dense black cylindrical pins standing in tight rows, shallow depth of field blurring the background into darkness, single soft top light, monochrome charcoal palette with faint lime #77DE3F specular points, tactile abstract texture

10:04 인덱스 제거 · 10:38 p99 8.4초 도달 · 10:41 경보 발생 · 11:02 배포 두 건 롤백 (효과 없음) · 11:26 실행 계획 비교로 원인 특정 · 11:48 인덱스 재생성 시작 · 12:10 재생성 완료 · 13:18 애플리케이션 순차 재시작 완료 · 14:16 정상 확인

검은 배경에서 은은한 초록빛을 띠며 휘어지는 곡면
portrait-01.webp900×1350 · 2:3 · 세로 이미지 Vertical abstract of a smooth dark surface curving into an S-shaped fold, soft green-tinted light grazing the crest, deep black falloff on both sides, lime #77DE3F highlight along the edge, 100mm lens, minimal sculptural rendering

Editor's note

이 글을
싣는 이유

이 장애는 고객사 서비스에서 일어났고, 게시 전에 해당 팀의 동의를 받았습니다. 숫자와 시각을 바꾸지 않은 이유는 그래야 다른 팀이 자기 상황과 비교할 수 있기 때문입니다.

지난 회고 모아보기

Next issue

42호는
8월 28일에

다음 호 주제는 "3년 된 기능 플래그를 정리하다 서비스를 내린 이야기"입니다.