Should you use uuid for database keys
이 글은 medium를 번역한 것으로, 대부분의 내용을 이 블로그에서 발췌하였습니다.
데이터베이스에서 테이블 디자인 시에 항상 첫 번째 고민은 그 테이블의 유니크 인덱스를 무엇으로 할지입니다. 대부분 기본적으로 AUTO INCREMENT INTEGER를 사용합니다. 이 경우 ID 기반 정렬, 검색을 쉽게 지원할 수 있어 선호되는 부분이 있습니다. 그러나 이 ID가 외부 노출이 되는 경우 혹은 해커가 파라미터를 수정해 테이블 정보를 접근하는 경우가 있어 보안적으로 중요한 경우에는 UUID를 사용합니다. 이 글에서는 UUID는 무엇인지, 그리고 성능적인 문제를 살펴보고, 개발자들이 주로 사용하는 다른 ID들을 살펴볼 것입니다. 마지막으로, 각각의 ID를 언제 사용하는지를 살펴 보려고 합니다.
UUID란?
UUID(Universally Unique Identifier)는 object나 record를 unique하게 결정하는 128-bit 숫자입니다. UUID를 생성하는 방식에 따라 총 8가지의 UUID가 있으며, UUIDv{n}으로 구분됩니다. 가장 흔하게 쓰이는 것은 UUIDv4로써, 무작위성으로 생성되는 숫자입니다.
9c55a1a6-d374-44b7-b754-bf70087348d0
uuid-generator 에서는 UUIDv1, UUIDv4, UUIDv7을 제공하며, 13번째에 적혀진 숫자를 보고 UUID의 버전을 인식할 수 있습니다. 위 예시에서는 4로, UUIDv4임을 알 수 있습니다.
UUID는 globally unique itentifer가 필요한 경우, 특히 다른 시스템 간 상호작용 없이 필요한 경우에 이상적입니다. 그러나 UUID를 데이터베이스 키, 특히 primary key로 사용하는 성능 저하의 가능성을 가지고 있습니다.
Insert Performance
대부분의 데이터베이스는 빠른 조회를 위해서 B+ tree 구조로 인덱스를 조직합니다. 항상 새로운 행이 삽입되면, B+ Tree는 쿼리 성능 최적화를 위해서 리밸런싱을 필요로 합니다. 만약 AUTO INCREMENT를 사용하게 되면, 자료가 항상 순차적으로 삽입되게 되고, 데이터베이스는 이를 tree에서 쉽게 정확한 위치에 집어넣을 수 있습니다. 그러나, UUIDv4의 경우 근본적으로 무작위하기 때문에, B+ Tree에 흩뿌려지게 되고 이는 지속적인 리밸런싱을 일으킵니다. 따라서 백만 단위 이상의 행을 가진 테이블을 다룬다면 이 리밸런싱은 엄청난 삽입 성능 이슈를 발생시킬 수 있습니다.
Higher Storage Costs
기본적으로 UUID는 AUTO INCREMENT INTEGER 보다 4배의 메모리를 사용합니다. 그러나 실제로는 UUID가 사람이 읽을 수 있는 형식으로 저장하기 때문에, 최대 20배(668bit)까지도 저장 용량을 사용하는데요, 이는 큰 데이터베이스일 수록 무시할 수 없는 용량의 차이를 가집니다. 100만 행에 대해서 테스트한 결과는 다음과 같습니다.
- UUID 테이블이 nteger 테이블의 최대 2.3배 크기를 가집니다.
- UUID id field가 integer id field의 최대 9.3배 스토리지를 요구합니다.
- UUID 열이 integer 열의 최대 3.5배 크기를 가집니다. 이러한 스토리지 차이는 쿼리 성능과 비용에서 잠재적인 차이를 가지게 됩니다.
UUID의 다른 대안
- UUIDv7
- UUIDv7은 time-based UUID로써, Unix Epoch timestamp를 기준으로 순서를 가지는 식별자입니다.
- UUIDv7은 정렬되어 있기 때문에, indexing performance에서 UUIDv4와 다르게 지속적인 리밸런싱을 유발하지 않고, 따라서 insert performance에서 페널티를 가지지 않습니다.
- ULID
- ULID는 UUID의 대체제로써 전역적으로 고유한 성질을 가지지만, 동시에 사전적 정렬이 가능하게 하는 장점을 가진 ID입니다. ULID는 48bit로 밀리세컨드 단위의 timestamp를 앞에 표현하고, 80bit로 무작위성을 부여한다. 따라서, 전혀 다른 곳에서 생성된 ULID가 주어지더라도, 앞의 48비트를 기준으로 정렬하여 삽입하는 것이 가능합니다.
- 고유성이 필요하지만 여러 곳에서 고유한 ID를 생성하거나, 정렬 성능이 중요한 경우에 권장됩니다.
결론
UUID는 분산 시스템에서 전역적인 고유 식별자로써 중요한 역할을 합니다. 그러나 UUID의 사용은 랜덤성이 가진 문제로써 중요한 성능과 스토리지 이슈를 일으킵니다. 따라서 시스템을 면밀히 살펴보고, UUIDv7과 ULID와 같은 다른 대체제를 사용하는 것이 중요합니다. 조금 더 단순한 poc나 toy project에서는 AUTO INCREMENT INTEGER를 사용한 뒤에 다른 ID를 채택하는 것이 더 나을 수 있습니다.