SĂ„ fungerar relationsdatabaser (del 1)

Hej, Habr! Jag presenterar för din uppmÀrksamhet en översÀttning av artikeln
"Hur fungerar en relationsdatabas".

NÀr det kommer till relationsdatabaser kan jag inte lÄta bli att tycka att nÄgot saknas. De anvÀnds överallt. Det finns mÄnga olika databaser tillgÀngliga, frÄn den lilla och anvÀndbara SQLite till den kraftfulla Teradata. Men det finns bara ett fÄtal artiklar som förklarar hur databasen fungerar. Du kan söka sjÀlv med "howdoesarelationaldatabasework" för att se hur fÄ resultat det finns. Dessutom Àr dessa artiklar korta. Om du letar efter den senaste buzzy-tekniken (BigData, NoSQL eller JavaScript), hittar du mer djupgÄende artiklar som förklarar hur de fungerar.

Är relationsdatabaser för gamla och för trĂ„kiga för att förklaras utanför universitetskurser, forskningsrapporter och böcker?

SĂ„ fungerar relationsdatabaser (del 1)

Som utvecklare hatar 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 Ären har jag tillbringat hundratals timmar för att verkligen förstÄ dessa konstiga svarta lÄdor som jag anvÀnder varje dag. Relationsdatabaser mycket intressant eftersom de baserad pÄ anvÀndbara och ÄteranvÀndbara koncept. Om du Àr intresserad av att förstÄ en databas, men aldrig har haft tid eller lust att fördjupa dig i detta breda Àmne, bör du njuta av den hÀr artikeln.

Även om rubriken pĂ„ denna artikel Ă€r tydlig, Syftet med den hĂ€r artikeln Ă€r inte att förstĂ„ hur man anvĂ€nder databasen. DĂ€rav, du borde redan veta hur man skriver en enkel anslutningsförfrĂ„gan och grundlĂ€ggande frĂ„gor SKICK; annars kanske du inte förstĂ„r den hĂ€r artikeln. Det Ă€r det enda du behöver veta, jag ska förklara resten.

Jag börjar med nÄgra grunder inom datavetenskap, som algoritmernas tidskomplexitet (BigO). Jag vet att nÄgra av er hatar det hÀr konceptet, men utan det kommer ni inte att kunna förstÄ krÄngligheterna i databasen. Eftersom detta Àr ett stort Àmne, Jag ska fokusera pÄ vad jag tycker Àr viktigt: hur databasen bearbetar SQL förfrÄgan. Jag ska bara presentera grundlÀggande databaskonceptsÄ att du i slutet av artikeln har en uppfattning om vad som hÀnder under huven.

Eftersom det hÀr Àr en lÄng och teknisk artikel som involverar mÄnga algoritmer och datastrukturer, ta dig tid att lÀsa igenom den. Vissa begrepp kan vara svÄra att förstÄ; du kan hoppa över dem och fortfarande fÄ den allmÀnna uppfattningen.

För de mer kunniga bland er Àr den hÀr artikeln uppdelad i 3 delar:

  • Översikt över databaskomponenter pĂ„ lĂ„g och hög nivĂ„
  • Översikt över frĂ„geoptimeringsprocessen
  • Översikt över transaktions- och buffertpoolshantering

Tillbaka till grunderna

För Är sedan (i en galax lÄngt, lÄngt borta...) var utvecklare tvungna att veta exakt hur mÄnga operationer de kodade. De kunde sina algoritmer och datastrukturer utantill eftersom de inte hade rÄd att slösa bort processorn och minnet pÄ sina lÄngsamma datorer.

I den hÀr delen kommer jag att pÄminna dig om nÄgra av dessa begrepp eftersom de Àr viktiga för att förstÄ databasen. Jag kommer ocksÄ att presentera konceptet databasindex.

O(1) vs O(n2)

Nuförtiden bryr sig mÄnga utvecklare inte om tidskomplexiteten hos algoritmer... och de har rÀtt!

