it-swarm-eu.dev

Mysql int vs varchar jako primární klíč (InnoDB Storage Engine?

Vytvářím webovou aplikaci (systém pro správu projektů) a přemýšlel jsem o tom, když dojde na výkon.

Mám uvnitř tabulky problémů a existuje 12 cizích klíčů, které odkazují na různé jiné tabulky. z nich, 8 z nich bych se musel připojit, abych získal titulní pole z ostatních tabulek, aby záznam měl nějaký smysl ve webové aplikaci, ale pak to znamená udělat 8 spojení, která se zdají být opravdu nadměrná, zvláště proto, že se jen stahuji 1 pole pro každou z těchto spojení.

Teď mi bylo také řečeno, abych použil primární inkrementační primární klíč (pokud není sharding problém, v tom případě bych měl použít GUID) z důvodů trvalosti, ale jak špatné je použít výkon varchar (maximální délka 32) moudrý? Myslím, že většina z těchto tabulek pravděpodobně nebude mít u mnoha záznamů (většina z nich by měla být pod 20). Také, pokud použiji titul jako primární klíč, nebudu muset dělat připojení 95% času, takže za 95% sql by dokonce došlo k nějakému zásahu výkonu (myslím). Jedinou nevýhodou, na kterou mohu myslet, je to, že mám, že budu mít větší využití místa na disku (ale den je to opravdu hodně).

Důvod, proč používám vyhledávací tabulky pro mnoho těchto věcí místo enumů, je ten, že potřebuji, aby všechny tyto hodnoty byly konfigurovatelné koncovým uživatelem prostřednictvím samotné aplikace.

Jaké jsou nevýhody používání varchar jako primárního klíče pro tabulku, s výjimkou, že existuje mnoho záznamů?

AKTUALIZACE - Některé testy

Takže jsem se rozhodl udělat nějaké základní testy na tyto věci. Mám 100 000 záznamů a jedná se o základní dotazy:

Základní dotaz VARCHAR FK

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

Základní INT FK dotaz

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

Také jsem spustil tyto dotazy s následujícími dodatky:

  • Vyberte konkrétní položku (kde i.key = 43298)
  • Seskupit i.id
  • Řadit podle (it.title pro int FK, i.issueTypeId pro varchar FK)
  • Limit (50000, 100)
  • Seskupte a omezte dohromady
  • Seskupte, objednávejte a omezujte dohromady

Výsledky pro tyto případy:

TYP TYPU: VARCHAR FK TIME/INT FK TIME


Základní dotaz: ~ 4ms/~ 52ms

Vyberte konkrétní položku: ~ 140ms/~ 250ms

Seskupit podle i.id: ~ 4ms/~ 2.8sec

Řadit podle: ~ 231 ms/~ 2 s

Limit: ~ 67ms/~ 343ms

Seskupte a omezte společně: ~ 504 ms/~ 2 s

Seskupte, objednávejte a omezujte společně: ~ 504 ms/~ 2,3 sekundy

Teď nevím, jakou konfiguraci bych mohl udělat, aby byl jeden nebo druhý (nebo oba) rychlejší, ale zdá se, že VARCHAR FK vidí rychlejší dotazy na data (někdy mnohem rychlejší).

Myslím, že si musím vybrat, zda toto zvýšení rychlosti stojí za extra velikost dat/indexu.

13
ryanzec

Pro primární klíče dodržuji následující pravidla:

a) Neměly by mít žádný obchodní význam - měly by být zcela nezávislé na aplikaci, kterou vyvíjíte, proto jdu pro numerická čísla generovaná automaticky. Pokud však potřebujete další sloupce, aby byly jedinečné, vytvořte jedinečné indexy, které to podporují

b) Měl by hrát ve spojení - připojení k varcharům vs celá čísla je asi 2x až 3x pomalejší, jak roste délka primárního klíče, takže chcete mít své klíče jako celá čísla. Protože všechny počítačové systémy jsou binární, mám podezření, že se jeho řetězec změnil na binární, pak ve srovnání s ostatními, což je velmi pomalé

