SQL 인덱스 원리와 튜닝: 느린 쿼리를 빠르게 만드는 법
데브노트 편집팀·2026.07.02·8분 읽기
ADVERTISEMENT
"인덱스 걸었는데 왜 안 빨라지죠?" 실무에서 정말 자주 나오는 질문입니다. 인덱스는 마법이 아니라 **정렬된 색인(목차)**입니다. 책 뒤 찾아보기처럼, 원리를 알아야 제대로 씁니다. 대부분의 RDB 인덱스는 B-Tree 구조로, 값이 정렬되어 있어 범위 검색과 정렬에 강합니다.
1. 인덱스는 언제 효과가 있나
-- email로 자주 조회한다면
CREATE INDEX idx_users_email ON users (email);
-- 실행 계획 확인 (인덱스 타는지 확인 필수)
EXPLAIN SELECT * FROM users WHERE email = 'a@b.com';
핵심: EXPLAIN으로 실제 인덱스를 타는지(type: ref 등) 반드시 확인하세요. 안 보고 추측하면 안 됩니다.
2. 복합 인덱스와 좌측 우선 규칙
(a, b, c) 순서로 만든 인덱스는 왼쪽부터 연속으로만 활용됩니다.
| WHERE 조건 | 인덱스 사용 |
|---|---|
a = ? | O |
a = ? AND b = ? | O |
b = ? 단독 | X (좌측 a 건너뜀) |
a = ? AND c = ? | a까지만 |
CREATE INDEX idx_orders ON orders (user_id, status, created_at);
-- 잘 탐: WHERE user_id = 5 AND status = 'PAID'
-- 못 탐: WHERE status = 'PAID' (user_id 없이 status만)
컬럼 순서 규칙: 등호(=) 조건 컬럼을 앞에, 범위(>, <) 조건은 뒤에 두고, 같은 등호끼리는 카디널리티(중복 적은 값)가 높은 컬럼을 앞에 둡니다.
3. 인덱스를 무력화하는 안티패턴
-- 나쁨: 컬럼을 가공하면 인덱스 못 씀
SELECT * FROM users WHERE YEAR(created_at) = 2026;
-- 좋음: 컬럼은 그대로, 값 쪽을 가공
SELECT * FROM users
WHERE created_at >= '2026-01-01' AND created_at < '2027-01-01';
주의: LIKE '%검색%'(앞에 와일드카드), OR 남발, 컬럼에 함수/연산 적용은 인덱스를 무력화합니다.
4. 커버링 인덱스로 한 단계 더
쿼리가 필요로 하는 컬럼을 인덱스에 모두 포함하면, 테이블 접근 없이 인덱스만 읽고 끝(index-only scan)나서 가장 빠릅니다.
-- user_id로 조회해 status, created_at만 본다면
CREATE INDEX idx_cover ON orders (user_id, status, created_at);
SELECT status, created_at FROM orders WHERE user_id = 5;
마무리 체크리스트
- 튜닝 전후
EXPLAIN으로 검증 - 복합 인덱스는 좌측 우선, 등호→범위 순서
- WHERE 컬럼에 함수/연산 금지
- 선두
%LIKE 피하기 - 인덱스도 쓰기 비용이 있으니 무분별하게 늘리지 말 것
ADVERTISEMENT
함께 보면 좋은 글
백엔드·인프라· 7분
개발자를 위한 Docker 입문: 이미지·컨테이너·Dockerfile
"제 컴퓨터에선 됐는데요"를 끝내는 Docker. 이미지와 컨테이너의 차이, Dockerfile 작성법, 자주 쓰는 명령어를 처음부터 정리합니다.
2026.07.11
백엔드·인프라· 6분
REST vs GraphQL: 무엇을 언제 선택할까
오버페칭·언더페칭 문제부터 캐싱·러닝커브까지. REST와 GraphQL의 본질적 차이와 프로젝트별 선택 기준을 실전 관점에서 비교합니다.
2026.07.10
백엔드·인프라· 7분
JWT 인증 제대로 이해하기: 세션과 무엇이 다른가
JWT가 무엇이고 왜 쓰는지, 액세스/리프레시 토큰 전략과 저장 위치, 보안 함정까지. 인증 설계의 기본기를 한 번에 정리합니다.
2026.07.09