Men nÀr du har att göra med mycket data (jag pratar inte tusentals) eller om du kÀmpar pÄ millisekunder, blir det avgörande att förstÄ detta koncept. Och som du kan förestÀlla dig mÄste databaser hantera bÄda situationerna! Jag kommer inte att tvinga dig att spendera mer tid Àn nödvÀndigt för att fÄ fram poÀngen. Detta kommer att hjÀlpa oss att förstÄ konceptet med kostnadsbaserad optimering senare (kosta baserat optimering).

Koncept

Algoritmens tidskomplexitet anvÀnds för att se hur lÄng tid det tar att slutföra en algoritm för en given mÀngd data. För att beskriva denna komplexitet anvÀnder vi matematisk notation med stor O. Denna notation anvÀnds med en funktion som beskriver hur mÄnga operationer en algoritm behöver för ett givet antal ingÄngar.

Till exempel, nÀr jag sÀger "den hÀr algoritmen har komplexitet O(some_function())", betyder det att algoritmen krÀver some_function(a_certain_amount_of_data) operationer för att bearbeta en viss mÀngd data.

I detta fall, Det Àr inte mÀngden data som spelar roll**, annars ** hur antalet operationer ökar med ökande datavolym. Tidskomplexitet ger inte ett exakt antal operationer, men Àr ett bra sÀtt att uppskatta exekveringstiden.

SĂ„ fungerar relationsdatabaser (del 1)

I den hÀr grafen kan du se antalet operationer kontra mÀngden indata för olika typer av algoritmtidskomplexitet. Jag anvÀnde en logaritmisk skala för att visa dem. Med andra ord, mÀngden data ökar 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.
  • VĂ€rsta svĂ„righeten - O(n2), dĂ€r antalet operationer vĂ€xer snabbt.
  • De andra tvĂ„ komplikationerna ökar lika snabbt.

ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹

Med en liten mÀngd data Àr skillnaden mellan O(1) och O(n2) försumbar. LÄt oss till exempel sÀga att du har en algoritm som behöver bearbeta 2000 element.

  • O(1)-algoritmen kommer att kosta dig 1 operation
  • O(log(n))-algoritmen kommer att kosta dig 7 operationer
  • O(n)-algoritmen kommer att kosta dig 2 000 operationer
  • O(n*log(n))-algoritmen kommer att kosta dig 14 000 operationer
  • O(n2)-algoritmen kommer att kosta dig 4 000 000 operationer

Skillnaden mellan O(1) och O(n2) verkar stor (4 miljoner operationer) men du kommer att förlora maximalt 2 ms, bara tid att blinka med ögonen. Moderna processorer kan faktiskt bearbeta hundratals miljoner operationer per sekund. Det Àr dÀrför som prestanda och optimering inte Àr ett problem i mÄnga IT-projekt.

Det Àr som sagt fortfarande viktigt att kÀnna till detta koncept nÀr man arbetar med enorma 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):

  • O(1)-algoritmen kommer att kosta dig 1 operation
  • O(log(n))-algoritmen kommer att kosta dig 14 operationer
  • O(n)-algoritmen kommer att kosta dig 1 000 000 operationer
  • O(n*log(n))-algoritmen kommer att kosta dig 14 000 000 operationer
  • O(n2)-algoritmen kommer att kosta dig 1 000 000 000 000 operationer

Jag har inte rÀknat ut, men jag skulle sÀga att med O(n2)-algoritmen hinner du dricka en kaffe (Àven tvÄ!). LÀgger du till ytterligare 0 till datavolymen kommer du att hinna ta en tupplur.

LÄt oss gÄ djupare

För din information:

  • En bra hashtabellsökning hittar ett element i O(1).
  • Att söka i ett vĂ€lbalanserat trĂ€d ger resultat i O(log(n)).
  • Sökning i en array ger resultat i O(n).
  • De bĂ€sta sorteringsalgoritmerna har komplexiteten O(n*log(n)).
  • En dĂ„lig sorteringsalgoritm har komplexiteten O(n2).

Notera: I följande delar kommer vi att se dessa algoritmer och datastrukturer.

Det finns flera typer av algoritmens tidskomplexitet:

  • genomsnittligt fallscenario
  • bĂ€sta fallet
  • och vĂ€rsta scenariot

