Hej, Habr! Jag presenterar översÀttningen av artikeln
.
NÀr det kommer till relationsdatabaser kan jag inte lÄta bli att tÀnka att det saknas nÄgot. De anvÀnds överallt. Det finns mÄnga olika databaser, frÄn den lilla och anvÀndbara SQLite till den kraftfulla Teradata. Men det finns bara nÄgra artiklar som förklarar hur en databas fungerar. Du kan sjÀlv söka pÄ "howdoesarelationaldatabasework" för att se hur fÄ resultat det finns. Dessutom Àr dessa artiklar korta. Om du letar efter de senaste trendteknologierna (BigData, NoSQL eller JavaScript) kommer du att hitta fler djupgÄende artiklar som förklarar hur de fungerar.
Ăr relationsdatabaser för gamla och trĂ„kiga för att förklara utanför universitetskurser, forskningsartiklar och böcker?

Som utvecklare avskyr jag att anvÀnda nÄgot jag inte förstÄr. Och om databaser har anvÀnts i mer Àn 40 Är mÄste det finnas en anledning. Under dessa Är har jag spenderat hundratals timmar pÄ att verkligen förstÄ dessa mÀrkliga svarta lÄdor som jag anvÀnder varje dag. Relationsdatabaser Àr mycket intressanta, för de Àr baserade pÄ anvÀndbara och ÄteranvÀndbara koncept.Om du Àr intresserad av att förstÄ databaser, men aldrig haft tid eller lust att grÀva ner dig i detta omfattande Àmne, kommer du att gilla den hÀr artikeln.
Ăven om titeln pĂ„ denna artikel Ă€r tydlig, Ă€r syftet med denna artikel inte att förstĂ„ hur man anvĂ€nder en databas.DĂ€rför borde du redan veta hur man skriver en enkel join-frĂ„ga och grundlĂ€ggande frĂ„gor; CRUDannars kanske du inte förstĂ„r den hĂ€r artikeln. Det Ă€r det enda du behöver veta, jag kommer att förklara allt annat.
Jag börjar med nÄgra grundlÀggande datavetenskaper, sÄsom tidskomplexitet av algoritmer (BigO). Jag vet att vissa av er avskyr detta koncept, men utan det kan du inte förstÄ nyanserna i databasen. Eftersom det Àr ett omfattande Àmne, kommer jag att fokusera pÄ det som jag anser Àr viktigt.: hur databasen hanterar SQL frÄgan.Jag kommer bara att presentera grundlÀggande koncept inom databaser,sÄ att du i slutet av artikeln har en uppfattning om vad som hÀnder under huven.
Eftersom detta Àr en lÄng och teknisk artikel som innehÄller mÄnga algoritmer och datastrukturer, ta dig tid att lÀsa den. Vissa begrepp kan vara svÄra att förstÄ; du kan hoppa över dem och ÀndÄ fÄ en allmÀn uppfattning.
För er som Àr mer insatta Àr denna artikel uppdelad i 3 delar:
- Ăversikt av lĂ„gnivĂ„- och högnivĂ„komponenter i databaser
- Ăversikt av queryoptimeringsprocessen
- Ăversikt av transaktionshantering och buffertpool
Tillbaka till grunderna
För mĂ„nga Ă„r sedan (i en avlĂ€gsen, avlĂ€gsen galaxâŠ) var utvecklare tvungna att exakt kĂ€nna till antalet operationer de kodade. De kunde sina algoritmer och datastrukturer utan och innan, eftersom de inte hade rĂ„d att slösa CPU och minne pĂ„ sina lĂ„ngsamma datorer.
I denna del kommer jag att pÄminna dig om nÄgra av dessa begrepp, eftersom de Àr nödvÀndiga för att förstÄ databaser. Jag kommer ocksÄ att introducera begreppet databasindex.
O(1) vs O(n2)
Idag bryr sig mÄnga utvecklare inte om tidskomplexiteten för algoritmer ⊠och de har rÀtt!
Men nÀr du hanterar stora mÀngder data (jag pratar inte om tusentals) eller om du kÀmpar för millisekunder, blir det kritiskt viktigt att förstÄ detta begrepp. Som du förstÄr mÄste databaser hantera bÄda situationerna! Jag kommer inte att lÄta dig spendera mer tid Àn nödvÀndigt för att fÄnga essensen. Detta kommer att hjÀlpa oss senare att förstÄ begreppet kostnadsbaserad (cost based optimization).
Konceptet
Tidskomplexiteten för en algoritm anvÀnds för att se hur lÄng tid det tar att köra algoritmen för en viss mÀngd data. För att beskriva denna komplexitet anvÀnds matematiska beteckningar av stor O. Denna notation anvÀnds med en funktion som beskriver hur mÄnga operationer algoritmen behöver för en given mÀngd indatan.
Till exempel, nÀr jag sÀger "denna algoritm har komplexitet O (some_function() )", betyder det att för att bearbeta en viss mÀngd data krÀver algoritmen some_function(a_certain_amount_of_data) operationer.
Följaktligen Det Àr viktigt inte mÀngden data**, utan hur ** antalet operationer ökar med ökande datamÀngd. Tidskomplexiteten ger inte ett exakt antal operationer, men det Àr ett bra sÀtt att uppskatta körtiden.

