레이블이 MySQL인 게시물을 표시합니다. 모든 게시물 표시
레이블이 MySQL인 게시물을 표시합니다. 모든 게시물 표시

목요일

MySQL slow query 완벽 진단 및 최적화 가이드

🔍 검색 키워드: 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를 점검하고 체계적으로 최적화하면, 사용자 경험은 물론 서버 리소스 효율도 크게 개선됩니다.

일요일

MySQL deadlock 완벽 해결 가이드

MySQL 고동시성 환경에서 deadlock은 흔한 문제입니다.

원인: 두 개 이상의 트랜잭션이 서로의 락을 기다리면서 교착상태 발생.

해결방법:
1. 트랜잭션 순서 통일 - 모든 트랜잭션에서 같은 순서로 테이블 접근
2. 타임아웃 설정 - innodb_lock_wait_timeout 단축
3. 애플리케이션 재시도 - deadlock 발생 시 자동 재시도
4. 격리 레벨 검토 - READ COMMITTED 사용
5. 인덱스 확인 - 전체 스캔 방지

가장 효과적인 방법은 접근 순서 통일과 재시도 로직입니다.