Tidskomplexitet Àr ofta det vÀrsta scenariot.

Jag pratade bara om tidskomplexiteten för algoritmen, men komplexiteten gÀller Àven för:

  • minnesförbrukning av algoritmen
  • disk I/O-förbrukningsalgoritm

Naturligtvis finns det komplikationer vÀrre Àn n2, till exempel:

  • n4: det hĂ€r Ă€r hemskt! NĂ„gra av de nĂ€mnda algoritmerna har denna komplexitet.
  • 3n: det hĂ€r Ă€r Ă€nnu vĂ€rre! En av algoritmerna vi kommer att se i mitten av den hĂ€r artikeln har denna komplexitet (och den anvĂ€nds faktiskt i mĂ„nga databaser).
  • factorial n: du kommer aldrig att fĂ„ dina resultat Ă€ven med en liten mĂ€ngd data.
  • nn: Om du stöter pĂ„ denna komplexitet bör du frĂ„ga dig sjĂ€lv om detta verkligen Ă€r ditt verksamhetsomrĂ„de...

Notera: Jag gav dig inte den faktiska definitionen av den stora O-beteckningen, bara en idé. Du kan lÀsa den hÀr artikeln pÄ Wikipedia för den verkliga (asymptotiska) definitionen.

MergeSort

Vad gör du nÀr du behöver sortera en samling? Vad? Du anropar sort()-funktionen... Ok, bra svar... Men för en databas mÄste du förstÄ hur den hÀr sort()-funktionen fungerar.

Det finns flera bra sorteringsalgoritmer, sÄ jag ska fokusera pÄ det viktigaste: slÄ samman sortering. Du kanske inte förstÄr varför det Àr anvÀndbart att sortera data just nu, men du bör göra det efter frÄgeoptimeringsdelen. Dessutom kommer förstÄelsen av merge sort att hjÀlpa oss att senare förstÄ den gemensamma databasanslutningsoperationen som kallas sammanfoga delta (fusionsförening).

Sammanfoga

Liksom mÄnga anvÀndbara algoritmer förlitar sig sammanslagningssortering pÄ ett knep: att kombinera 2 sorterade arrayer av storlek N/2 till en N-elementsorterad array kostar bara N operationer. Denna operation kallas sammanslagning.

LĂ„t oss se vad detta betyder med ett enkelt exempel:

SĂ„ fungerar relationsdatabaser (del 1)

Den hÀr figuren visar att för att bygga den slutliga sorterade 8-elements arrayen behöver du bara iterera en gÄng över de 2 4-element arrayerna. Eftersom bÄda arrayerna med 4 element redan Àr sorterade:

  • 1) du jĂ€mför bĂ„da aktuella elementen i tvĂ„ arrayer (i början ström = först)
  • 2) ta sedan den minsta för att sĂ€tta den i en 8 elementarray
  • 3) och flytta till nĂ€sta element i arrayen dĂ€r du tog det minsta elementet
  • och upprepa 1,2,3 tills du nĂ„r det sista elementet i en av arrayerna.
  • Sedan tar du de Ă„terstĂ„ende elementen i den andra arrayen för att lĂ€gga dem i en 8 element array.

Detta fungerar eftersom bÄda 4-elements arrayer Àr sorterade och sÄ att du inte behöver "gÄ tillbaka" i dessa arrayer.

Nu nÀr vi förstÄr tricket, hÀr Àr min pseudokod för sammanfogning:

array mergeSort(array a)
   if(length(a)==1)
      return a[0];
   end if

   //recursive calls
   [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);

   //merging the 2 small ordered arrays into a big one
   array result := merge(new_left_array,new_right_array);
   return result;

Merge sort delar upp ett problem i mindre problem och hittar sedan resultatet av de mindre problemen för att fÄ resultatet av det ursprungliga problemet (obs: denna typ av algoritm kallas divide and conquer). 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 den hÀr algoritmen som en tvÄfasalgoritm:

  • Divisionsfas, dĂ€r arrayen Ă€r uppdelad i mindre arrayer
  • Sorteringsfasen Ă€r dĂ€r smĂ„ arrayer kombineras (med hjĂ€lp av union) för att bilda en större array.

