Mysql int vs varchar birincil anahtar olarak (InnoDB Depolama Motoru?


13

Bir web uygulaması (proje yönetim sistemi) oluşturuyorum ve performans söz konusu olduğunda bunu merak ediyorum.

İçinde Sorunlar tablo var içinde çeşitli diğer tablolara bağlantı 12 yabancı anahtar vardır. bunlardan 8 tanesi, kayıtların bir web uygulamasında herhangi bir anlam ifade edebilmesi için diğer tablolardan başlık alanını almak için katılmam gerekir, ancak özellikle sadece çektiğim için gerçekten çok fazla görünen 8 birleştirme yapmak anlamına gelir. Bu birleştirmeler için 1 alan.

Şimdi de otomatik olarak artan birincil anahtar kullanmam söylendi (parçalama, bu durumda bir GUID kullanmalıyım bir endişe olmadığı sürece) kalıcılık nedenleriyle, ancak bir varchar (maksimum uzunluk 32) performansı akıllıca kullanmak ne kadar kötü? Demek istediğim, bu tabloların çoğu muhtemelen pek çok kayda sahip olmayacak (çoğu 20 yaşın altında olmalı). Ayrıca başlığı birincil anahtar olarak kullanırsam,% 95 oranında katılmam gerekmeyecek, bu yüzden sql'ın% 95'i için herhangi bir performans hitini bile oluşturacağım (sanırım). I-ebilmek düşün tek dezavantajı ben daha yüksek disk alanı kullanımı olacak (ama bir gün aşağı gerçekten büyük bir anlaşma).

Arama tabloları numaralandırma yerine bir sürü için kullanmamın nedeni, tüm bu değerleri uygulamanın kendisi aracılığıyla son kullanıcı tarafından yapılandırılabilir olması gerektiğidir.

Birçok kayda sahip olmayan bir tablonun birincil anahtarı olarak varchar kullanmanın dezavantajları nelerdir?

GÜNCELLEME - Bazı Testler

Ben de bu konuda bazı temel testler yapmaya karar verdim. 100000 kayıtları var ve bunlar temel sorgular:

Temel VARCHAR FK Sorgusu

SELECT i.id, i.key, i.title, i.reporterUserUsername, i.assignedUserUsername, i.projectTitle, 
i.ProjectComponentTitle, i.affectedProjectVersionTitle, i.originalFixedProjectVersionTitle, 
i.fixedProjectVersionTitle, i.durationEstimate, i.storyPoints, i.dueDate, 
i.issueSecurityLevelId, i.creatorUserUsername, i.createdTimestamp, 
i.updatedTimestamp, i.issueTypeId, i.issueStatusId
FROM ProjectManagement.Issues i

Temel INT FK Sorgusu

SELECT i.id, i.key, i.title, ru.username as reporterUserUsername, 
au.username as assignedUserUsername, p.title as projectTitle, 
pc.title as ProjectComponentTitle, pva.title as affectedProjectVersionTitle, 
pvo.title as originalFixedProjectVersionTitle, pvf.title as fixedProjectVersionTitle, 
i.durationEstimate, i.storyPoints, i.dueDate, isl.title as issueSecurityLevelId, 
cu.username as creatorUserUsername, i.createdTimestamp, i.updatedTimestamp, 
it.title as issueTypeId, is.title as issueStatusId
FROM ProjectManagement2.Issues i
INNER JOIN ProjectManagement2.IssueTypes `it` ON it.id = i.issueTypeId
INNER JOIN ProjectManagement2.IssueStatuses `is` ON is.id = i.issueStatusId
INNER JOIN ProjectManagement2.Users `ru` ON ru.id = i.reporterUserId
INNER JOIN ProjectManagement2.Users `au` ON au.id = i.assignedUserId
INNER JOIN ProjectManagement2.Users `cu` ON cu.id = i.creatorUserId
INNER JOIN ProjectManagement2.Projects `p` ON p.id = i.projectId
INNER JOIN ProjectManagement2.`ProjectComponents` `pc` ON pc.id = i.projectComponentId
INNER JOIN ProjectManagement2.ProjectVersions `pva` ON pva.id = i.affectedProjectVersionId
INNER JOIN ProjectManagement2.ProjectVersions `pvo` ON pvo.id = i.originalFixedProjectVersionId
INNER JOIN ProjectManagement2.ProjectVersions `pvf` ON pvf.id = i.fixedProjectVersionId
INNER JOIN ProjectManagement2.IssueSecurityLevels isl ON isl.id = i.issueSecurityLevelId

Bu sorguyu aşağıdaki eklemelerle de çalıştırdım:

  • Belirli bir öğe seçin (burada i.key = 43298)
  • Grup i.id
  • Sıralama: (int FK için it.title, varchar FK için i.issueTypeId)
  • Sınır (50000, 100)
  • Gruplama ve sınırlama
  • Gruplama, sipariş verme ve sınırlama

Bunların sonuçları:

QUERY TYPE: VARCHAR FK TIME / INT FK TIME


Temel sorgu: ~ 4ms / ~ 52ms

Belirli bir öğe seçin: ~ 140ms / ~ 250ms

Grup i.id'ye göre: ~ 4ms / ~ 2.8sn

Sipariş: ~ 231ms / ~ 2sn

Sınır: ~ 67ms / ~ 343ms

Gruplandırın ve birlikte sınırlandırın: ~ 504ms / ~ 2sn

Gruplayın, sipariş verin ve birlikte sınırlandırın: ~ 504ms /~2.3sec

Şimdi birini veya diğerini (veya her ikisini) daha hızlı yapmak için hangi yapılandırmayı yapabileceğimi bilmiyorum ama VARCHAR FK veri sorgularında daha hızlı görüyor gibi görünüyor (bazen çok daha hızlı).

Sanırım bu hız gelişiminin ekstra veri / dizin boyutuna değip değmeyeceğini seçmek zorundayım.


Testiniz bir şeyi gösteriyor. Ayrıca varsayılan MySQL ayarları gerçekten InnoDB için optimize değil çünkü çeşitli InnoDB ayarları (tampon havuzları, vb) ile test ediyorum.
ypercubeᵀᴹ

Ayrıca, dizin boyutundan da etkilenebileceğinden, Ekle / Güncelle / Sil performansını test etmelisiniz. Her InnoDB tablosunun bir kümelenmiş anahtarı genellikle PK'dir ve bu (PK) sütunu diğer tüm dizinlere de dahil edilir. Bu muhtemelen InnoDB'deki büyük PK'lerin büyük bir dezavantajı ve tablodaki birçok endekstir (ancak 32 bayt oldukça orta, büyük değil, bu yüzden bir sorun olmayabilir).
ypercubeᵀᴹ

Ayrıca, tablolarınızın 100K'dan (gerçekten büyük olmayan) daha fazla büyüyebileceğini düşünüyorsanız, daha büyük tablolarla (örneğin 10-100M satır aralığında veya daha büyük) test etmelisiniz.
ypercubeᵀᴹ

@ypercube Bu yüzden verileri 2 milyona çıkarıyorum ve int FK için select deyimi, varchar yabancı anahtarının oldukça sabit kaldığı yerlerde katlanarak yavaşlıyor. Varchar'ın, seçili sorgulardaki kazanç için disk / bellek gereksinimlerindeki fiyat değerinde olduğunu düşünün (bu belirli tabloda ve birkaçında kritik olacaktır).
ryanzec

Sonuçlara varmadan önce db (ve özellikle InnoDB) ayarlarınızı da kontrol etmeniz yeterlidir. Küçük referans tabloları ile üstel artış beklemem
ypercubeᵀᴹ

Yanıtlar:


9

Birincil anahtarlar için aşağıdaki kuralları izlerim:

a) Herhangi bir iş anlamı olmamalıdır - geliştirdiğiniz uygulamadan tamamen bağımsız olmalıdırlar, bu nedenle sayısal otomatik oluşturulan tamsayıları tercih ediyorum. Ancak, benzersiz olması için ek sütunlara ihtiyacınız varsa, bunu desteklemek için benzersiz dizinler oluşturun

