slow 쿼리는 어떻게 탐지할 수 있을까?
- 개발
- 2026. 2. 8. 23:09
Slow Query는 단순히 실행 시간이 길었던 쿼리를 의미하는 것이 아니라, 데이터베이스 관리 시스템(DBMS)이 내부적으로 측정한 쿼리 실행 시간이 사전에 정의된 임계값을 초과했다고 판단해 기록한 쿼리 로그를 말합니다. 즉, 애플리케이션이나 사용자 체감 기준이 아니라, DB 엔진 관점에서 '비정상적으로 오래 실행되었다'고 판단된 쿼리가 Slow Query로 분류됩니다.
slow쿼리는 어떻게 측정 할 수 있을까?
Slow Query는 DBMS가 쿼리 실행 시간을 측정한 뒤, 사전에 정의된 기준을 초과한 쿼리를 기록함으로써 판단할 수 있습니다.
본 글에서는 MySQL을 기준으로 설명하겠습니다.
MySQL은 실행 시점에 서버 설정 파일(my.cnf)에 정의된 설정을 로드하며, 이 설정을 기준으로 모든 쿼리의 실행 시간을 측정합니다.
이후 쿼리 실행 시간이 설정된 임계값을 초과할 경우, 해당 쿼리를 Slow Query로 판단하여 로그로 기록합니다.
하지만 현재 제 환경에서는 MySQL을 Docker 컨테이너로 실행하고 있기 때문에,
my.cnf 설정 파일은 로컬이 아닌 컨테이너 내부에 존재합니다.
이로 인해 Docker 설정을 변경하지 않는 이상, 해당 파일에 직접 접근하거나 수정하는 것은 어렵습니다.
따라서 MySQL 설정과 slow query 로그를 로컬에서도 확인하고 관리할 수 있도록, 볼륨을 통해 설정 파일과 로그 경로를 외부로 마운트하는 방식을 사용하려고 합니다.
볼륨 마운트
volumes:
- ./conf:/etc/mysql/conf.d
설정 경로를 conf.d로 지정한 이유는, MySQL이 해당 디렉터리 하위의 .cnf 파일들을 모두 로드하도록 설계되어 있기 때문입니다.
이를 통해 설정을 파일 단위로 분리하여 관리할 수 있으며, slow query, charset, InnoDB 설정 등을 각각 독립적으로 구성할 수 있습니다.
물론 단일 설정 파일(my.cnf)을 통해서도 동일한 설정이 가능하지만, conf.d 방식은 설정 확장성과 관리 측면에서 더 유연한 구조를 제공합니다.
하지만 실제 도커로 들어가서 확인한 결과
/etc/mysql/conf.d
sh-5.1# ls -al
total 8
drwxr-xr-x 2 root root 4096 Apr 15 2025 .
drwxr-xr-x 1 root root 4096 Feb 8 06:56 ..
sh-5.1#
기본적으로 제공되는 설정 파일은 존재하지 않으며, MySQL은 설정 파일을 자동으로 생성하지 않습니다.
따라서 slow query 설정을 적용하려면 개발자가 직접 설정 파일을 제공해야 합니다.
그렇다면, 어떻게 slow쿼리인지 DBMS에게 알려줄 수 있을까요?
방법은 총 3가지입니다.
설정 파일을 통해 알려준다.
설정 파일은 DBMS가 자동으로 생성하지 않으며, 개발자가 직접 작성하여 제공해야 합니다.
본 환경에서는 ./conf 디렉터리에 slow-query.cnf 파일을 직접 생성하였습니다.
[mysqld]
slow_query_log = ON
long_query_time = 0.1
slow_query_log_file = /logs/slow.log
[mysqld] 섹션을 명시하지 않으면 해당 설정은 MySQL 서버 프로세스에 적용되지 않으며,
결과적으로 slow query 설정이 정상적으로 동작하지 않습니다.
MySQL 설정 파일은 섹션 기반으로 동작하며, slow query와 같이 서버 동작에 영향을 주는 옵션은 반드시 [mysqld] 섹션 하위에 정의되어야 합니다.
DB 실행 옵션으로 알려준다. (도커 컴포즈 기준)
services:
mysql:
image: mysql:8.0
command:
- --slow_query_log=ON
- --long_query_time=0.1
이 설정은 MySQL 컨테이너가 시작될 때 내부적으로 다음과 같이 실행되는 것과 동일합니다.
mysqld --slow_query_log=ON --long_query_time=0.1
즉, 설정 파일을 거치지 않고 DBMS에게 slow query 판단 기준을 직접 전달하는 방식입니다.
GLOBAL 변수로 알려준다.
MySQL은 실행 중에도 GLOBAL 시스템 변수를 변경하여 slow query 판단 기준을 즉시 적용할 수 있습니다.
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
이 방식은 설정 파일이나 실행 옵션을 수정하지 않고, 이미 실행 중인 MySQL 서버의 전역 설정을 런타임에 변경합니다.
GLOBAL 변수를 변경하는 방식은 어디까지나 일시적인 테스트 목적에 적합한 방법입니다.
설정이 영속되지 않고 DB 재시작 시 초기화되기 때문에, 운영 환경이나 재현 가능한 환경 구성에는 적합하지 않습니다.
따라서 slow query 설정을 지속적으로 유지하고 관리하기 위해서는
설정 파일을 사용하는 방식 또는 DB 실행 옵션을 전달하는 방식 중 하나를 선택하여 진행하는 것이 적절합니다.
즉, DBMS에게 "이 시간 이상 걸리면 느린 쿼리로 판단하라"는 기준을 전달하는 것이며,
예를 들어 기준 시간을 1초로 설정하면 실행 시간이 1초를 초과한 쿼리들이 slow query로 평가되어 기록됩니다.
테스트를 위해 일부러 시간초를 0.000010정도로 뒀습니다.
현재 슬로 쿼리가 켜져있는지 어느 파일에 저장이 되었는지 알 수 있습니다.
현재 슬로 쿼리 확인 방법
SHOW GLOBAL VARIABLES LIKE 'slow_query_log';
이 쿼리는 slow_query가 켜져있는지 확인 할 수 있습니다.

