UUID üreteci

UUID’leri tek tek ya da ellişer üretin: rastgele v4 veya zamana göre sıralı v7 — zaman damgasıyla sıralı kalır ve veritabanı anahtarı olarak daha iyidir.

Sürüm

122 rastgele bit. Ne zaman oluşturulduğu hakkında hiçbir şey açığa çıkarmaz.

Kaç tane

Üretiliyor…

UUID nedir ve neden vardır

Bir UUID — Universally Unique Identifier (Evrensel Benzersiz Tanımlayıcı), Microsoft belgelerinde GUID de denir — tanıdık 8-4-4-4-12 gruplamasında 32 on altılık basamak olarak yazılan 128 bitlik bir değerdir. Tüm amacı, ayrı sistemlerin, aralarında hiçbir eşgüdüm olmadan bağımsızca tanımlayıcılar üretmesine ve yine de sonuçların çakışmayacağından emin olmasına olanak tanımaktır. Onu bir veritabanı otomatik artan sütunundan farklı kılan budur: iki sunucu, bir uçakta çevrimdışı iki mobil istemci ve bir artalan işi, kimseden izin istemeden aynı anda kayıtlar oluşturabilir.

Buradaki benzersizlik garantili değil, olasılıksaldır. Sürüm 4, 122 biti rastgeleliğe bırakır; bu, milyarlarca değer üretmenin bile bir tekrar olasılığını, verinin bunun yerine bir diskte sessizce bozulması olasılığının çok altında bıraktığı kadar büyük bir alandır. Pratikte onu benzersiz olarak ele alabilirsiniz; matematik zayıf halka değildir.

Sürüm 4 ve sürüm 7 — önemli olan seçim

Sürüm 4 baştan sona rastgeledir. Hiçbir bilgi taşımaz: ne zaman yapıldığını değil, kim tarafından değil, hangi sırada değil. Bu, onu nereye koyduğunuza bağlı olarak ya en büyük gücü ya da temel kusurudur.

2024’te RFC 9562’de standartlaştırılan sürüm 7, ilk 48 biti bir milisaniye Unix zaman damgasıyla değiştirir ve geri kalanı rastgelelikle doldurur. Zaman önce geldiği ve değer soldan sağa okunduğu için, v7 tanımlayıcılarını düz metin olarak sıralamak onları kronolojik olarak da sıralar.

Bu görsel bir fark değildir. Veritabanları birincil anahtarları sıralı bir B-ağacı dizininde tutar. v7 anahtarları ekleyin, her yeni satır ağacın sağ kenarına, bir öncekinin yanına düşer — yazdığınız sayfalar bellekte kalır ve dizin düzenli büyür. v4 anahtarları ekleyin, her yazma rastgele bir konuma düşer; böylece veritabanı her seferinde farklı bir sayfaya dokunur, önbellek isabet oranları düşer ve dizin parçalanır. Büyük, yoğun bir tabloda ekleme veriminde fark önemlidir; v7’nin birincil anahtarlar için bu kadar hızlı benimsenmesinin nedeni budur.

  • Veritabanı birincil anahtarları, olay ve günlük tanımlayıcıları ve oluşturulma zamanına göre sıralamak ya da aralık sorgusu yapmak isteyeceğiniz her şey için v7 seçin.
  • Tanımlayıcı güvenilmez bir yerde göründüğünde ve hiçbir şey — bir kaydın diğerinden hemen önce oluşturulduğu gerçeği dahil — sızdırmaması gerektiğinde v4 seçin.
  • Sıralamanın önemli olmadığı bir ilinti kimliği (correlation ID), bir idempotency anahtarı ya da bir dosya adı için ikisi de uygundur.

Ödünleşim tam da o sızıntıdır: bir v7 tanımlayıcısı, onu görene, milisaniyesine kadar, ne zaman oluşturulduğunu söyler. Bir satır kimliği için bu genellikle zararsız ve çoğu zaman yararlıdır. Bir parola sıfırlama token’ı ya da genel bir paylaşım bağlantısı için ise yayımlamayı düşünmediğiniz bir bilgidir — ve bunlar hiç UUID olmamalı, özel bir sır üretecinden rastgele token’lar olmalıdır.

