인프런 커뮤니티 질문&답변

김영욱님의 프로필 이미지

작성한 질문수

자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]

11강. MySQL에서 테이블 만들기

수업과는 조금 다른 실무 id취향 질문이 있습니다!

작성

·

30

1

안녕하세요, 테이블 생성중에 id관련된 내용을 보다가 궁금한게 생겨 질문 남깁니다!

 

개발자에 따라서 유한한 bigint로 이루어진 고유 id를 극혐하고 이론상 거의 무한한 uuid를 사용해야한다 주장하시는 분들이 꽤나 많던데, 태현님께서는 어떻게 생각하시나요?

 

저는 개인적으로 실무에서도 uuid대신 bigint로 이루어진 id를 사용중에 있고, 유저에게 노출이 필요한 유저 고유번호만 내부에서 사용하는 id값 이외에 uuid칼럼을 따로 만들어서 관리하거든요 🤔🤔

만약에 uuid를 사용한다면 조회의 성능이랑.. auto_increment를 사용할 수 없으니 매 번 insert문마다 서비스로직에서 uuid를 만들어서 넘겨야 하는것에 대해서도 그래야만 하는지 좀 의문이 있기도 하구요

답변 2

0

최태현님의 프로필 이미지
최태현
지식공유자

안녕하세요 영욱님! 🙂 좋은 질문 감사합니다.

 

질문 주신 것처럼 id를 만드는 방법에는 auto_increment를 사용하는 방식과 UUID를 사용하는 방식 그리고 snowflake처럼 bit-level에 의미를 부여해 분산 환경에서 고유한 id를 만드는 방식이 있습니다.

유사 snowflake 방식은 국내에서는 거의 사용될 일 없으니 잠시 논외로 치고 id 방식과 UUID 방식을 비교해 보면, 결론부터 말씀드려 저도 id를 기본으로 사용하되, 노출이 필요한 경우에만 uuid column을 따로 만들어 관리하는 방식을 선호합니다.

 

그 이유는 다음과 같습니다.

  1. id 라는 개념은 관리가 무척 편리합니다.

  2. 운영상 대응도 용이한 편입니다 (여러 테이블을 조회하며 운영 대응을 해야 할 때 id를 key로 하여 소통하는 것이 기억이 어려운 uuid를 기준으로 소통하는 것보다는 살짝 나은 느낌입니다 🙂)

  3. id를 사용하는 것이 성능상 더 유리한 측면이 있습니다.

  4. UUID와 다르게 순서가 보장됩니다. (상황에 따라 마이그레이션이 더 용이합니다)

 

3번은 조금 더 내용을 설명 드려야 할 것 같은데요!

MySQL은 secondary index를 만들 때 기본적으로 PK를 index tree 가장 뒤에 추가하게 됩니다.

예를 들어 다음과 같은 테이블이 있다고 해보죠!

table user
  - id (PK)
  - name
  - age

그리고 제가 (name, age)를 인덱스로 잡는다면 실제 인덱스 트리에는 (name, age, id) 가 들어가게 됩니다! name이 32byte / age가 4byte / id가 8 byte라면 나이브 하게 생각했을 때 인덱스 node 하나의 크기는 44byte입니다.

인덱스 node의 크기가 중요한 이유는 조회에 자주 사용될 인덱스 node의 크기가 작으면 작을 수록 실제 데이터를 Disk에 저장하는 단위인 Page에 더 많은 인덱스 node가 저장될 수 있기 때문입니다. 즉, 더 적은 node를 유지하면 Page를 덜 불러올 수 있고 Disk I/O를 덜 일으킬 수 있고, 조금 더 성능에 유리한 측면이 있다고 생각합니다.

 

만약 UUID를 - 을 제외하고 32글자를 단순 문자열로 저장하게 되면

  • charset을 신경쓰지 않았을 때 (보통 utf8mb4를 사용하니) 128 byte

  • charset을 신경쎴을 때 (특정 column만 latin으로 잡으면) 32 byte

인데요, charset을 매번 신경쓰기엔 살짝 쉽지 않은 측면이 있고 문자열 비교 등을 할 때에는 내부적으로 charset을 일치시켜야 하다 보니 암시적인 작업이 일어날 수도 있습니다. 그렇다고 charset을 신경쓰지 않으면 128byte라는 id에 비해 상당히 큰 크기를 갖게 되죠.

