SJSBiz SJSBiz

느린 쿼리(Slow Query) 로그 분석으로 성능 개선하기

읽는 시간 약 14분
(adsbygoogle = window.adsbygoogle || []).push({});

데이터베이스 성능 튜닝에서 가장 먼저 해야 할 일은 느린 쿼리를 찾아내는 것입니다. MySQL의 Slow Query Log는 실행 시간이나 검사 행 수가 기준을 초과하는 쿼리를 자동으로 기록해주는 강력한 도구입니다. 이 로그를 분석하고 최적화하면 서버 전체의 응답 속도를 획기적으로 개선할 수 있습니다. 이번 글에서는 Slow Query Log 설정부터 분석 방법, 실전 개선 사례까지 상세히 정리해 드리겠습니다.


느린 쿼리 로그란?

MySQL의 Slow Query Log는 설정된 기준보다 오래 실행된 쿼리나, 너무 많은 행을 검사한 쿼리를 별도의 로그 파일에 기록하는 기능입니다. 이 로그를 통해 어떤 쿼리가 성능 병목을 일으키는지 정확히 파악할 수 있으며, EXPLAIN과 결합하여 체계적인 최적화가 가능합니다.

특히 프로덕션 환경에서는 수천 개의 쿼리가 동시에 실행되므로, 눈으로 직접 감지하기 어려운 느린 쿼리를 자동으로 포착하는 것이 필수적입니다.


느린 쿼리 로그 설정 방법

Slow Query Log는 MySQL 설정 파일(my.cnf 또는 my.ini)에서 활성화하고, 기준 시간과 출력 방식을 지정할 수 있습니다.


📋 my.cnf 설정 예시

[mysqld]
# 느린 쿼리 로그 활성화
slow_query_log = 1

# 로그 파일 경로
slow_query_log_file = /var/log/mysql/slow-query.log

# 실행 시간 기준 (초), 2초 이상 실행된 쿼리 기록
long_query_time = 2

# 인덱스를 사용하지 않은 쿼리도 기록 (선택)
log_queries_not_using_indexes = 1

# 최소 검사 행 수 기준 (선택, MySQL 5.1+)
min_examined_row_limit = 1000

설정 변경 후 MySQL을 재시작하거나, 런타임에 다음 명령어로 즉시 적용할 수 있습니다.

-- 런타임 설정 변경 (재시작 없이 적용, 재시작 시 초기화)
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 2;
SET GLOBAL log_queries_not_using_indexes = 'ON';


📋 설정 확인 명령어

-- 현재 설정 확인
SHOW VARIABLES LIKE 'slow_query_log%';
SHOW VARIABLES LIKE 'long_query_time';
SHOW VARIABLES LIKE 'log_queries_not_using_indexes';


느린 쿼리 로그 형식과 구조

Slow Query Log에는 다음과 같은 정보가 기록됩니다.

# Time: 2026-07-23T10:15:30.123456Z
# User@Host: root[root] @ localhost []
# Query_time: 5.234567  Lock_time: 0.001234  Rows_sent: 100  Rows_examined: 500000
SET timestamp=1753269330;
SELECT * FROM orders WHERE status = 'pending' AND created_at < '2024-01-01';
항목의미
Time쿼리 실행 시각
User@Host쿼리를 실행한 사용자와 호스트
Query_time총 실행 시간 (초)
Lock_time테이블 락 대기 시간
Rows_sent클라이언트에 반환된 행 수
Rows_examined검사한 행 수 (중요 지표)

Rows_examined가 Rows_sent보다 훨씬 크다면, 불필요하게 많은 행을 검사하고 있다는 의미이며 인덱스 최적화가 필요합니다.


로그 분석 도구

수동으로 로그를 읽는 것도 가능하지만, 대용량 로그를 효율적으로 분석하려면 전용 도구를 사용하는 것이 좋습니다.


🔧 mysqldumpslow

MySQL에 기본 포함된 명령줄 도구입니다. 로그를 요약하고 정렬하여 가장 자주 발생하거나 가장 오래 걸린 쿼리를 빠르게 찾을 수 있습니다.

# 실행 시간 기준 상위 10개 쿼리
mysqldumpslow -s t -t 10 /var/log/mysql/slow-query.log

# 실행 횟수 기준 상위 쿼리
mysqldumpslow -s c -t 10 /var/log/mysql/slow-query.log

