Generador de ULID - Crea Identificadors Únics Ordenables en Línia Gratuït
Eina gratuïta de generació de ULID que crea identificadors únics i ordenables a l'instant. Genera ULID criptogràficament segurs per a bases de dades, API i sistemes distribuïts en línia.
Generador ULID
Documentació
Què és un ULID?
Un ULID (identificador lexicogràficament ordenable universalment únic) és un codi de 26 caràcters que s’utilitza per etiquetar registres d’una base de dades o un esdeveniment en un sistema distribuït. Combina l’hora actual amb dades aleatòries, de manera que una llista de ULID ordenada alfabèticament acaba ordenada segons l’hora de creació de cadascun. Un generador de ULID és una eina que produeix aquests codis.
Estructura d’un ULID
Un ULID té dues parts, escrites una darrere l’altra i sense separador:
- Marca temporal (10 caràcters): el nombre de mil·lisegons des de 1 de gener del 1970 (l’època Unix), amb una capacitat de fins a 48 bits.
- Aleatorietat (16 caràcters): 80 bits de dades aleatòries, afegides perquè dos ULID creats en el mateix mil·lisegon siguin gairebé amb tota seguretat diferents.
Ambdues parts s’escriuen mitjançant l’alfabet Base32 de Crockford: els dígits 0–9 i les lletres de la A a la Z, excepte les lletres I, L, O i U. Aquestes quatre lletres s’ometen perquè es poden confondre amb els dígits 0 i 1, o entre si. El resultat és una cadena curta, fàcil de llegir en veu alta i segura per inserir directament en un URL.
Com es calcula un ULID
Un generador de ULID construeix un identificador en tres passos.
- Llegir l’hora actual en mil·lisegons des de l’època Unix. És un nombre de 48 bits.
- Generar 80 bits de dades aleatòries, normalment a partir d’un generador de nombres aleatoris criptogràficament segur, que proporciona 10 bytes aleatoris.
- Codificar les dues parts per separat mitjançant l’alfabet Base32 de Crockford: la marca temporal de 48 bits es converteix en 10 caràcters, i el valor aleatori de 80 bits es converteix en 16 caràcters. Les dues cadenes codificades s’uneixen per formar el ULID final de 26 caràcters.
La codificació funciona llegint 5 bits alhora, perquè cada caràcter Base32 representa un dels 32 valors possibles (2^5 = 32). Deu caràcters poden contenir fins a 50 bits, prou per a la marca temporal de 48 bits. Setze caràcters contenen exactament 80 bits, que coincideixen amb la part aleatòria sense que en sobri cap.
Exemple resolt
Suposem que un generador de ULID s’executa en la marca temporal 1712345678901 (mil·lisegons des de l’època) i obté els bytes aleatoris [12, 240, 88, 3, 199, 45, 6, 231, 128, 17].
Pas 1 – codificar la marca temporal. Dividiu repetidament 1712345678901 per 32, prenent el residu cada vegada i llegint els dígits del darrer al primer (aquesta és la conversió estàndard de base). Això produeix la cadena de 10 caràcters 01HTQW311N.
Pas 2 – codificar l’aleatorietat. Agrupeu els 10 bytes aleatoris en un flux de 80 bits i, a continuació, llegiu-los de 5 bits en 5 bits, convertint cada grup de bits en un caràcter Base32. Això produeix la cadena de 16 caràcters 1KR5G0Y75M3EF00H.
Pas 3 – unir les parts. El ULID final és:
101HTQW311N 1KR5G0Y75M3EF00H
2escrit sense l’espai: 01HTQW311N1KR5G0Y75M3EF00H, amb 26 caràcters en total.
Ús d’aquest generador
El generador s’executa al navegador. Cada ULID es construeix a partir del rellotge del navegador i de 80 bits aleatoris nous obtinguts del generador de nombres aleatoris criptogràfics del navegador, de manera que cap identificador no s’envia a un servidor ni se n’obté cap.
- Nombre de ULID accepta un nombre enter del 1 al 50. El valor predeterminat és 1.
- Genera un ULID produeix un lot nou. Canviar el nombre també produeix un lot nou.
- Copia, en un resultat, copia aquell ULID. Quan se’n mostra més d’un, Copia-ho tot copia la llista sencera, un ULID per línia.
- Restableix torna a establir el nombre en 1 i genera un ULID nou.
Sota els resultats, el tauler Estructura del ULID divideix el primer ULID del lot en les seves dues parts: els primers 10 caràcters (la marca temporal) i els darrers 16 (l’aleatorietat).
ULID i UUID
| Característica | ULID | UUID (versió 4) |
|---|---|---|
| Longitud | 26 caràcters | 36 caràcters (amb guionets) |
| Ordenable per l’hora de creació | Sí | No |
| Codificació | Base32 de Crockford | Hexadecimal |
| Conté una marca temporal | Sí (48 bits) | No |
| Segur per a URL sense escapament | Sí | Sí |
Tots dos formats tenen l’objectiu de produir identificadors únics entre sistemes sense una autoritat central que assigni els números. La diferència pràctica principal és l’ordenació: com que un ULID comença amb una marca temporal, ordenar els ULID com a text pla també els ordena per hora. Un UUID aleatori no té aquesta propietat, ja que totes les seves parts són aleatòries.
Usos habituals
- Claus primàries de bases de dades: els ULID permeten que una base de dades indexi les files noves aproximadament en l’ordre en què s’han inserit, cosa que molts motors de bases de dades gestionen de manera més eficient que les claus completament aleatòries.
- Sistemes distribuïts: diversos servidors poden generar ULID de manera independent, sense coordinar-se amb un comptador central, i els resultats continuen sent gairebé amb tota seguretat únics.
- Registres d’esdeveniments: com que els ULID s’ordenen per hora, una llista d’identificadors d’esdeveniments també pot servir com a línia temporal aproximada.
- Identificadors d’API i de fitxers: el format fix de 26 caràcters, segur per a URL, funciona correctament en adreces web i noms de fitxer.
Entre els formats d’identificadors relacionats hi ha KSUID, que també codifica una marca temporal per a l’ordenació, i els identificadors Snowflake, utilitzats per Twitter i altres empreses, que combinen una marca temporal, un identificador de màquina i un comptador.
Exemples de codi
Cada exemple següent construeix un ULID de la mateixa manera que descriu l’especificació ULID: codifica la marca temporal de 48 bits en 10 caràcters Base32 de Crockford, codifica 80 bits d’aleatorietat en 16 caràcters més i els uneix.
JavaScript
1const ENCODING_CHARS = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
2
3function encodeULID(time, randomBytes) {
4 // Timestamp: 48 bits -> 10 characters
5 let timestampStr = '';
6 let t = time;
7 for (let i = 0; i < 10; i++) {
8 timestampStr = ENCODING_CHARS[t % 32] + timestampStr;
9 t = Math.floor(t / 32);
10 }
11
12 // Randomness: 80 bits -> 16 characters
13 let result = timestampStr;
14 let bits = 0;
15 let bitCount = 0;
16 for (let i = 0; i < 10; i++) {
17 bits = (bits << 8) | randomBytes[i];
18 bitCount += 8;
19 while (bitCount >= 5) {
20 bitCount -= 5;
21 result += ENCODING_CHARS[(bits >> bitCount) & 0x1f];
22 }
23 }
24 return result;
25}
26
27function generateULID() {
28 const randomBytes = crypto.getRandomValues(new Uint8Array(10));
29 return encodeULID(Date.now(), randomBytes);
30}
31
32console.log(generateULID());
33Python
1import os
2import time
3
4ENCODING_CHARS = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
5
6def encode_ulid(time_ms, random_bytes):
7 # Timestamp: 48 bits -> 10 characters
8 chars = []
9 t = time_ms
10 for _ in range(10):
11 chars.append(ENCODING_CHARS[t % 32])
12 t //= 32
13 timestamp_part = "".join(reversed(chars))
14
15 # Randomness: 80 bits -> 16 characters
16 bits = 0
17 bit_count = 0
18 random_part = []
19 for byte in random_bytes:
20 bits = (bits << 8) | byte
21 bit_count += 8
22 while bit_count >= 5:
23 bit_count -= 5
24 random_part.append(ENCODING_CHARS[(bits >> bit_count) & 0x1F])
25
26 return timestamp_part + "".join(random_part)
27
28def generate_ulid():
29 return encode_ulid(int(time.time() * 1000), os.urandom(10))
30
31print(generate_ulid())
32Java
1import java.security.SecureRandom;
2import java.time.Instant;
3
4public class ULIDGenerator {
5 private static final SecureRandom random = new SecureRandom();
6 private static final char[] ENCODING_CHARS =
7 "0123456789ABCDEFGHJKMNPQRSTVWXYZ".toCharArray();
8
9 public static String generateULID() {
10 long timestamp = Instant.now().toEpochMilli();
11 byte[] randomBytes = new byte[10];
12 random.nextBytes(randomBytes);
13
14 // Timestamp: 48 bits -> 10 characters
15 char[] timestampChars = new char[10];
16 long t = timestamp;
17 for (int i = 9; i >= 0; i--) {
18 timestampChars[i] = ENCODING_CHARS[(int) (t % 32)];
19 t /= 32;
20 }
21
22 // Randomness: 80 bits -> 16 characters
23 StringBuilder result = new StringBuilder(new String(timestampChars));
24 long bits = 0;
25 int bitCount = 0;
26 for (byte b : randomBytes) {
27 bits = (bits << 8) | (b & 0xFF);
28 bitCount += 8;
29 while (bitCount >= 5) {
30 bitCount -= 5;
31 result.append(ENCODING_CHARS[(int) ((bits >> bitCount) & 0x1F)]);
32 }
33 }
34 return result.toString();
35 }
36
37 public static void main(String[] args) {
38 System.out.println(generateULID());
39 }
40}
41Preguntes freqüents
Per a què serveix un ULID? Un ULID s’utilitza com a identificador únic d’una fila d’una base de dades, d’un recurs d’API o d’un esdeveniment registrat, en casos en què també és útil ordenar-los per hora de creació.
Quina llargada té un ULID? Un ULID sempre té 26 caràcters: 10 caràcters per a la marca temporal i 16 per a la part aleatòria.
Quina codificació utilitza un ULID? Un ULID utilitza l’alfabet Base32 de Crockford, format pels dígits 0–9 i les lletres de la A a la Z, amb I, L, O i U eliminades per evitar confusions amb altres caràcters.
En què es diferencia un ULID d’un UUID? Un ULID s’ordena per hora de creació i té 26 caràcters. Un UUID aleatori (versió 4) no s’ordena per hora i té 36 caràcters, inclosos els guionets.
Quina probabilitat hi ha que dos ULID col·lideixin? Els 80 bits d’aleatorietat fan que les col·lisions siguin extremadament improbables. Caldria generar aproximadament 1,3 bilions de ULID en el mateix mil·lisegon per arribar a una probabilitat del 50 % que dos coincideixin.
Es poden generar ULID sense connexió a Internet? Sí. Un ULID només necessita el rellotge local i un generador de nombres aleatoris, de manera que es pot crear completament sense connexió.
Referències
- "ULID Specification." GitHub, https://github.com/ulid/spec.
- "Crockford's Base32 Encoding." http://www.crockford.com/base32.html.