쿼리에 필요한 모든 컬럼이 인덱스 안에 들어 있어, 테이블 데이터를 읽지 않고 인덱스만으로 결과를 만드는 경우.
왜 빠른가 — 두 번째 탐색이 사라진다
InnoDB에서 보조 인덱스로 조회하면 보통 두 단계다.
- ① 보조 인덱스에서 조건에 맞는 항목 찾기 → 기본키를 얻음
- ② 그 기본키로 클러스터형 인덱스를 다시 탐색 → 행 전체를 읽음 ← 북마크 룩업
②는 행마다 발생하고, 인덱스와 다른 위치를 읽으므로 랜덤 I/O다. 결과가 1만 건이면 1만 번 일어난다.
필요한 컬럼이 인덱스에 다 있으면 ②를 통째로 건너뛴다.
예
-- 인덱스: (member_id, created_at, status)
SELECT status FROM orders
WHERE member_id = 100
ORDER BY created_at DESC;
WHERE의member_id— 인덱스에 있음ORDER BY의created_at— 인덱스에 있고 이미 정렬돼 있어 filesort도 불필요SELECT의status— 인덱스에 있음
→ 테이블을 한 번도 안 읽는다.
확인 방법
MySQL 실행계획 Extra 컬럼에
- Using index — ← 커버링 인덱스 (테이블 접근 없음)
- Using index condition — ← 다른 뜻! (인덱스 컨디션 푸시다운)
둘을 혼동하기 쉽다. Using index만 커버링이다.
페이징에서 특히 효과가 크다
-- 느림: 100만 행을 읽어서 100만 번 북마크 룩업 후 버림
SELECT * FROM orders ORDER BY created_at LIMIT 1000000, 20;
-- 빠름: 인덱스만으로 PK를 구한 뒤 20건만 조인
SELECT o.* FROM orders o
JOIN (SELECT id FROM orders ORDER BY created_at LIMIT 1000000, 20) t
ON o.id = t.id;
안쪽 서브쿼리가 커버링으로 끝나므로 버릴 행에 대해 테이블을 읽지 않는다. 이 기법을 지연 조인(deferred join) 이라 한다.
주의
SELECT *는 거의 항상 커버링이 깨진다. 필요한 컬럼만 명시해야 한다.- 커버링을 노려 컬럼을 계속 넣으면 인덱스가 비대해져 쓰기 비용과 메모리(버퍼 풀) 사용이 늘어난다. 자주 쓰는 핵심 쿼리 몇 개에만 적용한다.