Aynı milisaniye içinde sıralama

Birçok v7 uygulamasını yakalayan bir incelik: modern donanım milisaniye başına birden çok tanımlayıcı üretir. İki değer bir zaman damgasını paylaşırsa, sıraları ardından gelen rastgele bitlerle belirlenir — yani rastgele. Sıkı bir döngüde elli tane üretin, yalnızca kabaca sıralı elli değer alırsınız ve tam da v7’yi seçme nedeniniz olan özelliği yitirirsiniz.

RFC 9562 bunu monoton bir sayaçla ele alır ve bu araç onu uygular: zaman damgasının hemen ardındaki 12 bit bir milisaniye içinde yukarı doğru sayar; böylece bir parti yaklaşık değil, kesin biçimde artar. Sayaç dolarsa — aynı milisaniyede 4096’dan çok değer — üreteç, kaydırıp öncülünden önce sıralanan bir değer yaymak yerine bir sonraki milisaniyeyi ödünç alır. NTP düzeltmeleriyle gerçekleşen, saatin geriye gitmesini de aynı şekilde ele alır.

Bir v7 değerini soldan sağa okumak, örnek olarak 0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b’yi alarak:

  • 0190a1b2-c3d4 — milisaniye cinsinden 48 bitlik Unix zaman damgası. Önce geldiği için metin sırası zaman sırasıdır.
  • 7 — sürüm nibble’ı; bunu bir v4 değil, bir v7 yapan şey.
  • e5f — 12 bitlik monoton sayaç; tek bir milisaniye içinde artan.
  • 8 — varyant bitleri; RFC 9562 tarafından her modern UUID için sabitlenmiş (her zaman 8, 9, a ya da b).
  • a9b-0c1d2e3f4a5b — kalan 62 bit, tümüyle rastgele.

UUID’leri saklama ve kullanma

Standart biçim, tireli küçük harftir ve RFC 9562, üreteçlerin tam olarak bunu yaymasını söyler — bu araç bu yüzden hiçbir biçimlendirme seçeneği sunmaz. Yine de başka yazımlarla karşılaşacaksınız: Microsoft araçlarında büyük harfli ve süslü parantez içinde ve birinin daha kısa bir sütun istediği yerde tirelerden arındırılmış. Hepsi aynı 128 bittir ve karşılaştırmalar büyük/küçük harfe duyarsız olmalıdır.

Önemli olan yer saklamadır. Bir UUID 16 bayttır, ama metin biçimi 36 karakterdir — dolayısıyla onu bir dize olarak saklamak, satırdaki ve daha önemlisi onu içeren her dizindeki alanı iki katından fazla artırır. Bir tane varsa yerel bir tür kullanın:

uuid                      -- PostgreSQL: native 16-byte type
BINARY(16)                -- MySQL: compact; CHAR(36) wastes 20 bytes/row
uniqueidentifier          -- SQL Server
crypto.randomUUID()       // JavaScript: v4 only, needs a secure context
uuid.uuid4() / uuid7()    # Python: stdlib v4; v7 via a library

Son bir uyarı: bir UUID tanımlar, yetkilendirmez. Tahmin edilemez oldukları için, birini içeren listelenmemiş bir URL’yi özel saymak caziptir. Ama tanımlayıcılar sızar — günlükler, tarayıcı geçmişi, yönlendiren başlıkları ve ekran görüntüleri aracılığıyla — dolayısıyla gerçekten korunması gereken herhangi bir şeyin arkasında hâlâ gerçek bir izin denetimi olması gerekir.

Bir UUID’nin v4 mü v7 mi olduğu nasıl anlaşılır