I den hÀr grafen kan du se förhÄllandet mellan antalet operationer och mÀngden indata för olika typer av tidskomplexitet. Jag anvÀnde en logaritmisk skala för att visa dem. Med andra ord ökar mÀngden data snabbt frÄn 1 till 1 miljard. Vi kan se att:
- O(1) eller konstant komplexitet förblir konstant (annars skulle det inte kallas konstant komplexitet).
- O(log(n)) förblir lÄg Àven med miljarder data.
- Den vÀrsta komplexiteten Àr - O(n2), dÀr antalet operationer vÀxer snabbt.
- De tvÄ andra komplexiteterna ökar ocksÄ snabbt.
Exempel
Vid smÄ datamÀngder Àr skillnaden mellan O(1) och O(n2) obetydlig. Anta till exempel att du har ett algoritm som behöver bearbeta 2000 element.
- Algoritm O(1) kostar dig 1 operation
- Algoritm O(log(n)) kostar dig 7 operationer
- Algoritm O(n) kostar dig 2 000 operationer
- Algoritm O(n * log(n)) kostar dig 14 000 operationer
- Algoritm O(n2) kostar dig 4 000 000 operationer
Skillnaden mellan O(1) och O(n2) verkar stor (4 miljoner operationer) men du kommer att förlora högst 2 ms, bara tiden för att blinka. Faktiskt kan moderna processorer bearbeta . Det Àr dÀrför prestanda och optimering inte Àr ett problem i mÄnga IT-projekt.
Som jag redan nÀmnt Àr det fortfarande viktigt att förstÄ detta koncept nÀr man arbetar med stora mÀngder data. Om algoritmen den hÀr gÄngen mÄste bearbeta 1 000 000 element (vilket inte Àr sÄ mycket för en databas):
- Algoritm O(1) kostar dig 1 operation
- Algoritm O(log(n)) kostar dig 14 operationer
- Algoritm O(n) kostar dig 1 000 000 operationer
- Algoritm O(n * log(n)) kostar dig 14 000 000 operationer
- Algoritm O(n2) kostar dig 1 000 000 000 000 operationer
Jag har inte gjort nÄgra berÀkningar, men jag skulle sÀga att med algoritm O(n2) har du tid att ta en kaffe (till och med tvÄ!). Om du lÀgger till ytterligare 0 till datamÀngden har du tid att ta en tupplur.
LÄt oss gÄ djupare
För referens:
- Sökning i en bra hash-tabell hittar elementet pÄ O(1).
- Sökning i ett vÀlbalanserat trÀd ger resultat pÄ O(log(n)).
- Sökning i en array ger resultat pÄ O(n).
- De bÀsta sorteringsalgoritmerna har komplexiteten O(n * log(n)).
- En dÄlig sorteringsalgoritm har komplexiteten O(n2).
Obs: i kommande delar kommer vi att se dessa algoritmer och datatyper.
Det finns flera typer av tidskomplexitet för algoritmer:
- medelklassens scenario
- bÀsta möjliga utfall
- och vÀrsta scenariot
Tidskomplexiteten Àr ofta worst-case scenariot.
Jag har bara pratat om tidskomplexiteten för algoritmen, men komplexitet gÀller Àven för:
- minnesförbrukningen för algoritmen
- disk I/O som algoritmen anvÀnder
SjĂ€lvklart finns det komplexiteter som Ă€r vĂ€rre Ă€n nÂČ, exempelvis:
- nâŽ: det Ă€r fruktansvĂ€rt! NĂ„gra av de nĂ€mnda algoritmerna har en sĂ„dan komplexitet.
- 3n: det Àr Ànnu vÀrre! En av algoritmerna som vi kommer att se mitt i den hÀr artikeln har denna komplexitet (och den anvÀnds faktiskt i mÄnga databaser).
- fakultet n: du kommer aldrig att fÄ dina resultat ens med en liten mÀngd data.
- n˹: om du stöter pÄ denna komplexitet, bör du frÄga dig sjÀlv om detta verkligen Àr ditt omrÄde ...
Obs: jag gav dig inte en verklig definition av beteckningen 'stor O', utan bara en idé. Du kan lÀsa den hÀr artikeln för den verkliga (asymptotiska) definitionen.
MergeSort (Sammanslagningssortering)
Vad gör du nÀr du behöver sortera en samling? Vad? Du anropar funktionen sort() ... Okej, bra svar ... Men för databaser mÄste du förstÄ hur den hÀr funktionen sort() fungerar.
Det finns flera bra sorteringsalgoritmer, sÄ jag kommer att fokusera pÄ den viktigaste: sammanslagningssorteringen. Du kanske inte förstÄr varför datorsortering Àr anvÀndbart just nu, men du kommer att förstÄ det efter delen som handlar om optimering av frÄgor. Dessutom kommer förstÄelsen av sammanslagningssortering att hjÀlpa oss senare att förstÄ den allmÀnna operationen join i databaser, som kallas merge join (sammanslagningsjoin).
Merge (slÄ ihop)
Som mÄnga anvÀndbara algoritmer bygger sammanslagningssortering pÄ en finess: att slÄ samman 2 sorterade arrayer med storleken N / 2 till en sorterad array med N element kostar bara N operationer. Denna operation kallas sammanslagning.
LÄt oss titta pÄ vad det hÀr betyder genom ett enkelt exempel:

