MySQL Group By 성능 개선 with Distinct

KwonKusang·2023년 9월 19일
1
post-thumbnail

MySQL의 GROUP BY절은 그룹핑뿐만 아니라 정렬 작업도 같이 이루어진다. 요구사항에 따라 GROUP BY절을 최적화 해야하는 이유와 방법을 알아보고자 한다.

문제 상황

Query

EXPLAIN
SELECT m.gender
FROM member m
GROUP BY m.gender

Result

Extra 필드를 보면 Using filesort 라는 문구를 볼 수 있다. GRUOP BY절의 실행 결과는 그룹핑 대상으로 선언한 컬럼을 기준으로 기본적인 정렬이 이루어진다. 만약, 정렬 작업이 불필요한 요구사항에서 Group By절만 사용한다면 결과 데이터가 많아질 수록 정렬 작업으로 인해 성능이 크게 저하될 수 있음을 인지하여야 한다.

성능 TEST

정렬 작업의 유무는 GRUOP BY절의 실행 결과가 20,000건이라고 가정하고 테스트 했을 때, 평균 334ms -> 154ms 정도로 유의미한 성능 개선을 확인할 수 있었다. 실행 결과 데이터가 많은 환경일 수록 더욱 유의미한 차이가 발생할 수 있다.

해결 방안

1. DISATINCT 절

DISTINCT절은 그룹핑(중복 제거)을 한다는 점에 GROUP BY절 동일하게 동작할 수 있지만, 정렬을 하지 않는다.

DISTINCT의 내부 동작이 GROUP BY로 이루어져있다는 블로그 글을 보기도 했지만 공식 문서에서는 관련 내용을 확인하지 못했다... 대신 DISTINCT절과 GROUP BY의 전환에 관한 간단한 문서를 공유한다.
MySQL Reference - 8.2.1.18 DISTINCT Optimization

2. ORDER BY NULL

명시적으로 정렬을 하지 않겠다고 선언하는 방법이다.

Query

EXPLAIN
SELECT m.gender
FROM member m
GROUP BY m.gender
ORDER BY NULL

Result

위에서 본 Using filesort Extra 문구가 제거됨을 확인할 수 있다.

QueryDSL

.orderBy(null)을 작성하면 컴파일 에러가 발생한다. 우아한 형제들의 테크 콘서트에서 향로님께서 제시해주신 방법은 다음과 같은 싱글톤 객체를 만드는 것이다.

public class OrderByNull extends OrderSpecifier {

    public static final OrderByNull DEFAULT = new OrderByNull();
    
    private OrderByNull() {
        super(Order.ASC, NullExpression.DEFAULT, NullHandling.Default);
    }
}

// QueryDSL
...
.orderBy(OrderByNull.DEFAULT)
.fetch();

3. INDEX 사용

GROUP BY절이나 ORDER BY절은 명시된 컬럼의 순서가 인덱스를 구성하는 컬럼의 순서와 같으면 인덱스를 사용할 수 있다. 또한 ORDER BY절은 정렬되는 각 컬럼의 오름차순, 내림차순 옵션이 인덱스와 같거나 또는 정반대의 경우에만 사용가능하다.
인덱스를 사용하면 정렬 작업의 진행 유무와는 다르지만, 빠른 시간에 정렬 작업의 진행됨에 따라 성능 하락이 무의미하도록 개선할 수 있다.

2024-02-20 추가

Group By의 묵시적인 정렬은 MySQL 8.0 버전부터는 더이상 실행되지 않는다고 합니다.

profile
안녕하세요! 백엔드 개발자 권구상입니다.

4개의 댓글

comment-user-thumbnail
2023년 9월 19일

글 잘 보고 갑니다

답글 달기
comment-user-thumbnail
2023년 9월 19일

explain 습관화 !

답글 달기
comment-user-thumbnail
2023년 12월 6일

안녕하세요 테이블을 조인하고 만들어진 테이블(약 30GB)에서 중복제거를 위해 distinct column1 column2 이런식으로 코드를 돌렸는데요. 3시간이 지나도 계속 돌아가고 있는데 뭐가 잘못된걸까요 ㅠㅠ? ibd 파일 저장 경로에 들어가서 보니까 테이블을 조인할때는 ibd 파일 크기가 계속 커졌는데, distinct를 한 코드에서는 테이블 크기가 112kb로 증가하지 않습니다..

1개의 답글