GH GambleHub

Generowanie identyfikatorów

1) Dlaczego zwracać uwagę na identyfikatory

Identyfikator (ID) - podstawowy klucz podmiotu: linie bazy danych, wiadomości, plik, zamówienie. Jego właściwości zależą od:
  • Wyjątkowość i skala (kolizje, wzrost poziomy).
  • Porządek i sortowanie (korelacja czasu, replikacja, dedup).
  • Wydajność przechowywania (indeksy, gorące strony, rozmiar klucza).
  • Bezpieczeństwo (nieprzewidywalność, wycieki, zgadywanie).
  • Użyteczność/integracja (krótki, URL-bezpieczny, nie przypadek wrażliwy).

Wybór identyfikatora jest kompromisem pomiędzy entropią, zamawialnością, długością, szybkością generowania i eksploatacją.

2) Kluczowe wymagania i warunki

Wyjątkowość: prawdopodobieństwo zderzenia musi być mniejsze niż dopuszczalne ryzyko.
Entropia: „ile losowości” zawiera identyfikator (bit).
Czas-sortowalny/k-sortowalny-leksykograficzny sortowanie oparte na czasie.
Monotonia: nie malejąca sekwencja w węźle/strumieniu.
Lokalizacja wpisu: ile nowa wkładka jest skoncentrowana w „ogonie” indeksu (niebezpieczeństwo gorących stron).
Przewidywalność: Czy można odgadnąć sąsiednie identyfikatory (ważne dla bezpieczeństwa/API).
Reprezentacja: binarny/ciąg, Base16/32/36/58/64, łączniki, przypadek.

3) Główne rodziny identyfikatorów

3. 1 UUID

v4 (losowo): 122 bity entropii. Nieuporządkowane, dobre dla bezpieczeństwa i prostoty. Minus: indeksy „chaotyczne” ze względu na rozkład losowy - co jednak równomiernie rozprasza obciążenia i usuwa „gorące strony”.
v1 (czas + MAC): zorganizować, ale nosi MAC/czas (prywatność); często unikane.
v7 (time-ordered): milisekund time + random part. design for lexicographic sorting by time and good compression in the database. Kompromis: Pojawia się „gorący ogon” indeksu; traktowane przez ostrzenie/przedrostki/przyrost.

Wskazówki

Dla zewnętrznych interfejsów API i wymagań dotyczących lax - v4.
Dla baz danych zdarzeń/dzienników i klawiszy „sortowanych” - v7.

3. 2 ULID (Crockford Base32)

128 bitów: 48 bitów czasu (ms) + 80 bitów losowości. Leksykograficznie sortowane według czasu, przyjazne dla człowieka (bez 'I, L, O, U'), URL-safe. Istnieje zmienność monotonowa (z tym samym znaczkiem czasu, część losowa wzrasta).
Plusy: czytelność, zamawialność, przenośność.
Minusy: z bardzo dużą częstotliwością wkładek w jednym momencie - „gorący ogon”.

3. 3 KSUID

160 bitów: 32 bity czasu (sec) względem epoki + 128 bitów losowości. Większy zakres czasu i stabilne sortowanie, ciągi krótsze niż ULID? (nie - już, ale z własnym kodowaniem), dobre dla rozproszonych dzienników i obiektów.

3. 4 Płatki śniegu (k-sortowalne identyfikatory płatków)

Klasyczny schemat (niestandardowy):

[ timestamp bits ][ region/datacenter bits ][ worker bits ][ sequence bits ]

Właściwości: monotonowy wzrost na węźle, quasi-globalna wyjątkowość, krótka (64-bitowa) reprezentacja binarna.
Ryzyko: zależność od zegara (dryf czasowy/regresja), wyczerpanie sekwencji w jednym kleszczu, koordynacja bitów regionu/pracownika.
Traktowane: ochrona przed „zegarem z powrotem”, sekwencja rezerwy, wykrywacz czasu, dyscyplina PTP/NTP.

3. 5 sekwencji DB (SEKWENCJA/TOŻSAMOŚĆ)

