개발 학습 실습실

이 사이트의 실습은 자바스크립트를 켜야 움직입니다. 아래는 같은 내용을 글로 적은 것입니다.

공부 › DB › 인덱스와 실행 계획

복합·커버링 인덱스

복합 인덱스는 컬럼 순서대로만 쓸 수 있다

여러 컬럼을 묶은 인덱스는 첫 컬럼부터 차례로 정렬돼 있습니다. 그래서 WHERE 조건이 앞 컬럼부터 채워져야 탐색 범위를 좁힐 수 있고, 범위 조건을 만나면 그 뒤 컬럼은 범위를 좁히는 데 쓰이지 못합니다. 조회하는 컬럼이 모두 인덱스 안에 있어서 테이블을 찾아가지 않아도 되는 경우, 그 인덱스를 커버링 인덱스라고 합니다.

문서·표준 대조 · MySQL 공식 문서

이 실습에 나오는 개념

복합 인덱스와 Leftmost Prefix
복합 인덱스는 두 개 이상의 컬럼을 묶어 만든 인덱스다. 첫 번째 컬럼으로 먼저 정렬하고 그 안에서 두 번째 컬럼으로 정렬하는 식으로 구성되므로, 인덱스의 왼쪽 컬럼부터 차례로 조건에 사용될 때만 탐색에 쓸 수 있다. (a, b, c) 인덱스는 a 조건이나 a와 b 조건에는 쓰이지만 b만 있는 조건에는 쓰이지 않는다(예외적으로 MySQL 8.0.13 이상은 선두 컬럼의 값 종류가 적을 때 옵티마이저가 Skip Scan으로 이 인덱스를 쓸 수 있다).
커버링 인덱스
쿼리가 필요로 하는 모든 컬럼이 인덱스에 포함되어 있어, 테이블의 행을 읽지 않고 인덱스만으로 결과를 만들 수 있는 경우를 말한다. 테이블 접근이 생략되어 조회가 빨라진다. MySQL의 실행 계획에서는 Extra 항목에 Using index로 표시된다.
클러스터드 인덱스와 세컨더리 인덱스
클러스터드 인덱스는 인덱스의 끝(리프)에 행 데이터 자체가 저장된 인덱스로, 테이블의 데이터가 그 키 순서대로 저장된다. MySQL InnoDB에서는 기본 키가 클러스터드 인덱스다. InnoDB의 세컨더리(보조) 인덱스는 리프에 기본 키 값을 가지고 있어, 세컨더리 인덱스로 찾은 뒤 그 기본 키로 클러스터드 인덱스를 한 번 더 찾아 행을 읽는다.
카디널리티와 선택도
카디널리티는 컬럼에 들어 있는 서로 다른 값의 개수다. 서로 다른 값이 많을수록(예: 주민번호, 이메일) 조건 하나로 걸러지는 행이 적어 인덱스의 효과가 크고, 값의 종류가 적을수록(예: 성별) 조건에 해당하는 행이 많아 인덱스의 효과가 작다. 선택도는 카디널리티를 테이블의 행 수로 나눈 값으로, 1에 가까울수록 조건 하나로 행이 잘 걸러진다.
실행 계획(EXPLAIN)
데이터베이스가 쿼리를 어떤 방법으로 실행할지 정한 계획이다. 어떤 인덱스를 쓰는지, 테이블을 어떤 순서로 조인하는지, 몇 행을 읽을 것으로 예상하는지를 보여 준다. 쿼리 앞에 EXPLAIN을 붙여 확인하며, 쿼리가 느릴 때 원인을 찾는 출발점이다.

이렇게 바꿔 보세요

처음 설정으로 실행하면

  1. orders 테이블에는 100만 행이 있습니다. 인덱스는 member_id → status → created_at 순서로 정렬돼 있습니다. 첫 컬럼이 같은 것끼리 모여 있고, 그 안에서 둘째 컬럼 순, 다시 그 안에서 셋째 컬럼 순입니다.
  2. 1번째 컬럼 member_id: 조건 member_id = 7이 있고, 인덱스의 첫 컬럼이므로 이 조건으로 읽을 범위를 좁힙니다.
  3. 2번째 컬럼 status: 조건 status = 'PAID'이 있고, 앞 컬럼들이 모두 정해져 있으므로 이 조건으로 읽을 범위를 좁힙니다.
  4. 3번째 컬럼 created_at: 조건 created_at >= '2026-09-01'이 있고, 앞 컬럼들이 모두 정해져 있으므로 이 조건으로 읽을 범위를 좁힙니다.
  5. 인덱스에서 좁혀진 범위의 항목 50개를 읽습니다.
  6. SELECT *는 인덱스에 없는 컬럼도 필요합니다. 남은 50건마다 테이블(클러스터드 인덱스)로 가서 행 전체를 읽어 옵니다.

실행 결과

코드

CREATE INDEX idx_orders ON orders (member_id, status, created_at);

SELECT *
  FROM orders
 WHERE member_id = 7
   AND status = 'PAID'
   AND created_at >= '2026-09-01';

이 모형의 전제

100만 행 테이블에서 member_id = 7은 전체의 0.1%, status = 'PAID'는 50%, 날짜 조건은 10%가 해당한다고 가정한 계산입니다. 인덱스 컬럼을 왼쪽부터 써야 하고 범위 조건 뒤의 컬럼은 탐색 범위를 좁히지 못한다는 규칙은 MySQL 공식 문서의 복합 인덱스 설명을 따랐습니다. 순서가 끊긴 뒤의 조건을 인덱스 단계에서 걸러내는 것(Using index condition)은 MySQL의 인덱스 컨디션 푸시다운 동작입니다. 실제 옵티마이저는 통계에 따라 다른 계획을 고를 수 있으며, 여기서는 이 인덱스를 쓴다고 전제했습니다. 실제 DB의 EXPLAIN 결과와 대조한 것은 아닙니다.

앞 실습: 인덱스와 풀 스캔 · 다음 실습: 실행 계획 읽기

이 실습에 나오는 개념

이 실습에서 보는 것

설정

코드


      
    

그림

실행 비교

이렇게 바꿔 보세요