I denna bild ser du att för att bygga den slutliga sorterade arrayen med 8 element behöver du bara utföra en iteration över 2 st 4-elementarrayer. Eftersom bÄda 4-elementarrayerna redan Àr sorterande:
- 1) du jÀmför de aktuella elementen i de tvÄ arrayerna (i början Àr den aktuella = första)
- 2) ta sedan tar du den minsta för att placera den i en array med 8 element
- 3) och gÄr vidare till nÀsta element i arrayen dÀr du tog den minsta elementet
- och upprepa 1, 2, 3, tills du kommer till det sista elementet i en av arrayerna.
- Sedan tar du de ÄterstÄende elementen frÄn den andra arrayen för att placera dem i en array med 8 element.
Detta fungerar eftersom bÄda 4-element arrayerna Àr sorterade, och dÀrför behöver du inte 'gÄ tillbaka' i dessa arrayer.
Nu nÀr vi förstÄtt detta trick, hÀr Àr min pseudokod för merge:
array mergeSort(array a)
if(length(a)==1)
return a[0];
end if
//rekursiva anrop
[left_array right_array] := split_into_2_equally_sized_arrays(a);
array new_left_array := mergeSort(left_array);
array new_right_array := mergeSort(right_array);
//sammanfogar de 2 smÄ sorterade arrayerna till en stor
array result := merge(new_left_array,new_right_array);
return result;Merge sort delar upp problemet i mindre problem, och hittar sedan resultaten av de mindre problemen för att fÄ resultatet av det ursprungliga problemet (notera: denna typ av algoritmer kallas dela och hÀrska). Om du inte förstÄr denna algoritm, oroa dig inte; jag förstod det inte första gÄngen jag sÄg det. Om det kan hjÀlpa dig, ser jag denna algoritm som en tvÄfas algoritm:
- Delningsfas, dÀr arrayen delas upp i mindre arrayer
- Sorteringsfas, dÀr smÄ arrayer sammanfogas (anvÀnder merge) för att bilda en större array.
Delningsfas (Division phase)

