deniz.in

Piyasalar

Hava durumu

Hava durumu yükleniyor

· kaynak dev.to (home feed)

ORM'ların UUID'leri VARCHAR(36) olarak saklama alışkanlığı MySQL indexlerini dokuz kat şişiriyor

Bir dev.to yazısı, UUID sütunlarını utf8mb4 altında CHAR(36)'ya eşlemenin 16 baytlık bir değeri 144 bayta nasıl dönüştürdüğünü, MySQL indexlerini dokuz kat şişirdiğini ve BINARY(16) çözümünü anlatıyor.

ORM'ların UUID'leri VARCHAR(36) olarak saklama alışkanlığı MySQL indexlerini dokuz kat şişiriyor

16 baytlık bir tanımlayıcının 144 bayt olarak saklanması

dev.to'daki bir yazı, sessiz ama maliyetli bir şema hatasını ele alıyor: bir ORM bir UUID sütununu VARCHAR(36) veya CHAR(36) olarak eşlediğinde — API'lerin döndürdüğü şey string temsili olduğu için yaygın bir tercih — utf8mb4 karakter setini kullanan MySQL tabloları karakter başına dört bayt ayırır. 36 karakterlik bir UUID bu nedenle 144 bayt tüketir ve o sütundaki herhangi bir index, her kayıt için kabaca aynı yükü bir kez daha taşır.

Yazıda belirtildiği gibi, pratik sonuç index yoğunluğu. 8 KB'lık bir index sayfası string biçiminde yaklaşık 54 anahtar tutarken, aynı değerler BINARY(16) olarak saklandığında kabaca 372 anahtar tutuyor. Bir milyonluk bir tabloda bu, yaklaşık 16 MB'lık bir index ile 144 MB'a yakın bir index arasındaki fark anlamına geliyor — dokuz kat buffer-pool ayak izi ve onu taramak için gereken dokuz kat sayfa okuması. Aramalar ayrıca gizli bir karşılaştırma maliyeti ödüyor, çünkü eşleştirme 16 baytlık tek bir sabit değer yerine 36 bayta kadar stringleri karşılaştırmak demek.\n

Çözüm

MySQL için yazar, sütun tanımını şöyle değiştirmeyi öneriyor:

id BINARY(16) NOT NULL PRIMARY KEY

yazma işlemleri UUID_TO_BIN(?, 1) üzerinden, okuma işlemleri ise BIN_TO_UUID(id, 1) üzerinden geçiyor. İkinci argüman önemli: 1 geçmek, UUID içindeki zamana bağlı alanları yeniden sıralayarak aynı anda oluşturulan değerlerin index içinde birbirine yakın durmasını sağlıyor ve yerelliği artırıyor.

Postgres kullanıcıları bu sorundan büyük ölçüde varsayılan olarak kurtulur, çünkü yerleşik uuid tipi 16 bayttır. Orada hasar genellikle tip sisteminin bir kısıtından ziyade alışkanlıkla seçilen bir text sütunundan kaynaklanır.

Endişe edilmesi değerli çakışmalar

Yazı ayrıca yinelenen ID kaygısını da ele alıyor. Gerçek v4 çakışmaları istatistiksel olarak önemsizdir: 122 rastgele bit ile, bir milyar saklı UUID arasında herhangi bir çakışma olasılığı doğum günü sınırına göre yaklaşık 9 × 10⁻²⁰'dir. Prodüksiyonda gerçekte ortaya çıkan yinelenenler uygulama kodundan kaynaklanır — Math.random() gibi zayıf rastgelelikle tohumlanmış üreteçler, varsayılan olarak boş string olan sütunlar veya sonlarından karakterler kesilerek kısaltılmış tanımlayıcılar.

Kesme, sezginin öngördüğünden daha erken başarısız olur. Yazarın hesabına göre 8 onaltılık karakterlik bir önek yalnızca 32 bit taşır; bu da yaklaşık 77.000 satırda %50 çakışma şansı verir; 12 karaktere uzatmak (48 bit) bu eşiği yalnızca yaklaşık 20 milyona taşır. URL'lerdeki kısa ID'ler için öneri, bir UUID'yi kırpmak yerine, sistemin erişeceği toplam satır sayısına göre boyutlandırılmış şekilde bilinçli olarak üretmektir. Zamana göre sıralı v7 UUID'ler bu tür önekler için özellikle kötü bir kaynaktır, çünkü aynı pencerede oluşturulan her ID aynı timestamp karakterleriyle başlar.

Insert desenleri ikinci maliyet

Rastgele v4 değerleri ayrıca yazma performansına da yük bindirir. Baş baytlar rastgele olduğu için her insert B-tree indexinin rastgele bir yaprağına düşer. Tablo cache'i aştığında her insert, diskten bir sayfa çekip onu bölmek demektir; yazar bunu, tablolar birkaç milyon satıra ulaştığında yazma throughput'unun keskin şekilde düşmesinin nedeni olarak gösteriyor.

UUID v7 veya ULID'ler bunu timestamp'ı yüksek anlamlı bitlere yerleştirerek çözer, böylece yeni satırlar indexin sağ kenarına eklenir dağılmak yerine. Yazının belirttiği gibi ödünleşim, bu tür ID'lerin bir kaydın ne zaman oluşturulduğunu ifşa etmesidir; bu da onları zamanlamayı açıklamaması gereken tanımlayıcılar için uygun olmayan bir seçenek yapar.

Makalenin genel önerisi: tanımlayıcıları 16 bayt tutun, indexlenen değer zamana göre sıralı olsun ve UUID'leri kesilmiş string biçimleri üzerinden karşılaştırmaktan kaçının.

Neden önemli

Index boyutu, veritabanı performansında doğrudan bir kalem. Buffer pool'a sığan bir index bellekten sunulur; dokuz kat büyüğü hem aramalar hem de insertler için disk okumalarını kritik yola zorlar. Bu şişme tipik olarak açık bir karardan ziyade varsayılan bir ORM eşlemesinden geldiği için bir kod tabanındaki her UUID sütununa sessizce yayılabilir — ve çözüm, ölçülebilir getirisi olan sınırlı ve iyi anlaşılmış bir şema değişikliği: daha küçük indexler, daha ucuz karşılaştırmalar ve tablolar büyüdükçe daha öngörülebilir yazma throughput'u.

  • #mysql
  • #databases
  • #orm
  • #performance
  • #uuid

İlgili yazılar