백엔드 면접 용어 사전
데이터베이스covering index

커버링 인덱스

쿼리에 필요한 컬럼이 인덱스에 모두 있어 테이블 접근 없이 처리되는 경우.

쿼리에 필요한 모든 컬럼이 인덱스 안에 들어 있어, 테이블 데이터를 읽지 않고 인덱스만으로 결과를 만드는 경우.

왜 빠른가 — 두 번째 탐색이 사라진다

InnoDB에서 보조 인덱스로 조회하면 보통 두 단계다.

  • ① 보조 인덱스에서 조건에 맞는 항목 찾기 → 기본키를 얻음
  • ② 그 기본키로 클러스터형 인덱스를 다시 탐색 → 행 전체를 읽음 ← 북마크 룩업

②는 행마다 발생하고, 인덱스와 다른 위치를 읽으므로 랜덤 I/O다. 결과가 1만 건이면 1만 번 일어난다.

필요한 컬럼이 인덱스에 다 있으면 ②를 통째로 건너뛴다.

-- 인덱스: (member_id, created_at, status)

SELECT status FROM orders
WHERE member_id = 100
ORDER BY created_at DESC;
  • WHEREmember_id — 인덱스에 있음
  • ORDER BYcreated_at — 인덱스에 있고 이미 정렬돼 있어 filesort도 불필요
  • SELECTstatus — 인덱스에 있음

→ 테이블을 한 번도 안 읽는다.

확인 방법

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 *는 거의 항상 커버링이 깨진다. 필요한 컬럼만 명시해야 한다.
  • 커버링을 노려 컬럼을 계속 넣으면 인덱스가 비대해져 쓰기 비용과 메모리(버퍼 풀) 사용이 늘어난다. 자주 쓰는 핵심 쿼리 몇 개에만 적용한다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 데이터베이스 — 인덱스·트랜잭션·격리수준·N+1