I delningsfasen delas arrayen upp i enhetsarrayer i 3 steg. Det formella antalet steg Àr log(N) (eftersom N=8, log(N) = 3).
Hur vet jag detta?
Jag Ă€r ett geni! Kort sagt â matematik. IdĂ©n Ă€r att varje steg minskar storleken pĂ„ den ursprungliga arrayen med hĂ€lften. Antalet steg Ă€r det antal gĂ„nger du kan dela den ursprungliga arrayen i tvĂ„. Detta Ă€r den exakta definitionen av logaritmen (med bas 2).
Sorteringsfas (Sorting phase)

I sorteringsfasen börjar du med enhetsarrayer (enskilda element). Under varje steg tillÀmpar du ett antal sammanslagningsoperationer, och den totala kostnaden Àr N = 8 operationer:
- I det första steget har du 4 sammanslagningar som kostar 2 operationer vardera
- I det andra steget har du 2 sammanslagningar som kostar 4 operationer vardera
- I det tredje steget har du 1 sammanslagning som kostar 8 operationer
Eftersom det finns log(N) steg, den totala kostnaden N * log(N) operationer.
Fördelar med merge sort
Varför Àr denna algoritm sÄ kraftfull?
För att:
- Du kan Àndra den för att minska minnesanvÀndningen, sÄ att du inte skapar nya arrayer, utan direkt Àndrar den inmatade arrayen.
Observera: denna typ av algoritmer kallas (sortering utan extra minne).
- Du kan Àndra den för att anvÀnda diskutrymme och ett litet minnesutrymme utan stora kostnader för disk in / ut. Idén Àr att ladda i minnet endast de delar som för nÀrvarande bearbetas. Detta Àr viktigt nÀr du behöver sortera en tabell som Àr flera gigabyte stor med en minnesbuffer pÄ bara 100 megabyte.
Observera: denna typ av algoritmer kallas .
- Du kan Àndra den för att köras pÄ flera processer / trÄdar / servrar.
Till exempel Àr distribuerad sammanslagning av sortering en av nyckelkomponenterna (som Àr en struktur inom stora data).
- Denna algoritm kan förvandla bly till guld (verkligen!).
Denna sorteringsalgoritm anvÀnds i de flesta (om inte alla) databaser, men den Àr inte den enda. Om du vill veta mer kan du lÀsa detta , som diskuterar för- och nackdelar med de vanligaste sorteringsalgoritmerna i databaser.
Array, TrÀd och Hash-Tabell
Nu nÀr vi förstÄr idén om tidskomplexitet och sortering, mÄste jag berÀtta om 3 datatyper. Detta Àr viktigt eftersom de Àr grunden för moderna databaser. Jag kommer ocksÄ att introducera begreppet databasindex.
Array
En tvÄdimensionell array Àr den enklaste datatypen. En tabell kan ses som en array. Till exempel:

Denna 2D-array representerar en tabell med rader och kolumner:
- Varje rad representerar en enhet
- Kolumnerna lagrar egenskaper som beskriver enheten.
- Varje kolumn lagrar data av en viss typ (heltal, strÀng, datum ...).
Det Àr sÄ bekvÀmt att lagra och visualisera data, men nÀr du behöver hitta ett visst vÀrde fungerar det inte.
Till exempel, om du vill hitta alla killar som arbetar i Storbritannien mĂ„ste du gĂ„ igenom varje rad för att avgöra om den raden tillhör Storbritannien. Det kommer att kosta dig N operationer, dĂ€r N â antal rader, vilket Ă€r bra, men kan det finnas en snabbare vĂ€g? Nu Ă€r det dags att bekanta oss med trĂ€d.
Observera: de flesta moderna databaser erbjuder utökade matriser för effektiv lagring av tabeller: heap-organized tables och index-organized tables. Men det löser inte problemet med snabb sökning av ett specifikt villkor i en grupp av kolumner.
DatabastrÀd och index
Ett binÀrt söktrÀd Àr ett binÀrt trÀd med en speciell egenskap, nyckeln i varje nod mÄste vara:
- större Àn alla nycklar som lagras i den vÀnstra undertrÀdet
- mindre Àn alla nycklar som lagras i den högra undertrÀdet
LÄt oss se vad detta betyder visuellt
Tanken

