GH GambleHub

Génération d'identifiants

1) Pourquoi prêter attention aux identifiants

ID (ID) est la clé fondamentale de l'entité : chaînes OBD, messages, fichier, commande. Ses propriétés dépendent :
  • Unicité et échelle (collisions, croissance horizontale).
  • Ordre et tri (corrélation temporelle, réplication, déduplication).
  • Performances de stockage (index, pages chaudes, taille de la clé).
  • Sécurité (imprévisibilité, fuites, devinettes).
  • Utilisation/intégration (courte, URL-safe, non sensible à la casse).

Le choix de l'ID est un compromis entre entropie, rationalité, longueur, vitesse de génération et exploitation.

2) Exigences et termes clés

Unicité : la probabilité d'un conflit doit être inférieure au risque acceptable.
Entropie : « combien d'aléas » contient ID (bit).
Rationalisation (time-sortable/k-sortable) : tri lexicographique ≈ tri temporel.
Monotonie : séquence non décroissante à l'intérieur du nœud/flux.
Localisation de l'enregistrement : à quel point le nouvel insert est concentré dans la « queue » de l'index (danger des pages chaudes).
Prévisibilité : peut-on deviner les identifiants voisins (important pour la sécurité/API).
Représentation : binaire/chaîne, Base16/32/36/58/64, traits d'union, registre.

3) Principales familles d'identifiants

3. 1 UUID

v4 (random) : 122 bits d'entropie. Désordonner, bon pour la sécurité et la simplicité. Moins : les indices « chaotite » en raison de la distribution aléatoire - qui, cependant, dissipe uniformément les charges et supprime les « pages chaudes ».
v1 (time + MAC) : ordonner, mais porter le MAS/temps (vie privée) ; souvent évités.
v7 (temps-ordonné) : temps milliseconde + partie randomique. Conception selon le tri lexicographique en fonction du temps et une bonne compression en OBD. Compromis : la « queue chaude » de l'indice apparaît ; est traité par chardonnages/préfixes/incréments.

Conseils

Pour les API externes et les exigences d'ordre non appliquées, v4.
Pour les bases de données et les clés « triables », v7.

3. 2 ULID (Crockford Base32)

128 bits : 48 bits de temps (ms) + 80 bits aléatoires. Lexicographiquement trié en fonction du temps, humain amical (sans 'I, L, O, U'), URL safe. Il y a une variation monotone (avec la même marque de temps, la partie aléatoire augmente).
Avantages : lisibilité, rationalisation, portabilité.
Inconvénients : à très haute fréquence d'insertion à un moment donné - « queue chaude ».

3. 3 KSUID

160 bits : 32 bits de temps (secondes) par rapport à l'ère + 128 bits aléatoires. Plus grande plage temporelle et triabilité stable, les lignes sont plus courtes que l'ULID ? (non - plus long, mais avec son codage), bon pour les logs distribués et les objets.

3. 4 Snowflake-like (k-sortable flake IDs)

Schéma classique (personnalisable) :

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

Propriétés : croissance monotone sur un nœud, unicité quasi globale, représentation binaire courte (64 bits).
Risques : dépendance à l'horloge (dérive/régression du temps), épuisement de la sequence en un seul tic, coordination des bits region/worker.
Il est traité : protection contre le « clock back », sauvegarde de la séquence, détecteur de temps, PTP/NTP discipline.

3. 5 Séquences OBD (SEQUENCE/IDENTITY)

La génération monotone la plus simple dans une base de données/écharpe.
Avantages : court, rapide, pratique pour les tables locales.
Inconvénients : Difficile globalement dans un cluster distribué ; prévisible (dangereux comme clé publique), crée la queue chaude de l'indice.

3. 6 ID d'adresse de contenu (contenu hash)

SHA-256/Blake3 du contenu → ID stable, déduplication, vérification de l'intégrité, mise en cache.
Avantages : déterminisme, protection contre la substitution.
Inconvénients : génération coûteuse (CPU), collisions zéros pratiques, pas de tri temporaire, longueur.

4) Conflits et « paradoxe des anniversaires » (intuitivement)

La probabilité de conflit pour un ID aléatoire de taille 'b' bit à "n'génération est approximative :

