21P by neo 19일전 | favorite | 댓글 4개
  • UUID는 데이터베이스 테이블 기본 키로 자주 사용됨
    • 생성하기 쉽고 분산 시스템 간에 공유하기 쉬우며 고유성을 보장
    • UUID의 크기를 고려할 때 이것이 올바른 선택인지 의문이 들지만, 우리가 결정할 수 없는 경우가 많음
  • 이 글은 "UUID가 키에 적합한 형식인가"에 초점을 맞추지 않고 PostgreSQL에서 UUID를 기본 키로 효율적으로 사용하는 방법에 대해 설명

PostgreSQL와 UUID를 기본 키로 사용하기

  • UUID란?
    • UUID는 데이터베이스 테이블의 기본 키로 자주 사용됨
    • 분산 시스템 간에 쉽게 공유 가능하며 고유성을 보장함
    • UUID의 크기 때문에 적합한지 의문이 들 수 있지만, 선택의 여지가 없는 경우가 많음

PostgreSQL에서 UUID 데이터 타입

  • UUID를 문자열로 저장

    • PostgreSQL은 문자열을 저장하기 위한 text 데이터 타입을 제공함
    • 그러나 text 타입은 UUID를 저장하기에 적합하지 않음
    • PostgreSQL은 UUID를 위한 전용 데이터 타입 uuid를 제공함
    • uuid 타입은 128비트 데이터 타입으로, 하나의 값을 저장하는 데 16바이트가 필요함
    • text 타입은 1 또는 4바이트의 오버헤드가 추가됨
  • 실험 결과

    • 두 개의 테이블을 생성하여 비교: 하나는 text 타입, 다른 하나는 uuid 타입
    • 10,000,000개의 행을 삽입한 후 테이블 크기와 인덱스 크기를 비교
    • text 타입을 사용하는 테이블은 54% 더 크고, 인덱스 크기는 85% 더 큼

UUID와 B-Tree 인덱스

  • B-Tree 인덱스와 UUID

    • 랜덤 UUID는 B-Tree 인덱스에 적합하지 않음
    • B-Tree 인덱스는 순서가 있는 값과 잘 작동함
    • Java의 UUID.randomUUID()는 UUID v4를 반환하며, 이는 의사 랜덤 값임
    • UUID v7은 시간 순서대로 정렬된 값을 생성하여 B-Tree 인덱스에 적합함
  • UUID v7 사용

    • Java에서 UUID v7을 사용하려면 java-uuid-generator 라이브러리가 필요함
    • UUID v7을 생성하면 삽입 성능이 향상될 수 있음

UUID v7이 INSERT 성능에 미치는 영향

  • 실험
    • UUID v7을 사용하는 테이블을 생성하고, 10,000개의 행을 10번 삽입하여 성능을 측정
    • 결과는 다소 무작위적이지만, UUID v7을 삽입하는 것이 약 2배 더 빠름

추가 읽을거리

  • PostgreSQL 17에서 UUID v7이 네이티브로 지원될 가능성 있음
  • UUID v7 형식에 대한 정보
  • UUID가 데이터베이스 기본 키로서의 성능에 미치는 영향

요약

  • UUID의 길이 문제

    • 최적화가 이루어져도 UUID는 기본 키로서 최적의 타입이 아님
    • 선택의 여지가 있다면 TSID와 같은 다른 옵션을 고려할 것
  • 최적화 필요성

    • 대규모 데이터셋이나 높은 트래픽이 예상된다면 최적화를 고려해야 함
    • 기본 키 변경은 어려운 작업이므로 처음부터 올바르게 설정하는 것이 중요함
  • 주의사항

    • 필자는 PostgreSQL 전문가가 아니며, 배운 내용을 공유하는 것임
    • 유용했다면 댓글이나 트위터를 통해 피드백을 주길 바람