Bir tanımlayıcı size çoğu zaman nereden geldiğini söyleyen bir not olmadan ulaşır ve o nota ihtiyacınız da yoktur: sürüm, değerin kendi içine yazılıdır. İkisi de aynı 8-4-4-4-12 gruplamasında 32 onaltılık basamaktır, dolayısıyla fark biçimde değil — fark, sabit konumlardaki iki tek basamaktadır ve geri kalan her şey onlardan çıkar.

  • Üçüncü grubun ilk basamağı sürüm nibble’ıdır. Oradaki bir 4 sürüm 4, bir 7 ise sürüm 7 demektir; değerin başka hiçbir parçasının bu konuda söz hakkı yoktur.
  • Dördüncü grubun ilk basamağı varyant bitlerini taşır ve her iki sürümde de 8, 9, a ya da b olur — yani elinizdekinin hangisi olduğunu asla söylemez. Söylediği şey, değerin RFC 9562’ye uyduğudur; o konumda başka bir şey varsa, ya daha eski bir yerleşimdir ya da UUID değildir.
  • Sürüm nibble’ı 7 ise ilk on iki basamak oluşturulma zamanıdır: 1970 başından beri geçen milisaniyelerin onaltılık yazılmış 48 bitlik sayımı.
  • 4 ise okunacak başka bir şey yoktur. Bir v4 ne zamanı, ne makineyi, ne de sırayı kodlar; onu seçmenizin nedeni olan özellik tam da budur.

Yukarıdaki bölümün çözümlediği değeri ele alın: 0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b. Üçüncü grubu 7 ile açılıyor, demek ki bir sürüm 7; ilk on iki basamağı ise 0190a1b2c3d4 — bu onaltılık sayıyı onluğa çevirip Unix zaman damgası dönüştürücüye verin, karşınıza 2024 Temmuz’unda bir öğleden sonra çıksın. Yanındaki 9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d ise üçüncü grubunu 4 ile açar ve kendi ilk on iki basamağı hiçbir şeye çözülmeyen rastgele veridir. İkisinin uzlaştığı tam olarak tek bir nokta var: her ikisi de dördüncü grubunu, varyant bitlerinin izin verdiği dört basamaktan biriyle açar.

v7, çakışmaya v4 kadar dayanıklı mı?

Yerinde bir soru, çünkü v7 gerçekten daha az rastgelelik taşır. Sürüm 4, 128 bitinin 122’sini rastgele veriye ayırır. Sürüm 7 ise 48’ini zaman damgasına, burada 12’sini de monoton sayaca ayırır; geriye 62 rastgele bit kalır — yarıdan biraz fazlası. Çıplak bir sayı olarak okununca bu, ciddi bir gerileme gibi görünür.

  • Bu aracın tek bir partisi içinde bir yinelenme olası değil, imkânsızdır. Aynı milisaniyeyi paylaşan değerler farklı sayaç değerleri alır, milisaniyeyi paylaşmayan değerler ise farklı zaman damgaları taşır; dolayısıyla eşit olabilecek iki değer yoktur — bu olasılık değil, aritmetiktir.
  • Aynı anda üreten iki makine arasında olasılık en kötü ihtimalle 62 bit üzerindeki doğum günü problemidir ve bu problem kabaca uzayın karekökünde başa baş gelir: iki milyar değer dolayında, üstelik hepsi tek ve aynı milisaniyenin içinde.
  • Farklı milisaniyeler arasında bir v7 çakışması yalnızca olası değil değil, imkânsızdır; çünkü baştaki basamakların kendisi farklıdır.

Yani dürüst karşılaştırma 122 bite karşı 62 bit değildir. Sistemin bütün ömrü boyunca tekrar tekrar çekilen tek bir piyango ile, her milisaniyenin içinde ayrıca düzenlenip milisaniye bitince atılan çok daha küçük bir piyango arasındaki karşılaştırmadır. İkinci düzen daha güçlü olanıdır ve v4’ü yeğlemenin nedeni, karşılaştırma bölümünün verdiği neden olarak kalır — çakışma değil, bir v7’nin ne zaman oluşturulduğunu yüksek sesle söylemesi.

