본문 바로가기
Spring/Spring Study

[Spring] UUID, PK로 사용하면 어떨까?

by ♡˖GYURI˖♡ 2024. 7. 13.
728x90
우리 팀은 Entity의 PK로 Long형의 id 대신 UUID로 생성한 32자리 String 값을 사용하고 있다.
'숫자로 쉽게 노출되는 Long형의 id보다 보안성이 좋을 것이다.'라는 생각에 적용한 것인데, DB를 많이 차지한다는 단점이 있다. 이렇게 PK로 String 값을 쓰는 것이 많이 사용되는 방법일까? 라는 의문에 UUID부터 찾아보기로 했다.

 

 

 

 

UUID란?

Universally Unique Id Entifier

16진수 32개로 되어있는 조합으로 구성되어 있고, 고유성과 보안, 분산 시스템에서의 유용성, 쉬운 생성, 데이터베이스 인덱싱, 일관성 유지 및 충돌 가능성이 낮다는 장점으로 유용하게 사용되고 있다.

 

 

사용 방법

  • randomUUID() : 버전4의 랜덤한 UUID 생성
  • toString() : 객체 형식의 UUID를 문자열로 변환
  • fromString() : 문자열 형식의 UUID를 객체로 변환

 

UUID 버전

UUID는 버전 1부터 5까지 총 5가지의 버전이 있다. 이 중에서 우리 팀이 사용한 건 4버전이다.

  • 버전1 : 시간 기반 UUID, 타임스탬프와 MAC 주소를 기반으로 생성
  • 버전2 : DCE 보안 UUID, POSIX UID, GID 또는 식별자를 기반으로 생성
  • 버전3 : 이름 기반 UUID, 해시 함수를 사용하여 생성
  • 버전4 : 무작위 기반 UUID, 난수 생성기를 사용하여 생성
  • 버전5 : 이름 기반 UUID, SHA-1 해시 함수를 사용하여 생성

버전4의 난수 생성기를 사용한다는 건, 의사 난수라 발생하여 특정 상황일 경우 동일한 UUID가 생성될 수 있다는 이야기이다. 하지만 자바에서는 UUID 생성로직 내부에서 SecureRandom을 이용하고 있어 의사 난수가 발생하지 않는다.

 

 

중복 확률

일반적으로 UUID는 350,282,366,920,938,463,374,607,431,768,211,456개(3.4 X 10^38)를 생성할 수 있다.

매 초 10억개씩 85년동안 생성하면 중복이 발생활 확률은 50%라고 한다.

 

이 정도 확률이면 중복 발생은 거의 일어나지 않는다고 보는 것이 맞을 것 같다.

 

 

UUID Column Schema

32자리에 하이픈 4개까지 하면 총 36자리의 문자열이 생긴다. 하지만 이는 DB입장에서 보면 너무 큰 key값이다.

이걸 key로 지정해서 비교를 한다면 select할 때마다 비용이 크게 발생할 여지가 있다.

 

하지만 UUID에서 하이픈을 빼고 생각하면 32자리의 문자열이니 16byte로 나타낼 수 있게 된다.

16진수(4bit) * 32자리 = 128bit = 16byte

 

그래서 BINARY(16)을 사용하는 것이 mysql에서 적당한 UUID를 사용하는 제일 적절한 타입이라고 한다.

 

우리 팀은 이 부분을 간과한 것 같다.
JPA를 사용하여 바로 Entity를 생성하고, String값을 그대로 넣다보니 자동으로 VARCHAR 타입으로 저장되게 된다.

UUID를 VARCHAR로 저장하게 되면 각 문자가 1바이트씩 차지하여 36바이트를 차지하게 된다.
이는 UUID를 이진 데이터로 저장하는 BINARY(16)이 차지하는 공간보다 20바이트나 더 차지하는 것이다.

UUID를 만들 때 하이픈 4개를 뺀다고 한들, 32바이트를 차지하고 이는 BINARY(16)의 2배를 차지하는 것이다.

 

 

 

PK : Long형의 자동 증가 id와 UUID 중 고민🧐

현재 두 가지 방법 중 고민 중인데, 각각의 장단점을 찾아보았다.

 

 

Long형의 자동 증가 id

장점

  • 성능 효율성
    • 숫자형 id는 인덱싱과 비교 연산에서 빠르며 메모리 사용량이 적다.
    • Long 타입은 64비트로, 충분히 큰 범위를 제공해 대부분의 애플리케이션에서 유일성을 유지할 수 있다.
  • 가독성
    • 사람이 이해하기 쉽고, 디버깅 및 로깅에서 유리하다.
  • 데이터베이스 공간 효율성
    • Long 타입은 8바이트를 차지하므로 공간 효율성이 높다.
  • 순차성
    • id가 순차적으로 증가하므로 삽입 시 디스크 페이지 분할이 덜 발생해 인덱스 성능이 향상된다.

단점

  • 전역 유일성 보장 어려움
    • 단일 DB에서만 유일성을 보장하므로, 분산 시스템에서는 충돌을 피하기 위한 추가 작업이 필요하다.
  • 예측 가능성
    • 다음 id가 예측 가능하므로 보안 측면에서 취약할 수 있다.
  • 확장성 문제
    • 샤딩이나 복제된 DB 환경에서는 ID 충돌을 피하기 위한 전략이 필요하다.

 

UUID

장점

  • 전역 유일성
    • 전 세계적으로 유일한 ID를 생성할 수 있어, 분산 시스템이나 여러 DB 간에 충돌 걱정 없이 사용할 수 있다.
  • 보안성
    • 예측이 불가능한 고유 값으로 보안 측면에서 유리하다.
  • 병합 및 통합
    • 여러 시스템에서 데이터를 병합할 때 유일성 유지가 쉽다.

단점

  • 성능 저하
    • UUID는 16바이트(128비트) 크기를 가지며, String으로 저장할 경우 더 많은 메모리를 차지한다.
    • 인덱싱과 비교 연산에서도 성능이 저하될 수 있다.
  • 공간 비효율성
    • UUID를 String으로 저장하면 일반적으로 36문자(하이픈 포함) 또는 32문자(하이픈 제외)를 사용해, 더 많은 저장 공간이 필요하다.
  • 가독성 저하
    • 사람이 이해하고 기억하기 어려운 형태이다.
  • 삽입 성능
    • 랜덤하게 생성되므로, 디스크 페이지 분할이 자주 발생해 인덱스 성능이 저하될 수 있다.

 

 

결론적으로 단일 DB 환경에서는 Long형 자동 증가 id가 더 적합할 수 있다.

반대로 분산 시스템이나 글로벌 유일성이 필요할 경우 UUID가 더 적합할 수 있다.

 

 

진행 중인 프로젝트의 PK 설정을 수정해야 할 수도 있을 것 같은 불안감이 든다...🥹