본문으로 건너뛰기
개발 머꼬
개발 노트SQL
hohyeon.dev13

복합 인덱스를 만들었는데 조건 순서를 바꿨더니 안 쓰인 이유

  • #Engineering Note
  • #Performance
  • #SQL

문제 발생

(status, created_at) 복합 인덱스를 만들었는데, 어떤 쿼리는 빠르고 어떤 쿼리는 풀 스캔이었습니다.

-- 빠르다
SELECT * FROM posts WHERE status = 'published' AND created_at > '2026-01-01';

-- 인덱스를 안 쓴다
SELECT * FROM posts WHERE created_at > '2026-01-01';

원인 분석

복합 인덱스는 선행 컬럼부터 차례로 정렬된 하나의 구조입니다. 전화번호부가 성 → 이름 순으로 정렬된 것과 같아서, 성을 모르면 이름만으로는 찾을 수 없습니다.

MySQL 매뉴얼은 이를 가장 왼쪽 접두사(leftmost prefix) 규칙으로 설명합니다 — 인덱스를 쓰려면 WHERE의 각 AND 그룹에서 인덱스의 접두사가 사용되어야 합니다.

(part1, part2, part3) 인덱스 기준으로:

조건인덱스 사용
part1=1 AND part2=2 AND other=3사용 (part1, part2까지)
part1='hello' AND part3=5사용 (part1까지만)
part2=1 AND part3=2사용 못 함 (선행 컬럼 없음)
part1=1 OR part2=10사용 못 함

같은 원리가 LIKE에도 적용됩니다. 매뉴얼에 따르면 LIKE의 인자가 와일드카드로 시작하지 않는 상수 문자열일 때만 B-tree 인덱스를 쓸 수 있습니다.

WHERE key_col LIKE 'Patrick%'    -- 사용 ('Patrick' <= key_col < 'Patricl' 범위)
WHERE key_col LIKE '%Patrick%'   -- 사용 못 함
WHERE key_col LIKE other_col     -- 사용 못 함 (상수가 아님)

해결 방안

  1. 자주 쓰는 조건의 컬럼을 앞에 둡니다. 등호 조건이 범위 조건보다 앞에 오는 것이 보통 유리합니다 — 범위 조건 뒤의 컬럼은 인덱스 탐색에 쓰이지 못하기 때문입니다.
  2. 선행 컬럼 없이 조회하는 패턴이 있으면 별도 인덱스를 만듭니다. (created_at) 인덱스를 추가하는 식입니다. 다만 인덱스마다 쓰기 비용과 저장 공간이 늘어나므로 실제로 쓰이는 패턴만 만듭니다.
  3. (a, b)가 있으면 (a)는 따로 만들지 않습니다. 접두사 규칙 때문에 이미 커버됩니다. 반대로 (b)가 필요하면 별도로 만들어야 합니다.
  4. 앞뒤 와일드카드 검색이 요구사항이면 다른 도구를 봅니다. LIKE '%키워드%'는 구조적으로 인덱스를 못 씁니다. 전문 검색 인덱스(FULLTEXT)나 검색 엔진이 맞는 자리입니다. 이 저장소의 노트 검색도 데이터 규모가 작아 LIKE로 두고 있지만, 커지면 이 지점을 다시 봐야 합니다.
  5. 추측하지 말고 EXPLAIN으로 확인합니다. 어떤 인덱스가 선택됐는지, 몇 행을 훑는지가 나옵니다. 인덱스를 만들었다는 사실과 실제로 쓰인다는 사실은 별개입니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.