Detta trÀd har N = 15 element. Anta att jag letar efter 208:
- Jag börjar med roten, vars nyckel Àr 136. Eftersom 136<208, tittar jag pÄ den högra undertrÀdet av noden 136.
- 398>208, dÀrför tittar jag pÄ den vÀnstra undertrÀdet av noden 398
- 250>208, dÀrför tittar jag pÄ den vÀnstra undertrÀdet av noden 250
- 200<208, dÀrför tittar jag pÄ den högra undertrÀdet av noden 200. Men 200 har inget högra undertrÀd, vÀrdet existerar inte (eftersom om det fanns, skulle det vara i det högra undertrÀdet av 200).
Nu, lÄt oss sÀga att jag letar efter 40
- Jag börjar med roten, vars nyckel Àr 136. Eftersom 136 > 40, tittar jag pÄ den vÀnstra undertrÀdet av noden 136.
- 80 > 40, dÀrför tittar jag pÄ den vÀnstra undertrÀdet av noden 80
- 40= 40, noden existerar. Jag hÀmtar radens identifierare inom noden (detta finns inte pÄ bilden) och tittar i tabellen för den angivna radidentifieraren.
- Genom att kÀnna till radens identifierare kan jag veta exakt var data finns i tabellen, och dÀrför kan jag fÄ Ätkomst till dem omedelbart.
I slutÀndan kommer bÄda sökningarna att kosta mig antal nivÄer inom trÀdet. Om du noggrant lÀser delen om merge sort, bör du se att det finns log (N) nivÄer hÀr. Det visar sig att sökkostnaden Àr log(N), inte dÄligt!
à terkommer till vÄrt problem
Men detta Àr mycket abstrakt, sÄ lÄt oss ÄtergÄ till vÄrt problem. IstÀllet för en enkel integer, tÀnk er en strÀng som representerar nÄgon landsnamn i den tidigare tabellen. Anta att du har ett trÀd som innehÄller fÀltet "country" (kolumn 3) i tabellen:
- Om du vill veta vem som arbetar i Storbritannien
- tittar du pÄ trÀdet för att fÄ noden som representerar Storbritannien
- inuti "UKnode" hittar du platsen för arbetstagarnas poster i Storbritannien.
Denna sökning kommer att kosta log(N) operationer istÀllet för N operationer om du anvÀnder en array direkt. Det du just har presenterat var databasindex.
Du kan bygga ett indextrĂ€d för vilken grupp av fĂ€lt som helst (strĂ€ng, tal, 2 strĂ€ngar, tal och strĂ€ng, datumâŠ) sĂ„ lĂ€nge du har en funktion för att jĂ€mföra nycklar (dvs. grupper av fĂ€lt) sĂ„ att du kan stĂ€lla in ordningen bland nycklarna (vilket gĂ€ller för alla grundlĂ€ggande typer i databasen).
B+TreeIndex
Ăven om detta trĂ€d fungerar bra för att fĂ„ ett specifikt vĂ€rde, finns det ett STORT problem nĂ€r du behöver hĂ€mta flera element mellan tvĂ„ vĂ€rden. Det kommer att kosta O(N) eftersom du mĂ„ste titta pĂ„ varje nod i trĂ€det och kontrollera om den ligger mellan dessa tvĂ„ vĂ€rden (t.ex. med en ordnad genomgĂ„ng av trĂ€det). Dessutom Ă€r denna operation inte praktisk för disk I/O eftersom du mĂ„ste lĂ€sa hela trĂ€det. Vi behöver hitta ett sĂ€tt att effektivt utföra intervalförfrĂ„gan. För att lösa detta problem anvĂ€nder moderna databaser en modifierad version av det tidigare trĂ€det som kallas B+Tree. I B+Tree trĂ€det:
- endast de lÀgsta noderna (blad) lagrar information (platsen för rader i den relaterade tabellen)
- de andra noderna finns hÀr för att dirigera till rÀtt nod under sökningen.

