432. 소트 머지 조인 (Sort Merge Join)
⚠️ 이 문서는 두 개의 대용량 테이블을 합칠(JOIN) 때, 인덱스가 없어서 중첩 루프 조인(NL Join)을 쓰면 디스크가 폭발하는 상황을 막기 위해 **두 테이블을 먼저 예쁘게 정렬(Sort)한 다음 지퍼를 채우듯 한 번에 싹 병합(Merge)해버리는 '소트 머지 조인'**을 다룹니다.
핵심 인사이트 (3줄 요약)
- 본질: 조인할 두 테이블을 각자 조인 컬럼 기준으로 오름차순 정렬(Sort)한 뒤, 위에서부터 아래로 차례대로 비교하며 합치는(Merge) 방식이다.
- 가치: 중첩 루프 조인의 최악의 약점인 '랜덤 I/O(디스크 널뛰기)'가 전혀 발생하지 않기 때문에 대용량 데이터를 처리할 때 매우 안정적이다.
- 한계: 데이터를 정렬(Sort)하는 작업 자체가 메모리와 CPU를 엄청나게 잡아먹기 때문에, 현대 데이터베이스에서는 대부분 더 빠르고 메모리를 덜 먹는 **해시 조인(Hash Join - 433번 문서)**으로 대체되었다.
Ⅰ. 개요: 지퍼 채우기 (Context & Necessity)
부서 테이블(1만 건)과 직원 테이블(100만 건)을 조인하려고 한다.
그런데 조인 컬럼인 부서코드에 둘 다 인덱스가 없다!
- 인덱스가 없는 상태에서 중첩 루프 조인(431번 문서)을 쓰면? 1만 번 $\times$ 100만 번 = 100억 번을 뒤져야 한다. 서버가 며칠 동안 멈춘다.
이럴 때 옵티마이저는 소트 머지 조인을 선택한다.
- "일단 부서 테이블을 부서코드 순으로 '가나다순' 정렬해 놔!" (Sort)
- "직원 테이블도 부서코드 순으로 정렬해 놔!" (Sort)
- 두 테이블이 모두 정렬되었으니, 위에서부터 아래로 쭉 내려가면서 지퍼를 채우듯이(Merge) 겹치는 애들만 쏙쏙 묶어준다.
📢 섹션 요약 비유: 소트 머지 조인은 **'두 벌의 카드를 순서대로 맞추는 게임'**과 같습니다. 카드 뭉치가 뒤죽박죽일 때는 짝을 찾기 힘들지만, 일단 양쪽 다 숫자 순서대로 예쁘게 정렬(Sort)해두면, 위에서부터 카드 한 장씩 까보면서 같은 숫자끼리 딱딱 짝지어주는(Merge) 작업은 눈 감고도 할 수 있을 만큼 빠릅니다.
Ⅱ. 소트 머지 조인의 핵심 특징 ★
1. 인덱스가 없어도 빠르다
- 인덱스가 없으면 디스크를 미친 듯이 긁어대야 하는 NL 조인과 달리, 소트 머지 조인은 메모리에서 한 번 정렬만 끝내두면 순차적으로 쭉 읽어 내리기만 하면 된다. (순차 I/O의 승리)
2. 비동등 조인 (Non-Equi Join)이 가능하다
- 이건 해시 조인(Hash Join)이 절대 못 하는 소트 머지 조인만의 필살기다.
- 해시 조인은 무조건
A.id = B.id처럼 값이 똑같을(=) 때만 쓸 수 있다. - 하지만 소트 머지는 이미 크기순으로 '정렬'이 되어있기 때문에,
A.date >= B.date라거나BETWEEN같은 범위 조건(비동등 조인)에서도 훌륭하게 작동한다.
3. 치명적 단점: 정렬(Sort)의 비용
- 100만 건을 정렬하는 건 메모리(Sort Area)를 엄청나게 소모한다.
- 메모리가 꽉 차면 디스크에 임시 파일을 썼다 지웠다 해야 하므로(Temp Space I/O) 속도가 수십 배 느려진다.
Ⅲ. 조인 방식별 성능 교차점
데이터양에 따라 옵티마이저가 선택하는 1순위 조인 방식이 달라진다.
- 데이터가 소량일 때: 무조건
NL 조인이 빠르다. (정렬할 필요 없이 인덱스만 슉슉 타면 됨) - 데이터가 대량일 때:
해시 조인이 1짱이다. - 해시 조인을 못 쓸 때 (비동등 조인이거나 범위 검색일 때): 어쩔 수 없이
소트 머지 조인을 쓴다.
┌──────────────────────────────────────────────────────────────┐
│ 소트 머지 조인 (Sort Merge Join) 작동 구조 시각화 │
├──────────────────────────────────────────────────────────────┤
│ │
│ [ 👨💼 직원 (뒤죽박죽) ] [ 🏢 부서 (뒤죽박죽) ] │
│ (C부서, A부서, B부서) (B부서, C부서, A부서) │
│ │ │ │
│ ▼ 1. 각각 정렬 (Sort) ▼ │
│ [ 👨💼 직원 (정렬됨) ] [ 🏢 부서 (정렬됨) ] │
│ A부서: 철수 A부서: 기획팀 │
│ B부서: 영희 B부서: 영업팀 │
│ C부서: 민수 C부서: 개발팀 │
│ │
│ ▼ 2. 병합 (Merge - 지퍼 채우기!) │
│ [A:철수-기획팀] ──▶ [B:영희-영업팀] ──▶ [C:민수-개발팀] (끝!) │
└──────────────────────────────────────────────────────────────┘
Ⅳ. 결론
"소트 머지는 역사의 뒤안길로 가고 있지만, 대체 불가능한 영역이 있다."
과거 Oracle 데이터베이스 시절, 대용량 데이터를 처리하는 유일한 희망은 소트 머지 조인이었다. 하지만 더 빠르고 메모리도 덜 먹는 해시 조인(Hash Join)이 등장하면서 왕좌에서 밀려났다. 그럼에도 불구하고 우리가 이 알고리즘을 배워야 하는 이유는 두 가지다. 첫째, 조인 조건이 >= 나 < 처럼 부등호로 이루어졌을 때는 해시 조인이 불가능하므로 소트 머지가 유일한 대안이다. 둘째, 우연히 조인할 두 테이블이 이미 인덱스를 통해 예쁘게 정렬되어 있는 상태라면? (정렬 비용이 0원이라면?) 소트 머지 조인은 해시 조인조차 씹어먹는 우주 최강의 속도를 보여준다.
📌 관련 개념 맵
- 관련 연산: 관계 대수 JOIN ($\bowtie$, 409번 문서)
- 대안 방식: Nested Loop Join (431번), Hash Join (433번)
- 발생 조건: Non-Equi Join (비동등 조인), 인덱스가 없을 때 대용량 조인
- 주의할 점: Sort Area (메모리) 부족 시 디스크 I/O 발생으로 성능 폭락
👶 어린이를 위한 3줄 비유 설명
- 소트 머지 조인은 빨간 숫자 카드 뭉치와 파란 숫자 카드 뭉치에서 같은 숫자끼리 짝을 찾는 게임이에요.
- 카드 뭉치가 섞여 있으면 찾기 힘드니까, 먼저 두 뭉치 모두 1, 2, 3, 4 순서대로 예쁘게 정리(Sort)를 해둬요.
- 양쪽 다 정렬되어 있으니까, 맨 위에서부터 한 장씩 넘겨보면서 같은 숫자끼리 쓱쓱 묶어주기만(Merge) 하면 게임이 순식간에 끝난답니다!