Najprostsza generacja monotonu w jednym DBMS/shard.
Plusy: krótkie, szybkie, wygodne dla lokalnych stołów.
Minusy: trudne globalnie w rozproszonym klastrze; przewidywalne (niepewne jako klucz publiczny), tworzy gorący ogon indeksu.

3. 6 Identyfikatory adresu treści (zawartość skrótu)

Zawartość SHA-256/Blake3 → stabilny identyfikator, deduplikacja, sprawdzanie integralności, buforowanie.
Plusy: determinizm, ochrona przed substytucją.
Minusy: drogie pokolenie (CPU), kolizje to praktyczne zera, bez sortowania czasu, długość.

4) Kolizje i „paradoks urodzinowy” (intuicyjny)

Prawdopodobieństwo kolizji przypadkowego ID wielkości 'b' bitów w generacjach 'n' wynosi w przybliżeniu:

p ≈ 1 - exp (-n (n-1 )/2/2 ^ b) ≈ n ^ 2/2 ^ (b + 1) (for small p)
Przykłady:
  • UUIDv4 (122 bity) przy n = 10 ^ 12 (bilion) → p ~ 1e-14 (nieistotne).
  • 64-bitowy losowy → z n = 10 ^ 9 już p ~ 0. 027 (znaczące ryzyko).
  • Wniosek: 64-bitowa losowa jest często niewystarczająca dla ogromnych systemów; Użyj 96/128 bitów.

5) Indeksy, gorące strony i przechowywanie

Losowe klucze (v4) równomiernie rozmieścić wkładki przez drzewo indeksu → nie ma „ogon”, ale lokalizacja pamięci podręcznej jest gorsza.
Czas sortowane (v7/ULID/Snowflake) są wstawione „w ogonie” → lepsza lokalizacja i kompresja, ale ryzyko gorących stron pod wysokim równoległym zapisu.

Łagodzenie gorącego ogona:
  • prefiksy/shading przez lokatora/region (dodać 1-2 bajty przed czasem);
  • przeplatanie: część losowości w wyższych bitach;
  • wkładki wsadowe, fillfactor w drzewie B, automatyczne przejście do BRIN/klastrowania dla dużych kłód.
Rozmiar jest ważny:
  • „UUID (16B)” vs „BIGINT (8B) ”/„ INT8” pamięci/pamięci podręcznej; Base32/58/64 rzędy zwiększają rozmiar o 20-60%. Dla bazy danych, przechowywać binarne, serializować do łańcucha na krawędzi.

6) Bezpieczeństwo i prywatność

Nie używać SEQUENCE/INT jako publicznych identyfikatorów w URL/API: domyślne → wyliczenie zasobów.
Dodaj losowe, nieprzewidywalne identyfikatory (v4/v7/ULID/KSUID) dla odniesień zewnętrznych.
Nie koduj PII do identyfikatora. Jeśli chcesz włączyć atrybut, szyfruj/znak (na przykład JWE/JWS) lub użyj nieprzezroczystych żetonów.
Kodowanie URL: Base32 Crockford, Base58 (bez '0OIl'), Base64url.

7) Wielozadaniowość, prefiksy i routing

Format: '[LOKATOR _ PREFIX] - [ID]' lub binarne: 'lokator _ id | | id'.
Plusy: szybkie filtry/strony najemców, ochrona przed skanowaniem N + 1.
Minusy: może pogorszyć gęstość entropii w wyższych bitach → rozważyć dystrybucję (hash przedrostek).
Przyrostek hash (2-3 bajty) zmniejsza kolizje i pomaga odstrzelić routing: 'shard = hash (id)% N'.

8) Praktyczne zalecenia dotyczące wyboru

API, linki publiczne, usługi dystrybuowane bez ścisłego zamówienia: UUIDv4, ULID/KSUID.
Dzienniki/wydarzenia/zamówienia, gdzie często sortujemy według czasu: UUIDv7 lub ULID (monoton).
Ultra-wysoka przepustowość z lokalną monotonią i krótkim kluczem: 64-bitowy płatek śniegu (wymagana dyscyplina czasu).
Sklepienia artefaktów/budowli/blobs: adresowalne treści (SHA-256), a na górze - przyjazny dla człowieka krótki „pokaz” (Hashids/link).
Tabele lokalne w jednej bazie danych: SEQUENCE/IDENTITY + zewnętrzny „wrapper” dla linków publicznych (maskowanie).