Som du kan se finns det fler noder hÀr (dubbelt sÄ mÄnga). Faktiskt har du ytterligare noder, "beslutsnoder", som hjÀlper dig att hitta rÀtt nod (som lagrar platsen för raderna i den relaterade tabellen). Men sökningens komplexitet Àr fortfarande O(log(N)) (det finns bara en nivÄ till). Den stora skillnaden Àr att noderna pÄ den lÀgre nivÄn Àr kopplade till sina eftertrÀdare.
Med detta B+Tree, om du söker efter vÀrden mellan 40 och 100:
- Du behöver bara söka efter 40 (eller det nÀrmaste vÀrdet efter 40, om 40 inte finns), precis som du gjorde med det tidigare trÀdet.
- Samla sedan in eftertrÀdarna till 40 genom att anvÀnda direkta lÀnkar till eftertrÀdarna tills du nÄr 100.
Antag att du hittade M eftertrÀdare, och trÀdet har N noder. Att söka efter en specifik nod kostar log(N) precis som det tidigare trÀdet. Men efter att ha fÄtt denna nod fÄr du M eftertrÀdare i M operationer med lÀnkar till deras eftertrÀdare. Denna sökning kostar bara M+log(N) operationer jÀmfört med N operationer med det tidigare trÀdet. Dessutom behöver du inte lÀsa hela trÀdet (bara M + log (N) noder), vilket innebÀr mindre diskanvÀndning. Om M Àr litet (till exempel 200 rader) och N stort (1 000 000 rader) kommer detta att göra en STOR skillnad.
Men hÀr finns nya problem (igen!). Om du lÀgger till eller tar bort en rad i databasen (och dÀrmed i den relaterade B+Tree-indexet):
- du mÄste upprÀtthÄlla ordningen mellan noderna inom B+Tree, annars kan du inte hitta noder inom ett osorterat trÀd.
- du mÄste behÄlla det minimi möjliga antalet nivÄer i B+Tree, annars kommer tidskomplexiteten i O(log(N)) att bli O(N).
Med andra ord mÄste B+Tree vara sjÀlvordnande och balanserad. Lyckligtvis Àr detta möjligt med smarta borttagnings- och insÀttningsoperationer. Men detta kostar mycket: insÀttning och borttagning i B+ trÀdet kostar i O(log(N)). Det Àr dÀrför vissa av er har hört att anvÀnda för mÄnga index inte Àr en bra idé.Verkligen, du saktar ner snabb insÀttning/uppdatering/borttagning av en rad i tabellen, eftersom databasen mÄste uppdatera tabellens index med den dyra operationen O(log(N)) för varje index. Dessutom innebÀr tillÀgg av index en större belastning för transaktionshanteraren (kommer att beskrivas i slutet av artikeln).
För mer information kan du se artikeln pÄ Wikipedia om . Om du vill ha ett exempel pÄ implementering av B+Tree i databasen, kolla och frÄn huvudutvecklaren av MySQL. De Àr bÄda inriktade pÄ hur InnoDB (MySQL-motorn) hanterar index.
Obs: lÀsaren sa mig att pÄ grund av lÄgnivÄoptimeringar mÄste B+ trÀdet vara helt balanserat.
Hash-tabell
VÄr sista viktiga datastruktur Àr hash-tabellen. Den Àr mycket anvÀndbar nÀr du vill snabbt söka efter vÀrden. Dessutom hjÀlper förstÄelsen av hash-tabellen oss senare att förstÄ en allmÀn databasinoperation som kallas hash-join ( hash join). Denna datastruktur anvÀnds ocksÄ av databasen för att lagra vissa interna saker (till exempel, lÄsnings-tabell eller bufferpool, vi kommer att se bÄda dessa begrepp senare).
En hash-tabell Àr en databasstruktur som snabbt hittar ett element utifrÄn dess nyckel. För att skapa en hash-tabell mÄste du definiera:
- nyckel för dina element
- hash-funktion för nycklarna. BerÀknade hashes av nycklarna ger platsen för elementen (kallade segment) ).
- en funktion för att jÀmföra nycklar.NÀr du har hittat rÀtt segment mÄste du hitta elementet du söker inuti segmentet med hjÀlp av denna jÀmförelse.
Ett enkelt exempel
LÄt oss ta ett illustrativt exempel:

Denna hash-tabell har 10 segment. Eftersom jag Àr lat, har jag ritat endast 5 segment, men jag vet att ni Àr smarta, sÄ jag lÄter er förestÀlla er de andra 5 sjÀlva. Jag anvÀnde en hash-funktion med mod 10 pÄ nyckeln. Med andra ord, jag behÄller endast sista siffran av elementets nyckel för att hitta dess segment:
- om den sista siffran Àr 0, gÄr elementet till segment 0,
- om den sista siffran Àr 1, gÄr elementet till segment 1,
- om den sista siffran Àr 2, gÄr elementet till segment 2,
- âŠ
JÀmförelsefunktionen jag anvÀnde Àr helt enkelt jÀmlikhet mellan tvÄ heltal.
Anta att du vill hÀmta elementet 78:
- Hash-tabellen berÀknar hash-koden för 78, vilket Àr 8.
- Hash-tabellen letar i segment 8, och det första elementet den hittar Àr 78.
- Den returnerar elementet 78 till dig.
- Sökningen kostade bara 2 operationer (en för att berÀkna vÀrdet av hash-funktionen och en annan för att hitta elementet inuti segmentet).
Nu, lÄt oss sÀga att du vill hÀmta elementet 59:
- Hash-tabellen berÀknar hash-koden för 59, vilket Àr 9.
- Hash-tabellen letar i segment 9, det första hittade elementet Àr 99. Eftersom 99!=59 Àr elementet 99 inte det rÀtta elementet.
- Med samma logik tas det andra elementet (9), det tredje (79), âŠ, och sĂ„ vidare tills det sista (29).
- Elementet hittades inte.
- Sökningen kostade 7 operationer..
En bra hash-funktion
Som ni ser, beroende pÄ det vÀrde ni söker, Àr kostnaden inte densamma!
Om jag nu Àndrar hash-funktionen till modul 1 000 000 av nyckeln (det vill sÀga, tar de senaste 6 siffrorna), kommer den andra sökningen bara kosta 1 operation, eftersom det i segment 000059 inte finns nÄgra element. Den verkliga uppgiften Àr att hitta en bra hash-funktion som skapar segment som innehÄller ett mycket litet antal element..
I mitt exempel Àr det lÀtt att hitta en bra hash-funktion. Men det Àr ett enkelt exempel, att hitta en bra hash-funktion Àr svÄrare nÀr nyckeln Àr:
- en strÀng (till exempel - efternamn)
- 2 strÀngar (till exempel - efternamn och förnamn)
- 2 strÀngar och ett datum (till exempel - efternamn, förnamn och födelsedatum)
- âŠ
Med en bra hash-funktion kostar uppslagning i hash-tabellen O(1).
Array vs hash-tabell
Varför inte anvÀnda en array?
Hmm, bra frÄga.
- Hash-tabellen kan vara delvis inlÀst i minnet, medan andra segment kan kvarstÄ pÄ disken.
- Med en array mÄste du anvÀnda ett sammanhÀngande utrymme i minnet. Om du laddar en stor tabell Àr det mycket svÄrt att hitta tillrÀckligt sammanhÀngande utrymme.
- För en hash-tabell kan du vÀlja nyckel (till exempel, landet och personens efternamn).
För ytterligare information kan du lÀsa artikeln om , som Àr en effektiv implementation av hash-tabellen; du behöver inte förstÄ Java för att förstÄ de koncept som beskrivs i den hÀr artikeln.
KĂ€lla: habr.com
