Foundry
데이터베이스
중급
핵심

커버링 인덱스

인덱스만으로 쿼리 처리, 테이블 접근 불필요

커버링 인덱스 (Covering Index)

핵심 개념

쿼리에 필요한 모든 컬럼이 인덱스에 포함되어, 테이블 데이터를 읽지 않아도 되는 인덱스.

[일반 인덱스 조회]
1. 인덱스에서 조건 검색
2. 테이블로 이동 (랜덤 I/O) ← 느림!
3. 나머지 컬럼 읽기

[커버링 인덱스 조회]
1. 인덱스에서 조건 검색
2. 인덱스에서 바로 결과 반환 ← 빠름!
→ 테이블 접근 불필요!

EXPLAIN으로 확인

EXPLAIN SELECT user_id, status
FROM orders
WHERE user_id = 123;

-- Extra: Using index  ← 커버링 인덱스!

Using index가 표시되면 커버링 인덱스가 동작한 것.

실무 예시

-- 인덱스 생성
CREATE INDEX idx_orders_cover
  ON orders (user_id, status, created_at);

-- 커버링 O: 모든 컬럼이 인덱스에 포함
SELECT status, created_at
FROM orders
WHERE user_id = 123;

-- 커버링 X: total_price가 인덱스에 없음
SELECT status, total_price
FROM orders
WHERE user_id = 123;

성능 차이

항목일반 인덱스커버링 인덱스
테이블 접근필요 (랜덤 I/O)불필요
대량 데이터느림수배~수십배 빠름
EXPLAINUsing whereUsing index

트레이드오프

장점:
  - 읽기 성능 대폭 향상
  - 디스크 I/O 감소

단점:
  - 인덱스 크기 증가 (컬럼 추가)
  - INSERT/UPDATE 성능 저하
  - 메모리 사용량 증가

설계 팁

SELECT 절에 * 대신 필요한 컬럼만!
  → 커버링 인덱스 활용 가능성 ↑

자주 조회하는 패턴 분석
  → 해당 컬럼들로 인덱스 구성
면접에서 이렇게 나옵니다

Q.커버링 인덱스란 무엇이고, 왜 빠른가요?

쿼리에 필요한 컬럼이 인덱스 안에 모두 있어서 테이블을 읽지 않는 경우입니다.

INDEX (user_id, status, amount)

SELECT amount FROM orders WHERE user_id = ? AND status = ?
-- 세 컬럼이 모두 인덱스에 있다. 테이블 접근이 없다

빠른 이유는 두 가지입니다.

이유내용
테이블 접근이 없다보조 인덱스로 찾은 뒤 기본키로 다시 읽는 단계가 사라진다
읽는 양이 적다인덱스는 필요한 컬럼만 담아 테이블보다 작다

InnoDB 의 보조 인덱스는 기본키를 이미 품고 있어서, 기본키 컬럼은 인덱스에 명시하지 않아도 커버됩니다.

흔한 실수: 커버링을 위해 컬럼을 계속 추가하는 것. 인덱스가 커지면 쓰기 비용과 메모리 점유가 늘어 다른 쿼리가 느려집니다. 자주 쓰는 핵심 쿼리에만 적용합니다.

Q.EXPLAIN 결과에서 커버링 인덱스 사용 여부를 어떻게 확인하나요?

Extra 항목에 인덱스만 사용했다는 표시가 나옵니다.

DB표시
MySQLExtra 에 Using index
PostgreSQLIndex Only Scan

주의할 것은 비슷한 이름의 다른 표시입니다.

표시
Using index커버링. 테이블을 읽지 않았다
Using index condition인덱스에서 조건을 걸렀지만 테이블은 읽었다
Using where테이블을 읽은 뒤 걸렀다

PostgreSQL 의 Index Only Scan 은 조건이 하나 더 붙습니다. 가시성 맵이 최신이어야 하므로, 갱신이 잦은 테이블은 통계 갱신 전까지 힙 접근이 남습니다.

흔한 실수: Using index condition 을 커버링으로 읽는 것. 이름이 비슷하지만 테이블 접근이 남아 있습니다.

Q.SELECT *를 지양해야 하는 이유를 인덱스 관점에서 설명해주세요

필요 없는 컬럼까지 요구하면 커버링이 깨지기 때문입니다.

INDEX (user_id, status, amount)

SELECT amount FROM orders WHERE user_id = ?    -- 인덱스만으로 끝난다
SELECT * FROM orders WHERE user_id = ?         -- 테이블을 다시 읽어야 한다

인덱스에서 위치를 찾은 뒤 기본키로 테이블을 또 읽는 단계가 행마다 붙습니다. 결과가 1,000행이면 1,000번입니다.

그 밖의 이유도 있습니다.

이유내용
전송량쓰지 않는 컬럼까지 네트워크로 나른다. 큰 텍스트 컬럼이면 특히
스키마 변경에 취약컬럼이 추가되면 응답 구조가 조용히 바뀐다
의도 불명어떤 컬럼이 필요한지 코드에서 읽히지 않는다

흔한 실수: 컬럼 수가 적으면 상관없다고 보는 것. 커버링 여부가 갈리면 컬럼 수와 무관하게 접근 횟수가 배로 늘어납니다.

먼저 스스로 답해보고 아래 답변과 견줘보세요. 막히는 부분은 문제로 확인할 수 있어요.

읽었으면 문제로 확인해보세요

데이터베이스 문제를 풀면 틀린 문제가 자동으로 노트에 쌓입니다. 가입 없이 5문제를 먼저 풀어볼 수도 있어요.