데이터베이스 용어 사전
인덱스clustered index · 비클러스터형

클러스터형 인덱스

테이블 데이터 자체가 그 인덱스 순서로 저장되는 구조. InnoDB는 PK가 클러스터형이고 PostgreSQL에는 없다.

인덱스의 리프에 행 데이터가 통째로 들어 있는 구조. 인덱스가 곧 테이블이다. MySQL InnoDB와 PostgreSQL의 가장 큰 구조 차이이고, UUID PK 논쟁의 뿌리다.

두 엔진이 갈리는 지점

[MySQL InnoDB]  테이블 = PK 기준 B+Tree
  PK 인덱스 리프  →  [행 전체 데이터]
  보조 인덱스 리프 →  [PK 값]          ← 행 위치가 아니라 PK다
  • [PostgreSQL] — 테이블 = 순서 없는 힙(heap)
    • 모든 인덱스 리프 → [TID = 행의 물리 위치]
    • PK조차 별도 인덱스일 뿐이다

InnoDB에서 따라오는 세 가지 결과

① 보조 인덱스 조회는 2단계다.

  • WHERE email = 'a@b.com' — (email 보조 인덱스 사용)
    • ① email 인덱스 트리를 타고 내려가 PK 값을 얻는다
    • ② 그 PK로 PK 트리를 다시 타고 내려가 행을 얻는다

② PK가 크면 모든 보조 인덱스가 함께 커진다. 보조 인덱스 리프가 PK 값을 품기 때문이다. 그래서 PK는 작아야 한다.

③ PK는 단조 증가가 유리하다. 리프가 PK 순으로 정렬돼 있으니, AUTO_INCREMENT처럼 계속 커지는 값은 항상 맨 끝에 추가된다. 반면 랜덤한 UUIDv4를 PK로 쓰면 삽입이 트리 중간 여기저기에서 일어나 페이지 분할과 단편화가 생긴다.

-- 대안: 시간순으로 정렬되는 UUID를 쓴다
-- UUIDv7 · ULID → 앞부분이 타임스탬프라 단조 증가에 가깝다

PostgreSQL의 CLUSTER는 다르다

CLUSTER member USING idx_member_created;   -- 한 번 물리 정렬

이건 일회성이다. 이후 삽입은 다시 힙 아무 데나 들어가므로 정렬이 유지되지 않는다. PostgreSQL에 "클러스터형 PK"라는 개념은 없다.

면접 함정

  • "PK 인덱스는 어느 DB나 같다" → InnoDB에서는 데이터 그 자체, PostgreSQL에서는 힙을 가리키는 별도 구조다.
  • "UUID PK는 무조건 나쁘다" → InnoDB에서 랜덤 UUID가 문제이고, PostgreSQL은 힙이라 영향이 훨씬 작다. UUIDv7이면 InnoDB에서도 완화된다.

2단계 조회를 계획에서 확인하기

-- MySQL: 보조 인덱스로 찾고 PK 트리를 다시 타는지 본다
EXPLAIN SELECT name FROM member WHERE email = 'a@b.com';
-- key: idx_email,  Extra: (없음)          → 2단계 조회 (PK 트리로 한 번 더)

EXPLAIN SELECT id FROM member WHERE email = 'a@b.com';
-- key: idx_email,  Extra: Using index     → 커버링. PK 가 리프에 있으니 2단계 불필요

두 번째가 InnoDB에서 커버링이 공짜로 되는 경우다. 보조 인덱스 리프가 PK를 이미 품고 있기 때문이다.

PostgreSQL에서 물리 정렬을 흉내 내기

-- 일회성 정렬 (테이블 전체를 다시 쓴다. ACCESS EXCLUSIVE 락)
CLUSTER member USING idx_member_created;

-- 얼마나 정렬돼 있는지 확인 — correlation 이 1에 가까울수록 인덱스 순 = 물리 순
SELECT attname, correlation
  FROM pg_stats WHERE tablename = 'member' AND attname = 'created_at';

correlation이 1에 가까우면 인덱스 스캔이 사실상 순차 읽기가 되어 훨씬 빠르다. 시계열처럼 자연히 순서대로 쌓이는 데이터는 별도 조치 없이 이 값이 높다 — BRIN 인덱스가 그런 테이블에서 잘 통하는 이유다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 인덱스 — B+Tree·클러스터형·커버링(PostgreSQL·MySQL)