반응형
Database Tuning Series
성능의 양날의 검,
DB 인덱스를 망치는 5가지 실수
안녕하세요, code-resting입니다. 쿼리가 느려지면 가장 먼저 하는 일이 "인덱스 추가"입니다. 하지만 인덱스는 공짜가 아닙니다. 잘못 설계된 인덱스는 INSERT 성능을 떨어뜨릴 뿐만 아니라, 오히려 옵티마이저를 헷갈리게 만들어 전체 시스템 성능을 저하시키기도 하죠. 오늘은 실무에서 자주 범하는 인덱스 설계의 치명적인 실수들을 짚어보겠습니다.
1. 낮은 카디널리티(Cardinality)에 인덱스 걸기
성별(남/여)이나 활성화 여부(Y/N)처럼 값의 종류가 적은 컬럼에 인덱스를 거는 것은 최악의 선택 중 하나입니다.
- ❌ 문제점: 인덱스를 읽고 다시 테이블 레코드를 찾아가는(Random I/O) 비용이 그냥 전체 테이블을 다 읽는(Full Scan) 비용보다 커지면 옵티마이저는 인덱스를 무시합니다.
- ✅ 해결책: 값의 분포도가 높은(Unique한 값이 많은) 컬럼부터 우선적으로 인덱스를 고려하세요.
2. 복합 인덱스의 '순서' 무시
복합 인덱스 (A, B, C)를 만들었다고 해서 B나 C만으로 검색할 때 인덱스가 정상 작동할까요? 정답은 "그럴 수도 있고 아닐 수도 있다"입니다.
인덱스: (status, created_at)
-- 인덱스 잘 탐
SELECT * FROM orders WHERE status = 'COMPLETED' AND created_at > '2026-01-01';
-- 인덱스 효율 급감 (Index Skip Scan이 작동하지 않는 한 Full Scan 유도)
SELECT * FROM orders WHERE created_at > '2026-01-01';
복합 인덱스는 왼쪽 컬럼부터 순서대로 정렬되어 저장됩니다. 첫 번째 컬럼이 조건절에 없다면 인덱스 구조를 제대로 활용할 수 없습니다.
3. 인덱스 컬럼 가공하기
의외로 정말 많은 개발자가 저지르는 실수입니다. 조건절의 왼쪽(LHS)을 가공하면 인덱스를 탈 수 없습니다.
-- ❌ 인덱스 사용 불가 (컬럼을 DATE_FORMAT 함수로 감쌈)
SELECT * FROM logs WHERE DATE_FORMAT(reg_date, '%Y%m%d') = '20260302';
-- ✅ 인덱스 사용 가능 (비교 대상을 가공)
SELECT * FROM logs WHERE reg_date >= '2026-03-02 00:00:00' AND reg_date < '2026-03-03 00:00:00';
4. 중복되고 과도한 인덱스
인덱스가 많을수록 조회는 빨라질지 몰라도, CUD(Create, Update, Delete) 성능은 기하급수적으로 떨어집니다.
- 인덱스가 5개인 테이블에 데이터를 1건 넣으면, 내부적으로 6번의 쓰기 작업이 발생합니다.
- 이미
(A, B)인덱스가 있다면(A)인덱스는 삭제해도 무방합니다. (Prefix 매칭 때문)
💡 결론: EXPLAIN을 습관화하세요
인덱스 설계의 정답은 데이터의 분포와 쿼리 패턴에 달려 있습니다. 코드를 배포하기 전, 반드시 EXPLAIN 명령어를 통해 type이 ALL(Full Scan)인지 ref/range(Index Scan)인지 확인하는 습관을 들이세요. 2026년에도 DB 튜닝의 기본은 실행 계획 확인입니다.
반응형
'DB' 카테고리의 다른 글
| [실전] Spring Data Elasticsearch로 구현하는 '한글 자동 완성' 검색 시스템 (0) | 2026.03.05 |
|---|---|
| RDBMS로 감당 안 되는 검색, Elasticsearch로 10배 빠르게 만들기 (역색인의 비밀) (0) | 2026.03.04 |
| Spring Boot에서 Redis를 기본적인 Cache(spring-boot-starter-cache)로 사용하기 (0) | 2021.08.02 |
| REDIS (0) | 2021.03.20 |
| MsSQL Shrink (0) | 2021.02.25 |
댓글