ULID Generator - Maak Unieke Sorteerbare ID's Online Gratis
Gratis ULID-generatortool maakt direct unieke, sorteerbare identificatoren aan. Genereer cryptografisch veilige ULID's voor databases, API's & gedistribueerde systemen online.
ULID Generator
Documentatie
Wat is een ULID?
Een ULID (Universally Unique Lexicographically Sortable Identifier) is een code van 26 tekens die wordt gebruikt om records in een database of een gebeurtenis in een gedistribueerd systeem te identificeren. De code combineert de huidige tijd met willekeurige gegevens, zodat een alfabetisch gesorteerde lijst met ULID's uiteindelijk is gesorteerd op het tijdstip waarop elke code is gemaakt. Een ULID-generator is een hulpmiddel dat deze codes produceert.
Structuur van een ULID
Een ULID bestaat uit twee delen die zonder scheidingsteken achter elkaar worden geschreven:
- Tijdstempel (10 tekens): het aantal milliseconden sinds 1 januari 1970 (het Unix-epoch), met een bereik van maximaal 48 bits.
- Willekeur (16 tekens): 80 bits willekeurige gegevens, toegevoegd zodat twee ULID's die in dezelfde milliseconde zijn gemaakt vrijwel zeker van elkaar verschillen.
Beide delen worden geschreven met het Base32-alfabet van Crockford: de cijfers 0–9 en de letters A-Z, met uitzondering van de letters I, L, O en U. Deze vier letters zijn weggelaten omdat ze kunnen worden verward met de cijfers 0 en 1, of met elkaar. Het resultaat is een tekenreeks die kort is, gemakkelijk hardop kan worden gelezen en rechtstreeks in een URL kan worden gebruikt.
Een ULID berekenen
Een ULID-generator genereert een identificator in drie stappen.
- Lees de huidige tijd in milliseconden sinds het Unix-epoch. Dit is een getal van 48 bits.
- Genereer 80 bits willekeurige gegevens, normaal gesproken met een cryptografisch veilige generator van willekeurige getallen, wat 10 willekeurige bytes oplevert.
- Codeer beide delen afzonderlijk met het Base32-alfabet van Crockford: de tijdstempel van 48 bits wordt 10 tekens lang en de willekeurige waarde van 80 bits wordt 16 tekens lang. De twee gecodeerde tekenreeksen worden samengevoegd tot de uiteindelijke ULID van 26 tekens.
Bij de codering wordt het getal gelezen in groepen van 5 bits, omdat elk Base32-teken een van 32 mogelijke waarden vertegenwoordigt (2^5 = 32). Tien tekens bevatten maximaal 50 bits, wat genoeg ruimte biedt voor de tijdstempel van 48 bits. Zestien tekens bevatten precies 80 bits, wat overeenkomt met het deel voor de willekeur zonder dat er bits overblijven.
Uitgewerkt voorbeeld
Stel dat een ULID-generator draait op tijdstip 1712345678901 (milliseconden sinds het epoch) en de willekeurige bytes [12, 240, 88, 3, 199, 45, 6, 231, 128, 17] genereert.
Stap 1 – codeer de tijdstempel. Deel 1712345678901 herhaaldelijk door 32, neem telkens de rest en lees de cijfers van achter naar voren (dit is standaardgrondtalconversie). Dit levert de tekenreeks van 10 tekens 01HTQW311N op.
Stap 2 – codeer de willekeur. Pak de 10 willekeurige bytes samen tot een stroom van 80 bits en lees vervolgens telkens 5 bits uit. Zet elk blok van 5 bits om in een Base32-teken. Dit levert de tekenreeks van 16 tekens 1KR5G0Y75M3EF00H op.
Stap 3 – voeg de delen samen. De uiteindelijke ULID is:
101HTQW311N 1KR5G0Y75M3EF00H
2zonder spatie geschreven: 01HTQW311N1KR5G0Y75M3EF00H, in totaal 26 tekens.
Deze generator gebruiken
De generator draait in de browser. Elke ULID wordt opgebouwd uit de browserklok en 80 nieuwe willekeurige bits uit de cryptografische generator van willekeurige getallen van de browser, zodat geen enkele identificator naar een server hoeft te worden verzonden of daarvandaan te worden opgehaald.
- Aantal ULID's accepteert een geheel getal van 1 tot 50. De standaardwaarde is 1.
- ULID genereren maakt een nieuwe reeks. Als het aantal wordt gewijzigd, wordt ook een nieuwe reeks gemaakt.
- Kopiëren bij een resultaat kopieert die ene ULID. Als er meer dan één wordt weergegeven, kopieert Alles kopiëren de volledige lijst, met één ULID per regel.
- Resetten zet het aantal terug op 1 en genereert één nieuwe ULID.
Onder de resultaten splitst het paneel ULID-structuur de eerste ULID in de reeks op in de twee delen: de eerste 10 tekens (de tijdstempel) en de laatste 16 tekens (de willekeur).
ULID versus UUID
| Kenmerk | ULID | UUID (versie 4) |
|---|---|---|
| Lengte | 26 tekens | 36 tekens (met koppeltekens) |
| Sorteerbaar op aanmaaktijd | Ja | Nee |
| Encodering | Crockford Base32 | Hexadecimaal |
| Bevat een tijdstempel | Ja (48 bits) | Nee |
| URL-veilig zonder escaping | Ja | Ja |
Beide indelingen zijn bedoeld om identifiers te produceren die uniek zijn binnen systemen, zonder dat een centrale instantie nummers uitgeeft. Het belangrijkste praktische verschil is de sortering: omdat een ULID met een tijdstempel begint, worden ULID's bij sortering als gewone tekst ook op tijd gesorteerd. Een willekeurige UUID heeft deze eigenschap niet, omdat elk deel ervan willekeurig is.
Veelvoorkomende toepassingen
- Primaire sleutels van databases: met ULID's kan een database nieuwe rijen ongeveer in de volgorde indexeren waarin ze zijn ingevoegd. Veel database-engines verwerken dat efficiënter dan volledig willekeurige sleutels.
- Gedistribueerde systemen: afzonderlijke servers kunnen onafhankelijk ULID's genereren zonder afstemming met een centrale teller, en de resultaten zijn nog steeds vrijwel zeker uniek.
- Gebeurtenislogboeken: omdat ULID's op tijd worden gesorteerd, kan een lijst met gebeurtenis-ID's ook dienen als een globale tijdlijn.
- API- en bestandsidentifiers: de vaste, URL-veilige indeling van 26 tekens werkt goed in webadressen en bestandsnamen.
Verwante identificatie-indelingen zijn KSUID, dat ook een tijdstempel codeert om te kunnen sorteren, en Snowflake-ID's, die door Twitter en andere partijen worden gebruikt en een tijdstempel, een machine-identificatie en een teller combineren.
Codevoorbeelden
Elk voorbeeld hieronder bouwt een ULID op dezelfde manier als beschreven in de ULID-specificatie: codeer de tijdstempel van 48 bits als 10 Crockford Base32-tekens, codeer 80 bits willekeur als nog eens 16 tekens en voeg ze samen.
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}
41Veelgestelde vragen
Waarvoor wordt een ULID gebruikt? Een ULID wordt gebruikt als unieke identificator voor een databaseregel, een API-resource of een geregistreerde gebeurtenis, wanneer het ook nuttig is om op basis van de aanmaaktijd te sorteren.
Hoe lang is een ULID? Een ULID is altijd 26 tekens lang: 10 tekens voor de tijdstempel en 16 voor het willekeurige deel.
Welke codering gebruikt een ULID? Een ULID gebruikt het Base32-alfabet van Crockford, dat bestaat uit de cijfers 0–9 en de letters A-Z, met I, L, O en U verwijderd om verwarring met andere tekens te voorkomen.
Waarin verschilt een ULID van een UUID? Een ULID wordt op aanmaaktijd gesorteerd en is 26 tekens lang. Een willekeurige UUID (versie 4) wordt niet op tijd gesorteerd en is 36 tekens lang, inclusief koppeltekens.
Hoe groot is de kans op een botsing tussen twee ULID's? De 80 bits willekeur maken botsingen uiterst onwaarschijnlijk. Er zouden ongeveer 1,3 biljoen ULID's binnen dezelfde milliseconde moeten worden gegenereerd om de kans dat twee ervan overeenkomen op 50% te brengen.
Kunnen ULID's zonder internetverbinding worden gegenereerd? Ja. Een ULID heeft alleen de lokale klok en een generator van willekeurige getallen nodig en kan dus volledig offline worden gemaakt.
Referenties
- "ULID Specification." GitHub, https://github.com/ulid/spec.
- "Crockford's Base32 Encoding." http://www.crockford.com/base32.html.