MySQL'de UUID performansı?


86

MySQL veritabanımız için birincil anahtarlar olarak UUID değerlerini kullanmayı düşünüyoruz. Eklenen veriler düzinelerce, yüzlerce ve hatta binlerce uzak bilgisayardan üretilir ve saniyede 100-40.000 ekleme hızında eklenir ve hiçbir zaman güncelleme yapmayız.

Veritabanının kendisi, biz verileri toplamaya başlamadan önce genellikle yaklaşık 50 milyon kayda ulaşacaktır, bu nedenle çok büyük bir veritabanı değil, küçük de değil. Ayrıca InnoDB üzerinde çalışmayı planlıyoruz, ancak yaptığımız şey için daha iyi bir motor varsa bunu değiştirmeye açıkız.

Java'nın Type 4 UUID'sini kullanmaya hazırdık, ancak test sırasında bazı garip davranışlar görüyoruz. Birincisi, varchar (36) olarak depoluyoruz ve şimdi ikili (16) kullanmanın daha iyi olacağının farkındayım - ne kadar iyi olduğundan emin değilim.

Daha büyük soru şudur: 50 milyon kaydımız varken bu rastgele veriler endeksi ne kadar kötü bir şekilde alt üst ediyor? Örneğin, en soldaki bitlerin zaman damgalı olduğu bir tip-1 UUID kullansaydık daha iyi olur muyduk? Veya belki de UUID'leri tamamen atmalı ve auto_increment birincil anahtarlarını düşünmeliyiz?

MySQL'de bir dizin / birincil anahtar olarak depolandıklarında farklı UUID türlerinin performansı hakkında genel düşünceler / ipuçları arıyorum. Teşekkürler!


2
önemli bir ayrıntı eksik: birincil anahtarlar günlük sunucusu tarafından mı yoksa istemci makinelerin kendileri tarafından mı üretilecek?

1
@hop, verileri ekleyen 10-1000 istemci tarafından üretiliyorlar
Patrick Lightbody

Senaryonuzdaki evrensel benzersizliğe nerede ihtiyacınız var? Tavsiyem, auto_increment'e bağlı kalmak ve verileri gönderen uzak bilgisayarı tanımlamak için ayrı bir alan kullanmaktır. Burada tekerleği yeniden icat etmeye gerek yok.
Theodore Zographos

Yanıtlar:


36

Bir UUID, Evrensel Olarak Benzersiz bir Kimliktir. Burada düşünmeniz gereken evrensel kısım budur.

Eğer Do gerçekten evrensel benzersiz olması kimlikleri gerek? Öyleyse, UUID'ler tek seçeneğiniz olabilir.

Ben şiddetle eğer öneririm yapmak kullanımı UUIDs, bir numara olarak değil, bir dize olarak saklayabilirsiniz. 50M + kayıtlarınız varsa, depolama alanından tasarruf, performansınızı artıracaktır (ancak ne kadar olduğunu söyleyemem).

Kimliklerinizin evrensel olarak benzersiz olması gerekmiyorsa, o zaman kimliklerin bir tablo içinde benzersiz olacağını garanti eden auto_increment'i kullanmaktan çok daha iyisini yapabileceğinizi sanmıyorum (çünkü değer her seferinde artacaktır)


2
İlginç nokta; bu, anahtarların üretimini paralelleştirecektir. Bunun anahtar üretiminin performansını artıracağına inanıyorum. Ancak, UUID'yi depolamak için VARCHAR kullanıyorsanız, SEÇME performansına INSERT performansını seçiyorsunuz. SELECT performansını garantilemek için depolama için kesinlikle VARBINARY'yi seçmelisiniz. Ekstra adım olabilir INSERT performansını etkileyebilir, ancak SEÇ performans iyileştirme ile kapalı ödeme yapılır.
Dancrumb

12
Gerçek veriler üzerinde bazı kıyaslamalar yaptık ve anahtarsız GUID'ler oldukça hızlıydı, anahtarlı GUID'ler korkunçtu (BINARY olarak saklandığında bile) ve int w / AUTO_COMPLETE en hızlıydı. Dizisi nesil nedeniyle Guıd rastgele olan gerçekten berbat btree sahip daha fazla veri depolamak + maliyetiyle karşılaştırıldığında önemsiz görünüyordu ben bizim durumumuzda düşünüyorum, biz gerçekten, ağaçlardan ormanı eksik
Patrick Lightbody

