커버링 인덱스는 특정 쿼리의 조건과 반환에 필요한 컬럼을 모두 포함하여, 테이블의 실제 행을 추가로 읽지 않고 인덱스만으로 결과를 만들 수 있는 인덱스입니다. 커버링 여부는 인덱스 자체의 고정된 종류가 아니라 해당 쿼리와 인덱스의 관계로 결정됩니다.
예를 들어 다음 쿼리가 있다고 가정하겠습니다.
SELECT created_at, status
FROM orders
WHERE member_id = ?;(member_id, created_at, status) 복합 인덱스가 있다면 조건 검색과 SELECT 결과를 모두 인덱스에서 얻을 수 있습니다. InnoDB의 Secondary Index 조회에서 Clustered Index로 다시 이동하는 과정이 줄어들기 때문에, 많은 행을 조회할수록 I/O 절감 효과가 커질 수 있습니다. MySQL의 전통적인 실행 계획에서는 Extra의 Using index가 커버링 인덱스 사용을 나타내는 경우가 많습니다.
하지만 SELECT 컬럼을 모두 넣어 인덱스를 지나치게 넓게 만들면 저장 공간과 쓰기 비용이 증가하고 캐시 효율이 낮아집니다. 자주 실행되고 성능 영향이 큰 쿼리를 중심으로 설계해야 합니다.
JPA에서 엔티티 전체를 조회하면 커버링 인덱스를 사용하기 어려운 이유는 무엇인가요?
엔티티 조회는 일반적으로 매핑된 여러 컬럼을 SELECT하므로 인덱스에 모든 컬럼이 포함되지 않으면 실제 행 조회가 필요합니다. 필요한 필드만 DTO나 인터페이스 Projection으로 조회하면 더 작은 인덱스로 커버링할 가능성이 높아집니다.
SELECT *는 커버링 인덱스에 불리한가요?대부분 그렇습니다. 테이블의 모든 컬럼을 인덱스에 넣지 않는 이상 인덱스만으로 결과를 만들 수 없고, 불필요한 데이터 전송도 늘어납니다. 필요한 컬럼만 명시하는 것이 커버링 인덱스와 유지보수 측면에서 유리합니다.
커버링 인덱스라면 항상 가장 빠른 실행 계획인가요?
아닙니다. 조회 범위가 매우 넓거나 인덱스가 지나치게 크면 전체 테이블 스캔 등 다른 계획이 더 저렴할 수 있습니다. 데이터 분포와 캐시 상태까지 영향을 주므로
EXPLAIN ANALYZE와 실제 부하 측정으로 확인해야 합니다.