Clustering Index
클러스터형 인덱스는 프라이머리 키(PK) 값의 순서에 따라 테이블의 데이터가 물리적으로 정렬되어 저장되는 방식을 의미한다.
- 변경되는 경우엔 해당 레코드의 물리적인 위치도 변경
- 테이블 구조 자체는 일반 B-Tree와 비슷하지만, 클러스터링 인덱스의 리프노드에 레코드의 모든 컬럼이 저장
InnoDB 테이블은 반드시 클러스터형 인덱스를 가지며, 아래의 우선순위에 따라 클러스터링 키를 결정한다.
- 명시적으로 지정된 프라이머리 키(Primary Key)
- 첫 번째
NOT NULL+ 유니크 인덱스(Unique Index). - 내부적으로 6바이트 크기의 숨겨진
ROW_ID컬럼 생성
보조 인덱스가 PK를 참조하는 구조
섹션 제목: “보조 인덱스가 PK를 참조하는 구조”InnoDB의 모든 보조 인덱스는 리프 노드에 데이터의 물리적 주소 대신 프라이머리 키 값을 저장하게 되는데, 이는 다음과 같은 이유 때문이다.
- 보조 인덱스가 실제 레코드 주소를 가짐
- 실제 레코드 주소의 PK 변경 시 레코드의 주소가 변경 필요(클러스터링 된 상태여야 하기 때문)
- 그때마다 해당 테이블의 모든 인덱스에 대해 변경 작업 수행 필요
- 오버헤드 발생
PK를 클러스터링 인덱스로 사용하는 이유
섹션 제목: “PK를 클러스터링 인덱스로 사용하는 이유”PK를 클러스터링 인덱스로 사용하는 이유는 읽기 성능을 향상시키기 위함이고, 그 장단점은 다음과 같다.
- 장점
- 프라이머리 키 기반의 범위 검색(Range Scan) 시, 데이터가 물리적으로 인접해 있어 빠른 I/O 성능 제공
- 보조 인덱스가 프라이머리 키 값을 포함하므로, 쿼리에 필요한 모든 컬럼이 보조 인덱스와 프라이머리 키에 있다면 커버링 인덱스 사용 가능
- 단점
- 모든 보조 인덱스가 PK 값을 포함하므로, PK의 크기가 크면 보조 인덱스의 전체 크기도 상승
- 데이터 삽입 시 PK 값에 따라 물리적 저장 위치가 결정되므로, 순차적이지 않은 PK 값(예: UUID)을 사용하면 페이지 분할(Page Split)이 빈번하게 발생하여 쓰기 성능이 저하 발생
- 보조 인덱스를 통한 조회는 PK를 이용한 2차 조회로 추가적인 오버헤드 발생
PK 타입 선택 - Long vs UUID
섹션 제목: “PK 타입 선택 - Long vs UUID”PK 타입은 단순히 식별자 표현 방식의 차이가 아니라, 클러스터링 인덱스의 쓰기 패턴과 보조 인덱스의 크기, 분산 환경에서의 충돌 가능성까지 결정하는 설계 의사결정이다.
BIGINT AUTO_INCREMENT (Long)
섹션 제목: “BIGINT AUTO_INCREMENT (Long)”DB가 발급하는 단조 증가 정수 키이다.
- 장점
- 8 bytes로 가장 작아 보조 인덱스의 누적 크기가 최소
- 항상 클러스터링 인덱스의 우측 리프에 삽입되어 페이지 분할 거의 없음 → 쓰기 처리량과 버퍼 풀 캐시 적중률 모두 우수
- 비교·해시·직렬화 비용이 저렴
- 단점
- 외부에 노출되면 ID 추정으로 가입자 수·주문 수 같은 비즈니스 규모가 새거나 IDOR 공격 표면이 됨
- 단일 DB가 발급하므로 멀티 마스터·샤딩 환경에서 글로벌 유니크 보장이 별도 설계 필요
- INSERT 이후에야 값이 확정되므로 트랜잭션 외부에서 미리 알 수 없음
UUIDv4 (Random)
섹션 제목: “UUIDv4 (Random)”128bit 난수 기반 식별자이다.
- 장점
- 글로벌 유니크 → 분산·멀티 마스터·오프라인 클라이언트 환경에서 충돌 위험 없음
- 애플리케이션·클라이언트 측에서 사전 발급 가능 → INSERT 전 ID를 알 수 있어 워크플로 단순화
- 노출돼도 추정 가능한 정보가 거의 없음
- 단점
- 16 bytes로 Long의 2배 → 모든 보조 인덱스 누적 크기 증가
- 완전 랜덤 분포 → 클러스터링 인덱스에 무작위 위치로 삽입되어 페이지 분할 빈발, 단편화와 쓰기 amplification 발생
- hot 페이지가 흩어져 버퍼 풀 캐시 적중률 하락, 벌크 INSERT 처리량 급감
UUIDv7 (Time-ordered)
섹션 제목: “UUIDv7 (Time-ordered)”상위 48bit에 Unix epoch milliseconds 타임스탬프를 배치하고 나머지를 난수로 채워 시간 순 단조 증가를 보장하는 RFC 9562(2024) 표준 포맷이다.
- 장점
- 16 bytes는 그대로지만 ms 단위로 단조 증가 → 클러스터링 인덱스에서 항상 우측 리프에 삽입되어 페이지 분할이 BIGINT 수준으로 회복
- 글로벌 유니크와 사전 발급 가능성은 v4와 동일하게 유지 → 분산 환경 안전성 + 쓰기 효율 동시 달성
- 시간 순 정렬이 자연스러워 시계열 조회·페이징에 유리
- 단점
- Long 대비 여전히 2배 크기 → 보조 인덱스 누적 비용은 그대로 존재
- 타임스탬프 부분이 노출되어 생성 시각 추정이 가능 → 보안 민감 식별자에는 부적절
- Java 표준 라이브러리의
UUID.randomUUID()는 v4 → v7 생성에는 별도 라이브러리(예:java-uuid-generator, JDK 25+ 신규 API) 필요 - MySQL 8.x는
UUID_v7()내장 함수가 없어 애플리케이션에서 발급해야 함 (PostgreSQL 18은uuidv7()내장)
비교 정리
섹션 제목: “비교 정리”| 항목 | BIGINT AUTO_INCREMENT | UUIDv4 | UUIDv7 |
|---|---|---|---|
| 크기 | 8 bytes | 16 bytes | 16 bytes |
| 단조 증가 | O | X | O (ms 단위) |
| 페이지 분할 빈도 | 거의 없음 | 빈번 | 거의 없음 |
| 글로벌 유니크 | X (DB 인스턴스 단위) | O | O |
| 사전 발급 | X | O | O |
| 인덱스 캐시 효율 | 매우 높음 | 매우 낮음 | 높음 |
| 외부 노출 위험 | 추정 가능 (보안 위험) | 추정 어려움 | 시각 추정 가능 |
| 표준화 시점 | SQL 표준 이전 | RFC 4122 (2005) | RFC 9562 (2024) |
선택 가이드
섹션 제목: “선택 가이드”- 단일 DB · 외부 노출 없음 · 쓰기 처리량 최우선 →
BIGINT AUTO_INCREMENT - 분산 환경 · 사전 발급 필요 · 외부 노출 가능 →
UUIDv7 - UUIDv4는 신규 채택 시 v7 대비 쓰기 성능 손해만 발생하므로 권장하지 않음 (기존 시스템 호환·라이브러리 미지원 등의 제약이 없는 한)
- UUID 채택 시 MySQL은
BINARY(16)컬럼에 저장해 문자열 36 bytes 비용을 회피