p ≈ 1 - exp (-n (n-1 )/2/2 ^ b) ≈ n ^ 2/2 ^ (b + 1) (for small p)
Exemples :
  • UUIDv4 (122 bits) à n = 10 ^ 12 (trillion) → p ~ 1e-14 (négligeable).
  • Un rand de 64 bits → à n = 10 ^ 9 est déjà p ~ 0. 027 (risque perceptible).
  • Conclusion : Les 64 bits aléatoires sont souvent peu nombreux pour les grands systèmes ; utilisez 96/128 bits.

5) Index, pages chaudes et stockage

Les clés aléatoires (v4) répartissent uniformément les inserts dans l'arbre d'index → pas de « queue », mais moins de cache-localité.
Les pages triées par le temps (v7/ULID/Snowflake) sont insérées « dans la queue » → une meilleure localisation et compression, mais le risque de pages chaudes sous un enregistrement parallèle élevé.

Atténuation de la queue chaude :
  • préfixes/sharding par tenant/region (ajouter 1-2 octets avant l'heure) ;
  • interleaving : partie du hasard dans les bits plus anciens ;
  • inserts butch, fillfactor dans l'arbre B, passage automatique sur BRIN/clustering pour les grandes loges.
La taille est importante :
  • 'UUID (16B) 'vs' BIGINT (8B) '/' INT8 'permet d'économiser de la mémoire/du cache ; les lignes de Base32/58/64 augmentent la taille de 20 à 60 %. Pour les OBD stocker binaire, sérialiser dans une ligne sur le bord.

6) Sécurité et vie privée

Ne pas utiliser SEQUENCE/INT comme ID public dans l'URL/API : devinable → l'énumération des ressources.
Ajoutez des ID (v4/v7/ULID/KSUID) aléatoires et imprévisibles pour les liens externes.
Ne pas coder PII dans ID. Si vous voulez activer l'attribut - crypter/signer (par exemple, JWE/JWS) ou utiliser des jetons opaques.
Encodages URL sécurisés : Base32 Crockford, Base58 (sans '0OIl'), Base64url.

7) Multi-tenance, préfixes et routage

Format : '[TENANT _ PREFIX] - [ID]' ou binaire : 'tenant _ id | id'.
Avantages : filtres rapides/lots par locataire, protection contre les N + 1 scans.
Inconvénients : peut aggraver la densité d'entropie dans les bits supérieurs, → penser à la distribution (hachage préfixe).
Le suffixe hash (2-3 octets) réduit les collisions et aide le shard-rooting : 'shard = hash (id) % N'.

8) Conseils pratiques pour le choix

API, liens publics, services distribués sans ordre strict : UUIDv4, ULID/KSUID.
Logs/événements/ordres où nous trions souvent par temps : UUIDv7 ou ULID (monotone).
Bande passante ultra haute avec monotonie locale et clé courte : Snowflake-like 64-bit (la discipline du temps est nécessaire).
Entrepôts d'artefacts/bilds/blobs : adresse de contenu (SHA-256) et en plus, une courte « vitrine » (Hashids/lien) amicale.
Tables locales dans une base de données : SEQUENCE/IDENTITY + « emballage » externe pour les références publiques (masking).

9) Réalisations et exemples

9. 1 PostgreSQL

Gardez l'UUID binaire, les index sont 'btree' ou 'hash' par besoin.

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);
Sequential hot fix : pour time-sorted ID, ajoutez un « sel » aux bits plus anciens ou partez par tenant :
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 (compteurs atomiques/monotonium)

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

9. 3 Générateur de type snowflake (pseudo-code)

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 dans les applications

Go

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())

Node. 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) Encodages et représentations

Binaire dans la base de données (« BYTEA », « UUID ») → compact et rapide. Sur le bord, convertir en :
  • Base32 Crockford (ULID) : insensible au registre, sans caractères visuellement similaires.
  • Base58 : bref Base32/64 pour les tokens humains, URL safe.
  • Base64url : court, mais '-' et '_' dans l'URL.

Stabilisez le registre et le format (traits d'union/absence) pour éviter les doublons lors de la comparaison des lignes.

11) Test-playbooks et observabilité

Collisions : métrique 'id _ collision _ total' (doit être 0), alerte à> 0.
Distribution des préfixes : histogramme des octets plus anciens - recherchez l'achat.
Vitesse de génération : 'ids _ per _ sec', p99 latence du générateur.
Clock skew (pour Snowflake) : offset des nœuds, événements « clock went back ».
Queues d'index : p95/p99 'INSERT' latency ; proportion de verrous/pages chaudes.