Indelningsfas

SĂ„ fungerar relationsdatabaser (del 1)

I divisionssteget delas arrayen in i enhetliga arrayer i 3 steg. Det formella antalet steg Àr log(N) (eftersom N=8, log(N) = 3).

Hur vet jag detta?

Jag Àr ett geni! Med ett ord - matematik. Tanken Àr att varje steg delar storleken pÄ den ursprungliga arrayen med 2. Antalet steg Àr antalet gÄnger du kan dela den ursprungliga arrayen i tvÄ. Detta Àr den exakta definitionen av en logaritm (bas 2).

Sorteringsfas

SĂ„ fungerar relationsdatabaser (del 1)

I sorteringsfasen börjar man med enhetliga (single-element) arrayer. Under varje steg tillÀmpar du flera 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 sammanfogning som kostar 8 operationer

Eftersom det finns log(N)-steg, total kostnad N * log(N) operationer.

Fördelar med merge sort

Varför Àr denna algoritm sÄ kraftfull?

eftersom:

  • Du kan Ă€ndra det för att minska minnesfotavtrycket sĂ„ att du inte skapar nya matriser utan direkt modifierar inmatningsmatrisen.

Obs: denna typ av algoritm kallas in-plats (sortering utan extra minne).

  • Du kan Ă€ndra det sĂ„ att det anvĂ€nder diskutrymme och en liten mĂ€ngd minne samtidigt utan att det Ă„drar sig betydande disk I/O-overhead. Tanken Ă€r att bara ladda de delar som för nĂ€rvarande bearbetas i minnet. Detta Ă€r viktigt nĂ€r du behöver sortera en multi-gigabyte-tabell med endast en 100-megabyte minnesbuffert.

Obs: denna typ av algoritm kallas extern sortering.

  • Du kan Ă€ndra det sĂ„ att det körs pĂ„ flera processer/trĂ„dar/servrar.

Till exempel Àr distribuerad sammanslagning en av nyckelkomponenterna Hadoop (som Àr en struktur i big data).

  • Denna algoritm kan förvandla bly till guld (pĂ„ riktigt!).

Denna sorteringsalgoritm anvÀnds i de flesta (om inte alla) databaser, men den Àr inte den enda. Vill du veta mer kan du lÀsa detta forskningsarbete, som diskuterar för- och nackdelar med vanliga databassorteringsalgoritmer.

Array-, trÀd- och hashtabell

Nu nÀr vi förstÄr idén om tidskomplexitet och sortering, borde jag berÀtta om 3 datastrukturer. Detta Àr viktigt eftersom de Àr grunden för moderna databaser. Jag kommer ocksÄ att presentera konceptet databasindex.

array

En tvÄdimensionell array Àr den enklaste datastrukturen. En tabell kan ses som en array. Till exempel:

SĂ„ fungerar relationsdatabaser (del 1)

Denna 2-dimensionella array Àr en tabell med rader och kolumner:

  • Varje rad representerar en enhet
  • Kolumner lagrar egenskaper som beskriver entiteten.
  • Varje kolumn lagrar data av en specifik typ (heltal, strĂ€ng, datum...).

Detta Àr praktiskt för att lagra och visualisera data, men nÀr du behöver hitta ett specifikt vÀrde Àr detta inte lÀmpligt.

Om du till exempel vill hitta alla killar som jobbar i Storbritannien, mÄste du titta pÄ varje rad för att avgöra om den raden tillhör Storbritannien. Det kommer att kosta dig N transaktionervar N - antal rader, vilket inte Àr dÄligt, men kan det finnas ett snabbare sÀtt? Nu Àr det dags för oss att bekanta oss med trÀden.

Obs: De flesta moderna databaser tillhandahÄller utökade arrayer för att lagra tabeller effektivt: heap-organized-tabeller och index-organized-tabeller. Men detta Àndrar inte problemet med att snabbt hitta ett specifikt tillstÄnd i en grupp kolumner.

DatabastrÀd och index

