MySQL DISTINCT
DISTINCT를 자주 사용하는 것은 성능 측면에서 문제가 될 수 있습니다. 그 이유는 DISTINCT가 컴퓨팅 작업을 많이 필요로 하기 때문입니다. 좀 더 구체적으로 이유를 살펴보면 다음과 같습니다.
1. 추가적인 정렬과 필터링 작업
- DISTINCT는 결과 집합에서 중복된 행을 제거하기 위해 사용되며, 이는 내부적으로 많은 연산을 수반합니다. 일반적으로 DISTINCT를 사용하면 데이터베이스 엔진은 결과를 정렬한 후 중복을 제거하는 과정을 거치게 됩니다.
- 이 과정에서 데이터의 양이 많아질수록 정렬과 중복 제거 작업의 복잡성이 증가합니다. 특히, 수백만 건 이상의 데이터에서 DISTINCT를 사용할 경우 CPU와 메모리 리소스를 많이 소모하게 되어 성능 저하로 이어질 수 있습니다.
2. 인덱스의 비효율적인 사용
- 쿼리에서 DISTINCT를 사용하는 경우, 인덱스를 사용하지 못하거나 제대로 활용하지 못할 수 있습니다. 이는 데이터베이스가 효율적인 인덱스를 통해 데이터를 빠르게 찾는 대신 모든 결과를 일단 가져와 중복을 제거해야 한다는 의미입니다.
- 결과적으로, 인덱스를 잘 활용할 수 있는 쿼리와 비교했을 때 성능 저하가 발생하게 됩니다.
3. 쿼리 최적화 어려움
- DISTINCT는 단순히 결과에서 중복을 제거하는 기능이지만, 이 작업을 통해 데이터베이스 엔진이 최적화하기 어려워질 수 있습니다. 특히 복잡한 조인이나 서브쿼리가 포함된 쿼리에서 DISTINCT를 사용하면, 데이터베이스가 효율적인 실행 계획을 수립하기 어렵게 되어 쿼리 성능이 저하될 수 있습니다.
예시
예를 들어, 다음과 같은 쿼리를 생각해볼 수 있습니다:
SELECT DISTINCT customer_id
FROM orders
orders 테이블의 행 수가 많을 경우, 이 쿼리는 모든 행을 읽고 customer_id 값들을 정렬한 뒤 중복을 제거하는 과정을 거칩니다. 데이터가 많을수록 이 작업의 복잡성은 증가하게 됩니다.
좀 더 복잡한 쿼리에서 DISTINCT가 인덱스를 제대로 사용하지 못하게 되는 경우를 살펴보겠습니다. 다음 예제에서는 여러 테이블을 조인하고, 중복된 데이터를 제거하는 복잡한 시나리오를 고려해 보겠습니다.
예제 테이블: employees와 departments
employees 테이블
employee_id department_id last_name salary
1 | 10 | Smith | 50000 |
2 | 20 | Johnson | 60000 |
3 | 10 | Williams | 55000 |
4 | 30 | Brown | 70000 |
5 | 20 | Davis | 62000 |
6 | 30 | Wilson | 73000 |
departments 테이블
department_id department_name
10 | Sales |
20 | Engineering |
30 | HR |
복잡한 DISTINCT 쿼리 예시
SELECT DISTINCT e.department_id, d.department_name, e.salary
FROM employees e
JOIN departments d ON e.department_id = d.department_id
WHERE e.salary > 50000
ORDER BY e.salary DESC;
쿼리 설명:
- 이 쿼리는 employees와 departments 테이블을 조인한 후, salary가 50,000보다 큰 행들을 가져옵니다.
- 그다음, 결과 집합에서 중복된 department_id, department_name, salary 조합을 제거하기 위해 DISTINCT를 사용합니다.
- 마지막으로 salary를 기준으로 내림차순으로 정렬합니다.
DISTINCT로 인한 성능 문제 및 인덱스 비효율성
- 중복 제거를 위한 추가 작업:
- employees와 departments 테이블을 조인한 후, DISTINCT를 적용하여 중복을 제거하는 작업은 복잡도가 상당히 높아질 수 있습니다. 데이터베이스는 먼저 조인된 결과 전체를 계산하고, 그 후 중복된 행을 제거하는 과정을 거치게 됩니다. 이 과정에서 조인된 데이터의 양이 많을 경우, 인덱스가 있더라도 조인 후 모든 행을 검사해야 하므로 성능 저하가 발생할 수 있습니다.
- 인덱스의 비효율적 사용:
- 이 쿼리에서는 department_id와 salary 모두 조건에 사용되지만, DISTINCT 때문에 조인 결과 전체를 처리하고 중복을 제거해야 합니다. 만약 salary나 department_id에 인덱스가 걸려 있다 하더라도, 조인 후 DISTINCT를 사용하면서 데이터베이스는 정렬 및 필터링을 위한 추가 작업을 해야 하므로 인덱스의 효율을 떨어뜨릴 수 있습니다.
- 정렬 오버헤드:
- ORDER BY e.salary DESC가 추가되면서 정렬 작업도 수행해야 합니다. DISTINCT와 ORDER BY가 결합되면 데이터베이스는 정렬 후 중복을 제거하거나, 중복 제거 후 정렬을 해야 하는데, 이 작업 역시 인덱스를 사용하는 데 비효율적일 수 있습니다. 특히 조인된 데이터의 결과가 크면 메모리 사용량도 크게 증가하게 됩니다.
개선 방안
- 서브쿼리 사용:
- DISTINCT를 사용하기 전에 데이터를 줄이기 위해 서브쿼리를 사용할 수 있습니다. 이렇게 하면 중복 제거 작업을 최소화할 수 있습니다.
- 이 방식은 먼저 employees 테이블에서 중복을 줄인 데이터를 가져온 후, 나머지 조인을 수행하므로 불필요한 중복 제거 작업을 줄일 수 있습니다.
SELECT e.department_id, d.department_name, e.salary
FROM (
SELECT DISTINCT department_id, salary
FROM employees
WHERE salary > 50000
) e
JOIN departments d ON e.department_id = d.department_id
ORDER BY e.salary DESC;
- GROUP BY 활용:
- DISTINCT 대신 GROUP BY를 활용하면 인덱스를 더 잘 사용할 수 있는 경우가 많습니다.
- GROUP BY를 사용하여 각 부서별로 최대 급여를 계산하면, 데이터베이스가 인덱스를 활용하여 데이터를 그룹화하고 불필요한 중복 제거 과정을 줄일 수 있습니다. 예를 들어:
SELECT e.department_id, d.department_name, MAX(e.salary) AS max_salary
FROM employees e
JOIN departments d ON e.department_id = d.department_id
WHERE e.salary > 50000
GROUP BY e.department_id, d.department_name
ORDER BY max_salary DESC;
복잡한 조인과 DISTINCT가 결합된 쿼리는 특히 데이터 양이 많을 때 성능 저하를 유발할 수 있습니다. 이를 해결하기 위해 서브쿼리나 GROUP BY를 적절히 사용하여 인덱스 활용을 극대화하고, 중복 제거를 효율적으로 수행하는 것이 중요합니다.
대안
- DISTINCT를 사용하기 전에 데이터의 구조나 쿼리를 재설계하는 것이 좋습니다.
- 데이터베이스 설계를 잘하고, 필요에 따라 적절한 인덱스를 사용하는 것이 중복된 데이터를 피하고, DISTINCT를 필요하지 않게 하는 좋은 방법입니다.
- 특정 상황에서는 GROUP BY를 통해서도 중복을 처리할 수 있으며, GROUP BY가 인덱스를 더 잘 활용할 수 있기 때문에 성능이 더 나을 수 있습니다.
따라서 DISTINCT는 데이터의 중복을 방지하기 위해 유용한 도구이지만, 남용할 경우 성능에 부정적인 영향을 미칠 수 있기 때문에 필요하지 않은 경우에는 피하는 것이 좋습니다.
'Interview > DB' 카테고리의 다른 글
Database Explain을 통한 최적화 분석 (0) | 2024.10.17 |
---|---|
VARCHAR vs TEXT (0) | 2024.10.17 |
MySQL Group by와 Having (0) | 2024.10.17 |
Database Select Query (0) | 2024.10.17 |
Database Commit vs Rollback (0) | 2024.10.17 |