GN⁺의 정리

  • 이 글은 PostgreSQL에서 UUID를 기본 키로 사용할 때의 효율적인 방법을 다룸
  • UUID v7을 사용하면 삽입 성능이 향상될 수 있음을 실험을 통해 보여줌
  • 대규모 데이터셋이나 높은 트래픽이 예상되는 경우 최적화가 필요함
  • TSID와 같은 다른 옵션도 고려해볼 만함
Hacker News 의견
  • B-tree 친화적인 기본 키로 bigserial을 사용하고, 외부 레코드 로케이터 옵션으로 문자열로 인코딩된 UUID를 고려할 것을 권장함

    • 비기술적인 사용자가 인용할 경우, PNR 스타일 로케이터와 같은 간단한 옵션을 먼저 고려할 것
    • 서비스나 애플리케이션의 스키마 내에서 PK 유형을 혼합하지 말 것
    • 고유 식별자로 UUIDv7을 사용할 때는 타임코드가 내재된 데이터에만 사용할 것
    • hashids를 사용하지 말 것; 암호화 품질이 없고 일상적인 사람들에게 친숙하지 않음
    • 인코딩 시 base64나 하이픈이 포함된 알파벳을 사용하지 말 것
  • 데이터베이스 스키마 설계 시 관심사의 분리와 기계적 동조의 원칙을 염두에 둘 것

  • Stripe의 타이핑된 랜덤 ID는 실제로 랜덤이 아님

    • 메타데이터, 포함된 타임스탬프, 샤드 및 참조 키, 버전 정보 등이 포함됨
    • 개인적으로 base58로 인코딩된 AES 암호화 bigserial+HMAC 로케이터를 선호함
  • Postgres에서 랜덤 UUID는 큰 문제가 아님

    • UUID(16바이트)는 serial(4바이트)이나 bigserial(8바이트)보다 크지만, 전체 테이블 수준에서는 큰 문제가 아님
  • Postgres에서 serial vs. random UUID vs. ordered UUID를 고려하기 전에 다른 많은 것들을 걱정해야 함

  • 최근 Postgres PK로 ULID를 선택했으며, 이 기사에서 많은 도움을 받았음: https://brandur.org/nanoglyphs/026-ids

  • ULID를 선호하는 이유는 UUID 유형과 호환되며, 타임스탬프가 내장되어 있어 ID로 정렬하면 타임스탬프 순으로 정렬됨

  • 비교에 'int64'도 포함되면 UUID와 전통적인 접근 방식의 오버헤드를 비교할 수 있어 좋을 것임

  • 삽입 성능은 성능을 평가하는 나쁜 방법임

    • B-Tree 성능은 삽입 시 더 좋지만, 대규모 트랜잭션에서는 어떨지 의문임
  • SQLite에서 UUID4가 선호되는 이유는 트랜잭션 잠금 동안 페이지 캐시 충돌 가능성이 적기 때문임

    • Postgres 시스템에서도 비슷하게 적용될 수 있음
  • 정수 자동 증가 기본 키를 선호함

    • 이해하기 쉽고 정렬하기 간단함
    • 대규모 배치 프로젝트에서 마지막 기본 키를 저장하고 그보다 큰 모든 것을 가져올 수 있음
  • UUIDv7 삽입 시간 벤치마크는 UUID 생성 시간을 포함함

    • 단순히 인덱스 업데이트 비용을 분리해서 보고 싶음
  • PostgreSQL 17에서 UUIDv7 지원이 포함될 가능성이 낮음

    • 최근 작업에서 커미터가 제거되었고, 버전 17은 이미 기능 동결 상태임
  • python-ulid를 사용하기 시작했으며, ULID가 UUID보다 우수함

  • UUID v7 표준 링크가 오래되었으므로, RFC 9562를 참조할 것: https://datatracker.ietf.org/doc/html/rfc9562

uuid에 표준형식(16진수 + 하이픈) 대신 base62 인코딩을 바라는건 무리일까요?

uuidv7 은 무적이다
uuidv8+은 "신"이고

가장 큰 허들은,, 인간친화적이지 않다는 것.. 저는 아직 많은부분에서 이부분이 필요하네요..