427. 결합 인덱스 (Composite Index) 순서의 중요성
⚠️ 이 문서는 한 테이블에서 여러 개의 조건을 묶어서 검색할 때 성능을 올리기 위해 2개 이상의 컬럼을 합쳐서 만드는 '결합 인덱스'에서, **"어떤 컬럼을 1빠따로 세우느냐"에 따라 인덱스가 우주선이 되기도 하고 쓸모없는 돌덩이가 되기도 하는 '순서의 중요성'**을 다룹니다.
핵심 인사이트 (3줄 요약)
- 본질: 결합 인덱스는 여러 컬럼을 묶어서 하나의 B-Tree 목차를 만드는 것이다. (예:
[성별 + 이름])- 원리: 인덱스는 무조건 첫 번째 컬럼을 기준으로 먼저 정렬되고, 그 안에서 두 번째 컬럼이 정렬된다. 전화번호부 책이 '지역 $\rightarrow$ 이름' 순서로 정렬된 것과 같다.
- 실무 규칙: 쿼리에서 항상 쓰는 컬럼(필수 조건)이면서, 동시에 값의 종류가 많아서 대상을 확 줄여줄 수 있는(분포도가 좋은) 컬럼을 무조건 1순위에 둬야 한다.
Ⅰ. 개요: 전화번호부 책 찾기 (Context & Necessity)
"서울에 사는 김철수 찾아줘!"
우리가 [지역, 이름] 순서로 결합 인덱스를 만들었다고 치자.
- '서울'을 먼저 찾는다. (수십만 명)
- 그 서울 사람들 안에서 '김철수'를 찾는다. (아주 빠름)
그런데 만약 검색 조건이 **"지역은 상관없고, 이름이 '김철수'인 사람 다 찾아줘!"**라면?
- 인덱스는 '지역' 순서대로 정렬되어 있다. (강원도-김철수, 경기도-김철수, 서울-김철수...)
- 첫 번째 기준인 '지역'을 안 주니까, 인덱스 책갈피를 펼쳐도 어디로 점프해야 할지 모른다.
- 결과: 인덱스를 타지 못하고 테이블 처음부터 끝까지 다 뒤져야 한다 (Full Table Scan).
📢 섹션 요약 비유: 결합 인덱스는 **'년-월-일'**로 정렬된 다이어리와 같습니다. "2026년 4월"을 찾으라고 하면 2026년을 펴서 4월을 금방 찾죠. 하지만 연도를 안 알려주고 **"연도 상관없이 그냥 모든 4월을 다 찾아와!"**라고 하면, 다이어리를 1페이지부터 끝까지 다 넘겨봐야 하는 것과 같은 원리입니다.
Ⅱ. 결합 인덱스 컬럼 순서 정하기 3대 원칙 ★
DBA(데이터베이스 관리자) 면접에서 반드시 나오는 질문이다.
1. 항상 사용되는가? (가장 중요)
- 사용자들이 검색창에서 검색할 때, **절대 빼먹지 않고 항상 입력하는 조건(WHERE)**을 무조건 1순위(선두 컬럼)로 둬야 한다.
- 첫 번째 컬럼이 쿼리 조건에서 빠지는 순간, 그 인덱스는 죽은 인덱스가 된다.
2. 분포도(Selectivity)가 좋은가?
- 분포도가 좋다는 말은 값의 종류가 다양해서 경쟁자를 한 방에 팍 줄여준다는 뜻이다.
- ❌ 나쁜 예:
[성별, 이름]$\rightarrow$ 성별은 남/여 2개뿐이다. 남자를 쳐봤자 데이터가 500만 건이나 남는다. - 🟢 좋은 예:
[이름, 성별]$\rightarrow$ 이름이 '독고철수'인 사람을 치면 데이터가 단 3명으로 줄어든다. 검색이 1초 만에 끝난다.
3. 점(Point) 검색인가, 선(Range) 검색인가?
= (Equal)조건으로 딱 떨어지는 컬럼을 앞에 두고,<, >, BETWEEN처럼 범위(Range)로 찾는 컬럼을 뒤로 미뤄야 한다.- 범위 검색이 앞에 오면, 그 범위에 해당하는 모든 데이터를 풀 스캔하듯 뒤져야 하므로 뒤에 있는 인덱스가 힘을 쓰지 못한다.
Ⅲ. 실무 사례: 인덱스 타는지 안 타는지 맞혀보기
인덱스가 [A, B, C] 순서로 결합되어 있다고 가정하자.
WHERE A = 1 AND B = 2 AND C = 3- 🟢 완벽하게 인덱스를 탄다.
WHERE A = 1 AND B = 2- 🟢 잘 탄다. (C는 없어도 앞에서부터 순서대로 탔기 때문)
WHERE B = 2 AND C = 3- ❌ 인덱스 못 탄다! (Full Scan) 선두 컬럼인 A가 없기 때문이다.
WHERE A = 1 AND C = 3- 🔺 A까지만 인덱스를 타고, 중간에 B가 뚫려버려서 C는 인덱스의 도움을 받지 못한다.
┌──────────────────────────────────────────────────────────────┐
│ 결합 인덱스 (Composite Index)의 정렬 및 검색 시각화 │
├──────────────────────────────────────────────────────────────┤
│ │
│ [ 📑 결합 인덱스 생성: (부서명, 직급) 순서 ] │
│ │
│ 부서명 (1순위 정렬) 직급 (2순위 정렬) │
│ ───────────────────────────────── │
│ 개발팀 대리 │
│ 개발팀 부장 │
│ 개발팀 사원 │
│ 영업팀 대리 │
│ 영업팀 사원 │
│ │
│ [ 👨💻 쿼리 테스트 ] │
│ 1. "개발팀의 대리 찾아!" -> 개발팀 찾고, 바로 대리 찾음 (초고속 🚀) │
│ 2. "대리 찾아!" -> 부서명을 모르니 어디로 갈지 모름 (풀스캔 🐌) │
└──────────────────────────────────────────────────────────────┘
Ⅳ. 결론
"인덱스는 다다익선이 아니라 일당백이다." 초보 개발자는 쿼리가 느릴 때마다 조건절에 있는 컬럼들에 1개짜리 단일 인덱스(Single Index)를 덕지덕지 만든다. 그러면 DB는 데이터를 넣을 때마다 인덱스 책갈피를 5개, 10개씩 새로 써야 하므로 시스템이 마비된다. 고수는 사용자의 검색 패턴 10가지를 분석한 뒤, 그 10가지를 모두 아우를 수 있는 가장 똑똑한 순서의 '결합 인덱스' 1~2개만을 만들어내어 읽기와 쓰기 속도를 동시에 구원해 낸다.
📌 관련 개념 맵
- 기반 구조: B+Tree 인덱스 (422번 문서)
- 대척점 개념: Single Column Index (단일 컬럼 인덱스)
- 중요 지표: Selectivity(선택도), Cardinality(분포도)
- 실행 계획: Index Range Scan (429번 문서)
👶 어린이를 위한 3줄 비유 설명
- 학교 도서관의 책이 "학년 $\rightarrow$ 반 $\rightarrow$ 번호" 순서대로 꽂혀 있어요. (결합 인덱스)
- "3학년 2반 15번 찾아줘!" 하면 한 번에 쓱 찾을 수 있죠. "3학년 2반 다 찾아줘!" 해도 바로 찾을 수 있어요.
- 근데 갑자기 "몇 학년, 몇 반인진 모르겠고 15번 학생만 다 찾아봐!"라고 하면? 결국 학교의 모든 책장을 다 뒤져봐야 한답니다! (순서가 중요한 이유)