Var olan bir tabloyu v4’ten v7’ye geçirmek

v7’yi seçtikten sonra gelen soru, tabloda hâlihazırda duran satırlara ne olacağıdır; içinizi rahatlatan kısım ise sütunun hiç değişmesi gerekmemesidir. Her iki sürüm de aynı 36 karakterlik metin biçimindeki aynı 128 bittir; bu yüzden PostgreSQL’deki bir uuid sütunu, bir BINARY(16) ya da bir CHAR(36) karışımı fark etmeden taşır. Yeni satırlar için v7 üretmeye başlar ve orada durursunuz; ne bir geçiş adımı vardır ne de geriye dönük doldurma.

  • Hemen elde edilen: bundan sonra eklenen her satır başta bir zaman damgası taşır, böylece yeni anahtarlar dizine dağılmak yerine dizinin bir ucunda yan yana iner. Bu kazanç, yeni yazmaların nereye gittiğiyle ilgilidir ve ilk eklemeden itibaren oradadır.
  • Hiç gelmeyen: zaten orada duran satırlar sonsuza dek sırasız kalır. Kendisine hiç verilmemiş bir oluşturulma zamanını bir değerin içine koyabilecek hiçbir şey yoktur; her tanımlayıcıyı yeniden üretmek ise ona işaret eden her yabancı anahtarı yeniden yazmak demektir — üreteci değiştirmekten çok daha büyük bir iş ve yalnızca dizin uğruna nadiren buna değer.
  • Dikkat edilecek olan: sütunu sıralamak tek bir şey anlatmayı bırakır. Milisaniye sayımı 48 bitlik bir alan için küçük bir sayıdır; bu yüzden v7 anahtarları aralığın alt tarafında dar bir şeritte toplanırken v4 anahtarları aralığın tamamına yayılır ve arada bir tanesi o şeridin içine düşer.

Isıran şey bu son maddedir, çünkü anahtara göre sıralayan bir sorgu yeni veride doğru görünür ve eskisini sessizce yanlış bildirir. Tablonun tamamı için tek bir sıra gerekiyorsa bir oluşturulma zamanı sütunu ekleyip ona göre sıralayın ve tanımlayıcının yeniden tanımlayıcı olmasına izin verin. Bu arada karışım en azından okunabilir kalır: sürüm nibble’ı her değerin içinde durur, dolayısıyla bir sorgu gerektiğinde iki dönemi ikinci bir sütun olmadan da ayırt edebilir.

Sıkça sorulan sorular

