인덱스 종류
클러스터 인덱스 (InnoDB의 PK 인덱스):
리프 노드에 진짜 데이터 레코드 전체가 저장됨
보조 인덱스 (Secondary Index):
리프 노드에는 실제 데이터가 없고,
기본키(PK)의 값만 저장돼 있어서 → 다시 클러스터 인덱스를 lookup해야 함
어디에 사용함?
- WHERE 조건에 자주 등장하는 컬럼 빠른 필터링 가능
- JOIN 키로 자주 사용되는 컬럼 탐색 비용 줄임
- ORDER BY / GROUP BY 에 자주 등장 정렬/그룹핑 시 디스크 정렬 없이 처리 가능
- COUNT, MAX, MIN 커버링 인덱스가 있으면 효율적
어디엔 사용하면 안좋을까
수정이 잦은 경우
삽입 시
- 리프 노드가 꽉 찼을 경우 → 새로운 노드를 만들어 반을 나눔
- 부모 노드에도 포인터 업데이트 필요 → 부모도 꽉 차 있으면 재귀적 split
- 최악의 경우 루트까지 split → 트리의 높이 증가
- 일반:
O(log N)
- 최악 (모든 노드 split): 여전히
O(log N)
,
하지만 실제 비용은 I/O 수 + 복사 비용 증가
삭제 시
- 삭제 후 노드가 너무 비게 되면 → 이웃 노드와 병합
- 병합 시 부모 노드 포인터 삭제 → 부모도 병합될 수 있음 📌 이때도 트리 전체가 위로 ripple될 수 있음 → 역시 최악의 경우 루트까지 병합, 트리의 높이 줄어들 수도 있음
Rebalancing
- split/merge 외에도 노드 간 **데이터 재배치(shift)**가 일어날 수 있음
- 이건 디스크 블록 단위에서 추가적인 I/O 오버헤드 발생
인덱스 탈지말지 결정 가능?
- 클라이언트가 쿼리 전송
- 파싱 (Parsing) syntax AST
- 쿼리 재작성 (Rewriting)
- 최적화 (Optimization) → 인덱스 탈지말지, 조인 순서 변경 →
EXPLAIN으로 확인
- 실행 계획 생성
- 실행 (Execution)
- 결과 반환
인덱스 안타면 어떻게 가져옴?
→ 모든 row full scan
디스크에서 가져오는 방식
InnoDB의 페이지 크기는 16KB
버퍼 풀(Buffer Pool)
[디스크]
| 페이지1 | 페이지2 | 페이지3 | ... |
→ 페이지 단위로 읽어서 ↓
[Buffer Pool]
| 페이지1 | 페이지2 | ... |
LRU 기반 페이지 교체 전략
이때, Optimizer가 인덱스 탈지 말지 결정함
B+Tree vs LSM
보조인덱스
InnoDB는 내부적으로 **숨겨진 6바이트 row_id(PK)**를 생성해서
그걸 보조 인덱스의 보조 정렬 기준으로 사용해.
→ 그래서 InnoDB는 PK 없으면 느려짐 → 항상 PK 설정을 권장함