그리고 물론 secondary index를 생각하지 않더라도 PK가 작으면 PK를 통해 조회가 자주 일어날 때도 도움이 되죠 🙂

 

H/W 성능이 무척 좋아진 요즘 저런 byte-level 최적화가 얼마나 성능에 영향을 미칠까 싶기도 하지만 똑같은 비용 (혹은 감수할만한 비용) 으로 괜찮은 효과를 볼 수 있다면 미리미리 준비해 놓는 것도 좋다고 생각합니다. 특히 이 경우에는 개인적으로 단점 대비 장점이 더 크다고 생각하고요!

 

당연히 id도 단점이 있을 수 있는데, 말씀해주신 것처럼 다음 데이터의 PK를 유추할 수 있다는 점입니다. 그래서 이런 경우는 uuid를 id와 별도로 따로 만들어 넣은 다음 uuid를 secondary index로 잡고 uuid를 통한 API를 만들 수 있죠.

물론 이럴 때 굳이 service layer에서 uuid를 매번 만들어 넘겨주실 필요는 없고 Java를 기준으로는..

public class Person {
  private Long id = null;
  // 2회 이상 사용된다면 유틸로 관리하셔도 좋습니다.
  private String uuid = UUID.randomUUID().toString(); 
}

처럼 아예 기본 값을 Domain Level에서 관리해주시는 것도 방법입니다.

 

또한 id가 예측 가능한 것이 항상 문제인가? 라는 관점도 있을 수 있습니다. 5번 데이터 다음에 6번 데이터가 올 걸 알고 있다고 하더라도 결국 해당 데이터에 접근하기 위해서는 적절한 권한을 획득해야 하고, 그 권한이 없다면 해당 데이터에 접근하지 못할테니까요! 다음 데이터의 PK를 예측할 수는 있어도 어떻게 활용할 수가 없는거죠. (물론, 완전히 공개된 데이터는 논외이고 UUID를 사용하는 편이 좋습니다 🙂)

 

사실 이 내용은 약간은 어려운 내용이 많이 포함되어 있어 본 강의에서 바로 담기에 어려운 측면이 있었습니다. 언젠가 (다른 강의에서) 조금 더 정리된, 풍부한 내용으로 찾아뵐 수 있도록 하겠습니다! 🙇

좋은 질문 주셔서 감사합니다. 또 언제든지 편하게 질문 올려주세요~ 감사사합니다. 🙏

김영욱님의 프로필 이미지
김영욱
질문자

이른 아침부터 정성스러운 양질의 답변 감사합니다🙏🙏

항상 고민스러운 항목이었는데 나름의 기준을 만드는데 많은 도움이 될 것 같아요!!

답변 너무 감사드리고, 오늘도 좋은하루 보내세요!!

0

안녕하세요, 인프런 AI 인턴입니다. 영욱님께서 제기하신 질문은 데이터베이스 설계에서 자주 논의되는 주제입니다. ID 필드 선택 시 BIGINT를 사용할 것인지 UUID를 사용할 것인지는, 각 접근 방식의 장단점을 이해하고 프로젝트의 성격에 따라 결정을 내려야 합니다.

  • BIGINT: 주로 auto_increment를 통해 순차적으로 ID를 생성하는 방식으로, 이 경우 인덱스 정렬이 효율적이어서 조회 성능이 뛰어날 수 있습니다. 하지만 데이터베이스에 대한 직접적인 노출이 이루어질 수 있는 경우라면 보안상의 문제로 다른 칼럼으로 노출을 관리하는 방법이 있습니다.

  • UUID: 이론적으로 무한에 가까운 고유 식별자를 생성해 주기 때문에 분산 시스템에 적합합니다. 그러나 인덱싱의 효율성 측면에서 BIGINT보다 상대적으로 낮을 수 있으며, 매 삽입 시 UUID를 생성해야 한다는 점에서 추가 로직이 요구됩니다.

다음의 자료에서 기존 유저들이 이와 관련된 고민을 어떻게 해결했는지를 확인하실 수 있습니다:

이 자료들로 ID 선택에 대한 추가적인 통찰을 얻으실 수 있을 것입니다. 각 접근 방식은 각각의 장단점이 있으므로 프로젝트의 특성과 요구 사항에 맞게 선택하는 것이 중요합니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.