MySQL 슬로 쿼리 로그 읽는 법
슬로 쿼리 로그는 이미 갖고 있는 가장 값싼 데이터베이스 프로파일러입니다. 항목 하나가 long_query_time 이상 걸린(또는 log_queries_not_using_indexes 가 켜져 있으면 인덱스를 쓰지 않은) 문장 하나이며, 걸린 시간, 잠금을 기다린 시간, 몇 줄을 읽어 몇 줄을 돌려줬는지가 함께 남습니다.
파일을 이 페이지에 놓으면 보드에 바로 나옵니다. 총 시간 순으로 줄 세운 쿼리 지문(파레토 그림), 지문별 읽은 줄 ÷ 보낸 줄, 잠금 대기, 그리고 가장 심하게 훑는 쿼리를 짚는 「먼저 볼 것」 한 줄입니다. 아래 한 줄 명령은 서버에서 같은 질문에 답합니다.
무엇이 남나
다음을 모두 만족하는 문장이 남습니다.
- 관리 문장(ALTER TABLE · ANALYZE TABLE · CREATE INDEX · OPTIMIZE TABLE 등)이 아니거나,
log_slow_admin_statements가 켜져 있습니다. long_query_time초 이상 걸렸거나,log_queries_not_using_indexes가 켜져 있고 행을 찾는 데 인덱스를 쓰지 않았습니다.min_examined_row_limit줄 이상을 읽었습니다.log_throttle_queries_not_using_indexes로 걸러지지 않았습니다.
long_query_time 의 기본값은 10초, 최솟값은 0 이며 마이크로초 단위까지 줄 수 있습니다. 로그는 기본으로 꺼져 있습니다. 최소 설정은 다음과 같습니다.
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
log_output = FILE파일을 읽을 때 알아 둘 점입니다.
- 처음 잠금을 얻는 시간은 실행 시간에 들어가지 않습니다.
- 문장은 실행을 마치고 잠금을 다 푼 뒤에 쓰이므로, 파일 순서가 실행 순서와 다를 수 있습니다.
- 해석되지 않는 문장(문법 오류)은 남지 않습니다.
- 문장 안의 비밀번호는 평문으로 남지 않게 고쳐 씁니다.
log_timestamps가 파일 속 시각의 시간대를 정합니다.
항목 하나, 줄마다
# Time: 2026-10-18T10:41:02.551000Z
# User@Host: report[report] @ [10.0.3.5] Id: 5102
# Query_time: 12.880415 Lock_time: 0.412003 Rows_sent: 1 Rows_examined: 9120400
SET timestamp=1792320062;
SELECT COUNT(*) FROM order_items WHERE created_at >= '2026-10-01';| 줄 | 담긴 것 |
|---|---|
# Time: | 항목을 쓴 시각입니다. |
# User@Host: | 계정, 클라이언트 호스트나 주소, 연결 번호입니다. |
# Query_time: | 실행 시간(초)입니다. |
Lock_time: | 잠금을 얻는 데 쓴 시간(초)입니다. |
Rows_sent: | 클라이언트로 보낸 줄 수입니다. |
Rows_examined: | 서버 계층에서 읽은 줄 수입니다. 저장 엔진 안의 처리는 세지 않습니다. |
SET timestamp= | 문장 실행을 시작한 Unix 시각입니다. |
| 문장 | SQL 본문이며 여러 줄일 수 있고 세미콜론으로 끝납니다. 기본 데이터베이스가 바뀌면 use db; 줄이 붙습니다. |
위 항목은 한 줄을 돌려주려고 912만 줄을 읽었고, 잠금을 0.4초 기다렸고, 12.9초 걸렸습니다. 이 비율이 가장 먼저 볼 값입니다.
log_slow_extra 를 켜면 늘어나는 칸
log_slow_extra 를 켜면 # Query_time 줄에 문장별 계수가 붙습니다. Thread_id · Errno · Killed · Bytes_received · Bytes_sent, 핸들러 읽기(Read_first · Read_last · Read_key · Read_next · Read_prev · Read_rnd · Read_rnd_next), 정렬 계수(Sort_merge_passes · Sort_range_count · Sort_rows · Sort_scan_count), Created_tmp_disk_tables · Created_tmp_tables · Start · End 입니다. 한 파일에 붙은 줄과 안 붙은 줄이 섞일 수 있으며 칸 수로 구분합니다. 아래 명령은 앞 네 칸만 쓰므로 어느 쪽이든 동작합니다.
읽는 법
| 신호 | 대개 뜻하는 것 | 다음 할 일 |
|---|---|---|
| Rows_examined 가 Rows_sent 보다 훨씬 큼 | 몇 줄을 찾으려고 훑고 있습니다. 인덱스가 없거나 쓸 수 없거나, 인덱스 없는 칼럼으로 거릅니다. | 문장을 EXPLAIN 하고 WHERE · ORDER BY 칼럼을 보십시오. |
| Query_time 중 Lock_time 이 큰 몫 | 일을 하기보다 다른 트랜잭션을 기다렸습니다. | 그 시각에 잠금을 쥔 것을 찾으십시오. 긴 트랜잭션, 대량 갱신, DDL. |
| 같은 모양의 문장이 많음 | 한 번은 적당해도 한 쿼리 모양이 전체 시간을 차지합니다. | 지문으로 묶어 Query_time 을 더하고 1등부터 고치십시오. |
| 항목이 몇 분에 몰림 | 늘 느린 쿼리보다는 부하 급증, 배치 작업, 경합 사건입니다. | 배포 · cron 작업 · 웹 서버 로그와 시각을 맞춰 보십시오. |
문장 모양으로 묶어 Query_time 을 더하는 것이 슬로 로그 요약 도구가 하는 일이며, MySQL 은 이를 위해 mysqldumpslow 를 함께 줍니다. 0.8초짜리라도 하루 5만 번 도는 쿼리가 30초짜리 보고서 한 번보다 중요합니다.
슬로 쿼리 로그용 한 줄 명령
# Query_time 줄은 $3 = Query_time, $5 = Lock_time, $7 = Rows_sent, $9 = Rows_examined 로 나뉩니다. 모두 항목 여러 개가 든 표본 파일로 실행해 확인했습니다.
파일에 문장이 몇 개인가
grep -c '^# Query_time' slow.log1초 넘게 걸린 문장은 몇 개인가
awk '/^# Query_time/ && $3 > 1' slow.log | wc -l문장 수와 실행 시간 합계
awk '/^# Query_time/ {s += $3; n++} END {printf "%d statements, %.1f s total\n", n, s}' slow.log가장 느린 문장과 SQL 첫 줄
awk '/^# Query_time/ {qt = $3; next} /^(#|SET timestamp|use )/ {next} qt != "" {print qt, $0; qt = ""}' slow.log | sort -rn | head보낸 한 줄당 읽은 줄 수, 나쁜 순
awk '/^# Query_time/ && $7 > 0 {printf "%d %s/%s\n", $9/$7, $9, $7}' slow.log | sort -rn | head잠금 대기가 0.1초를 넘는 문장
awk '/^# Query_time/ && $5 > 0.1 {print $5, $3}' slow.log | sort -rn느린 문장을 내는 계정
awk '/^# User@Host/ {print $3}' slow.log | sort | uniq -c | sort -rn느린 문장을 내는 클라이언트 주소
grep '^# User@Host' slow.log | grep -oE '\[[0-9.]+\]' | sort | uniq -c | sort -rn문장 첫 줄의 모양으로 묶기 — 리터럴을 ? 와 N 으로 바꿈
awk '/^SET timestamp/ {getline; print}' slow.log | sed -E "s/'[^']*'/?/g; s/[0-9]+/N/g" | sort | uniq -c | sort -rn | head시간당 항목 수(log_timestamps 가 다르게 정하지 않으면 UTC)
grep '^# Time:' slow.log | cut -c9-21 | uniq -c
자주 묻는 질문
Query_time 과 Lock_time 은 무엇이 다른가요?
Query_time 은 실행 시간, Lock_time 은 잠금을 얻는 데 쓴 시간입니다. 처음 잠금을 얻는 시간은 실행 시간에 넣지 않습니다.
빠른 쿼리가 슬로 로그에 있는 이유는?
log_queries_not_using_indexes 가 켜져 있고 그 쿼리가 행을 찾는 데 인덱스를 쓰지 않았거나, long_query_time 이 아주 낮게(0 이면 전부 남음) 설정돼 있습니다.
항목 순서가 뒤섞인 이유는?
MySQL 은 문장이 끝나고 잠금을 푼 뒤에 씁니다. 일찍 시작한 긴 문장은 늦게 시작한 짧은 문장보다 뒤에 남습니다. 시작 시각은 SET timestamp 로 보십시오.
Rows_examined 가 Rows_sent 보다 크게 높으면?
몇 줄을 돌려주려고 많은 줄을 읽었다는 뜻입니다. 대개 알맞은 인덱스가 없어 전체나 넓은 범위를 훑습니다. EXPLAIN 으로 확인하십시오.
재시작 없이 슬로 로그를 켤 수 있나요?
됩니다. slow_query_log 와 slow_query_log_file 은 실행 중에 바꿀 수 있는 전역 시스템 변수이며, slow_query_log 를 1 로 두면 켜집니다.
슬로 로그에 비밀번호가 남나요?
문장 속 비밀번호는 그대로 남지 않게 고쳐 씁니다. 다만 SQL 본문과 리터럴 값은 그대로 남으므로 로컬에서 분석하는 것이 중요합니다.