인덱스가 안 타는 케이스
인덱스를 만들어도 쿼리 패턴에 따라 인덱스를 사용하지 않고 풀 스캔을 하는 경우가 있습니다. 실무에서 흔히 발생하는 함정들입니다.
1. 앞에 와일드카드 (LIKE '%...')
WHERE name LIKE '%철수' -- X 인덱스 미사용
WHERE name LIKE '%철%' -- X
WHERE name LIKE '철수%' -- O 인덱스 사용
인덱스는 정렬된 트리이므로 앞부분이 고정되어야 탐색 가능합니다.
2. 인덱스 컬럼에 함수/연산 적용
-- 함수
WHERE UPPER(name) = 'CHEOLSU' -- X
WHERE name = 'cheolsu' -- O
-- 날짜 연산
WHERE YEAR(created_at) = 2026 -- X
WHERE created_at >= '2026-01-01' AND created_at < '2027-01-01' -- O
-- 산술 연산
WHERE age + 1 = 30 -- X
WHERE age = 29 -- O
인덱스에는 원본 값이 저장되어 있어서, 변환된 값으로는 탐색할 수 없습니다.
3. 묵시적 형변환
-- id 컬럼이 INT 타입일 때
WHERE id = '1' -- X 문자열 → 숫자 변환 발생
WHERE id = 1 -- O
타입이 다르면 DB가 내부적으로 형변환을 적용 → 함수 적용과 동일하게 인덱스 무력화.
4. OR 조건
-- name, age 각각에만 인덱스가 있을 때
WHERE name = '철수' OR age = 28 -- X 보통 풀스캔
해결:
UNION ALL로 분리- 또는
(name, age)복합 인덱스 추가
5. 부정 조건
WHERE name != '철수' -- X
WHERE name NOT IN (...) -- X
WHERE name NOT LIKE '철수%' -- X
부정 조건은 거의 모든 행이 매칭되므로 인덱스를 타도 이득이 없어 옵티마이저가 풀스캔을 선택하는 경우가 많습니다.
6. 복합 인덱스의 선두 컬럼 누락
CREATE INDEX idx ON users(name, age);
WHERE age = 28 -- X 선두 컬럼(name) 없음
WHERE name = '철수' -- O
WHERE name = '철수' AND age = 28 -- O
복합 인덱스는 왼쪽부터 순서대로 사용해야 합니다.
7. 카디널리티가 너무 낮을 때
CREATE INDEX idx_gender ON users(gender); -- 남/여 2개뿐
WHERE gender = '남' -- 옵티마이저가 풀스캔 선택
값 종류가 적으면 인덱스를 타도 결과가 너무 많아 풀스캔이 더 빠릅니다.
확인 방법
EXPLAIN으로 실행 계획을 확인할 수 있습니다.
EXPLAIN SELECT * FROM users WHERE UPPER(name) = '철수';
type: ALL→ 풀 스캔type: ref,range,const→ 인덱스 사용key컬럼이NULL→ 인덱스 미사용