핵심 인사이트 (3줄 요약)
- 본질: 드라이빙 테이블 (Driving Table)은 조인의 시작점이면서 이후 반복 횟수를 결정하는 집합이고, 드리븐 테이블 (Driven Table)은 그 반복마다 탐색되는 대상이다.
- 가치: 선택도 (Selectivity)가 높은 조건으로 드라이빙 집합을 먼저 줄이고, 드리븐 쪽에 적절한 인덱스나 접근 경로를 준비하면 중첩 루프 조인 (NL Join, Nested Loop Join)의 비용을 급격히 낮출 수 있다.
- 판단 포인트: 원본 테이블 크기보다 필터 후 기수성 (Cardinality), 조인 방식, 드리븐 접근 경로가 더 중요하며,
FROM절에 먼저 썼다고 자동으로 드라이빙이 되지는 않는다.
Ⅰ. 개요 및 필요성
드라이빙 테이블과 드리븐 테이블은 조인 실행 순서를 이해할 때 가장 먼저 잡아야 할 개념이다. 특히 중첩 루프 조인에서는 한쪽 집합을 먼저 읽고, 그 결과 각 행마다 다른 쪽을 반복 탐색한다. 이때 먼저 읽는 쪽이 드라이빙, 반복 탐색당하는 쪽이 드리븐이다.
이 구분이 중요한 이유는 조인 비용이 단순히 "두 테이블을 한 번씩 읽는 비용"이 아니기 때문이다. 드라이빙 집합이 10건이면 드리븐 탐색도 10번이면 끝나지만, 드라이빙 집합이 10만 건이면 같은 탐색이 10만 번 반복된다. 즉 조인의 체감 성능은 드라이빙 집합의 크기에 의해 크게 좌우된다.
아래 그림은 조인 비용이 왜 드라이빙 집합의 행 수에 민감한지를 압축해서 보여 준다. 핵심은 드리븐 비용이 한 번이 아니라 드라이빙 행 수만큼 곱해진다는 점이다.
┌────────────────────────────────────────────────────────────────────┐
│ Join cost is multiplied by outer rows │
├────────────────────────────────────────────────────────────────────┤
│ driving rows = 5 -> driven lookup repeated 5 times │
│ driving rows = 500 -> driven lookup repeated 500 times │
│ driving rows = 50,000 -> driven lookup repeated 50,000 times │
│ │
│ rough cost ≒ access(driving) + rows(driving) x lookup(driven) │
└────────────────────────────────────────────────────────────────────┘
따라서 드라이빙 테이블의 본질은 "먼저 읽는 테이블"이라는 문장으로 끝나지 않는다. 더 정확히는 조인 반복 횟수를 결정하는 제어 손잡이라고 이해해야 한다. 이 관점을 잡아야 옵티마이저 실행 계획을 읽을 때 왜 어떤 테이블이 먼저 잡혔는지 해석할 수 있다.
- 📢 섹션 요약 비유: 드라이빙 테이블은 택배 기사에게 먼저 쥐여 주는 배송 목록과 같다. 목록이 5건이면 5번만 들르면 되지만, 목록이 5만 건이면 같은 도로도 끝없이 왕복하게 된다.
Ⅱ. 아키텍처 및 핵심 원리
드라이빙/드리븐 개념이 가장 선명하게 드러나는 곳은 중첩 루프 조인이다. 엔진은 보통 드라이빙 접근 → 조건 필터링 → 각 행마다 드리븐 접근 → 결과 결합 순서로 움직인다. 이때 드리븐 테이블은 반복 접근을 버텨야 하므로, 조인 키 인덱스나 매우 효율적인 접근 경로가 준비되어 있어야 한다.
| 역할 | 엔진 입장 의미 | 이상적인 조건 | 잘못 잡았을 때 문제 |
|---|---|---|---|
| 드라이빙 테이블 | 반복문의 바깥쪽 집합 | 필터 후 결과 건수가 매우 적음 | 반복 횟수 자체가 커짐 |
| 드리븐 테이블 | 반복문 안쪽에서 매번 탐색되는 대상 | 조인 키 인덱스, 빠른 랜덤 접근 | 인덱스 없으면 반복 풀스캔 위험 |
| 조인 조건 | 두 집합을 묶는 연결 키 | 높은 선택도, 통계 정확성 | 잘못된 기수 추정으로 순서 오판 |
| 옵티마이저 통계 | 어느 쪽을 먼저 읽을지 판단하는 근거 | 최신 통계 정보 유지 | 잘못된 드라이빙 선택 |
아래 구조는 "작게 줄인 뒤 반복 접근한다"는 중첩 루프 조인의 핵심 원리를 보여 준다. 좋은 실행 계획은 드라이빙에서 이미 행 수를 최대한 줄여 놓고, 드리븐은 인덱스로 짧게 찌른다.
┌────────────────────────────────────────────────────────────────────┐
│ Nested Loop Join execution │
├────────────────────────────────────────────────────────────────────┤
│ Driving access: CUSTOMER where grade = 'VIP' -> 120 rows │
│ │ │
│ ├─ row 1 -> ORDERS(customer_id) index lookup │
│ ├─ row 2 -> ORDERS(customer_id) index lookup │
│ ├─ row 3 -> ORDERS(customer_id) index lookup │
│ └─ row 120 -> ORDERS(customer_id) index lookup │
│ │
│ Good plan = small filtered outer + fast inner access │
└────────────────────────────────────────────────────────────────────┘
중요한 오해 하나는 "원래 작은 테이블이 무조건 드라이빙"이라는 생각이다. 실제로는 기본 테이블 크기보다 필터 후 결과 건수가 더 중요하다. 예를 들어 주문 테이블이 5억 건이라도 order_date = today 조건으로 3,000건만 남는다면, 고객 테이블 1,000만 건보다 오히려 주문 쪽이 더 좋은 드라이빙이 될 수 있다.
또한 드라이빙/드리븐 개념은 조인 방식에 따라 다른 이름으로 나타나기도 한다. 해시 조인 (Hash Join)에서는 Build Input/Probe Input이라는 표현을 더 많이 쓰고, 소트 머지 조인 (Sort Merge Join)에서는 양쪽 정렬 후 병합이 핵심이 된다. 따라서 이 개념은 특정 키워드 암기가 아니라, 물리 조인이 어느 쪽을 먼저 줄이고 어느 쪽을 반복 접근하는지 보는 시각으로 기억해야 한다.
- 📢 섹션 요약 비유: 드라이빙은 먼저 뽑아 놓는 손님 명단이고, 드리븐은 그 명단을 보고 매번 열어 보는 서류 캐비닛과 같다. 명단이 짧고 캐비닛 서랍이 잘 정리돼 있어야 일이 빨라진다.
Ⅲ. 비교 및 연결
드라이빙/드리븐을 제대로 이해하려면 비슷해 보이는 다른 개념과 경계를 분리해야 한다. 특히 SQL 작성 순서, 조인 순서 (Join Order), 해시 조인의 Build/Probe와 혼동하면 실행 계획 해석이 꼬인다.
| 개념 | 무엇을 뜻하는가 | 드라이빙/드리븐과의 관계 |
|---|---|---|
FROM 절 작성 순서 | 사람이 SQL을 적은 문장 순서 | 옵티마이저는 이를 바꿔 다른 드라이빙을 선택할 수 있음 |
| 조인 순서 (Join Order) | 여러 테이블을 어떤 순서로 묶을지 | 드라이빙 선택은 조인 순서를 구성하는 핵심 결정 |
| 드라이빙 / 드리븐 | 특정 물리 조인 단계의 바깥쪽/안쪽 역할 | 중첩 루프 조인에서 특히 중요 |
| Build / Probe | 해시 조인에서 해시 테이블 생성 쪽과 조회 쪽 | 반복 조회의 방향을 본다는 점에서 유사하지만 동일 용어는 아님 |
가장 중요한 비교 포인트는 "작은 테이블"과 "작게 남는 테이블"의 차이다. 드라이빙 판단은 정적 크기 비교가 아니라, 조건 적용 후 남는 행 수를 보는 문제다. 이 때문에 선택도, 기수성, 분포도 같은 통계 정보가 조인 성능과 직접 연결된다.
또 하나의 연결점은 액세스 경로다. 드라이빙을 아무리 잘 골라도 드리븐에 적절한 인덱스가 없으면 반복 풀스캔이 벌어져 성능이 무너진다. 반대로 드리븐 인덱스가 매우 좋다면, 소량의 드라이빙 결과만 가지고도 매우 빠른 온라인 거래 처리 (OLTP, Online Transaction Processing) 질의를 만들 수 있다.
즉 드라이빙/드리븐은 독립된 단어쌍이 아니라, 선택도 → 기수성 추정 → 조인 순서 → 액세스 경로로 이어지는 옵티마이저 사고 흐름의 한 축이다. 이 연결이 보여야 "왜 해시 조인이 아니라 NL Join이 선택됐는가", "왜 힌트가 먹었는데도 느린가" 같은 질문에 답할 수 있다.
- 📢 섹션 요약 비유: 먼저 만날 사람을 고르는 일과, 만난 뒤 주소록에서 집을 찾는 일은 다르다. 약속 인원이 적어도 주소록이 엉망이면 시간이 오래 걸리고, 주소록이 좋아도 약속 인원이 너무 많으면 하루가 모자란다.
Ⅳ. 실무 적용 및 기술사 판단
실무에서 드라이빙 테이블 판단은 "어느 테이블이 더 작으냐"보다 "어느 쪽을 먼저 읽으면 전체 탐색 횟수를 가장 많이 줄일 수 있느냐"로 내려야 한다. 예를 들어 CUSTOMER 1,000만 건, ORDERS 5억 건이 있어도 CUSTOMER.grade = 'VIP' 조건으로 200건만 남고 ORDERS.customer_id 인덱스가 잘 잡혀 있다면 CUSTOMER를 드라이빙으로 두는 것이 자연스럽다. 반대로 ORDERS.order_date = today로 3,000건만 남고 고객 조건이 약하다면, 훨씬 큰 원본 테이블이라도 ORDERS가 드라이빙이 될 수 있다.
힌트는 마지막 수단이어야 한다. Oracle 기준으로 LEADING, USE_NL 같은 힌트로 조인 순서를 고정할 수는 있지만, 통계가 오래됐거나 인덱스가 부적절한 상태를 힌트로 덮으면 특정 시점에만 빨랐던 실행 계획을 영구 고정하는 부작용이 생긴다. 먼저 할 일은 최신 통계 확보, 조건 푸시다운, 불필요 컬럼 제거, 드리븐 인덱스 정비다.
기술사 판단 체크리스트
- 각 후보 테이블의 필터 후 예상 건수는 얼마인가?
- 중첩 루프 조인이 유리한 소량 결과 질의인가?
- 드리븐 테이블의 조인 키 또는 결합 조건에 인덱스가 있는가?
- 비용 기반 옵티마이저 (CBO, Cost-Based Optimizer)가 참고하는 통계가 최신인가?
- 사람이 적은
FROM순서를 실제 실행 순서로 오해하고 있지 않은가? - 힌트를 쓰기 전에 조인 방식 자체가 맞는지 검토했는가?
자주 나오는 안티패턴
- 원본 테이블 행 수만 보고 무조건 작은 테이블을 드라이빙으로 고정하는 경우
- 드리븐 인덱스 없이 중첩 루프 조인을 강제로 유도하는 경우
FROM A, B에서 A를 먼저 썼다고 A가 항상 드라이빙이라고 믿는 경우- 통계 부정확 문제를 해결하지 않고 힌트로만 조인 순서를 봉인하는 경우
기술사 관점에서 기억할 핵심 문장은 이렇다. 드라이빙 테이블은 조인의 반복 횟수를 결정하고, 드리븐 테이블은 그 반복을 감당할 액세스 경로를 제공해야 한다. 둘 중 하나만 맞아도 부족하고, 둘이 함께 맞아야 실제 성능이 나온다.
- 📢 섹션 요약 비유: 단체 관광에서 먼저 짜는 일정표가 곧 이동 횟수를 결정하고, 각 관광지의 출입구 구조가 실제 입장 속도를 결정한다. 일정표만 좋아도 안 되고, 출입구만 넓어도 안 된다.
Ⅴ. 기대효과 및 결론
드라이빙/드리븐 개념을 정확히 잡으면 실행 계획을 단순 암기에서 벗어나 구조적으로 읽을 수 있다. 어떤 질의가 느린지 볼 때도 "인덱스가 있나?"에서 끝나지 않고, 왜 이 집합이 반복의 바깥쪽으로 선택됐는가를 추적하게 된다. 그 결과 조인 성능 튜닝이 훨씬 예측 가능해진다.
기대효과는 명확하다. 적절한 드라이빙 선택은 반복 횟수를 줄이고, 적절한 드리븐 접근 경로는 반복당 비용을 낮춘다. 다만 한계도 있다. 대량 동등 조인에서는 해시 조인이 더 유리할 수 있고, 정렬된 결과가 중요하면 소트 머지 조인이 나을 수 있다. 즉 드라이빙/드리븐은 만능 정답이 아니라, 특히 NL Join을 읽고 튜닝할 때 가장 날카로운 관점이다.
결론적으로 이 개념은 "누가 먼저 읽히는가"보다 **"누가 반복 횟수를 만들고, 누가 그 반복을 빠르게 받아내는가"**로 기억하는 것이 정확하다. 이 한 줄이 잡히면 드라이빙 테이블은 단순 용어가 아니라 조인 성능의 핵심 제어 변수로 보이기 시작한다.
- 📢 섹션 요약 비유: 좋은 조인 설계는 먼저 줄여야 할 손님 줄과, 그 손님을 빠르게 통과시킬 창구를 함께 고르는 일과 같다. 줄만 짧아도 창구가 막히면 느리고, 창구만 빨라도 줄이 너무 길면 여전히 오래 걸린다.
📌 관련 개념 맵
| 개념 | 연결 포인트 |
|---|---|
| 중첩 루프 조인 (NL Join, Nested Loop Join) | 드라이빙/드리븐 개념이 가장 직접적으로 드러나는 조인 방식 |
| 선택도 (Selectivity) | 어떤 조건이 드라이빙 집합을 얼마나 작게 줄이는지 판단하는 기준 |
| 기수성 (Cardinality) | 필터 후 예상 행 수로, 조인 순서 결정의 핵심 입력 |
| 조인 순서 (Join Order) | 여러 테이블 조합에서 드라이빙 후보를 어떤 순서로 잡을지 정하는 문제 |
| 액세스 경로 (Access Path) | 드리븐 테이블이 인덱스 탐색인지 풀스캔인지 결정하는 요소 |
| 비용 기반 옵티마이저 (CBO, Cost-Based Optimizer) | 통계 정보를 기반으로 드라이빙/드리븐과 조인 방식을 선택하는 주체 |
| 해시 조인 (Hash Join) | 대량 동등 조인에서 NL Join 대신 고려되는 다른 물리 조인 방식 |
📈 관련 키워드 및 발전 흐름도
조건 선택도 파악
│
▼
필터 후 기수성 추정
│
▼
드라이빙 테이블 선택
│
▼
드리븐 액세스 경로 결정
│
▼
조인 반복 횟수 최소화
│
▼
조인 순서 / 힌트 / 해시 조인 대안 검토
이 흐름은 "조건 분석 → 남는 행 수 추정 → 드라이빙 선택 → 드리븐 탐색 최적화 → 전체 조인 전략 확정"으로 이어지는 튜닝 사고 순서를 보여 준다.
👶 어린이를 위한 3줄 비유 설명
- 먼저 누구를 데리고 다닐지 정하면 하루에 몇 번 움직일지가 거의 결정돼요.
- 같이 다니는 친구가 적으면 문을 여는 횟수도 적어서 빨라져요.
- 그래서 조인에서는 먼저 고를 팀과, 그다음 빨리 찾을 서랍을 같이 잘 정해야 해요.