v4 mü v7 mi kullanmalıyım?
Veritabanı birincil anahtarları ve oluşturulma zamanına göre sıralayacağınız her şey için v7 kullanın: baştaki zaman damgası, eklemeleri dizine dağıtmak yerine sonunda kümelenmiş tutar. Bir tanımlayıcı, ne zaman oluşturulduğu dahil hiçbir şeyi açığa çıkarmaması gerektiğinde v4 kullanın.
İki UUID hiç aynı olur mu?
Mümkündür ama yok denecek kadar olası değildir. Sürüm 4’ün 122 rastgele biti vardır; bu yüzden milyarlarca değer üretildikten sonra bile bir çakışma olasılığı, depolamanın onları bunun yerine sessizce bozması olasılığının çok altında kalır.
Sürüm 1, 3 ve 5’e ne oldu?
Sürüm 1, bir zaman damgasını ve makinenin MAC adresini kodlar; bu, donanım kimliğini sızdırır ve bir tarayıcıda hiç üretilemez. Sürüm 3 ve 5, bir UUID’yi bir ad alanı ve bir addan MD5 ya da SHA-1 kullanarak deterministik olarak türetir — aynı girdinin her zaman aynı tanımlayıcıyı üretmesi gerektiğinde yararlıdır, ama yeni bir tane üretmekten farklı bir iştir.
Bir UUID, gizli bir token olarak kullanılacak kadar güvenli mi?
Bir v4 UUID tahmin edilemezdir, ama bir v7 UUID oluşturulma zamanını açıkça kodlar ve hiçbiri bir kimlik bilgisi olarak tasarlanmamıştır. Parola sıfırlamaları, oturum token’ları ya da paylaşım bağlantıları için, tanımlayıcının tahmin edilmesinin zor olmasına güvenmek yerine özel bir rastgele sır üretin ve sunucuda izinleri denetleyin.
v7 partim neden başka araçlarda kusursuz sıralı değil?
Çünkü birçok uygulama monoton sayacı atlar. Birkaç değer bir milisaniyeyi paylaştığında, sıraları ardından gelen rastgele bitlere düşer. Bu araç RFC 9562’nin sayacını uygular; böylece burada üretilen bir parti kesin biçimde artar.
Bir UUID’yi bir veritabanında nasıl saklamalıyım?
Bir tane varsa yerel 16 baytlık bir türde — PostgreSQL’de uuid, SQL Server’da uniqueidentifier, MySQL’de BINARY(16). Bunun yerine 36 karakterlik metin biçimini saklamak, satırdaki ve sütunu içeren her dizindeki alanı iki katından fazla artırır.
Bunlar sizin sunucularınızda mı üretiliyor?
Hayır. Platformun kriptografik rastgele kaynağı olan crypto.getRandomValues kullanılarak tarayıcınızda üretilir — crypto.randomUUID’nin kullandığının aynısı. Hiçbir değer hiçbir yere gönderilmez ve sayfayı yeniden yüklemek tümüyle yeni bir küme üretir.
Bir v7 UUID’nin ne zaman oluşturulduğunu anlayabilir miyim?
Evet, değerin kendisinden başka bir şeye gerek yok. İlk on iki onaltılık basamağı, 1970 başından beri geçen milisaniyelerin sayımıdır: bunları onluğa çevirip sonucu Unix zaman damgası dönüştürücüye verin. Bir v4’te böyle bir alan yoktur, dolayısıyla oradaki aynı on iki basamak rastgeledir ve hiçbir şeye çözülmez.
v7’nin rastgele bitleri v4’ünkinden az mı?
Evet — burada 62, v4’ün 122’sine karşı; çünkü yeri zaman damgası ile sayaç kaplıyor. Bu, uygulamada bir çakışmayı daha olası kılmaz: iki v7 değeri ancak aynı milisaniyede oluşturulmuşlarsa çakışabilir, yani o 62 bit sistemin bütün ömrü boyunca değil tek bir milisaniyenin içinde harcanır; üstelik bu aracın tek bir partisi içinde sayaç, yinelenmeyi yalnızca olası değil değil, imkânsız kılar.
v4 ve v7 UUID’ler aynı sütunda durabilir mi?
Durabilir. Aynı metin biçimindeki aynı 128 bittir; bu yüzden şemada hiçbir şey değişmez ve geçiş de yoktur — bundan sonra v7 üretir, eski satırları olduğu gibi bırakırsınız. Gözetilecek tek şey sıralamadır: yeni satırlar kendi aralarında oluşturulma sırasındadır, ama eski rastgele satırlar onların çevresine dağılmıştır; dolayısıyla anahtara göre sıralamak tablonun bütünü için bir zaman sırası değildir.
UUID sürüm 6 ve 8 nedir?
RFC 9562 ikisini de v7 ile birlikte tanımlar. Sürüm 6, değerin kronolojik sıralanabilmesi için zaman damgası alanları yeniden düzenlenmiş sürüm 1’dir ve zaten v1’e bağlanmış sistemlere yöneliktir — RFC, bunun dışındaki her şeyin v7 kullanması gerektiğini söyler. Sürüm 8 ise kendi yerleşimlerine kasıtlı olarak açık bırakılmış bir yuvadır; orada yalnızca sürüm ve varyant bitleri sabittir, kalan 122 bit sizindir. Bu araç ikisini de üretmez.

İlgili araçlar