# 특정 테이블 관련 쿼리만 필터
mysqldumpslow -s t -t 10 -g "orders" /var/log/mysql/slow-query.log


🔧 pt-query-digest (Percona Toolkit)

Percona에서 제공하는 고급 분석 도구로, 가장 권장되는 방식입니다. 쿼리 패턴을 그룹화하고, 실행 시간 분포, 히트맵, 예상 최적화 방향까지 제시합니다.

# 설치
sudo apt-get install percona-toolkit

# 기본 분석
pt-query-digest /var/log/mysql/slow-query.log

# 상위 20개 쿼리만 출력
pt-query-digest --limit 20 /var/log/mysql/slow-query.log

# 특정 시간대 로그만 분석
pt-query-digest --since "2026-07-23 09:00:00" --until "2026-07-23 12:00:00" /var/log/mysql/slow-query.log


🔧 MySQL Performance Schema

MySQL 5.6+부터 내장된 Performance Schema를 활용하면 로그 파일 없이도 느린 쿼리를 실시간으로 모니터링할 수 있습니다.

-- 느린 쿼리 통계 확인
SELECT 
    DIGEST_TEXT AS query,
    COUNT_STAR AS exec_count,
    AVG_TIMER_WAIT / 1000000000000 AS avg_time_sec,
    MAX_TIMER_WAIT / 1000000000000 AS max_time_sec,
    SUM_ROWS_EXAMINED AS total_rows_examined
FROM performance_schema.events_statements_summary_by_digest
ORDER BY AVG_TIMER_WAIT DESC
LIMIT 10;


실전 분석 및 개선 사례

로그를 분석했다면 이제 실제로 쿼리를 개선해야 합니다. EXPLAIN과 결합하여 단계별로 접근합니다.


(adsbygoogle = window.adsbygoogle || []).push({});

🚨 사례 1: 풀 테이블 스캔으로 인한 지연

# Time: 2026-07-23T10:15:30.123456Z
# Query_time: 8.456789  Lock_time: 0.002345  Rows_sent: 50  Rows_examined: 2000000
SELECT * FROM products WHERE category_id = 5 AND price > 100000;

문제 분석:

  • Query_time: 8.45초 — 매우 느림
  • Rows_examined: 2,000,000 — 200만 행 검사
  • Rows_sent: 50 — 반환은 50행뿐

EXPLAIN 분석:

EXPLAIN SELECT * FROM products WHERE category_id = 5 AND price > 100000;
-- type: ALL, possible_keys: NULL, rows: 2000000

해결:

-- 복합 인덱스 생성
ALTER TABLE products ADD INDEX idx_category_price (category_id, price);

-- 개선 후 EXPLAIN
-- type: range, key: idx_category_price, rows: 1500

인덱스 추가 후 Query_time이 8.45초에서 0.02초로 개선되었습니다.


🚨 사례 2: 잘못된 복합 인덱스 순서

# Query_time: 3.234567  Rows_sent: 200  Rows_examined: 800000
SELECT * FROM orders 
WHERE status = 'completed' 
  AND created_at BETWEEN '2024-01-01' AND '2024-12-31'
ORDER BY created_at DESC;

기존 인덱스가 (created_at, status)로 생성되어 있었습니다. 하지만 WHERE 조건에서 status = 'completed'는 등호 조건이고, created_at은 범위 조건입니다. 복합 인덱스에서 등호 조건 컬럼을 앞에, 범위 조건 컬럼을 뒤에 배치해야 합니다.

-- 기존 인덱스 제거
DROP INDEX idx_created_status ON orders;

-- 올바른 순서로 재생성
ALTER TABLE orders ADD INDEX idx_status_created (status, created_at);

인덱스 순서 변경 후 Query_time이 3.23초에서 0.15초로 개선되었습니다.


🚨 사례 3: N+1 쿼리 문제

# Query_time: 0.005000  Rows_sent: 1  Rows_examined: 1
# 동일한 패턴이 1000회 반복
SELECT * FROM users WHERE id = 1;
SELECT * FROM users WHERE id = 2;
SELECT * FROM users WHERE id = 3;
...

단일 쿼리는 빠르지만 애플리케이션에서 반복 호출되어 총합이 큰 지연을 만드는 경우입니다. pt-query-digest로 분석하면 동일한 패턴이 그룹화되어 나타납니다.