c) Použijte nejmenší možný datový typ - pokud očekáváte, že vaše tabulka bude mít jen velmi málo sloupců, řekněte 52 amerických států, pak použijte pro nejmenší možný kód, možná CHAR (2), pro 2místný kód, ale pořád bych si vybral droboun (128) pro sloupec vs velká hodnota, která může dosáhnout až 2 miliard

Také budete mít problém s kaskádováním vašich změn z primárních klíčů do ostatních tabulek, pokud se například změní název projektu (což není neobvyklé)

Vyhledejte sekvenční celá čísla pro automatické zvyšování primárních klíčů a získejte vestavěné účinnosti, které databázové systémy poskytují s podporou změn v budoucnosti

Ve vašich testech neporovnáváte rozdíl ve výkonu kláves Varchar vs int, ale spíše náklady na více připojení. Není divu, že dotazování 1 tabulky je rychlejší než připojení mnoha tabulek.
Jednou nevýhodou primárního klíče varchar je zvyšování velikosti indexu, jak atxdba poukázalo. I když vaše vyhledávací tabulka nemá žádné jiné indexy kromě PK (což je docela nepravděpodobné, ale možné), každá tabulka, která vyhledává odkazy, bude mít v tomto sloupci index.
Další špatnou věcí na přírodních primárních klíčích je to, že se jejich hodnota může změnit, což způsobuje mnoho kaskádových aktualizací. Ne všechny RDMS, například Oracle, vám dokonce umožňují mít on update cascade. Obecně je změna hodnoty primárního klíče považována za velmi špatnou praxi. Nechci říkat, že přírodní primární klíče jsou vždy zlé; pokud jsou hodnoty vyhledávání malé a nikdy se nemění, myslím, že to může být přijatelné.

Jednou z možností, kterou byste mohli zvážit, je implementace materializovaného pohledu. Mysql nepodporuje přímo, ale můžete dosáhnout požadované funkce s triggery na podkladové tabulky. Takže budete mít jednu tabulku, která obsahuje vše, co potřebujete k zobrazení. Pokud je výkon přijatelný, také se nemusíte potýkat s problémem, který v tuto chvíli neexistuje.

6
a1ex07

Největší nevýhodou je opakování PK. Poukázali jste na zvýšení využití místa na disku, ale je jasné, že větší velikost indexu je váš větší problém. Protože innodb je seskupený index, každý sekundární index interně ukládá kopii PK, kterou nakonec používá k nalezení odpovídajících záznamů.

Říkáte, že se očekává, že tabulky budou „malé“ (20 řádků je skutečně velmi malé). Pokud máte dost RAM) k nastavení velikosti innodb_buffer_pool_size na

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

Pak to udělejte a pravděpodobně budete sedět hezky. Obecně platí, že byste chtěli ponechat alespoň 30% - 40% celkové systémové paměti pro další režii a mezipaměť mysql. A to za předpokladu, že se jedná o vyhrazený DB server. Pokud máte na systému spuštěny další věci, musíte vzít v úvahu i jejich požadavky.

3
atxdba

Kromě odpovědi na @atxdba - která vám vysvětlila, proč by bylo použití číselné lepší pro místo na disku, chtěl bych přidat dva body:

  1. Pokud je vaše tabulka problémů založena na VARCHAR FK a řekněme, že máte 20 malých VARCHAR (32) FK, váš záznam může dosáhnout až 20x32bytes délky, zatímco jak jste zmínili, ostatní tabulky jsou vyhledávací tabulky, takže INT FK může být TINYINT FK, který pro 20 polí a 20 bajtů záznamů. Vím, že pro několik stovek záznamů se to moc nezmění, ale až se dostanete k několika milionům, myslím, že oceníte úsporu místa

  2. Pro problém s rychlostí bych uvažoval o použití krycích indexů, protože se zdá, že pro tento dotaz nezískáváte tolik dat z vyhledávacích tabulek, které bych použil pro krycí index, a znovu proveďte test, který jste poskytli s VARCHAR FK/W/COVERING INDEX A pravidelné INT FK.

Doufám, že to může pomoci,

1
Spredzy