Ett binÀrt söktrÀd Àr ett binÀrt trÀd med en speciell egenskap, nyckeln vid varje nod mÄste vara:

  • större Ă€n alla nycklar lagrade i det vĂ€nstra undertrĂ€det
  • mindre Ă€n alla nycklar lagrade i det högra undertrĂ€det

LĂ„t oss se vad detta betyder visuellt

Idé

SĂ„ fungerar relationsdatabaser (del 1)

Detta trÀd har N = 15 element. LÄt oss sÀga att jag letar efter 208:

  • Jag börjar pĂ„ roten vars nyckel Ă€r 136. Sedan 136<208 tittar jag pĂ„ det högra undertrĂ€det till nod 136.
  • 398>208 dĂ€rför tittar jag pĂ„ det vĂ€nstra undertrĂ€det av nod 398
  • 250>208 dĂ€rför tittar jag pĂ„ det vĂ€nstra undertrĂ€det av nod 250
  • 200<208, dĂ€rför tittar jag pĂ„ det högra undertrĂ€det i nod 200. Men 200 har inget höger undertrĂ€d, vĂ€rde finns inte (eftersom om det finns kommer det att finnas i det högra undertrĂ€det 200).

LÄt oss sÀga att jag letar efter 40

  • Jag börjar pĂ„ roten vars nyckel Ă€r 136. Eftersom 136 > 40 tittar jag pĂ„ det vĂ€nstra undertrĂ€det av nod 136.
  • 80 > 40, dĂ€rför tittar jag pĂ„ det vĂ€nstra undertrĂ€det av nod 80
  • 40=40, noden finns. Jag hĂ€mtar rad-ID inuti noden (visas inte pĂ„ bilden) och letar i tabellen efter angivet rad-ID.
  • Genom att kĂ€nna till rad-ID:t kan jag veta exakt var data finns i tabellen, sĂ„ att jag kan hĂ€mta den direkt.

I slutÀndan kommer bÄda sökningarna att kosta mig antalet nivÄer inne i trÀdet. Om du lÀser delen om merge sort noggrant, bör du se att det finns log(N) nivÄer. Det visar sig, sökkostnadslogg(N), inte dÄligt!

LÄt oss ÄtergÄ till vÄrt problem

Men det hÀr Àr vÀldigt abstrakt, sÄ lÄt oss ÄtergÄ till vÄrt problem. IstÀllet för ett enkelt heltal, förestÀll dig en strÀng som representerar landet för nÄgon i föregÄende tabell. LÄt oss sÀga att du har ett trÀd som innehÄller fÀltet "land" (kolumn 3) i tabellen:

  • Om du vill veta vem som jobbar i Storbritannien
  • du tittar pĂ„ trĂ€det för att fĂ„ noden som representerar Storbritannien
  • inuti "UKnode" hittar du platsen för brittiska arbetsuppgifter.

Denna sökning kommer att kosta log(N) operationer istÀllet för N operationer om du anvÀnder arrayen direkt. Det du just presenterade var databasindex.

Du kan bygga ett indextrÀd för vilken grupp av fÀlt som helst (strÀng, nummer, 2 rader, nummer och strÀng, datum...) sÄ lÀnge du har en funktion för att jÀmföra nycklar (dvs. fÀltgrupper) sÄ att du kan stÀlla in ordning bland nycklarna (vilket Àr fallet för alla grundlÀggande typer i databasen).

B+TrÀdindex

Ä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 fĂ„ flera element mellan tvĂ„ vĂ€rden. Detta 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 bestĂ€lld korsning av trĂ€det). Dessutom Ă€r denna operation inte disk I/O-vĂ€nlig eftersom du mĂ„ste lĂ€sa hela trĂ€det. Vi mĂ„ste hitta ett sĂ€tt att effektivt utföra intervallförfrĂ„gan. För att lösa detta problem anvĂ€nder moderna databaser en modifierad version av det tidigare trĂ€det som heter B+Tree. I ett B+trĂ€d:

  • endast de lĂ€gsta noderna (löven) butiksinformation (placering av rader i den relaterade tabellen)
  • resten av noderna Ă€r hĂ€r för routing till rĂ€tt nod under sökning.