Game day:
  • L'injection « clock drift/back » → s'assurer que le générateur attend/bascule.
  • Le dépassement de « sequence » en milliseconde → la vérification d'attente de next_ms.
  • Le parallélisme de masse → s'il n'y a pas de tempêtes de blocage dans l'indice.

12) Anti-modèles

AUTO_INCREMENT/SEQUENCE comme ID public : devinez, fuites. Utilisez un ID public opaque sur l'intérieur.
UUIDv1 (IAU/temps) vers l'extérieur : intimité.
Un ID aléatoire de 64 bits par trillion d'enregistrements : risque réel de conflits.
« Générateur central » global sans HA : SPOF et goulot d'étranglement.
Time-sorted IDs sans protection clock back : duplication/régression d'ordre.
Mélanger différents formats d'ID sans version/préfixe explicite → chaos dans les déboires/migrations.
Enregistrement de l'ID en tant que chaîne avec différents registres/formulaires → doublons cachés.

13) Chèque de mise en œuvre

  • Le format (v4/v7/ULID/KSUID/Snowflake/SEQ/hash) est sélectionné pour les exigences de domaine.
  • Des exigences d'ordre ont été définies (si la triabilité est nécessaire).
  • La probabilité de collisions (b bit, n générations) a été estimée et un seuil de risque a été défini.
  • Codage conçu (binaire en OBD + vitrine à lecture humaine).
  • Pour time-sorted - clock back protection, sequence-limit et NTP/PTP discipline.
  • Pour les ID publics, l'imprévisibilité (rand/ULID/KSUID), l'absence de PII.
  • L'itinéraire shard (hash (id) % N), les préfixes multi-tenants.
  • Observabilité : métriques des collisions, de la distribution, des retards, de l'alignement.
  • Cas d'essai de dépassement de séquence/compétition élevée/longueur de fenêtre.
  • Documentation sur le format, la version, l'époque, le balisage des bits et le plan de migration.

14) FAQ

Q : Que choisir « par défaut » pour les microservices ?
A : UUIDv7 ou ULID : Ordonnabilité dans le temps, beaucoup d'entropie, simple génération sur le bord. Pour les API externes - ULID/UUIDv4 aussi.

Q : Il faut un ID court et humain.
A : Codage ULID/KSUID ou Base58 d'un ID aléatoire/temporel de 128 bits. Rappelez-vous la longueur et les conflits.

Q : Peut-on faire un ID « numérique court », mais en toute sécurité ?
A : Oui : stockez la SEQ interne et donnez à l'extérieur le jeton opaque (rand 96-128 bits) ou Hashids avec sel + signature.

Q : Comment migrer de SEQ à UUIDv7 ?
A : Entrez une nouvelle colonne 'id _ new' (UUID), bidirectionnez, publiez des liens vers un nouvel ID, puis changez de clé RC/étrangère et supprimez l'ancienne.

Q : Pourquoi mes inserts avec ULID sont-ils devenus « chauds » ?
R : Insérez des clés strictement croissantes dans un seul index. Diviser par lots/tenant, mélanger les bits supérieurs, utiliser les inserts batch.

15) Résultats

Un bon ID est le bon ensemble de propriétés pour une tâche : une entropie suffisante, un tri prévisible (si nécessaire), une publicité sûre et une exploitation saine des indices. Choisissez le UUIDv4/ULID/UUIDv7/KSUID pour la simplicité et la distribution, Snowflake pour la monotonie dense et les clés courtes (dans la discipline du temps), la séquence pour les tables locales, le hachage du contenu pour les artefacts. Mettez en place l'observabilité et les tests - et les identifiants ne seront plus une source de surprises.

Contact

Prendre contact

Contactez-nous pour toute question ou demande d’assistance.Nous sommes toujours prêts à vous aider !

Telegram
@Gamble_GC
Commencer l’intégration

L’Email est obligatoire. Telegram ou WhatsApp — optionnels.

Votre nom optionnel
Email optionnel
Objet optionnel
Message optionnel
Telegram optionnel
@
Si vous indiquez Telegram — nous vous répondrons aussi là-bas.
WhatsApp optionnel
Format : +code pays et numéro (ex. +33XXXXXXXXX).

En cliquant sur ce bouton, vous acceptez le traitement de vos données.