비교할 문제
서브쿼리와 JOIN 비교에서 확인할 질문은 상관 subquery와 JOIN 집계 중 어느 plan이 실제 data에서 유리한가입니다. 부모 row 보존, 중복과 NULL 결과를 동일하게 만들고 같은 isolation에서 비교합니다. 결과값, 순서, 오류 처리와 부수 효과가 다르면 같은 성능 비교가 아닙니다. 먼저 두 후보가 같은 일을 하는지 작은 입력으로 확인합니다.
원인 분석과 재현
subquery와 JOIN은 optimizer가 같은 plan으로 바꿀 수도 있고 NULL·중복·상관 실행 의미가 다를 수도 있어 SQL 형태만으로 속도를 일반화할 수 없습니다.
부모 100·10만 row, 자식 0·1·100개인 skew와 index 유무에서 r_loops, examined row와 temp 사용을 봅니다. runtime version, 운영체제, CPU와 memory 제한도 결과와 함께 기록합니다. 이 예제의 plan과 문법은 MariaDB 기준이며 다른 DB 제품의 결과로 일반화하지 않습니다.
비교 질문은 상관 subquery와 JOIN 집계 중 어느 plan이 실제 data에서 유리한가입니다.
| 후보 | 유리한 조건 | 함께 치르는 비용 |
|---|---|---|
| 상관 subquery | 부모별 독립 집계가 명확하고 optimizer가 변환 가능 | outer row마다 subplan이 반복될 수 있음 |
| JOIN + GROUP BY | 한 번의 집합 처리 | 중복 확대, group memory와 부모 보존 의미 확인 |
ANALYZE FORMAT=JSON
SELECT c.id, (SELECT COUNT(*) FROM orders o WHERE o.customer_id = c.id) AS order_count
FROM customers c WHERE c.active = TRUE;
ANALYZE FORMAT=JSON
SELECT c.id, COUNT(o.id) AS order_count
FROM customers c LEFT JOIN orders o ON o.customer_id = c.id
WHERE c.active = TRUE GROUP BY c.id;
해결 방안과 판정
warm-up과 측정 구간을 나누고 여러 번 반복합니다. 서비스 지연은 median과 p95를, 처리량 비교는 단위 시간당 완료 수와 오류율을 함께 기록합니다. CPU time, allocation, RSS·heap, GC, I/O 중 이 작업에 직접 관련된 지표만 선택합니다. subquery라는 이유만으로 느리다고 단정하지 않습니다. optimizer 변환과 실제 r_loops를 확인하고 결과 row 수부터 대조합니다.
차이가 반복 실행의 흔들림보다 작다면 더 단순하고 읽기 쉬운 구현을 선택합니다. 차이가 충분히 크더라도 실제 요청 전체의 지연과 자원 사용이 개선되는지 확인한 뒤 적용합니다.
공식 문서와 적용 범위
문서의 기본값과 동작은 version에 따라 달라질 수 있습니다. 현재 DB 제품과 version, schema·index·통계 상태, 데이터 분포와 동시 부하에서 다시 확인합니다.