b) Birleşimlerde çalışmalıdır - tamsayılara karşı varchars'a katılmak birincil anahtarın uzunluğu arttıkça yaklaşık 2x ila 3x daha yavaştır, bu nedenle anahtarlarınızı tamsayı olarak kullanmak istersiniz. Tüm bilgisayar sistemleri ikili olduğundan, coz olduğundan şüphelenirim dize ikili olarak değiştirilir ve diğerlerine kıyasla çok yavaştır.

c) Mümkün olan en küçük veri türünü kullanın - tablonuzun 52 ABD eyaleti gibi çok az sütuna sahip olmasını bekliyorsanız, 2 haneli kod için mümkün olan en küçük türü belki CHAR (2) kullanın, ancak yine de bir minik (128) sütun için 2 milyara kadar çıkabilen büyük bir int

Ayrıca, örneğin proje adı değişirse (nadir değildir), birincil anahtarlardan diğer tablolara yaptığınız değişiklikleri basamaklandırmayla ilgili bir zorluk yaşarsınız.

Birincil anahtarlarınız için sıralı otomatik artan tamsayılara gidin ve veritabanı sistemlerinin gelecekte yapılacak değişiklikler için destek sağlayacağı dahili verimlilikleri kazanın


1
Dizeler ikili olarak değiştirilmez; en başından beri ikili olarak depolanırlar. Başka nasıl saklanırlar? Belki de büyük / küçük harfe duyarlı olmayan karşılaştırmaya olanak tanıyan işlemleri mi düşünüyorsunuz?
Tüm Ticaretten Jon