1
sayı olarak depolamak, ikili biçimde depolamak anlamına gelir mi? ancak ikili biçim insanlar için okunamaz. Uuid birincil anahtarının büyük baytları nedeniyle yavaş mı? Eğer öyleyse, otomatik artırmayı uuid için başka bir sütunla saklayabilirim. O zaman performans azalmaz. Haklı mıyım
Chamnap

4
Kesin konuşursak, UUID evrensel olarak benzersizdir, yani dünyanın başka hiçbir yerinde asla görünmeyecektir. Buna yalnızca verilerinizi herkese açık olarak paylaşıyorsanız ihtiyacınız var. Bir UUID'yi sayı olarak saklamaya gelince, binaryformattan bahsetmiyorum. 288 bitlik bir diziden ziyade 128 bitlik bir sayı demek istiyorum. Örneğin, ASCII'deki 'merhaba' kelimesi 68 65 6C 6C 6F448,378,203,247 sayısıdır. '68656C6C6F' dizesini saklamak 10 bayt gerektirir. 448,378,203,247 sayısı yalnızca 5'i gerektirir. Sonuçta, UUID'deki ilk U'ya gerçekten ihtiyacınız olmadıkça , daha iyisini auto_increment
yapamazsınız

1
O): @Chamnap: Bir yığın taşması soru sormak öner
Dancrumb

78

Benim işimde UUID'yi PK olarak kullanıyoruz. Deneyimlerimden size söyleyebileceğim şey, ONLARI PK olarak KULLANMAYIN (bu arada SQL Server).

1000'den az kayda sahip olduğunuzda sorun değil, ancak milyonlarınız olduğunda yapabileceğiniz en kötü şey bu. Neden? UUID sıralı olmadığından, her yeni kayıt eklendiğinde MSSQL'in kaydı eklemek için doğru sayfaya gitmesi ve ardından kaydı yerleştirmesi gerekir. Bunun gerçekten çirkin sonucu, sayfaların hepsinin farklı boyutlarda olması ve parçalanmış olmalarıdır, bu yüzden şimdi periyodik parçalanma yapmamız gerekiyor.

