메인 콘텐츠로 건너뛰기

VillageSQL is a drop-in replacement for MySQL with extensions.

All examples on this page work on VillageSQL. Install Now →
커버링 인덱스는 쿼리에 필요한 모든 열을 포함하는 인덱스입니다. 먼저 인덱스의 기본 동작 방식을 이해하면 도움이 됩니다 — MySQL 인덱스의 동작 방식은 여기에도 적용되는 B-트리 구조, 키 길이, 선택도 규칙을 다룹니다. MySQL이 실제 테이블 행을 건드리지 않고 인덱스만으로 쿼리에 답할 수 있을 때, 이를 인덱스 전용 스캔이라고 합니다. 큰 테이블에서는 이것이 단일 쿼리에 적용할 수 있는 가장 큰 최적화 중 하나입니다.

행 조회의 동작 방식(그리고 비용이 큰 이유)

일반적인 인덱스 조회는 두 단계입니다:
  1. MySQL이 인덱스를 검색하여 어떤 행이 일치하는지 찾습니다
  2. 일치하는 각 행에 대해 MySQL이 테이블에서 전체 행을 가져옵니다(“행 조회” 또는 “테이블 복귀 조회”)
2단계가 비용이 큰 부분입니다. InnoDB의 클러스터형 인덱스는 테이블 데이터가 기본 키 순서로 물리적으로 정렬되어 있음을 뜻합니다. 보조 인덱스로 행을 조회하면 기본 키 값이 흩어져 있어 각 행 조회가 서로 다른 디스크 페이지에 대한 무작위 I/O가 될 가능성이 높습니다. 규모가 커지면 이 무작위 읽기가 쿼리 시간을 지배합니다. 커버링 인덱스는 2단계를 완전히 없앱니다.

EXPLAIN에서 커버링 인덱스 식별하기

EXPLAIN은 커버링 인덱스가 사용 중일 때 알려줍니다: Extra: Using index를 찾으세요. EXPLAIN 출력에 아직 익숙하지 않다면 MySQL에서 EXPLAIN 읽기가 모든 핵심 열을 다룹니다.
Using index — MySQL이 인덱스만 읽었습니다. 행 조회가 없습니다. 이제 인덱스에 없는 열을 추가해 보세요:
이제 Extra가 비어 있습니다 — MySQL은 인덱스로 행을 찾은 다음 order_total을 얻기 위해 전체 행을 가져옵니다. 행 조회가 돌아왔습니다.

커버링 인덱스 설계하기

목표는 쿼리가 건드리는 모든 열 — WHERE, ORDER BY, GROUP BY, SELECT 목록에 있는 열 — 을 포함하는 것입니다. 열 순서는 WHERE와 ORDER BY 필터링에 대해 여전히 최좌측 접두사 규칙을 따릅니다:
  • user_id가 첫 번째 — 동등 필터
  • created_at이 두 번째 — 정렬 열(인덱스가 이미 정렬되어 있어 filesort 없음)
  • status가 마지막 — SELECT에만 있고 필터링이나 정렬에는 없음
SELECT 열은 어떤 행이 찾아지는지나 어떤 순서인지에 영향을 주지 않기 때문에 마지막에 옵니다 — 그저 따라가는 것뿐입니다.

커버링 인덱스가 가치 있는 경우

자주 묻는 질문

커버링 인덱스가 SELECT *에 도움이 되나요?

아니요. 인덱스는 SELECT 목록의 모든 열을 포함할 때만 쿼리를 커버할 수 있습니다. SELECT *는 모든 열을 선택하므로 모든 열을 인덱싱해야 하는데, 그 시점에는 사실상 테이블을 인덱스로 복제한 것이어서 유용하지 않습니다.

커버링 인덱스가 그래도 SELECT * 쿼리 속도를 높일 수 있나요?

예, 부분적으로는 그렇습니다. MySQL이 행 조회를 해야 하더라도 인덱스가 가져올 행의 범위를 좁혀 줍니다. 개선은 WHERE 필터링에서 오는 것이지 행 조회를 없애는 데서 오는 것이 아닙니다.

커버링 인덱스가 도움이 될지 어떻게 알 수 있나요?

EXPLAIN을 실행하고 Extra를 확인하세요. Using index condition이 보이거나 아무것도 없고(Using index 없음) rows가 크다면 커버링 인덱스가 도움이 될 수 있습니다. EXPLAIN ANALYZE는 실제 행 수와 추정 행 수를 보여 주므로 옵티마이저의 선택을 확인하는 데 유용합니다.

문제 해결

함께 보기