Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

UUID 및 ULID에 대해 자세히 알아보기

요전날 HB 팀이 채팅을 하고 있었는데 우리의 dev-ops 마스터인 Ben이 특정 시스템에 대해 UUID 대신 ULID를 사용했으면 좋겠다고 말했습니다.

다른 노련한 엔지니어와 마찬가지로 내 반응은 엉뚱한 말을 중얼거린 다음 Google에 몰래 들어가서 ULID가 무엇인지 알아내는 것이었습니다.

2시간 후 나는 천 야드를 바라보고 나타났고 고유 식별자의 세계는 내가 상상할 수 있었던 것보다 더 크고 경이롭다는 것을 깨달았습니다.

ULID를 시작하기 전에 기본 사항으로 돌아가서 UUID가 무엇인지 논의해 보겠습니다.

"일반" ID의 문제는 무엇입니까?

데이터베이스를 사용하는 대부분의 웹 애플리케이션은 기본적으로 자동으로 증가하는 숫자 ID를 사용합니다. 예를 들어 Rails에서 다음과 같은 동작을 볼 수 있습니다.

p1 = Person.create!
p1.id
# => 1

p2 = Person.create!
p2.id
# => 2

데이터베이스는 레코드 생성 시 증가하는 카운터를 저장하기 때문에 순차적 ID를 생성할 수 있습니다.

이 패턴은 데이터베이스 외부에서도 볼 수 있습니다. 때때로 우리는 수동으로 ID를 할당해야 하고 Redis 인스턴스에 커스텀 카운터를 저장할 수 있습니다.

순차 ID는 소량의 사용 사례에서 구현하기 쉽지만 양이 증가함에 따라 문제가 더 커집니다.

  • 각 삽입이 ID를 받기 위해 줄을 서서 기다려야 하기 때문에 레코드를 동시에 생성하는 것은 불가능합니다.
  • 순차 ID를 요청하려면 네트워크 왕복이 필요하며 성능이 저하될 수 있습니다.
  • 순차 ID를 제공하는 데이터 저장소를 확장하는 것은 어렵습니다. 서로 다른 서버의 카운터가 동기화되지 않는 것에 대해 걱정해야 합니다.
  • 카운터가 있는 노드는 단일 실패 지점이 되기 쉽습니다.

Sequential id는 또한 데이터를 누출하는데, 이는 경우에 따라 문제가 될 수 있습니다.

  • 내 것이 아닐 수도 있는 리소스의 ID를 쉽게 추측할 수 있습니다.
  • 사용자를 만들고 ID가 20이면 서비스에 사용자가 20명이라는 것을 알 수 있습니다.

UUID는 웹 규모입니다.

UUID는 순차 ID와 약간 다르게 보입니다. 일반적으로 32개의 16진수로 표현되는 128비트 숫자입니다.

123e4567-e89b-12d3-a456-426655440000

UUID는 RFC 4122에 정의된 특정 알고리즘을 사용하여 생성됩니다. UUID는 순차 ID에서 발생하는 많은 문제를 해결하려고 시도합니다.

  • 공유 상태나 노드 간 조정 없이 노드 수에 관계없이 UUID를 생성할 수 있습니다.
  • 순차 ID보다 추측하기 쉽지 않습니다(나중에 자세히 설명)
  • 데이터 세트의 크기를 공개하지 않습니다.

문제는 두 노드가 동일한 ID를 독립적으로 생성할 가능성이 적다는 것입니다. 이 이벤트를 "충돌"이라고 합니다.

UUID의 다양한 맛

RFC 4122에는 다섯 가지 유형의 UUID 알고리즘이 정의되어 있습니다. 두 가지 범주로 나뉩니다.

  • 시간 및 임의성 기반 알고리즘은 우리가 논의한 것들입니다. 실행할 때마다 새로운 UUID가 생성됩니다.
    • 유형 4 :무작위로 생성된 ID입니다. 아마도 새로운 코드에 대한 최선의 선택일 것입니다.
    • 유형 1 :ID에는 호스트의 MAC 주소와 현재 타임스탬프가 포함됩니다. 추측하기 쉽기 때문에 더 이상 사용되지 않습니다.
    • 유형 2 :흔하지 않은 것 같습니다. 오래된 형태의 RPC를 위해 특별히 제작된 것으로 보입니다.
  • 이름 기반 알고리즘 조금 다릅니다. 주어진 입력 세트에 대해 항상 동일한 UUID를 생성합니다.
    • 유형 5 :SHA-1 해시를 사용하여 UUID를 생성합니다. 추천합니다.
    • 유형 3 :MD5 해시를 사용하며 MD5가 너무 안전하지 않기 때문에 더 이상 사용되지 않습니다.