SĂ„ fungerar relationsdatabaser (del 1)

Som du kan se finns det fler noder hÀr (tvÄ gÄnger). Du har faktiskt ytterligare noder, "beslutsnoder", som hjÀlper dig att hitta rÀtt nod (som lagrar platsen för raderna i den tillhörande tabellen). Men sökkomplexiteten Àr fortfarande O(log(N)) (det finns bara en nivÄ till). Den stora skillnaden Àr att noder pÄ den lÀgre nivÄn Àr anslutna till sina efterföljare.

Med detta B+Tree, om du letar efter vÀrden mellan 40 och 100:

  • Du behöver bara leta efter 40 (eller det nĂ€rmaste vĂ€rdet efter 40 om 40 inte finns) som du gjorde med föregĂ„ende trĂ€d.
  • Samla sedan 40 arvingar med hjĂ€lp av direkta arvingar tills du nĂ„r 100.

LÄt oss sÀga att du hittar M efterföljare och trÀdet har N noder. Att hitta en specifik nod kostar log(N) som föregÄende trÀd. Men nÀr du vÀl fÄr den hÀr noden kommer du att fÄ M efterföljare i M-operationer med referenser till deras efterföljare. Denna sökning kostar bara M+log(N) operationer jÀmfört med N operationer pÄ föregÄende trÀd. Dessutom behöver du inte lÀsa hela trÀdet (endast M+log(N)-noder), vilket innebÀr mindre diskanvÀndning. Om M Àr litet (t.ex. 200 rader) och N Àr stort (1 000 000 rader) blir det STOR skillnad.

Men det finns nya problem hÀr (igen!). Om du lÀgger till eller tar bort en rad i databasen (och dÀrmed i det tillhörande B+Tree-indexet):

  • du mĂ„ste upprĂ€tthĂ„lla ordning mellan noderna inuti ett B+Tree, annars kommer du inte att kunna hitta noderna inuti ett osorterat trĂ€d.
  • du mĂ„ste behĂ„lla minsta möjliga antal nivĂ„er i B+Tree, annars blir O(log(N)) tidskomplexiteten O(N).

Med andra ord mÄste B+Tree vara sjÀlvbestÀllande och balanserat. Lyckligtvis Àr detta möjligt med smarta borttagnings- och infogningsoperationer. Men detta kommer till en kostnad: insÀttningar och raderingar i ett B+-trÀd kostar O(log(N)). Det Àr dÀrför nÄgra av er har hört det att anvÀnda för mÄnga index Àr inte en bra idé. Verkligen, du saktar ner snabb infogning/uppdatering/radering av en rad i en tabelleftersom databasen behöver uppdatera tabellens index med en dyr O(log(N))-operation för varje index. Dessutom innebÀr att lÀgga till index mer arbetsbelastning för transaktionsansvarig (kommer att beskrivas i slutet av artikeln).

För mer information kan du se Wikipedia-artikeln om B+TrÀd. Om du vill ha ett exempel pÄ att implementera B+Tree i en databas, ta en titt denna artikel О denna artikel frÄn en ledande MySQL-utvecklare. De fokuserar bÄda pÄ hur InnoDB (MySQL-motorn) hanterar index.

Notera: En lÀsare sa till mig att B+-trÀdet borde vara helt balanserat pÄ grund av lÄgnivÄoptimeringar.

Hastbar

VÄr sista viktiga datastruktur Àr hashtabellen. Detta Àr mycket anvÀndbart nÀr du snabbt vill slÄ upp vÀrden. Dessutom kommer förstÄelsen av en hashtabell att hjÀlpa oss att senare förstÄ en vanlig databaskopplingsoperation som kallas en hash-join ( hash gÄ med). Denna datastruktur anvÀnds ocksÄ av databasen för att lagra vissa interna saker (t.ex. lÄsbord eller buffertpool, vi kommer att se bÄda dessa begrepp senare).

