MySQL을 운영하다 보면 가장 자주 만나는 문제 중 하나가 slow query입니다. 평소에는 괜찮던 API가 갑자기 느려지거나, 특정 화면만 유독 오래 걸릴 때 실제 원인을 따라가 보면 결국 느린 SQL 하나가 병목인 경우가 많습니다.
이 글에서는 아래 내용을 정리합니다.
- MySQL 쿼리가 느릴 때 어디부터 확인할지
- 인덱스와 실행 계획을 어떻게 봐야 할지
- 정렬, 풀스캔, 조인에서 자주 생기는 문제
핵심은 slow query는 SQL 문장 하나만 보는 것이 아니라, 실행 계획과 데이터 접근 방식까지 함께 봐야 풀린다는 점입니다.
slow query는 왜 생길까
대표적인 원인은 아래와 같습니다.
- 인덱스를 타지 못하는 조건
- 너무 많은 행을 읽는 풀스캔
- 큰 정렬이나 임시 테이블
- 비효율적인 조인 순서
- 반환 row 수가 과도한 쿼리
즉, “쿼리가 느리다”는 현상 뒤에는 실제로는 여러 종류의 병목이 숨어 있습니다.
먼저 무엇을 봐야 할까
입문 단계에서는 아래 순서가 가장 실용적입니다.
- 어떤 쿼리가 느린지 찾기
EXPLAIN으로 실행 계획 보기- 인덱스 사용 여부 확인
- 읽는 row 수가 비정상적으로 많은지 보기
- 정렬과 조인 비용 확인
즉, 감으로 SQL을 고치기보다 실제 실행 방식부터 확인하는 것이 중요합니다.
EXPLAIN에서 특히 봐야 하는 것
EXPLAIN을 보면 초보자도 아래 항목부터 보면 도움이 됩니다.
typekeyrowsExtra
특히:
ALL: 풀스캔 가능성key = NULL: 인덱스 미사용 가능성rows가 과도하게 큼: 읽는 양이 너무 많음Using filesort,Using temporary: 정렬/임시 테이블 비용 가능성
이런 신호는 slow query 분석의 출발점이 됩니다.
인덱스는 왜 중요한가
많은 slow query는 결국 필요한 행만 빠르게 찾지 못해서 생깁니다. 인덱스가 맞지 않으면 MySQL은 훨씬 많은 데이터를 읽어야 합니다.
특히 아래 경우를 자주 봅니다.
- WHERE 조건 컬럼에 인덱스 없음
- 조인 키에 인덱스 없음
- ORDER BY와 조건 순서가 어긋남
- 함수 적용으로 인덱스를 못 탐
즉, 인덱스가 있다는 사실보다 “이 쿼리 패턴에 맞는 인덱스인가”가 더 중요합니다.
자주 보이는 실수
1. SELECT * 를 습관적으로 사용한다
필요 없는 컬럼까지 많이 읽으면 I/O가 커지고, 커버링 인덱스 이점도 줄어들 수 있습니다.
2. LIMIT 없이 큰 결과를 그대로 읽는다
데이터 탐색용 쿼리가 서비스 요청 경로에 들어오면 급격히 느려질 수 있습니다.
3. 인덱스를 너무 많이 추가한다
읽기 성능은 좋아질 수 있어도 쓰기 비용과 관리 복잡도가 커집니다.
정리할 때 어떤 관점이 좋을까
slow query를 볼 때는 아래 질문이 좋습니다.
- 이 쿼리는 몇 row를 읽는가
- 정말 필요한 row만 읽는가
- 인덱스로 바로 찾을 수 있는가
- 정렬과 조인 비용이 큰가
이 질문에 답하면서 보면 원인을 훨씬 빨리 좁힐 수 있습니다.
FAQ
Q. 인덱스만 추가하면 무조건 해결되나
아닙니다. 잘못된 인덱스는 효과가 거의 없고, 쿼리 구조 자체를 바꿔야 할 때도 많습니다.
Q. EXPLAIN만 보면 충분한가
출발점으로는 좋지만, 실제 트래픽과 데이터 분포까지 봐야 더 정확합니다.
Q. 입문자는 무엇부터 익히면 좋을까
EXPLAIN, 인덱스 기본, WHERE/ORDER BY/JOIN 관계를 먼저 익히면 매우 큰 도움이 됩니다.
Read Next
- 트래픽이 늘면서 연결 수 문제가 생긴다면 MySQL too many connections 가이드로 이어집니다.
- 잠금 대기와 트랜잭션 문제는 MySQL lock wait timeout 가이드와 같이 보면 좋습니다.
심사 대기 중에는 광고 대신 관련 가이드를 먼저 보여줍니다.
먼저 읽어볼 가이드
검색 유입이 많은 핵심 글부터 이어서 보세요.
- 미들웨어 트러블슈팅 가이드: Redis vs RabbitMQ vs Kafka 개발자를 위한 미들웨어 트러블슈팅 허브 글입니다. Redis, RabbitMQ, Kafka 중 어떤 증상부터 먼저 봐야 하는지와 어떤 문제 패턴이 각 시스템에 가까운지 정리합니다.
- Kubernetes CrashLoopBackOff: 먼저 볼 것들 startup failure, probe, config, resource limit 관점에서 CrashLoopBackOff를 어떻게 나눠서 봐야 하는지 정리한 가이드입니다.
- Kafka consumer lag가 계속 늘 때: 트러블슈팅 가이드 Kafka consumer lag가 계속 늘어날 때 무엇부터 봐야 하는지 정리합니다. poll 주기, 처리 속도, rebalance, consumer 설정까지 실전 기준으로 다룹니다.
- Kafka Rebalancing Too Often 가이드 Kafka consumer group에서 rebalance가 너무 자주 일어날 때 membership flapping, poll timing, protocol, assignment churn을 어떤 순서로 봐야 하는지 설명하는 실전 가이드입니다.
- Docker container가 계속 재시작될 때: 먼저 확인할 것들 exit code, command failure, environment mistake, health check 관점에서 Docker restart loop를 푸는 실전 가이드입니다.
심사 대기 중에는 광고 대신 관련 가이드를 먼저 보여줍니다.