Slow Query 탐지 방법

1,524 단어·4 분·원문(.md)

슬로우 쿼리는 말 그대로 데이터베이스에서 실행하는데 지나치게 오랜 시간이 걸리는 비효율적인 쿼리를 의밓나다.

데이터가 적을 대는 문제가 없던 쿼리도 서비스가 성장하여 데이터가 쌓이면 느려지게 될 수 있고.

혹은 인덱싱이 되지않거나 비효율적인 탐색을 한다거나, 뭐 그런 이유에서든.

db, cpu와 메모리를 과도하게 점유하고, 애플리케이션과 db간 커넥션을 오랫동안 물고 있게 되어 결국 서비스 전체의 마비나 지연을 초래한다.

  • 임계치 threshold 기준: 보통 몇 초 이상 걸리는 쿼리를 정의할 수 있는데, 상대적인것때문에 0.5초도 무거운 서비스 특징이라면 그게 슬로우 쿼리가 되는것이다.
  • 주요 원인: 적절한 인덱스가 없거나 full scan, 불필요한 데이터를 join하는경우나 비효율적인 sort등에서 발생한다.

슬로우 쿼리를 탐지하는 2가지 주요 접근법 #

슬로우 쿼리를 찾아내는 방법은 크게 db자체가 제공하는 기본 로그 기능을 활용하는 방법과 외부 모니터링 툴을 사용하는 방법으로 나뉜다.

DB 자체 기능 slow query log #

mysql, postgresql, oracle등 대부분의 rdb는 쿼리 실행 시간이 특정 임계치를 넘으면 별도의 파일이나 테이블에 해당 쿼리를 기록하는 기능을 제공한다.

  • 장점: db 엔진 자체에서 측정하므로 가장 정확하고, 추가적인 툴 도입 비용이 들디 않는다.
  • 단점: 로그 파일을 직접 열어서 분석해야하므로 가독성이 떨어질 수 있으며, 여러대의 db를 운영할경우 관리가 번거롭다

APM Application Performance Monitoring 툴 활용 #

데이터독, 뉴렐릭, pinpoint, scouter등 애플리케이션 모니터링 도구를 통해 어떤 api 경로에서 어떤 쿼리가 오래걸렸는지 시각적으로 추적한다.

  • 장점: 웹 대시보드를 통해 직관적으로 확인할 수 있고, 사용자의 어떤 요청이 슬로우쿼리르 유발햇는지 앞뒤 맥락을 파악하기 쉽다
  • 단점: 유료 툴의 경우 비용이 발생해, 에이전트 설치등 초기 세팅이 필요하다.

정리하면 db 자체기능은 분석 주체가 디비 자체고 텍스트파일 log or db테이블에 기록하여 쿼리 자체 정보에 집중해 맥락을 파악한다. 주기적인 배치분석이나 dba 상세 튜닝에서 쓰인다.

apm 모니터링툴은 외부 모니터링 에이전트 대시보드가 주체며 시각화된 그래프나 타임라인 trace를 파악할 수 있으며 요청이 api 단위의 전체 실행 흐름 파악이용이하다. 실시간 장애감지나 개발자의 빠른 원인 파악에 도움을 준다.

예시 자료 및 결과 셋 (명령어, 설정, 분석) #

가장 널리 쓰이는 관계형 데이터베이스중 하나인 mysql을 기준으로, 슬로우 쿼리를 분석하는 과정을 알아보자.

슬로우 쿼리 로그 설정 my.cnf or mysqld.cnf #

**[mysqld]
# 슬로우 쿼리 로그 활성화 (1: On, 0: Off)
slow_query_log = 1

# 로그 파일이 저장될 경로 지정
slow_query_log_file = /var/log/mysql/mysql-slow.log

# 이 시간(초)을 초과하는 쿼리만 기록 (예: 2초 이상 걸리면 기록)
long_query_time = 2.0

# 인덱스를 타지 않는 쿼리는 시간이 짧아도 무조건 기록 (선택 사항)
log_queries_not_using_indexes = 1**

위처럼 시간(2.0)이 초과된 쿼리가 실행된다면 /var/log/mysql/mysql-slow.log 경로에 파일에 다음과 같은 정보가 쌓인다.

# 터미널에서 실시간 로그 확인
tail -f /var/log/mysql/mysql-slow.log

# Time: 2026-03-15T14:31:00.123456Z
# User@Host: app_user[app_user] @  [192.168.1.50]  Id: 100
# Query_time: 3.510234  Lock_time: 0.000100 Rows_sent: 10  Rows_examined: 1500000
SET timestamp=1646092000;
SELECT * FROM user_logs WHERE action = 'login' ORDER BY created_at DESC LIMIT 10;

192.168.1.50 서버에서 요청한 SELECT * FROM user_logs WHERE action = 'login' ... 쿼리가 실행되는데 3.51초가 걸렸다. 클라이언트에게 보낸 줄은 10줄(Rows_sent) 뿐인데 이를 찾기 위해 db 내부적으로 150만줄(Rows_examined)을 뒤졌다는 비효율을 보여준다.

원인 분석을 위한 실행 계획 확인 (Explain) #

로그를 통해 문제의 쿼리를 찾았다면 db 터미널 혹은 툴에서 쿼리 앞에 Explain을 붙여 db가 이 쿼리를 어떻게 실행하고 있는지 실행 계획을 확인해야한다.

EXPLAIN SELECT * FROM user_logs WHERE action = 'login' ORDER BY created_at DESC LIMIT 10;

출력 예시가 표처럼 뜰텐데

id,select_type,table,type,possible_keys,key,rows,Extra
--------------------------------------------------------
1,SIMPLE,user_logs,ALL,NULL,NULL,1500000,Using filesort

요약하면 type이 ALL이고 key가 NULL인것은 인덱스를 사용하지 못하고 테이블 전체를 스캔하고 있다는 뜼이며

또한 Extra에 Using filesort가 잇는것은 메모리나 디스크상에서 무거운 정렬 작업을 수행했다는 의미로 인덱스 추가가 시급한 상태다.

이렇게 감지된 슬로우 쿼리를 해결하기위해 인덱스를 추가하거나 쿼리 구조를 개선하는 접근법으로 이어갈 수 있다.

SRE/question/q_38.md