SQL에서 GROUP BY, DISTINCT, ORDER BY는 데이터 집계나 정렬을 위해 자주 사용되는 구문입니다. 하지만 대용량 데이터에서 무심코 사용하면 성능이 급격히 저하될 수 있습니다. 오늘은 각 구문이 어떻게 동작하는지, 그리고 어떻게 최적화할 수 있는지 살펴보겠습니다.
GROUP BY 최적화
GROUP BY는 동일한 값을 가진 행들을 그룹화하여 집계 연산을 수행합니다.
1) 기본 동작 방식
- 서버는 대상 컬럼을 기준으로 데이터를 정렬(SORT)한 후 그룹핑 처리
- 집계 함수(SUM, COUNT, AVG 등)와 함께 사용
SELECT category, COUNT(*) AS cnt
FROM products
GROUP BY category;
2) 성능 저하 원인
- 정렬(Sort) 작업이 추가적으로 발생
- 데이터가 많을수록 메모리/디스크 사용량 급증
3) 최적화 방법
인덱스 활용
- 그룹핑 대상 컬럼에 적합한 인덱스를 생성하면 정렬 작업을 줄일 수 있음
CREATE INDEX idx_products_category ON products(category);
GROUP BY와 WHERE 조합
- 먼저 WHERE로 데이터 범위를 줄인 후 GROUP BY 수행
SELECT category, COUNT(*)
FROM products
WHERE status = 'active'
GROUP BY category;
중간 집계 테이블 활용
- 실시간이 필요 없는 경우 미리 집계한 결과를 저장한 테이블 사용
DISTINCT 최적화
DISTINCT는 중복 행을 제거합니다.
1) 기본 동작 방식
- 내부적으로 GROUP BY와 유사하게 동작 (중복을 제거하기 위해 정렬 또는 해시 처리)
SELECT DISTINCT category
FROM products;
2) 성능 저하 원인
- 불필요한 컬럼 포함 시 불필요한 정렬 및 비교 연산 발생
- 대량 데이터에서 메모리 사용량 증가
3) 최적화 방법
- 필요한 컬럼만 SELECT
- DISTINCT 대상 컬럼만 포함하여 불필요한 데이터 처리 방지
-- 비효율
SELECT DISTINCT * FROM products;
-- 효율적
SELECT DISTINCT category FROM products;
인덱스 이용
- DISTINCT 컬럼에 인덱스를 걸면 중복 제거 속도가 빨라짐
EXISTS 또는 JOIN으로 대체
- DISTINCT 대신 EXISTS로 중복 제거 로직을 대체하는 경우가 있음
SELECT category
FROM products p
WHERE EXISTS (
SELECT 1
FROM categories c
WHERE c.id = p.category_id
);
ORDER BY 최적화
ORDER BY는 결과 집합을 특정 컬럼 기준으로 정렬합니다.
1) 성능 저하 원인
- 데이터 정렬은 많은 CPU와 메모리를 사용
- 인덱스를 사용하지 못하면 Filesort 발생 → 성능 저하
2) 최적화 방법
- 인덱스 순서와 동일하게 ORDER BY
-- 인덱스 생성
CREATE INDEX idx_products_price ON products(price);
-- 인덱스를 활용한 정렬
SELECT * FROM products ORDER BY price;
LIMIT와 함께 사용
- 전체 데이터 정렬 대신 필요한 범위만 정렬
SELECT * FROM products ORDER BY price LIMIT 10;
정렬 컬럼 최소화
- 여러 컬럼을 정렬할 경우 인덱스 복합 구성 고려
커버링 인덱스(Covering Index) 활용
- SELECT 컬럼과 ORDER BY 컬럼이 인덱스에 모두 포함되면 디스크 I/O 감소
실행 계획(Execution Plan)으로 확인
GROUP BY, DISTINCT, ORDER BY 최적화 여부를 확인하려면 EXPLAIN으로 실행 계획을 분석합니다.
EXPLAIN SELECT category, COUNT(*)
FROM products
GROUP BY category;
- Using index → 인덱스를 사용 중
- Using temporary; Using filesort → 임시 테이블 및 디스크 정렬 발생 (개선 필요)
정리
- GROUP BY: 인덱스를 적극 활용하고, 데이터 범위를 줄인 후 그룹화
- DISTINCT: 불필요한 컬럼 제거, 인덱스 사용, EXISTS 대체 가능
- ORDER BY: 인덱스 순서와 동일하게 정렬, LIMIT 사용
- 실행 계획을 통해 항상 병목 지점을 파악할 것