GUID sınıfı yazılım dünyasının en sorgulanmayan yapı taşlarından biridir. Bir Guid.NewGuid() çağırır, dönen değeri primary key olarak kullanır ve işimize bakarız. Ama dağıtık sistemlerin, microservice yapılarında ve yüksek hacimli veritabanlarının olduğu bir dünyada bu rastgelelik, aslında görünmeyen ama çok pahalı bir maliyet taşır. .NET 9 ile gelen Guid.CreateVersion7() metodu, tam olarak bu maliyeti hedef alıyor.
RFC 4122
Klasik Guid.NewGuid(), RFC 4122‘nin tanımladığı version 4 UUID üretir. 128 bitin neredeyse tamamı kriptografik olarak güvenli rastgele sayılardan oluşur. Bu tasarımın amacı tahmin edilemezlik ve çarpışma (collision) olasılığının pratikte sıfıra yakın olmasıdır. Bu amacına da ulaşıyor açıkçası.
Güvenlik açısından mantıklı bir yaklaşım olsa da veritabanı açısından bir zor bir yapıdır. Birçok dezavantajı bulunur.
Veritabanı Yapısı
SQL veritabanı yapılarında primary key genellikle clustered index temelini oluşturur ve fiziksel disk üzerindeki satır sırasını belirler. Rastgele bir GUID her INSERT işleminde B-Tree’nin rastgele bir noktasına sıkışarak girer.

Üstteki diyagramda iki senaryo var:
Sıralı anahtar (int / UUIDv7): Yaprak sayfalar (leaf pages) anahtar aralığına göre sıralı dizilmiştir: 1-10, 11-20, 21-30, 31-40. Yeni bir satır geldiğinde (örneğin 41), bu değer mevcut sayfaların hiçbirine girmez. Dizinin en sonuna yeni bir sayfaya eklenir. Motorun yapması gereken tek şey son sayfaya yazmaktır. Mevcut hiçbir sayfa bozulmaz.
Rastgele anahtar (UUIDv4): Aynı dört sayfa var ama gelen değerler tamamen rastgele. 17 gibi bir değer üretildiğinde bu B-Tree’nin ortasındaki 11-20 aralığına düşer. Fakat sorun şu, o sayfa zaten dolu. Motorun elinde üç seçenek yok, tek seçenek vardır.
Önce dolu sayfayı ikiye böler. Sonra satırların yaklaşık yarısını yeni bir sayfaya taşır. Ardından yeni satırı uygun sayfaya yerleştirir. Üst seviyedeki index düğümlerini (non-leaf pages) güncelleyerek yeni sayfayı işaret ettirir.
Tek bir INSERT işlemi için epey bir iş değil mi?
İşte UUIDv7’nin çözdüğü tam olarak budur. Zaman damgası önde olduğu için üretilen değerler her zaman artan sırada gelir. Bu da yeni satırların neredeyse her zaman dizinin sonuna düşmesini sağlar.
RFC 9562

Konunun resmi kaynağı RFC 9562’dir. Bu standardın getirdiği en önemli değişikliklerden biri, UUID versiyon 6, 7 ve 8’in tanımlanmasıdır.
UUIDv6: Eski UUIDv1’in (MAC adresi + zaman damgası tabanlı) bit sırasını yeniden düzenleyerek sıralanabilir hale getirir. Geriye dönük uyumluluk odaklıdır.
UUIDv7: Sıfırdan doğrudan veritabanı dostu olmayı hedefleyerek tasarlanmıştır.
UUIDv8: Üreticilere (vendor) özel deneysel kullanım alanı bırakır.
IETF’in bu değişikliği yapma gerekçesi tam olarak yukarıda anlattığım pratik problemdi. Dağıtık sistemlerde merkezi bir ID üreticisine ihtiyaç duymadan (auto-increment aksine) benzersiz kimlik üretebilmek isteniyordu ama bunu yaparken index yakınlığını feda etmek istemiyorlardı. RFC’nin kendi ifadesiyle, zaman damgasının UUID’nin başında olması sayesinde ardışık üretilen değerler index üzerinde birbirine yakın konumlanır ve bu da nesnelerin daha iyi kümelenmesini ve daha iyi performansı sağlar.
RFC 9562’nin UUIDv7 için tanımladığı bit yapısı IETF makalesi içerisinde detaylandırılmış.
Guid.CreateVersion7()
.NET 9, System.Guid tipine bu RFC’yi doğrudan implemente eden bir statik metot ekledi.
var guid = Guid.CreateVersion7();
var timedGuid = Guid.CreateVersion7(DateTimeOffset.UtcNow.AddDays(-1));Zaman bazlı üretim yapılıyor. İlk 48 bit, milisaniye hassasiyetinde Unix epoch zaman damgasını taşır. Bir GUID’e bakarak yaklaşık olarak ne zaman üretildiğini görebilirsiniz.
Art arda üretilen GUID değerleri byte sırasına göre karşılaştırıldığında artan bir dizilim oluşturur. Aslında auto-increment bir tamsayı gibi.
Veritabanı dostudur. Clustered index yapılarda her bir INSERT dizinin sonuna eklenir. Sayfa bölünmeleri ciddi oranda azalır.
122 bit rastgelelik içerir. Sadece 6 bit sabit ama geri kalan alan benzersizliği garanti eder. Yani sıralılık kazanırken tahmin edilebilirlik tamamen feda edilmez.
Özellikle event-driven mimarilerde correlation ID olarak kullanılırsa trace edilebilirlik kolaylaşacaktır. Log tutma açısından çok değerli bir gelişim.
Sonuç
Guid.CreateVersion7(), .NET ekosistemine entegre şekilde gelen ama arkasında ciddi bir standardizasyon çabası (RFC 9562) ve on yıllık bir pratik problemin çözümü yatan bir yeniliktir.
Mikroservis mimarisinde çalışıyorsanız ve servisleriniz birbirinden bağımsız olarak kimlik üretmek zorundaysa, Guid.CreateVersion7() muhtemelen artık varsayılanınız olmalı. Tek bir monolitik veritabanınız varsa ve saf performans peşindeyseniz, bigint hala rakipsiz. İkisi arasındaki doğru seçim mimarinizin dağıtıklık derecesine bağlı.



Yorum bırakın