MySQL의 GROUP BY절은 그룹핑뿐만 아니라 정렬 작업도 같이 이루어진다. 요구사항에 따라 GROUP BY절을 최적화 해야하는 이유와 방법을 알아보고자 한다.
EXPLAIN
SELECT m.gender
FROM member m
GROUP BY m.gender
Extra 필드를 보면 Using filesort
라는 문구를 볼 수 있다. GRUOP BY절의 실행 결과는 그룹핑 대상으로 선언한 컬럼을 기준으로 기본적인 정렬이 이루어진다. 만약, 정렬 작업이 불필요한 요구사항에서 Group By절만 사용한다면 결과 데이터가 많아질 수록 정렬 작업으로 인해 성능이 크게 저하될 수 있음을 인지하여야 한다.
정렬 작업의 유무는 GRUOP BY절의 실행 결과가 20,000건이라고 가정하고 테스트 했을 때, 평균 334ms
-> 154ms
정도로 유의미한 성능 개선을 확인할 수 있었다. 실행 결과 데이터가 많은 환경일 수록 더욱 유의미한 차이가 발생할 수 있다.
DISTINCT절은 그룹핑(중복 제거)을 한다는 점에 GROUP BY절 동일하게 동작할 수 있지만, 정렬을 하지 않는다.
DISTINCT의 내부 동작이 GROUP BY로 이루어져있다는 블로그 글을 보기도 했지만 공식 문서에서는 관련 내용을 확인하지 못했다... 대신 DISTINCT절과 GROUP BY의 전환에 관한 간단한 문서를 공유한다.
MySQL Reference - 8.2.1.18 DISTINCT Optimization
명시적으로 정렬을 하지 않겠다고 선언하는 방법이다.
EXPLAIN
SELECT m.gender
FROM member m
GROUP BY m.gender
ORDER BY NULL
위에서 본 Using filesort
Extra 문구가 제거됨을 확인할 수 있다.
.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();
GROUP BY절이나 ORDER BY절은 명시된 컬럼의 순서가 인덱스를 구성하는 컬럼의 순서와 같으면 인덱스를 사용할 수 있다. 또한 ORDER BY절은 정렬되는 각 컬럼의 오름차순, 내림차순 옵션이 인덱스와 같거나 또는 정반대의 경우에만 사용가능하다.
인덱스를 사용하면 정렬 작업의 진행 유무와는 다르지만, 빠른 시간에 정렬 작업의 진행됨에 따라 성능 하락이 무의미하도록 개선할 수 있다.
Group By의 묵시적인 정렬은 MySQL 8.0 버전부터는 더이상 실행되지 않는다고 합니다.
글 잘 보고 갑니다