6

Testlerinizde varchar ve int anahtarlarının performans farkını karşılaştırmak yerine, birden fazla birleştirme maliyetini karşılaştırıyorsunuz. 1 tabloyu sorgulamanın birçok tabloya katılmaktan daha hızlı olması şaşırtıcı değildir.
Varchar birincil anahtarının bir dezavantajı, atxdba'nın işaret ettiği gibi dizin boyutunu arttırmaktır . Arama tablonuzda PK hariç başka dizinler olmasa bile (bu pek olası değildir, ancak mümkündür), aramaya başvuran her tablonun bu sütunda bir dizini olacaktır.
Doğal birincil anahtarlarla ilgili bir başka kötü şey, değerlerinin çok sayıda basamaklı güncellemeye neden olacak şekilde değişebilmesidir. Oracle gibi tüm RDMS'ler,on update cascade. Genel olarak, birincil anahtar değerini değiştirmek çok kötü bir uygulama olarak düşünülür. Doğal birincil anahtarların her zaman kötü olduğunu söylemek istemiyorum; Arama değerleri küçükse ve asla değişmezse, kabul edilebilir olabileceğini düşünüyorum.

Dikkate almak isteyebileceğiniz seçeneklerden biri, somutlaştırılmış görünüm uygulamaktır. Mysql doğrudan desteklemiyor, ancak alttaki tablolarda tetikleyicilerle istediğiniz işlevselliği elde edebilirsiniz. Böylece, göstermeniz gereken her şeye sahip bir tablonuz olacak. Ayrıca, performans kabul edilebilirse, şu anda var olmayan sorunla mücadele etmeyin.


3

En büyük dezavantajı PK'nın tekrarlanmasıdır. Disk alanı kullanımında bir artışa işaret ettiniz, ancak açık bir şekilde belirtmek gerekirse, artan dizin boyutu sizin en büyük endişenizdir. İnnodb kümelenmiş bir dizin olduğundan, her ikincil dizin dahili olarak PK'nın eşleştiği kayıtları bulmak için kullandığı bir kopyasını dahili olarak depolar.

Tabloların "küçük" olması gerektiğini söylüyorsunuz (20 satır gerçekten çok küçük). İnnodb_buffer_pool_size değerini eşit olacak şekilde ayarlamak için yeterli RAM'iniz varsa

select sum(data_length+index_length) from information_schema.tables where engine='innodb';

Öyleyse bunu yap ve muhtemelen güzel oturacaksın. Genel bir kural olarak, diğer mysql ek yükü ve önbellek için toplam sistem belleğinin en az% 30 -% 40'ını bırakmak istersiniz. Ve bu onun özel bir DB sunucusu olduğu varsayılıyor. Sistemde çalışan başka şeyler varsa, gereksinimlerini de dikkate almanız gerekir.


1

@Atxdba yanıtına ek olarak - sayısal alanın disk alanı için neden daha iyi olacağını açıklayan iki nokta eklemek istedim:

  1. Sorunlar tablonuz VARCHAR FK tabanlıysa ve diyelim ki 20 küçük VARCHAR (32) FK'niz varsa, kaydınız 20x32bayt uzunluğa ulaşabilirken, belirttiğiniz gibi diğer tablolar arama tablolarıdır, bu nedenle INT FK, TINYINT FK olabilir. 20 alan için 20 baytlık bir kayıt. Birkaç yüz kayıt için çok fazla değişmeyeceğini biliyorum ama birkaç milyona ulaştığınızda yerden tasarruf etmeyi takdir edersiniz

  2. Hız sorunu için ben kaplama dizinleri kullanmayı düşünecektim, bu sorgu için göründüğü gibi arama tablolarından çok fazla miktarda veri dizin kapsayan gitmek için gidip bir kez daha VARCHAR FK / W / COVERING ile sağlanan testi yapmak değil ENDEKS VE düzenli INT FK.

Umarım yardımcı olabilir,

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.