본 문서는 MySQL InnoDB 스토리지 엔진을 기준으로, 인덱스의 구조·동작·스캔 방식과 설계 시 유의점을 체계적으로 정리합니다.
요약: 세컨더리 인덱스는 항상 PK로 한 단계 더 조회가 필요하므로, 가능하면 커버링 인덱스를 설계하여 Back to Table을 줄이는 것이 좋습니다.
(A, B, C) 순으로 만들어졌는데 B 또는 C만 단독 조건으로 사용할 때 등 선두 컬럼 불일치 상황.(A, B, C)는 다음 패턴을 효율적으로 지원합니다: A, (A, B), (A, B, C) 및 각각의 범위 조건.B만, C만 조건이면 선두 불일치로 Range Scan이 어려워 Index Full Scan/Full Table Scan으로 전락할 수 있습니다.ORDER BY A, B가 인덱스 순서와 동일하면 filesort 없이 처리 가능.GROUP BY A, B도 유사하게 인덱스 정렬 활용.INDEX(user_id, created_at, amount)로 SELECT user_id, created_at, amount ... 를 충족.WHERE func(col) = ..., WHERE col + 1 = ..., LIKE '%suffix' 는 인덱스 활용이 어렵습니다.LIKE 'prefix%'), 생성(가상) 컬럼 + 인덱스.type: range, ref, eq_ref, ALL 등)과 사용 인덱스를 확인하십시오.USE INDEX, FORCE INDEX)는 최후의 수단으로 일시적 교정에 활용합니다.-- 복합 인덱스: 사용자 → 기간 → 정렬키
CREATE INDEX ix_orders_user_created_amount
ON orders(user_id, created_at, amount);
-- 전형적 쿼리 1: 사용자/기간 필터 + 시간순 정렬 (filesort 회피)
SELECT user_id, created_at, amount
FROM orders
WHERE user_id = ?
AND created_at BETWEEN ? AND ?
ORDER BY created_at DESC;
-- 전형적 쿼리 2: 최근 사용자별 첫 주문 시각 (MIN) - 루스 스캔 후보
SELECT user_id, MIN(created_at)
FROM orders
GROUP BY user_id;
-- 필요한 컬럼만 읽고 끝나므로 Back to Table 회피
CREATE INDEX ix_orders_cover ON orders(user_id, created_at, amount);
SELECT user_id, created_at, amount
FROM orders
WHERE user_id = ?
AND created_at >= ?;
-- 비권장: 함수 적용으로 인덱스 사용 제한
SELECT * FROM users WHERE DATE(created_at) = '2025-11-01';
-- 권장: 범위 조건으로 변환
SELECT * FROM users
WHERE created_at >= '2025-11-01 00:00:00'
AND created_at < '2025-11-02 00:00:00';