Ruby에서는 uuidtools를 통해 UUID를 생성할 수 있습니다. 보석. 신비한 유형 2를 제외한 모든 유형을 지원합니다.

# Code stolen from the uuidtools readme. :)
require "uuidtools"

# Type 1
UUIDTools::UUID.timestamp_create
# => #<UUID:0x2adfdc UUID:64a5189c-25b3-11da-a97b-00c04fd430c8>

# Type 4
UUIDTools::UUID.random_create
# => #<UUID:0x19013a UUID:984265dc-4200-4f02-ae70-fe4f48964159>

# Type 3
UUIDTools::UUID.md5_create(UUIDTools::UUID_DNS_NAMESPACE, "www.widgets.com")
# => #<UUID:0x287576 UUID:3d813cbb-47fb-32ba-91df-831e1593ac29>

# Type 5
UUIDTools::UUID.sha1_create(UUIDTools::UUID_DNS_NAMESPACE, "www.widgets.com")
# => #<UUID:0x2a0116 UUID:21f7f8de-8051-5b89-8680-0195ef798b6a>

ULID로 이동

참고: 이 블로그 게시물의 원본 버전에서 ULID 사양에 대한 링크를 잊어버렸습니다. 여기있어. Ruby 및 기타 언어로 구현된 링크를 제공합니다.

ULID는 고유 식별자에 대한 유용한 새로운 정보입니다. 가장 분명한 차이점은 약간 다르게 보인다는 것입니다.

01ARZ3NDEKTSV4RRFFQ69G5FAV

두 개의 base32로 인코딩된 숫자로 구성됩니다. UNIX 타임스탬프 다음에 임의의 숫자가 옵니다. 사양에 정의된 구조는 다음과 같습니다.

01AN4Z07BY      79KA1307SR9X4MV3

|----------|    |----------------|
 Timestamp          Randomness
   48bits             80bits

이 구조는 매력적입니다! 기억한다면 UUID는 타임스탬프 또는 임의성에 의존하지만 ULID는 타임스탬프 을 모두 사용합니다. 무작위.

결과적으로 ULID에는 몇 가지 흥미로운 속성이 있습니다.

  • 사전순(즉, 알파벳순)으로 정렬할 수 있습니다.
  • 타임스탬프는 밀리초 단위로 정확합니다.
  • UUID보다 예쁘다 :)

이것은 몇 가지 멋진 가능성을 열어줍니다.

  • 데이터베이스를 날짜별로 파티션하는 경우 ULID에 포함된 타임스탬프를 사용하여 올바른 파티션을 선택할 수 있습니다.
  • 밀리초 정밀도가 허용되는 경우 별도의 created_at 열 대신 ULID를 기준으로 정렬할 수 있습니다.

몇 가지 가능한 단점도 있습니다.

  • 타임스탬프를 노출하는 것이 애플리케이션에 좋지 않은 생각이라면 ULID가 최선의 선택이 아닐 수 있습니다.
  • sort by ulid 밀리초 미만의 정확도가 필요한 경우 접근 방식이 작동하지 않을 수 있습니다.
  • 인터넷에 따르면 일부 ULID 구현은 방탄이 아닙니다.

결론

UUID는 표준이며 앞으로도 계속 사용될 것입니다. 그것들은 영원히 존재했으며 라이브러리는 상상할 수 있는 모든 언어로 제공됩니다. 그러나 새로운 접근 방식은 특히 분산 시스템이 점점 더 많이 실행되는 세계에 들어서면서 고려할 가치가 있습니다. 새로운 고유 ID 접근 방식을 사용하면 RFC4122 발행 시 일반적이지 않던 문제를 해결하는 데 도움이 될 수 있습니다.