9) Implementacje i przykłady

9. 1 PostgreSQL

Przechowywać UUID binarne, indeksy - 'btree' lub 'hash' w razie potrzeby.

sql
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

CREATE TABLE orders (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- или uuid_generate_v4()
created_at timestamptz NOT NULL DEFAULT now(),
tenant smallint NOT NULL
);

-- For time-sortable (UUIDv7) store binary (uuid), generation in the application.
-- If you want a cluster by time:
CREATE INDEX ON orders (created_at DESC);
Sekwencyjny hot fix: dla sortowanego w czasie identyfikatora dodaj "sól' do górnych bitów lub wynik według najemcy:
sql
CREATE TABLE orders_t1 PARTITION OF orders FOR VALUES IN (1);
CREATE TABLE orders_t2 PARTITION OF orders FOR VALUES IN (2);

9. 2 Redis (liczniki atomowe/monutonia)

bash
INCR "seq: orders" # local sequence combine: epoch_ms<<20     (worker_id<<10)      (seq & 1023)

9. 3 Generator płatków śniegu (pseudokoda)

pseudo const EPOCH =  1704067200000  # custom epoch (ms)
state: last_ms=0, seq=0, worker=7, region=3

next():
now = epoch_ms()
if now < last_ms: wait_until(last_ms)    # защита от clock back if now == last_ms:
seq = (seq + 1) & ((1<<12)-1)      # 12 бит if seq == 0: wait_next_ms()
else:
seq = 0 last_ms = now return (now-EPOCH)<<22      region<<17      worker<<12      seq

9. 4 ULID/UUID w aplikacjach

Idź dalej

go
// ULID t:= time. Now(). UTC()
entropy:= ulid. Monotonic(rand. New(rand. NewSource(t. UnixNano())), 0)
id:= ulid. MustNew(ulid. Timestamp(t), entropy)

//UUID v7 (if there is a library)
id:= uuid. Must(uuid. NewV7())

Węzeł. js

js import { ulid } from 'ulid';
import { v4 as uuidv4 } from 'uuid';
const id1 = ulid();
const id2 = uuidv4(); // v4

Python

python import uuid, time id_v4 = uuid. uuid4()
For v7, use a library (for example, uuid6/7 third-party packages)

10) Kodowania i reprezentacje

Binarne w bazie danych ('BYTEA', 'UUID') → kompaktowe i szybkie. Na krawędzi przelicz na:
  • Base32 Crockford (ULID): przypadek nieczuły, brak wizualnie podobnych znaków.
  • Base58: w skrócie Base32/64 dla żetonów czytelnych dla ludzi, URL-bezpieczne.
  • Base64url: krótki, ale '-' i' _ 'w adresie URL.

Ustabilizować obudowę i format (łączniki/brak), aby uniknąć duplikatów podczas porównywania ciągów.

11) Testowanie odtwarzaczy i obserwowalność

Kolizje: metryczne 'id _ collision _ total' (musi być 0), alert na> 0.
Dystrybucja prefiksu: histogram wysokich bajtów - szukamy zakupu.
Szybkość generowania: 'ids _ per _ sec', opóźnienie generatora p99.
Zegar skew (na płatek śniegu): węzły offsetowe, „zegar wrócił” wydarzenia.
Ogony indeksowe: p95/p99 „INSERT” opóźnienie; odsetek zamków/gorących stron.

Dzień gry:
  • Wtrysk „drift/back zegar” → upewnij się, że generator czeka/przełącza.
  • „napływ” w milisekundach → czek next_ms.
  • Masowy paralelizm → czy są burze zamków w indeksie.

12) Anty-wzory