En hashtabell Àr en datastruktur som snabbt hittar ett element genom sin nyckel. För att bygga en hashtabell mÄste du definiera:

  • Đșлюч för dina element
  • hash-funktion för nycklar. De berĂ€knade nyckelhascharna anger platsen för elementen (kallas segment ).
  • funktion för att jĂ€mföra nycklar. NĂ€r du har hittat rĂ€tt segment mĂ„ste du hitta det element du letar efter inom segmentet med hjĂ€lp av denna jĂ€mförelse.

Enkelt exempel

LĂ„t oss ta ett tydligt exempel:

SĂ„ fungerar relationsdatabaser (del 1)

Denna hashtabell har 10 segment. Eftersom jag Àr lat, tog jag bara bilder pÄ 5 segment, men jag vet att du Àr smart, sÄ jag lÄter dig förestÀlla de andra 5 pÄ egen hand. Jag anvÀnde en hash-funktion modulo 10 av nyckeln. Med andra ord lagrar jag bara den sista siffran i elementets nyckel för att hitta dess segment:

  • om den sista siffran Ă€r 0, hamnar elementet i segment 0,
  • om den sista siffran Ă€r 1, hamnar elementet i segment 1,
  • om den sista siffran Ă€r 2, hamnar elementet i omrĂ„de 2,
  • .

JÀmförelsefunktionen jag anvÀnde Àr helt enkelt likhet mellan tvÄ heltal.

LÄt oss sÀga att du vill fÄ element 78:

  • Hashtabellen berĂ€knar hashkoden för 78, vilket Ă€r 8.
  • Hashtabellen tittar pĂ„ segment 8, och det första elementet den hittar Ă€r 78.
  • Hon returnerar artikel 78 till dig
  • Sökning kostar endast 2 operationer (en för att berĂ€kna hashvĂ€rdet och den andra för att slĂ„ upp elementet inom segmentet).

LÄt oss nu sÀga att du vill fÄ element 59:

  • Hashtabellen berĂ€knar hashkoden för 59, vilket Ă€r 9.
  • Hashtabellen söker i segment 9, det första elementet som hittas Ă€r 99. Eftersom 99!=59 Ă€r element 99 inte ett giltigt element.
  • Med samma logik tas det andra elementet (9), det tredje (79), ..., det sista (29).
  • Elementet hittades inte.
  • Sökandet kostade 7 operationer.

Bra hashfunktion

Som du kan se, beroende pÄ vilket vÀrde du letar efter, Àr kostnaden inte densamma!

Om jag nu Àndrar hashfunktionen modulo 1 000 000 av nyckeln (det vill sÀga tar de sista 6 siffrorna) kostar den andra uppslagningen bara 1 operation eftersom det inte finns nÄgra element i segment 000059. Den verkliga utmaningen Àr att hitta en bra hashfunktion som skapar hinkar som innehÄller ett mycket litet antal element.

I mitt exempel Àr det lÀtt att hitta en bra hashfunktion. Men det hÀr Àr ett enkelt exempel, att hitta en bra hashfunktion Àr svÄrare nÀr nyckeln Àr:

  • strĂ€ng (till exempel - efternamn)
  • 2 rader (till exempel - efternamn och förnamn)
  • 2 rader och datum (till exempel - efternamn, förnamn och födelsedatum)
  • .

Med en bra hashfunktion kostar hashtabellssökningar O(1).

Array vs hashtabell

Varför inte anvÀnda en array?

Hmm, bra frÄga.

  • Hashbordet kan vara delvis laddad i minnet, och de Ă„terstĂ„ende segmenten kan finnas kvar pĂ„ disken.
  • Med en array mĂ„ste du anvĂ€nda sammanhĂ€ngande utrymme i minnet. Om du laddar ett stort bord det Ă€r mycket svĂ„rt att hitta tillrĂ€ckligt kontinuerligt utrymme.
  • För en hashtabell kan du vĂ€lja den nyckel du vill ha (till exempel land och personens efternamn).

För mer information kan du lÀsa artikeln om javaHashMap, som Àr en effektiv implementering av en hashtabell; du behöver inte förstÄ Java för att förstÄ begreppen som behandlas i den hÀr artikeln.

KĂ€lla: will.com

LĂ€gg en kommentar