ULID-generaatori leht: Unikaalsed, ajaliselt sorteeritavad identifikaatorid
See leht võimaldab valida identifikaatorivorminguks ULID, määrata vajalike ID-de arvu (1 kuni 100) ja klõpsuga luua loendi ainulaadsetest, ajaliselt sorteeritavatest ULID-idest. Iga ID on 26-märgiline, tõstutundetu string, mida saab kopeerida üksikult või kõiki korraga. Erinevalt teistest sama tööriista vormingutest on ULID lühem kui UUID (26 märki vs 36) ja kasutab ainult Crockfordi base32 tähestikku – seega on need tõstutundetud ja ilma sidekriipsudeta. ULID-id on leksikograafiliselt sorteeritavad loomisaja järgi, sest esimesed 10 märki kodeerivad millisekunditäpsusega ajatemplit. Need on ka URL-ohutud ilma igasuguse escapinguta. Sellel lehel pole lüliteid suurtähtede ega sidekriipsude jaoks, sest ULID on olemuslikult tõstutundetu ja sidekriipsudeta. Juhuslik komponent hõlmab ülejäänud 16 märki (80 bitti) ja kogu aluseks olev väärtus on 128 bitti.
ULID-i sisemine struktuur: aeg ja juhuslikkus
ULID (Universally Unique Lexicographically Sortable Identifier) on 128-bitine identifikaator, mis jaguneb kaheks selgeks osaks:
- Esimesed 10 märki (48 bitti): millisekunditäpsusega ajatempel alates Unixi epohhist (00:00:00 UTC, 1. jaanuar 1970). Kuna ajatempel on kodeeritud Crockfordi base32 abil, mahub 48 bitti 10 märgi sisse. See tähendab, et iga ULID sisaldab oma loomise aega täpsusega 1 ms, mis võimaldab ID-sid kronoloogiliselt sortida.
- Järgmised 16 märki (80 bitti): juhuslik osa, mis tagab unikaalsuse isegi siis, kui mitu ID-d luuakse samal millisekundil. 80 bitti pakub 2^80 = umbes 1,2 × 10^24 võimalikku juhuslikku väärtust, mis teeb kokkupõrke tõenäosuse äärmiselt väikeseks – isegi miljoni ID genereerimisel sekundis on dubleerimise oht tühine.
Kogu string on 26 märki pikk, kasutades ainult Crockfordi base32 tähestiku märke (0–9, A–Z välja arvatud I, L, O, U). See tähendab, et ULID-id on nii URL-ohutud kui ka kergesti loetavad: ei esine segiajamist suur- ja väiketähtede vahel (kõiki saab lugeda nii suurte kui väikestena) ning visuaalselt sarnased märgid (näiteks 1 ja I, O ja 0) on tähestikust eemaldatud.
Crockfordi base32 kodeering: miks just see tähestik?
Crockfordi base32 on spetsiaalselt loodud olema inimloetav ja vältima vigu. Tähestik koosneb numbritest 0–9 ja tähtedest A–Z, millest on välja jäetud:
- I (võib segi ajada numbriga 1)
- L (võib segi ajada numbriga 1 või suurtähega I)
- O (võib segi ajada numbriga 0)
- U (võib segi ajada tähega V)
Lisaks on kodeering tõstutundetu – see tähendab, et sama ULID stringi saab kirjutada nii suurte kui väikeste tähtedega ja see käitub identselt. See on suur eelis võrreldes UUID-ga, kus suur- ja väiketähtede erinevus on oluline (näiteks segiajamine a ja A vahel võib põhjustada vigu). Praktikas võib ULID-i kopeerida e-kirjast, veebilehelt või käsitsi üles kirjutada ilma mureta, kas tähed on õiges suuruses.
Crockfordi base32 kasutamine on ka põhjus, miks ULID on 26 märki pikk, mitte 36 nagu UUID. Kui UUID kasutab 128 biti esitamiseks kuueteistkümnendsüsteemi (32 märki + 4 sidekriipsu), siis base32 võimaldab iga märgiga kodeerida 5 bitti (2^5 = 32 võimalust), mis annab 26 märki (128 / 5 ≈ 25,6, ümardatuna 26-ni). Täpne jaotus: 48 bitti ajatempli jaoks = 10 märki, 80 bitti juhuslikkuse jaoks = 16 märki.
Võrdlus UUID v4 ja UUID v7-ga
| Omadus | ULID | UUID v4 | UUID v7 |
|---|---|---|---|
| Pikkus (märke) | 26 | 36 (32 + 4 sidekriipsu) | 36 (32 + 4 sidekriipsu) |
| Ajatempel | Jah, 48-bitine millisekundites | Ei, täiesti juhuslik | Jah, 48-bitine millisekundites (RFC 4122 värskendus) |
| Tõstutundlikkus | Ei (kõik märgid tõstutundetud) | Jah (A-F vs a-f) | Jah (A-F vs a-f) |
| Sidekriipsud | Ei | Jah (4 kohal) | Jah (4 kohal) |
| Sorteeritavus | Leksikograafiline loomisaja järgi | Sorteerimine juhuslik | Leksikograafiline loomisaja järgi |
| URL-ohutus | Täielik (kõik märgid RFC 3986 ohutud) | Sidekriipsud ohutud, aga suur-väiketähtede erinevus võib segadust tekitada | Sama nagu UUID v4 |
Peamine erinevus UUID v4-ga on see, et ULID võimaldab ID-de sortimist ilma eraldi ajatempli veeruta. UUID v4 puhul on uued ID-d andmebaasis laiali pillutatud, mis halvendab B-puu indeksi jõudlust. UUID v7 lahendab selle, kuid on endiselt 36 märki pikk ja sisaldab sidekriipse. ULID on lühem, puhtam ja inimloetavam.
Sorteerimiskäitumine ja samal millisekundil loodud ID-d
ULID-id on loodud olema leksikograafiliselt sorteeritavad loomisaja järgi. Kui võrrelda kahte ULID-i stringina, siis varasema ajatempliga ID on alati eespool. See kehtib isegi siis, kui ID-sid on loodud erinevatel masinatel – eeldusel, et nende süsteemikellad on sünkroonitud.
Kui aga mitu ULID-i genereeritakse täpselt samal millisekundil, siis nende järjestus sõltub juhuslikust osast. Teisisõnu, samal millisekundil loodud ID-de puhul ei ole garanteeritud, et need on omavahel kronoloogilises järjekorras. See on ULID-i teadlik disainivalik: esimesed 10 märki on ajapõhised, ülejäänud 16 on juhuslikud. Kui on vaja absoluutset järjestust ka samal millisekundil, tuleks kasutada teistsugust lähenemist (näiteks monotoonne loendur), aga see ohverdaks juhuslikkuse ja suurendaks kokkupõrke tõenäosust. Enamikus rakendustes pole samal millisekundil loodud ID-de täpne järjestus kriitiline – oluline on, et kõik eri ajahetkedel loodud ID-d oleksid korrektses järjestuses.
Kokkupõrke tõenäosus: 80-bitine juhuslik komponent
ULID-i juhuslik osa on 80 bitti. Kui ID-sid genereerida kiirusega 1000 ID-d sekundis (mis on tüüpiline enamikule rakendustele), siis:
- Ühe millisekundi jooksul võib tekkida mitu ID-d, kuid 80-bitine juhuslik osa tagab, et kokkupõrke tõenäosus on praktiliselt null.
- Matemaatiliselt: kui N on ID-de arv, siis vähemalt ühe kokkupõrke tõenäosus on ligikaudu N^2 / (2 * 2^80). Isegi kui N = 10^12 (triljon), on tõenäosus umbes 10^-9 ehk üks miljardist. Tegelikes rakendustes genereeritakse harva nii palju ID-sid, seega kokkupõrked on statistiliselt võimatud.
See erineb UUID v4-st, mis kasutab 122-bitist juhuslikku osa (ülejäänud 6 bitti on versiooni ja variandi jaoks). UUID v4 teoreetiline kokkupõrke tõenäosus on veelgi väiksem, kuid praktikas on ka ULID piisavalt turvaline.
Tähtis on märkida, et kõik ID-d genereeritakse brauseris kohapeal, kasutades crypto.getRandomValues() – tugevat krüptograafilist juhuslikkust. Ühtegi ID-d ei saadeta serverisse, mis tagab privaatsuse.
URL-ohutus ja kodeerimine
Kõik Crockfordi base32 märgid (0–9, A–Z, ilma I-L-O-U) on URL-ohutud vastavalt RFC 3986-le. See tähendab, et neid ei pea protsendikodeerima, isegi kui need sisalduvad URL-i tee- või päringuosas. Näiteks UUID puhul tuleb mõnikord sidekriipsudega ettevaatlik olla, aga ULID puhul pole mingeid erisümboleid – ainult numbrid ja tähed. See teeb ULID-id ideaalseks avalike API-de identifikaatoriteks: neid saab panna URL-i ilma igasuguse eeltöötluseta.
Kohalik genereerimine ja privaatsus
Tööriist genereerib kõik ID-d brauseris, kasutades crypto.getRandomValues(). See tähendab, et:
- Ühtegi andmeid ei saadeta serverisse – isegi mitu korda vajutades jäävad kõik toimingud lokaalseks.
- Privaatsuse vaatenurgast on see oluline: kui töötad tundlike andmetega (näiteks test-ID-d reaalsüsteemile), ei leki midagi võrku.
- ID-de loomine on äärmiselt kiire – tuhandete ID-de genereerimine võtab millisekundeid.
- Status read: "Ready." näitab, et leht on valmis, "Generated." kuvatakse pärast ID-de loomist, ja "Copied all!" ilmub kui kõik ID-d on lõikelauale kopeeritud.
Kasulikud stsenaariumid: kes seda vajab
- Veebi- ja mobiiliarendajad vajavad lühikesi, URL-ohutuid ID-sid, mida saab sortida loomisaja järgi ilma eraldi ajatempli veeruta. Näiteks sündmuste logimisel või tellimuste haldamisel on ajalise järjestuse säilitamine lihtne.
- Andmebaasiadministraatorid saavad kasutada ULID-i primaarvõtmena. Kuna uued ID-d on ajaliselt monotoonselt kasvavad, paraneb B-puu indeksi jõudlus – erinevalt UUID v4-st, mis põhjustab indeksi killustumist.
- API-disainerid soovivad avalikke ID-sid, mida on raske ära arvata (juhuslik osa on piisavalt pikk) ja mis on samas inimloetavad (pole segadust suur- ja väiketähtedega).
- Kõik, kellele UUID tundub liiga pikk või kohmakas – 26 märki on 10 märgi võrra lühem kui 36, mis võib suurte andmekogumite puhul säästa märkimisväärselt ruumi nii andmebaasis kui ka andmeedastuses.
KKK (Korduma Kippuvad Küsimused)
Miks mul ei ole valikut suurtähtede või sidekriipsude jaoks ULID-i puhul? Sest ULID on olemuslikult tõstutundetu (Crockfordi base32 võimaldab nii suur- kui väiketähti, aga neid koheldakse võrdsetena) ja sidekriipsudeta. Täiendavad lülitid pole vajalikud.
Kas ma saan muuta genereeritavate ID-de arvu rohkem kui 100? Ei, tööriist aktsepteerib ainult väärtusi 1 kuni 100. Kui vajad rohkemaid, saad genereerida mitu korda.
Mis juhtub, kui ma klõpsan üksikut ULID-i? See kopeeritakse lõikelauale. Klõpsates "Copy all" kopeeritakse kogu loend.
Kas ULID-id on garanteeritult unikaalsed nagu UUID v4? Teoreetiliselt on võimalik kokkupõrge, kuid 80-bitine juhuslik komponent muudab selle praktiliselt võimatuks (tõenäosus on äärmiselt väike). Enamiku rakenduste jaoks on ULID piisavalt unikaalne.
Kas ma saan ULID-e kasutada mitmel seadmel ilma dubleerimiseta? Jah, kui iga seade kasutab oma süsteemikella ajatemplit ja juhuslikkust, on ID-d unikaalsed. Kuid samal millisekundil loodud ID-d võivad olla identsed ainult siis, kui juhuslik osa langeb kokku – see on aga ülimalt ebatõenäoline.
Miks on ajatempel just millisekundites? Millisekundid on piisavalt täpsed enamiku rakenduste jaoks ning 48 bitti mahutab ajavahemiku kuni aastani 10889 (2^48 ms ≈ 8924 aastat alates 1970). See on palju kaugemale kui enamik süsteeme kunagi vajavad.