MySQL 인덱스 최적화 전략: 느린 쿼리를 100배 빠르게 만드는 마법
1. 서론: 인덱스는 왜 중요한가?
데이터베이스의 성능은 곧 서비스의 응답 속도와 직결됩니다. 데이터가 수만 건일 때는 문제가 없다가, 수백만 건을 넘어서는 순간 웹 사이트가 느려지는 경험을 해보셨을 겁니다. 이때 가장 먼저 확인해야 할 것이 바로 **인덱스(Index)**입니다.
인덱스는 책의 맨 뒤에 있는 ‘색인’과 같습니다. 방대한 데이터 속에서 내가 원하는 정보를 찾기 위해 처음부터 끝까지 다 뒤지는 ‘Full Table Scan’을 방지하고, B-Tree 구조를 통해 원하는 위치로 즉시 점프하게 해줍니다.
2. 실무 사례: 500만 건의 주문 테이블 쿼리 최적화
상황: 쇼핑몰 프로젝트에서 특정 사용자의 최근 주문 내역을 조회하는 쿼리가 5초 이상 소요되고 있습니다.
기존 쿼리:
SELECT * FROM orders
WHERE user_id = 12345
ORDER BY created_at DESC
LIMIT 10;
문제점: user_id에 인덱스가 없다면, MySQL은 500만 건의 데이터를 모두 확인해야 합니다.
해결책: 복합 인덱스(Composite Index) 생성
단순히 user_id에만 인덱스를 거는 것이 아니라, 정렬 조건까지 포함한 복합 인덱스를 생성하여 성능을 극대화합니다.
# 최적의 인덱스 생성
CREATE INDEX idx_user_id_created_at ON orders (user_id, created_at DESC);
이 설정을 통해 MySQL은 특정 사용자의 데이터를 찾자마자 이미 정렬된 상태로 데이터를 읽어오기 때문에, 5초 걸리던 쿼리가 0.01초 내외로 단축됩니다.
3. 인덱스 설계 시 반드시 지켜야 할 원칙
- 카디널리티(Cardinality)가 높은 컬럼 선택: 성별(남/여)처럼 중복도가 높은 컬럼보다는 주민번호나 이메일처럼 중복도가 낮은 컬럼에 인덱스를 걸어야 효과적입니다.
- WHERE 절뿐만 아니라 ORDER BY, GROUP BY도 고려: 쿼리 실행 계획(Explain)을 확인하여 인덱스가 정렬 과정에서도 사용되는지 확인해야 합니다.
- 인덱스 과유불급: 인덱스는 조회 속도를 높여주지만, INSERT, UPDATE, DELETE 시에는 인덱스도 함께 갱신해야 하므로 쓰기 성능을 저하시킵니다. 꼭 필요한 곳에만 생성하세요.
4. 인덱스가 작동하지 않는 나쁜 쿼리 예시
인덱스를 만들어 놓고도 제대로 활용하지 못하는 경우입니다:
- 컬럼을 가공하는 경우:
WHERE ABS(score) > 100(인덱스 사용 불가) ->WHERE score > 100 OR score < -100으로 변경. - 부정형 조건을 사용하는 경우:
WHERE status != 'DELETED'(인덱스 효율이 매우 떨어짐). - LIKE 연산자 앞에 %를 붙이는 경우:
WHERE name LIKE '%찬열'(Full Scan 유발).
5. 마치며: EXPLAIN을 습관화하자
내가 만든 인덱스가 실제로 잘 작동하는지 확인하는 가장 좋은 방법은 쿼리 앞에 EXPLAIN 키워드를 붙여 실행 계획을 분석하는 것입니다. type 항목이 ref나 range가 아닌 ALL로 나온다면 즉시 개선이 필요합니다.
데이터가 쌓이기 전에 미리 인덱스 전략을 수립하여, 사용자에게 쾌적한 환경을 제공하는 개발자가 됩시다.