EXPLAIN
- MySQL이 쿼리를 실행할 때 어떤 인덱스를 썼는지, 얼마나 효율적으로 조회했는지 보여주는 분석 도구
key : 실제 사용한 인덱스 이름
type : 조회 방식, ref면 꽤 괜찮은 편
rows : mysql이 예상한 조회 행 수
extra : 추가 실행 정보
!!! Using filesort !!!
- 인덱스 순서대로 바로 결과를 못 뽑아서 MySQL이 따로 정렬했다는 뜻
즉, 조건 검색은 인덱스로 했지만, order by 순서와 인덱스 순서가 안 맞음
-> mysql이 결과를 다시 정렬함
= Using filesort
SELECT *
FROM workout_segments
WHERE workout_id = 1
AND is_anomaly = 1
ORDER BY segment_index ASC;
1. workout_id가 1인 구간 찾기
2. 그중 이상 구간만 찾기
3. segment_index 순서대로 정렬하기
기존 인덱스: (workout_id, is_anomaly, anomaly_score)
근데, segment_index 순서대로 정렬하길 원함 -> 기존 인덱스에서 anomaly_score는 필요 없음
즉, workout_id, is_anomaly 조건으로 데이터 찾았는데, 정렬은 segment_index로 되지 않음
그래서 MySQL이 따로 정렬한 것 = Using filesort 발생
<새로 제안한 인덱스>
CREATE INDEX idx_workout_segments_workout_anomaly_segment_index
ON workout_segments (workout_id, is_anomaly, segment_index);
- 근데 새로 제안한 인덱스는 구간 순서대로 보기 좋은 인덱스임.
- 만약 이상 점수 순으로 보기 좋은 인덱스를 원한다면 기존을 유지하는게 맞음
SELECT *
FROM workouts
WHERE user_id = 1
AND started_at BETWEEN '2026-07-02 00:00:00' AND '2026-07-08 23:59:59'
ORDER BY started_at DESC;
1. user_id가 1인 운동 찾기
2. started_at이 이번 주 범위 안인 것 찾기
3. started_at 최신순으로 정렬하기
근데 실제로 MySQL이 고른 인덱스는 (user_id, sport_type, started_at)
- 쿼리에 sport_type이 없음 즉, 엔티티에는 있지만 쿼리에는 없음
아, 테이블 만들 때 인덱스 생성함 -> 인덱스를 잘 생성하는게 중요함
근데, 고르는건 MySQL 옵티마이저의 선택이라 이후 수정이 필요한 것
!! Using filesort는 조건 검색 후 MySQL이 결과를 따로 정렬했다는 뜻이다.
즉, 인덱스를 사용하긴 했지만 ORDER BY까지 인덱스로 해결하지는 못한 상태다.
그래서 WHERE 조건 컬럼 뒤에 ORDER BY 컬럼이 이어지는 복합 인덱스를 추가하면 정렬 비용을 줄일 수 있다.
그럼 어떻게 수정?
1. FORCE INDEX로 수정할 수 있는데
EXPLAIN
SELECT *
FROM workouts FORCE INDEX (idx_workouts_user_started_at)
WHERE user_id = 1
AND started_at BETWEEN '2026-07-02 00:00:00' AND '2026-07-08 23:59:59'
ORDER BY started_at DESC;
-> MYSQL아 네가 알아서 인덱스 고르지 말고 강제로 (user_id, started_at) 인덱스 써봐라
2. ANALYZE TABLE 사용
- MySQL 한테 테이블 통계 정보 다시 계산해줘, 그래야 다음에 인덱스 잘 고를 수 있어
=> MySQL은 쿼리 실행 전에 어떤 인덱스 쓸지 판단, 이때 테이블 통계 정보 참고
=> 오래되건 데이터 많이 바뀌면 이상한 인덱스 고름 그래서 ANALYZE TABLE 사용으로 통계 갱신
<개선 결과>
CREATE INDEX idx_workout_segments_workout_anomaly_segment_index
ON workout_segments (workout_id, is_anomaly, segment_index);
ANALYZE TABLE workout_segments;
ANALYZE TABLE workouts;
ANALYZE TABLE condition_events;
EXPLAIN
SELECT *
FROM workout_segments
WHERE workout_id = 1
AND is_anomaly = 1
ORDER BY segment_index ASC;
- 새 인덱스 생성 -> 통계 갱신 -> 확인
| 1 | SIMPLE | workout_segments | ref | uk_workout_segments_workout_index,idx_workout_segments_anomaly,idx_workout_segments_workout_anomaly_segment_index | idx_workout_segments_workout_anomaly_segment_index | 9 | const,const | 1 | 100.00 |
-> Extra = NULL 값 !!
EXPLAIN
SELECT *
FROM workouts FORCE INDEX (idx_workouts_user_started_at)
WHERE user_id = 1
AND started_at BETWEEN '2026-07-02 00:00:00' AND '2026-07-08 23:59:59'
ORDER BY started_at DESC;
| 1 | SIMPLE | workouts | range | idx_workouts_user_started_at | idx_workouts_user_started_at | 13 | 1 | 100.00 | Using index condition; Backward index scan |
-> Using file 없어짐!
'프로젝트 이슈' 카테고리의 다른 글
| RunGuard 개발 - 캐시 무효화 검증 (0) | 2026.07.10 |
|---|---|
| RunGuard 개발 - k6 & Tomcat thread & HikariCP connection pool (0) | 2026.07.08 |
| RunGuard 개발 로컬 캐시(Caffeine) (0) | 2026.07.08 |
| RunGuard 개발 예외처리 (0) | 2026.07.08 |
| RunGuard 개발 DTO (0) | 2026.07.08 |