DB

[MySQL] JOIN의 성능 원리와 최적화 전략

인생아 2025. 7. 8. 14:51
반응형

MySQL에서 데이터를 결합하기 위해 가장 많이 쓰는 기능이 바로 JOIN이다.
특히 테이블이 정규화된 구조라면 거의 모든 쿼리에 JOIN이 들어간다고 봐도 무방하다.
하지만 JOIN을 잘못 사용하면 의도치 않은 N*N 조인, 풀 스캔, 임시 테이블 생성으로 인해 성능이 급격히 저하된다.

🧠 JOIN의 내부 동작 방식

MySQL 옵티마이저는 JOIN을 처리할 때 중첩 루프 조인(Nested Loop Join) 방식을 기본으로 사용한다.
즉, 한 테이블에서 각 row를 하나씩 읽으며 다른 테이블에 조건이 일치하는 row를 반복해서 찾는 구조다.

예시:

SELECT u.name, o.amount
FROM users u
JOIN orders o ON u.id = o.user_id;

이 쿼리는 내부적으로 다음과 같이 작동한다.

  • users 테이블의 모든 row를 순차적으로 탐색
  • 각 row마다 orders 테이블에서 user_id = u.id 조건으로 검색
  • 일치하는 row가 있을 경우 결과에 포함

이 구조는 단순하지만 인덱스가 없을 경우 orders 테이블 전체를 매번 탐색하게 된다.
이로 인해 row 수가 많을수록 성능이 기하급수적으로 떨어지는 병목 지점이 발생한다.

반응형

🔎 EXPLAIN으로 JOIN 분석하기

EXPLAIN
SELECT u.name, o.amount
FROM users u
JOIN orders o ON u.id = o.user_id;

대표적으로 확인할 컬럼은 아래와 같다.

  • type: ref 이상이어야 인덱스를 타는 구조
  • key: 실제 사용된 인덱스
  • rows: 조인 대상 row 수
  • Extra: Using where, Using index, Using temporary 등 여부

type = ALL, Extra = Using temporary; Using filesort 조합은 성능 이슈 신호다.

⚙️ 성능 최적화를 위한 전략

✅ 1. 조인 조건 컬럼에 반드시 인덱스를 걸 것

ALTER TABLE orders ADD INDEX idx_user_id (user_id);

조인 조건이 되는 컬럼(user_id)에는 반드시 인덱스가 있어야 성능이 급격히 향상된다.

✅ 2. 필요한 컬럼만 SELECT

SELECT u.name, o.amount

불필요한 컬럼을 SELECT하는 경우 InnoDB에서 디스크 접근이 늘어나게 되며, 조인 성능에 악영향을 준다.

✅ 3. 조인 순서에 주의

MySQL은 기본적으로 옵티마이저가 조인 순서를 결정하지만, 복잡한 쿼리에서는 STRAIGHT_JOIN 키워드로 순서를 강제할 수도 있다.

SELECT STRAIGHT_JOIN u.name, o.amount
FROM users u
JOIN orders o ON u.id = o.user_id;

데이터 건수가 적은 테이블을 먼저 처리하는 것이 일반적으로 유리하다.

✅ 4. LEFT JOIN은 꼭 필요한 경우만 사용

LEFT JOIN은 모든 왼쪽 테이블 row를 유지하므로 쿼리 처리 비용이 높다.
조건에 따라 INNER JOIN으로 바꾸면 성능이 더 좋아질 수 있다.

✅ 5. 조인 수가 많은 경우 서브쿼리 분해도 고려

3개 이상의 테이블이 조인될 경우 옵티마이저가 잘못된 조인 순서를 선택하는 경우도 있다.
이럴 땐 복잡한 쿼리를 CTE나 서브쿼리로 쪼개고 각각 인덱스를 활용하도록 분리하는 것이 효과적이다.

반응형

🧪 실무 예제: 조인 튜닝 전/후

튜닝 전 쿼리:

SELECT * FROM orders o
JOIN users u ON o.user_id = u.id
WHERE u.status = 'active';
  • 실행 시간: 4.8초
  • EXPLAIN 결과: type = ALL, rows = 80000, Extra = Using temporary

튜닝 후 쿼리:

ALTER TABLE users ADD INDEX idx_status_id (status, id);

SELECT o.id, o.amount
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE u.status = 'active';
  • 실행 시간: 0.21초
  • EXPLAIN 결과: type = ref, rows = 342, Extra = Using where; Using index

단순 인덱스 추가와 SELECT 필드 제한만으로도 20배 이상 성능 향상

🧠 실전 조언

  • JOIN을 많이 쓴다고 느려지는 게 아니라, 인덱스 없이 JOIN하면 느려지는 것
  • 항상 EXPLAIN으로 실행 계획을 확인해야 한다
  • MySQL의 JOIN은 옵티마이저에 의존하는 만큼 데이터 분포, 테이블 크기, 인덱스 구성이 모두 성능에 영향을 준다
  • 쿼리 캐시 또는 결과 캐싱도 검토해볼 수 있다

✅ 정리

  • JOIN은 MySQL에서 가장 많이 쓰이지만 성능 문제를 일으키는 주요 원인이 되기도 한다.
  • 조인 조건에는 반드시 인덱스를 걸어야 하며, 불필요한 SELECT 컬럼은 줄이는 것이 좋다.
  • 조인 순서와 유형(INNER vs LEFT)에 따라 실행계획이 달라지고 성능도 큰 차이를 만든다.
  • 복잡한 조인은 서브쿼리나 CTE로 분해하여 관리 가능성을 높이고 성능도 개선할 수 있다.

🔗 공식 문서 참고
MySQL 8.0 Reference Manual - Join Optimization

 

반응형