-- N+1 해결: IN 절로 일괄 조회
SELECT * FROM users WHERE id IN (1, 2, 3, ..., 1000);

-- 또는 JOIN 사용
SELECT u.* FROM users u
JOIN (VALUES (1), (2), (3), ...) AS v(id) ON u.id = v.id;


실무 체크리스트

느린 쿼리를 발견하고 개선하는 체계적인 프로세스를 정리하면 다음과 같습니다.

  • Slow Query Log 활성화 — long_query_time은 보통 1~2초 권장
  • log_queries_not_using_indexes — 인덱스 미사용 쿼리도 함께 포착
  • pt-query-digest로 로그 분석 — 패턴 그룹화 및 우선순위 설정
  • Rows_examined vs Rows_sent 비교 — 비효율적 필터링 탐지
  • EXPLAIN으로 실행 계획 확인 — type, key, Extra 컬럼 중심
  • 인덱스 추가/수정 — 복합 인덱스 순서, 커버링 인덱스 고려
  • 쿼리 재작성 — 서브쿼리 최적화, N+1 제거, LIMIT 적용
  • 개선 후 재측정 — 동일 조건에서 Query_time 비교
  • 주기적 모니터링 — cron으로 pt-query-digest 자동 실행


자주 묻는 질문

long_query_time을 너무 낮추면 안 되나요?

0.1초 이하로 설정하면 로그 파일이 과도하게 커지고 디스크 I/O가 증가할 수 있습니다. 초기에는 1~2초로 설정하고, 주요 병목을 해결한 후 점진적으로 낮추는 것이 좋습니다. 프로덕션에서는 보통 1초, 개발 환경에서는 0.5초를 권장합니다.


Slow Query Log가 너무 커지면 어떻게 하나요?

logrotate를 설정하여 일별 또는 주별로 로그를 분할하고 압축하는 것이 좋습니다. 또는 Performance Schema를 사용하여 파일 기반 로깅 없이 메모리에서 직접 분석할 수도 있습니다.

# logrotate 설정 예시 (/etc/logrotate.d/mysql-slow)
/var/log/mysql/slow-query.log {
    daily
    rotate 7
    compress
    missingok
    notifempty
    create 640 mysql mysql
}


모든 느린 쿼리에 인덱스를 추가해야 하나요?

아니요. 인덱스는 읽기 성능을 높이지만 쓰기 성능을 낮춥니다. INSERT, UPDATE, DELETE 시마다 인덱스도 함께 갱신해야 하므로, 자주 실행되는 쿼리나 Rows_examined가 극단적으로 큰 쿼리에 우선적으로 적용하세요. 테이블당 3~5개 내외의 핵심 인덱스만 유지하는 것이 권장됩니다.


마무리

이번 글에서는 느린 쿼리(Slow Query) 로그 분석으로 성능 개선하기를 자세히 살펴보았습니다. Slow Query Log는 데이터베이스 성능 튜닝의 출발점이자 나침반입니다. 로그를 분석하는 것만으로도 병목의 80%를 찾아낼 수 있으며, EXPLAIN과 결합하면 체계적인 최적화가 가능합니다.

핵심을 정리하면 다음과 같습니다: Slow Query Log를 활성화하여 느린 쿼리를 포착하고, pt-query-digest로 패턴을 분석하며, EXPLAIN으로 실행 계획을 확인하고, 인덱스와 쿼리를 개선한 뒤 반드시 재측정하세요.

느린 쿼리 로그 분석 관련 궁금한 점이나 문제가 있으시면 댓글로 남겨 주세요. 도움이 되셨다면 이 글을 주변에 공유해 주시는 것도 잊지 마세요!


(adsbygoogle = window.adsbygoogle || []).push({});
깨비

함께 보면 좋은 글

댓글 0

첫 댓글을 남겨보세요.

error: Content is protected !!
광고보고 콘텐츠 계속 읽기
원치않으시면 뒤로가기를 해주세요

광고 차단 알림

광고 클릭 제한을 초과하여 광고가 차단되었습니다.

단시간에 반복적인 광고 클릭은 시스템에 의해 감지되며, IP가 수집되어 사이트 관리자가 확인 가능합니다.

광고보고 콘텐츠 계속 읽기
원치않으시면 뒤로가기를 해주세요