🔍 검색 키워드: MySQL slow query, 느린 쿼리 로그, MySQL 성능 진단, 쿼리 최적화, EXPLAIN, slow_query_log, MySQL 병목
MySQL 서버를 운영하다 보면 "어떤 쿼리가 느린지 알 수 없다"는 답답한 상황을 마주치게 됩니다. 특히 야간에 갑자기 데이터베이스 응답이 느려지면, 어디부터 손을 대야 할지 막막하죠. 이 글에서는 MySQL slow query log를 활용해 느린 쿼리를 찾아내고 체계적으로 최적화하는 방법을 알려드립니다.
증상: 느린 쿼리는 어떻게 나타나나?
MySQL이 느려질 때 보이는 전형적인 증상들:
- 애플리케이션이 데이터베이스 요청에서 멈춰 있음
- 커넥션 풀이 고갈되었다는 에러 메시지
- DB CPU 사용률이 100%에 가까움
- 특정 시간대(주로 야간 배치 시간)에 연쇄적으로 느려짐
원인 분석: slow query log 활성화 및 확인
1단계: slow query log 활성화
MySQL 설정 파일(my.cnf 또는 my.ini)에 다음을 추가합니다:
[mysqld] slow_query_log = 1 slow_query_log_file = /var/log/mysql/slow-query.log long_query_time = 2 log_queries_not_using_indexes = 1
MySQL 재시작:
sudo systemctl restart mysql
2단계: slow query log 확인
cat /var/log/mysql/slow-query.log
또는 MySQL 클라이언트에서:
SET SESSION long_query_time = 0; SELECT sleep(3); SELECT * FROM large_table WHERE id > 1000000;
로그 파일에 다음과 같이 기록됩니다:
# Time: 2026-09-09T08:15:23.456789Z # User@Host: user@[localhost] # Query_time: 3.234567 Lock_time: 0.000123 Rows_sent: 5000 Rows_examined: 1000000 SELECT * FROM large_table WHERE id > 1000000;
중요한 메트릭 해석:
- Query_time: 쿼리 실행에 걸린 전체 시간
- Rows_examined: 서버가 검사한 행의 수 (클수록 비효율적)
- Rows_sent: 클라이언트로 반환된 행의 수
- 비율이 중요: Rows_examined / Rows_sent 비율이 크면 불필요한 행을 많이 검사 중
해결방법: EXPLAIN으로 원인 찾기 및 최적화
1단계: EXPLAIN으로 쿼리 실행 계획 분석
느린 쿼리 앞에 EXPLAIN을 붙여 실행 계획을 확인합니다:
EXPLAIN SELECT * FROM users WHERE created_at > '2026-01-01' AND status = 'active';
결과에서 확인할 항목:
- type: 조인 타입. ALL은 풀 테이블 스캔(매우 느림), ref나 eq_ref는 양호
- possible_keys: 사용할 수 있는 인덱스 목록
- key: 실제로 사용한 인덱스. NULL이면 인덱스를 사용하지 않음
- rows: 서버가 검사할 것으로 예상하는 행 수. 작을수록 좋음
- Extra: 추가 정보. Using filesort, Using temporary는 성능 악화 신호
2단계: 인덱스 추가 또는 최적화
느린 쿼리에서 자주 필터링되는 컬럼에 인덱스를 추가:
ALTER TABLE users ADD INDEX idx_status_created (status, created_at);
복합 인덱스(composite index) 작성 시 주의:
- WHERE 절에서 사용되는 순서대로 컬럼 배열
- 카디널리티가 높은 컬럼(고유값이 많은)을 먼저 배치
3단계: 쿼리 리팩토링
- 불필요한 컬럼 제거: SELECT * 대신 필요한 컬럼만 명시
- 조인 최적화: 큰 테이블 조인 전에 필터링
- 서브쿼리 제거: 불가피한 경우 JOIN으로 변경
예시:
# 느린 쿼리 (서브쿼리 + 풀 조인) SELECT * FROM orders WHERE user_id IN (SELECT id FROM users WHERE status = 'active'); # 최적화된 쿼리 (JOIN + 인덱스) SELECT o.* FROM orders o INNER JOIN users u ON o.user_id = u.id WHERE u.status = 'active';
실무 체크리스트
| 단계 | 확인 항목 | 개선 방법 |
|---|---|---|
| 진단 | slow query log 활성화? | MySQL 설정 파일에 slow_query_log = 1 추가 |
| 분석 | Rows_examined / Rows_sent 비율? | 비율이 100 이상이면 인덱스 확인 필요 |
| 분석 | EXPLAIN type = ALL? | 풀 테이블 스캔 중. 인덱스 추가 필수 |
| 최적화 | WHERE 절 컬럼에 인덱스? | 복합 인덱스로 효율성 증대 |
| 최적화 | 불필요한 컬럼 선택? | SELECT *를 구체적인 컬럼으로 변경 |
| 모니터링 | 주기적으로 느린 쿼리 검토? | 주 1회 slow query log 분석 스케줄 추천 |
느린 쿼리는 서비스 전체의 병목입니다. 정기적으로 slow query log를 점검하고 체계적으로 최적화하면, 사용자 경험은 물론 서버 리소스 효율도 크게 개선됩니다.