인덱스 종류

클러스터 인덱스 (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 오버헤드 발생

인덱스 탈지말지 결정 가능?

  1. 클라이언트가 쿼리 전송
  2. 파싱 (Parsing) syntax AST
  3. 쿼리 재작성 (Rewriting)
  4. 최적화 (Optimization) 인덱스 탈지말지, 조인 순서 변경 EXPLAIN으로 확인
  5. 실행 계획 생성
  6. 실행 (Execution)
  7. 결과 반환

인덱스 안타면 어떻게 가져옴?

모든 row full scan

디스크에서 가져오는 방식

InnoDB의 페이지 크기는 16KB

버퍼 풀(Buffer Pool)

[디스크]
| 페이지1 | 페이지2 | 페이지3 | ... |

→ 페이지 단위로 읽어서 ↓

[Buffer Pool]
| 페이지1 | 페이지2 | ... |

LRU 기반 페이지 교체 전략

이때, Optimizer가 인덱스 탈지 말지 결정함

B+Tree vs LSM

https://jaeyeong951.medium.com/%EC%83%89%EC%9D%B8-index-%EC%9D%98-%EB%91%90-%EA%B0%80%EC%A7%80-%ED%98%95%ED%83%9C-lsm-%ED%8A%B8%EB%A6%AC-b-%ED%8A%B8%EB%A6%AC-7a4ab7887db5

보조인덱스

InnoDB는 내부적으로 **숨겨진 6바이트 row_id(PK)**를 생성해서
그걸 보조 인덱스의 보조 정렬 기준으로 사용해.

→ 그래서 InnoDB는 PK 없으면 느려짐 → 항상 PK 설정을 권장함