현재 슬로 쿼리 시간은 몇 초로 설정 되었는지 확인
SHOW GLOBAL VARIABLES LIKE 'long_query_time';

슬로 쿼리 로그 파일 위치
SHOW GLOBAL VARIABLES LIKE 'slow_query_log_file';

해당 경로는 로컬 파일 시스템이 아니라 Docker 컨테이너 내부 경로이기 때문에,
로컬 환경에서는 직접 확인할 수 없습니다. 따라서 Docker 컨테이너에 직접 접속하여 로그를 확인해야 합니다.
컨테이너 내부에서 확인해보면 다음과 같은 slow query 로그를 확인할 수 있습니다.
# Time: 2026-02-08T11:02:37.482305Z
# User@Host: application[application] @ [ip] Id: 45
# Query_time: 0.000810 Lock_time: 0.000004 Rows_sent: 1 Rows_examined: 2
SET timestamp=1770548557;
select p1_0.id,p1_0.author,p1_0.category,p1_0.content,p1_0.created_at,p1_0.deleted_at,p1_0.is_public,p1_0.title,p1_0.updated_at from boards p1_0 where p1_0.deleted_at is null order by p1_0.created_at desc limit 10;
slow query 로그에서 우리가 확인해야 할 핵심 정보는 다음 네 가지입니다.
- Query_time
실제 쿼리 실행 시간으로, slow query 판단의 가장 핵심적인 지표입니다. - Lock_time
락 대기 시간으로, 쿼리 자체의 문제인지 혹은 동시성 문제인지를 판단하는 데 사용됩니다. - Rows_examined
쿼리 실행 과정에서 읽은 row 수로, 인덱스 사용 여부 및 효율성을 판단하는 핵심 지표입니다. - SQL
실제 실행된 쿼리로, 이후 EXPLAIN 등을 통해 튜닝 대상이 되는 쿼리입니다.
현재 환경에서는 long_query_time을 0.0001초로 설정했기 때문에,
위 쿼리는 DBMS 기준으로 slow query 로그에 기록된 것이 맞습니다.
다만 일반적인 운영 환경에서는 보통 1초 내외로 기준 시간을 설정하여, 실제로 성능에 영향을 줄 가능성이 있는 쿼리만을 선별합니다.
여기서 고민이 생겼습니다. 어떻게 모니터링 할 수 있을까요?
AOP를 활용한 모니터링 방식도 고려해 보았습니다. AOP를 통해 메서드나 API 처리 시간을 측정하고,
일정 기준을 초과할 경우 알람을 보내는 구조입니다. 하지만 이 방식에는 한 가지 결정적인 의문이 남았습니다.
AOP를 통해 감지한 지연이 과연 DB의 slow query를 의미하는가라는 점입니다.
AOP에서 측정되는 값은 DB가 아닌 애플리케이션 관점의 처리 시간입니다. 이 시간에는 SQL 실행 시간뿐만 아니라,
트랜잭션 대기, 커넥션 획득, 직렬화, 네트워크 지연 등 여러 요소가 함께 포함됩니다.
즉, AOP 기반 측정 결과는 DB가 판단한 slow query가 아니라 애플리케이션이 느리다고 판단한 결과에 가깝습니다.
이로 인해 실제 DB에서는 정상적으로 처리된 쿼리임에도 알람이 발생할 수 있으며,
반대로 DB 기준의 slow query를 놓칠 가능성도 존재합니다.
이러한 이유로 AOP를 slow query 모니터링의 판단 기준으로 사용하는 것은 정확하지 않다고 판단했습니다.
보다 정확한 알람을 위해, 애플리케이션 기준으로 측정되는
AOP 방식은 제외하고 DB가 slow query로 판단한 결과를 기반으로 모니터링을 구성하기로 결정했습니다.
마무리
코드가 생각보다 많은 관계로 설명은 나중에 하기로 하고.. 아무튼 중요한 핵심은 슬로 쿼리는 db에서 발생된 결과를 읽어야 한다는점이라고 생각합니다. 찾다보니 많은 다른 인프라들을 활용해 작업할 수는 있었습니다. 하지만 그걸 쓴다고 해서 제가 이해할 거 같지 않아 과감히 포기했습니다.
'개발' 카테고리의 다른 글
| API 게이트 웨이 (0) | 2026.02.09 |
|---|---|
| 슬로쿼리 모니터링 인터페이스 제작 (0) | 2026.02.09 |
| 마스킹 확장 개발기 (2) (0) | 2026.02.06 |
| 마스킹 확장 개발기 (1) (0) | 2026.02.05 |
| API 트래픽이 늘어나면 우리는 어떤 선택들을 하게 될까? (0) | 2026.02.03 |