AUTO_INCREMENT/SEQUENCE jako dowód publiczny: zgadnij, wycieki. Użyj publicznego nieprzezroczystego identyfikatora na wewnętrznej.
UUIDv1 (MAC/czas) out: prywatność.
64-bitowy przypadkowy identyfikator na bilion wpisów: realne ryzyko kolizji.
Globalny „centralny generator” bez HA: SPOF i wąskie gardło.
Identyfikatory sortowane w czasie bez ochrony pleców zegara: duplikaty/regresja zamówienia.
Mieszanie różnych formatów ID bez wyraźnej wersji/prefiksu → chaos w debacie/migracji.
Zapisywanie identyfikatora jako łańcucha z różnymi rejestrami/formularzami → ukryte duplikaty.

13) Lista kontrolna wdrażania

  • Wybrany format (v4/v7/ULID/KSUID/Snowflake/SEQ/hash) dla wymagań domeny.
  • Zdefiniowane wymagania dotyczące zamówień (czy wymagana jest sortowalność).
  • Prawdopodobieństwo kolizji (b bity, n pokolenia) szacuje się i ustala próg ryzyka.
  • Kodowanie jest zaprojektowane (binarne w prezentacji DB + czytelne dla ludzi).
  • Dla sortowanych czasowo - ochrona pleców zegara, ograniczenia sekwencji i dyscyplina NTP/PTP.
  • Dla identyfikatorów publicznych - nieprzewidywalność (losowa/ULID/KSUID), brak PII.
  • Przemyślane hash (id)% N, prefiksy dla wielu najemców.
  • Obserwowalność: kolizja, dystrybucja, opóźnienie, metryki skew zegara.
  • Sekwencja/Contention/Skrzynki testowe z przelotem długości okna.
  • Format, wersja, epoka, mapa bitowa i dokumentacja planu migracji.

14) FAQ

P: Co wybrać „domyślnie” dla mikroservices?
Odp.: UUIDv7 lub ULID: zamawianie czasu, dużo entropii, proste generowanie na krawędzi. Dla zewnętrznych API, ULID/UUIDv4 jest również ok.

P: Potrzebujesz krótkiego i czytelnego identyfikatora.
Odp.: Kodowanie ULID/KSUID lub Base58-128-bit losowe/tymczasowe. Pamiętaj o długości i kolizjach.

P: Czy możliwe jest „krótkie numeryczne” identyfikatory, ale bezpieczne?
Odp.: Tak: przechowywać wewnętrzny SEQ, a na zewnątrz dać nieprzezroczysty token (losowo 96-128 bitów) lub Hashids z solą + podpis.

P: Jak migrować z SEQ do UUIDv7?
Odp.: Wprowadź nową kolumnę 'id _ new' (UUID), dwutorową, opublikuj odniesienia do nowego identyfikatora, a następnie przełącz klucze DC/foreign i usuń stary.

P: Dlaczego moje wkładki ULID stały się „gorące”?
Odp.: Wstaw ściśle rosnące klucze do jednego indeksu. Partycja/najemca, wymieszać bity wysokiej klasy, używać wkładek wsadowych.

15) Kwoty całkowite

Dobrym identyfikatorem jest prawidłowy zestaw właściwości dla problemu: wystarczająca entropia, przewidywalne sortowanie (w razie potrzeby), bezpieczny rozgłos i zdrowe wykorzystanie indeksów. Wybierz UUIDv4/ULID/UUIDv7/KSUID dla prostoty i dystrybucji, płatek śniegu dla gęstej monotonii i krótkich klawiszy (dla dyscypliny czasu), sekwencje dla lokalnych tabel, hashes treści dla artefaktów. Określić obserwowalność i testy - i identyfikatory przestają być źródłem niespodzianek.

Contact

Skontaktuj się z nami

Napisz do nas w każdej sprawie — pytania, wsparcie, konsultacje.Zawsze jesteśmy gotowi pomóc!

Telegram
@Gamble_GC
Rozpocznij integrację

Email jest wymagany. Telegram lub WhatsApp są opcjonalne.

Twoje imię opcjonalne
Email opcjonalne
Temat opcjonalne
Wiadomość opcjonalne
Telegram opcjonalne
@
Jeśli podasz Telegram — odpowiemy także tam, oprócz emaila.
WhatsApp opcjonalne
Format: kod kraju i numer (np. +48XXXXXXXXX).

Klikając przycisk, wyrażasz zgodę na przetwarzanie swoich danych.