Bir otomatik artırma kullandığınızda, MSSQL her zaman son sayfaya gider ve sonunda eşit boyutta sayfalarla (teoride) sonuçlanır, bu nedenle bu kayıtları seçme performansı çok daha iyidir (ayrıca INSERT'ler tablo / sayfayı Elveda).

Bununla birlikte, UUID'yi PK olarak kullanmanın en büyük avantajı, DB kümelerimiz varsa, birleştirme sırasında çakışma olmayacak olmasıdır.

Aşağıdaki modeli tavsiye ederim: 1. PK INT Kimliği 2. UUID olarak otomatik olarak oluşturulan ek sütun.

Bu şekilde, birleştirme işlemi mümkündür (UUID sizin GERÇEK anahtarınız olurken, PK size iyi performans sağlayan geçici bir şey olacaktır).

NOT: En iyi çözüm NEWSEQUENTIALID kullanmaktır (yorumlarda söylediğim gibi), ancak yeniden düzenleme yapmak için fazla zamanı olmayan (ve daha da kötüsü, tüm ekleri kontrol etmeyen) eski uygulama için bunu yapmak mümkün değildir. Ama gerçekten 2017 itibariyle, en iyi çözümün NEWSEQUENTIALID veya NHibernate ile Guid.Comb yapmak olduğunu söyleyebilirim.

Bu yardımcı olur umarım


Bu terimlerin ne anlama geldiğini gerçekten bilmiyorum, ama gerçek şu ki, indekslerin her ay yeniden indekslenmesi gerekiyor. Bahsettiğiniz şey yeniden dizin oluşturma görevini ortadan kaldırıyorsa, bilmiyorum ama sorabilirim.
Kat Lim Ruiz

3
Düşündüğüm bir şey de bunun ebeveyn-çocuk ilişkileri için pek işe yaramayabileceği. Bu durumda, alt tabloya eklemeniz gerektiğini düşünüyorum: parent-pk, parent-guid. Aksi takdirde veritabanları arasındaki referansları kaybedebilirsiniz. Bunu çok fazla düşünmedim, herhangi bir örnek de vermedim, ama buna ihtiyaç olabilir
Kat Lim Ruiz

4
Eğer NEWSEQUENTIALID () kullanabilir sql sunucusunda @KatLimRuiz technet.microsoft.com/en-us/library/ms189786.aspx performans sorununu önlemek için
giammin

Aslında, ancak NEWSEQUENTIALID yalnızca VARSAYILAN olarak çalışır. Öyleyse tüm DAL'ınızı bunun etrafında tasarlamanız gerekiyor, bu yeni projeler için uygun ancak büyük miras için o kadar kolay değil
Kat Lim Ruiz

@KatLimRuiz deha. Bu harika bir uzlaşma
jmgunn87

26

Dikkate alınması gereken bir nokta, Otomatik artışların birer birer üretildiği ve paralel bir çözüm kullanılarak çözülemeyeceğidir. UUID'leri kullanma mücadelesi, sonunda elde etmek istediklerinize karşı potansiyel olarak feda ettiğiniz şeye bağlıdır.

Performans hakkında kısaca :

Yukarıdakine benzer bir UUID, çizgiler dahil 36 karakter uzunluğundadır. Bu VARCHAR'ı (36) saklarsanız, karşılaştırma performansını önemli ölçüde düşürürsünüz. Bu sizin birincil anahtarınızdır, yavaş olmasını istemezsiniz.

Bit düzeyinde, bir UUID 128 bittir, bu da 16 bayta sığacağı anlamına gelir, bunun insan tarafından okunabilir olmadığını ancak depolamayı düşük tutacağını ve 32 bitlik int veya 2'den yalnızca 4 kat daha büyük olduğunu unutmayın. 64 bitlik bir int'den kat daha büyük. Bir VARBINARY kullanacağım (16) Teorik olarak, bu çok fazla ek yük olmadan çalışabilir.

Aşağıdaki iki gönderiyi okumanızı tavsiye ederim:

Sanırım ikisi arasında, sorunuza cevap veriyorlar.


2
Aslında, bu soruyu göndermeden önce bu iki makaleyi de okudum ve burada hala iyi bir cevabım yoktu. Örneğin, tip 1 ve tip 4 UUIDS hakkında konuşmayın :(
Patrick Lightbody

Adil, cevabımı bir dokunuşla güncelledim. Ancak bunun çok fazla ekstra bilgi sağladığını düşünmüyorum.
Kyle Rosendo

@Patrick: Sorunuza çok fazla farklı konu koydunuz.

1
9 yıl sonra, ancak gelecek nesillerde de tamsayı kimliklerinin aksine uygulamaların güvenli bir şekilde UUID'ler oluşturarak nesli veritabanından tamamen kaldırabileceği unutulmamalıdır. Performans optimizasyonu için UUID'lerin manipülasyonu (zaman damgasına dayalı ancak saf bir şekilde sıralanabilmeleri için değiştirilmiş) SQL dışındaki hemen hemen tüm dillerde oldukça kolaydır. Neyse ki bugün neredeyse tüm veritabanları (MySQL dahil) UUID birincil anahtarlarını eskisinden çok daha iyi kullanıyor.
Miles Elam

5

UUID'den kaçınma eğilimindeyim çünkü saklanması ve birincil anahtar olarak kullanılması bir acıdır, ancak avantajları vardır. Bunlardan en önemlisi, EŞSİZ olmaları.

Genellikle sorunu çözerim ve çift anahtarlı alanları kullanarak UUID'den kaçınırım.

KOLLEKTÖR = BİR MAKİNEYE ATANMIŞ EŞSİZ

KİMLİK = KOLLEKTÖR TARAFINDAN TOPLANAN KAYIT (auto_inc alanı)

Bu bana iki şey sunuyor. Otomatik artırma alanlarının hızı ve birlikte toplanıp gruplandırıldıktan sonra merkezi bir konumda depolanan verilerin benzersizliği. Ayrıca, verilerin nerede toplandığına göz atarken, bunun genellikle ihtiyaçlarım için oldukça önemli olduğunu biliyorum.

Müşteriler için diğer veri setleriyle uğraşırken UUID kullanmaya karar verdikleri, ancak yine de verilerin toplandığı bir alana sahip olan ve gerçekten çaba israfı olan birçok vaka gördüm. Anahtarınız gerçekten yardımcı olduğu için yalnızca iki (veya gerekirse daha fazla) alan kullanmak.

UUID kullanarak çok fazla performans isabeti gördüm. Hile gibi hissediyorlar ...


3

Her ekleme için merkezi olarak benzersiz anahtarlar üretmek yerine, anahtar bloklarını ayrı sunuculara ayırmaya ne dersiniz? Anahtarları tükendiğinde yeni bir blok talep edebilirler. Ardından, her bir kesici uç için bağlayarak genel gider sorununu çözersiniz.

Anahtar sunucusu bir sonraki kullanılabilir kimliği korur

  • Sunucu 1, kimlik bloğu ister.
  • Anahtar sunucusu döndürür (1,1000)
    Sunucu 1, yeni bir blok talep etmesi gerekene kadar 1000 kayıt ekleyebilir
  • Sunucu 2, dizin bloğu ister.
  • Anahtar sunucusu döndürür (1001,2000)
  • vb...

Bir sunucunun gerekli anahtar sayısını talep edebileceği veya kullanılmayan blokları anahtar sunucusuna geri gönderebileceği daha karmaşık bir sürüm elde edebilirsiniz, bu durumda elbette kullanılan / kullanılmayan blokların bir haritasını tutması gerekir.


Teoride ilginç bir öneri. Pratikte bunu yönetmek karmaşık olacaktır. Daha pratik bir çözüm, muhtemelen Schworak'ın verdiği yanıt olacaktır.
Simon East

2

Her sunucuya işlemsel bir şekilde sayısal bir kimlik atardım. Ardından, eklenen her kayıt kendi sayacını otomatik olarak artıracaktır. Sunucu Kimliği ve Kayıt Kimliği kombinasyonu benzersiz olacaktır. Sunucu Kimliği alanı endekslenebilir ve Sunucu Kimliğine (gerekirse) dayalı olarak gelecekteki seçim performansı çok daha iyi olabilir.


2

Kısa cevap, birçok veritabanının, indeksleme metotları ile UUID'lerin yüksek mertebeli bitlerdeki kasıtlı entropisi arasındaki bir çelişki nedeniyle performans problemlerine (özellikle yüksek INSERT hacimlerinde) sahip olmasıdır. Birkaç yaygın saldırı vardır:

  • bunu önemsemeyen farklı bir dizin türü seçin (örneğin, MSSQL'de kümelenmemiş)
  • Entropiyi daha düşük sıralı bitlere taşımak için verileri munge (örneğin, MySQL'de V1 UUID'lerin baytlarını yeniden sıralama)
  • UUID'yi otomatik artışlı int birincil anahtar ile ikincil bir anahtar haline getirin

... ama bunların hepsi hack - ve muhtemelen bu konuda kırılgan olanlar.

En iyi cevap, ancak maalesef en yavaş olanı, satıcınızın UUID'leri tıpkı diğer türler gibi birincil anahtarlar olarak ele alabilmesi için ürününü geliştirmesini istemektir. Yaygın bir kullanım durumu haline gelen ve sadece büyümeye devam edecek olan şeyi çözmedeki başarısızlıklarını telafi etmek için kendi yarı pişmiş hack'lerinizi yuvarlamanız için sizi zorlamamalılar.


1

El yapımı bazı UID'ler ne olacak? Binlerce sunucunun her birine bir kimlik verin ve birincil anahtarı otomatik artırmanın birleşik anahtarı, MachineID ???


Bunu düşündüm ve bazı kriterler çalıştırmam gerekebilir. 1000 makinenin her birinde zaman damgası ile birlikte geçici bir yerel sıra bile yeterli olabilir. Ör: machine_id + temp_seq + timestamp
Patrick Lightbody

Her zaman damgası onayını sıfırlayan bir temp_sequence olabilir mi? Emin değilim.
MindStalker

1

Birincil anahtar merkezi olmayan bir şekilde oluşturulduğundan, yine de bir otomatik artırma kullanma seçeneğiniz yoktur.

Uzak makinelerin kimliğini gizlemeniz gerekmiyorsa, UUID'ler yerine Type 1 UUID'leri kullanın. Oluşturmaları daha kolaydır ve en azından veritabanının performansına zarar veremezler.

Aynı şey varchar (char, gerçekten) ve ikili için de geçerlidir: yalnızca sorunlara yardımcı olabilir. Performansın ne kadar geliştirildiği gerçekten önemli mi?


0

Bu sorunun oldukça eski olduğunun farkındayım ama araştırmamda buna değindim. Birkaç şey olduğundan beri (SSD her yerde bulunur InnoDB'de güncellemeler var vb.).

Araştırmamda performans üzerine oldukça ilginç bir yazı buldum :

GUID / UUID indeks ağaçlarının rastlantısallığından dolayı oldukça dengesizleşebileceğini iddia ederek . MariaDB KB'de başka bir gönderinin bir çözüm önerdiğini buldum . Ama o zamandan beri yeni UUID_TO_BIN bununla ilgileniyor. Bu işlev yalnızca MySQL'de (test edilmiş sürüm 8.0.18) mevcuttur ve MariaDB'de (sürüm 10.4.10) mevcut değildir.

TL; DR: UUID'yi dönüştürülmüş / optimize edilmiş BINARY (16) değerleri olarak saklayın.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.