프로젝트에서 식별자를 설계할 때 단순히 Long auto increment를 쓸지, UUID를 쓸지 고민한 적이 있다. 외부에 노출되는 ID를 예측하기 어렵게 만들고 싶을 때 UUID는 매력적인 선택이다.
하지만 UUID를 PK로 쓰면 인덱스 성능이 나빠진다는 이야기도 자주 보였다. 그래서 무작정 UUID를 쓰기보다, 어떤 종류의 UUID가 어떤 성격을 갖는지 먼저 확인할 필요가 있었다.
이 글은 UUIDv4, ULID, UUIDv7을 비교하면서 인덱스 관점에서 어떤 차이가 있는지 정리한 기록이다.
처음 보인 문제
UUIDv4는 랜덤 값에 가깝다. 예측이 어렵다는 장점이 있지만, B-Tree 인덱스 입장에서는 삽입 위치가 계속 흩어질 수 있다.
auto increment ID는 값이 증가하므로 보통 인덱스 뒤쪽에 순차적으로 들어간다. 반면 UUIDv4는 새 값이 인덱스의 여러 위치에 들어갈 수 있다. 이 때문에 페이지 분할이나 캐시 효율 문제가 생길 수 있다.
즉, UUIDv4가 항상 느리다는 뜻보다는, 쓰기 패턴과 인덱스 구조에 따라 불리할 수 있다는 의미로 이해했다.
해결 방안
UUIDv4의 랜덤성이 부담된다면 시간 순서 특성을 가진 식별자를 고려할 수 있다. 이때 ULID와 UUIDv7이 후보가 된다.
ULID는 시간 정보와 랜덤 정보를 함께 사용한다. 문자열로 표현했을 때 정렬 가능하다는 장점도 있다. UUIDv7도 시간 기반 UUID로, 기존 UUID 생태계와 더 잘 맞으면서 정렬 친화적인 특성을 가진다.
이 둘은 완전히 같은 선택지는 아니지만, 공통적으로 랜덤 UUID보다 인덱스 삽입 위치를 더 예측 가능하게 만들 수 있다.
트레이드오프
그렇다고 시간 순서 식별자가 항상 더 좋은 것은 아니다. 랜덤성이 필요한 경우도 있다.
예를 들어 외부에 노출되는 ID에서 생성 시점을 추측하기 어렵게 만들고 싶거나, 특정 도메인에서 순서 정보가 노출되는 것이 부담이라면 랜덤 UUID가 더 적절할 수 있었다.
반대로 내부 PK나 쓰기 성능이 중요한 테이블에서는 정렬 가능한 식별자가 더 나을 수 있다.
결국 식별자는 성능, 보안, 운영 편의성 사이에서 선택해야 했다.
선택 기준
프로젝트에서 식별자를 고를 때는 아래 기준을 확인한다.
- 외부에 노출되는 ID인가
- 생성 순서가 노출되어도 괜찮은가
- 쓰기 성능과 인덱스 효율이 중요한 테이블인가
- DB와 애플리케이션에서 해당 타입을 다루기 쉬운가
- 분산 환경에서 충돌 가능성을 어떻게 볼 것인가
이 기준 없이 단순히 UUID가 좋다거나 나쁘다고 말하기는 어려웠다.
마무리
UUID PK와 인덱스 성능을 보면서 확인한 것은, 식별자 선택도 도메인과 운영 조건에 따라 달라진다는 점이었다.
UUIDv4는 예측 불가능성이 장점이지만 인덱스 쓰기 패턴에서는 불리할 수 있었다. ULID와 UUIDv7은 시간 순서 특성을 통해 그 문제를 줄일 수 있지만, 순서 정보가 어느 정도 드러날 수 있다.
식별자는 단순한 값이 아니라 데이터가 쌓이고 조회되는 방식에 영향을 주는 설계 요소였다.