참고) https://americanopeople.tistory.com/378
https://www.borntodare.me/mysql_uuid
https://mactto.tistory.com/entry/PostgreSQL%EC%97%90%EC%84%9C-PK%EB%A1%9C-UUID%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%A0-%EB%95%8C-%EA%B3%A0%EB%A0%A4%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94-%EC%84%B1%EB%8A%A5-%EC%9D%B4%EC%8A%88
🤔 고민한 이유
- pk처럼 유저에게 추측하기 쉽고, 안전하지 않은 데이터를 넘겨주면 위험할 것이라고 생각했다. 만약 id 2인걸로 바꿔서 낸다면?
- 다른 사용자의 데이터나 상품의 데이터를 확인하려는 시도가 생긴다
- 공격자가 시스템의 데이터 구조를 추측할 수 있다
- 예기치 못한 보안 취약점을 줄이기 위해, UUID를 활용해 세션 토큰처럼 랜덤한 값을 조회 키로 사용하는 방식을 고민했다
- 만약 uuid가 충돌이 난다면 다른 값으로 바꾸는 방법을 생각하는 쪽으로 판단했다
🛑 PK만 이용할 경우
- Auto Increment Pk는 키 값이 외부에 노출되기 쉬우며 의도치 않게 정보가 노출될 수 있다.
- 고객 수 정보, 상품 수 정보, 주문 수 정보들이 노출 될 위험이 있다.
- 다만 이 정보가 진짜 노출되면 안되는가?
- 분산 데이터베이스 환경에서 Auto Increment PK는 고유성을 보장하지 못할 수 있다
- 여러 클러스터에서 동일한 키 값이 생성될 가능성이 있습니다
- 데이터 무결성을 해치고 저장 실패를 초래한다
- 그래서 NoSQL이 ID를 UUID 타입으로 사용하는 것이다
- UUID를 사용해 분산 환경에서 데이터의 고유성을 보장할 수 있다
🔑 UUID 란
참고) https://www.borntodare.me/mysql_uuid
- 고유한 값을 생성하기 위해 표준화된 식별자이다
- 128비트 길이의 식별자로, 일반적으로 16바이트
바이너리 데이터 또는 36문자의 문자열로 표현된다
- BINARY(16): 16바이트로 저장되며, 효율적이다
- CHAR(36): 하이픈(-)이 포함된 문자열 형식으로 저장될 경우 36바이트를 차지함, 더 많은 공간 사용한다
- UUID에는 여러 버전이 있어, 각각 다른 방법으로 고유성을 보장한다
- 주로 사용되는 UUID 버전은 UUIDv1, UUIDv4, UUIDv7
- 자바는 UUIDv4를 사용한다
⚠️ UUID를 PK로 이용할 경우
- UUID는 랜덤 값으로 생성되기 때문에, 레코드 삽입 시 임의의 위치에 저장된다
- PK는 자동 정렬(클러스터링 인덱스) 특성을 가지므로, 이로 인해 성능 저하가 발생한다
- UUID는 더 많은 저장 공간을 필요로 하며, 테이블 및 인덱스 크기가 증가한다
- 이는 쿼리 성능에도 부정적인 영향을 줄 수 있다
- 페이지 분할 및 디스크 단편화 문제가 발생하여 삽입/삭제 작업의 성능을 저하시킬 수 있다
✨ UUID와 PK를 같이 사용할 경우
- 구성: Auto Increment를 Pk로 사용하여 삽입 성능을 최적화하고, UUID를 세컨더리 인덱스로 설정하는 방식이다
- 클러스터링 인덱스는 순차적으로 유지되면서도, UUID를 통한 고유성을 확보 가능하다
- 식별값이 외보 노출로 인해 UUID가 손상된다면 Pk 변경작업은 매우 비싼데, UUID가 PK와 별개로 사용되는 경우 UUID를 변경하는 작업은 훨씬 저렴하다
- 만약 UUID가 외부에 노출되어 손상된 경우, UUID만 변경해도 데이터 식별이 가능하다
- 반면, PK가 손상되면 전체 데이터베이스의 무결성을 유지하기 위한 대규모 변경 작업이 필요하다