Forberedelse til SQL-interview · Lektion

Join-algoritmer: Nested Loop, Hash, Merge

Sådan udføres hvert join, og hvornår hver metode er det rigtige valg.

Lektion 3 af 413 trin

Join-algoritmer: Nested Loop, Hash, Merge er en gratis Forberedelse til SQL-interview-lektion på CoddyKit. Dette er lektion 3 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i Forberedelse til SQL-interview, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Forberedelse til SQL-interview-kurset indeholder 4 lektioner i alt.

Joins er algoritmer, ikke kun syntaks

Du kender allerede INNER JOIN som syntaks. På seniorniveau spørger interviewere, hvordan databasen rent faktisk udfører et join. Der er tre algoritmer:

  • Nested Loop Join
  • Hash Join
  • Merge Join (sorteringsfletning)

Den logiske jointype (INNER, LEFT) er uafhængig af algoritmen. Planlæggeren vælger algoritmen ud fra tabellernes størrelse, indekser og sorteringsrækkefølge. At vide, hvornår hver algoritme er bedst, er kernen i denne lektion.

Nested Loop Join

Nested Loop er den enkleste algoritme: For hver række i den ydre tabel gennemgås den indre tabel for at finde match. I pseudokode er det to løkker, hvor den ene ligger inden i den anden.

Naivt giver det O(ydre * indre), hvilket er meget langsomt for store tabeller. Men algoritmen bliver fremragende, når den indre side har et indeks på joinnøglen: Hver ydre række udløser et billigt indeksomslag i stedet for en fuld scanning af den indre tabel.

Det er planlæggerens favorit, når den ydre tabel er lille, og den indre joinkolonne er indekseret.

Nested Loop  (cost=0.42..120.5 rows=15 width=72)
  ->  Seq Scan on customers c  (rows=3)
  ->  Index Scan using idx_orders_cust on orders o
        Index Cond: (o.customer_id = c.id)
        (loops=3)

Sådan aflæser du løkker i en Nested Loop

Det tydeligste tegn på en nested loop er loops på den indre node. Eksemplet viser loops=3, fordi den ydre side producerede 3 rækker, så den indre indeksscanning blev kørt 3 gange.

Problemet opstår, når den ydre side er stor. Hvis den ydre side producerer 2 millioner rækker, køres den indre side 2 millioner gange. Selv et hurtigt opslag på 0,01 ms bliver til 20 sekunder.

På et interview skal du påpege enhver nested loop, hvor loops er stort over en indre tabel uden et godt indeks. Det er den langsomme forespørgsel.

Hash Join

Hash Join håndterer store, usorterede tabeller godt. Den kører i to faser:

  • Opbygning: Læs den mindre tabel, og indlæs den i en hash-tabel i hukommelsen med joinkolonnen som nøgle.
  • Opslag: Gennemgå den større tabel; beregn hashværdien for joinnøglen for hver række, og slå den op i hash-tabellen.

Hver tabel læses kun én gang, hvilket giver cirka O(ydre + indre). Der kræves hverken indekser eller sorteret input, og derfor dominerer denne algoritme store analytiske joins med lighedsbetingelser.

Hash Join  (cost=18.0..520.0 rows=900 width=72)
  Hash Cond: (o.customer_id = c.id)
  ->  Seq Scan on orders o  (rows=100000)
  ->  Hash  (rows=500)
        ->  Seq Scan on customers c  (rows=500)

Begrænsninger ved Hash Join

Der er to ting, du skal nævne om hash joins:

  • De fungerer kun med lighedsbetingelser (a.id = b.id). En intervalbetingelse som a.x < b.y kan ikke bruge en hash join.
  • Opbygningssiden skal kunne være i work_mem. Hvis den ikke kan det, skriver Postgres batchene til disk (du vil se Batches: > 1 og diskforbrug), hvilket gør joinet markant langsommere.

En hash join med en enorm opbygningsside og en meget lille work_mem er derfor en reel performancefejl, som du bør påpege.

Hash  (actual rows=2000000 loops=1)
  Buckets: 65536  Batches: 16  Memory Usage: 4096kB

Merge Join

Merge Join (sorteringsfletning) kræver, at begge input er sorteret efter joinnøglen. Derefter gennemløber den begge synkront, ligesom når to sorterede lister flettes, og flytter den markør, der er bagefter.

Den er effektiv, når input allerede er sorteret, for eksempel når det kommer direkte fra et indeks i nøglens rækkefølge, fordi der så ikke er brug for et sorteringstrin. Den understøtter også interval- og ulighedsjoins i modsætning til hash join.

Hvis input ikke er sorteret på forhånd, tilføjer planlæggeren eksplicitte Sort-noder, og omkostningen ved sorteringen kan gøre hash join billigere i stedet.

Merge Join  (cost=0.85..210.0 rows=900 width=72)
  Merge Cond: (o.customer_id = c.id)
  ->  Index Scan using idx_orders_cust on orders o
  ->  Index Scan using customers_pkey on customers c

Huskeregel for valg af algoritme

Husk, hvornår hver algoritme er bedst:

  • Nested Loop, når den ydre tabel er lille, og den indre joinnøgle er indekseret; det er også den eneste mulighed for joins uden lighedsbetingelser, når input ikke er sorteret.
  • Hash Join, når store, usorterede tabeller joines med en lighedsbetingelse; der kræves ingen indekser.
  • Merge Join, når begge input allerede er sorteret efter nøglen (ofte via indekser), eller ved intervaljoins; den er fremragende til meget store, forhåndssorterede datasæt.

Planlæggeren estimerer omkostningen ved hver mulighed og vælger den billigste ud fra sine estimater for antal rækker.

Hukommelses- og sorteringsomkostninger

Ressourceforbruget varierer markant, og interviewere spørger ofte ind til det:

  • Nested Loop, bruger minimal hukommelse; omkostningen domineres af gentagne opslag i den indre tabel.
  • Hash Join, kræver hukommelse til hash-tabellen; skriver til disk, hvis den bliver for stor.
  • Merge Join, er billig at flette, men dyr, hvis den først skal sortere; sorteringer bruger også work_mem og kan skrive til disk.

Hvis du øger work_mem, kan du derfor ændre en langsom hash join eller sortering, der skriver til disk, til en, der foregår i hukommelsen. Det er et konkret optimeringssvar.

Derfor gik en Nested Loop galt

Et klassisk scenarie: En forespørgsel var hurtig i udviklingsmiljøet, men langsom i produktionen. Planen viser en Nested Loop med loops=3000000.

Planlæggeren undervurderede antallet af rækker på den ydre side (forældede statistikker sagde 3 rækker, men virkeligheden er 3 millioner), så den valgte en nested loop. Med korrekte statistikker ville den have valgt en hash join.

Dit svar på interviewet: Kør ANALYZE, så estimatet bliver korrekt. Derefter skifter planlæggeren til en hash join, og forespørgslen bliver markant hurtigere.

Nested Loop  (cost=0.42..50.0 rows=3 width=72)
  ->  Seq Scan on big_outer  (actual rows=3000000 loops=1)
  ->  Index Scan on inner_t  (actual rows=1 loops=3000000)

Sådan påvirker du valget

Du bør normalt ikke tvinge algoritmer igennem, men du kan gøre det under test for at sammenligne dem. Postgres stiller metodebaserede indstillinger til rådighed:

SET enable_nestloop = off; og tilsvarende for enable_hashjoin og enable_mergejoin. Slå én indstilling fra, kør EXPLAIN ANALYZE igen, og se, om alternativet faktisk er hurtigere.

De rigtige løsninger er stadig friske statistikker, de rigtige indekser, tilstrækkelig work_mem og selektive prædikater. Tvangsvalg er kun til diagnosticering.

SET enable_nestloop = off;
EXPLAIN ANALYZE
SELECT * FROM orders o JOIN customers c ON o.customer_id = c.id;
SET enable_nestloop = on;

Opsummering: Joins i stor skala

Lad os samle det for en analytisk arbejdsbelastning, der joiner to store faktatabeller og dimensionstabeller på et id:

  • Hvis dimensionstabellen kan være i hukommelsen, kan du forvente en Hash Join, som ofte er bedst.
  • Hvis begge tabeller kommer sorteret fra indekser, kan en Merge Join undgå opbygningen af en hash-tabel.
  • En Nested Loop her ville være et advarselssignal og skyldes normalt et dårligt estimat.

At aflæse, hvilken algoritme planlæggeren valgte, og vurdere, om den burde have valgt den, er præcis det seniorniveau, disse spørgsmål tester.

Hurtigt tjek

Du joiner to store, usorterede tabeller med en lighedsbetingelse a.id = b.id. Ingen af dem har et nyttigt indeks, og statistikkerne er korrekte. Hvilken joinalgoritme vil planlæggeren mest sandsynligt vælge?

Opsummering

De tre joinalgoritmer:

  • Nested Loop, ydre række gange opslag i den indre tabel; fremragende med en lille ydre tabel og en indekseret indre nøgle, men farlig, når loops er enormt.
  • Hash Join, opbygning og opslag; bedst til store, usorterede joins med lighedsbetingelser, begrænset til lighedsbetingelser og afgrænset af work_mem.
  • Merge Join, synkront gennemløb af sorteret input; ideel, når data allerede er sorteret, eller ved intervaljoins.

Planlæggeren vælger ud fra omkostninger og statistikker. En overraskende nested loop med et enormt antal løkker betyder næsten altid et dårligt estimat af antallet af rækker. Opdatér statistikkerne.

Gratis at komme i gang

Lær SQL med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
30
Lektioner
120

Ofte stillede spørgsmål

Er lektionen “Join-algoritmer: Nested Loop, Hash, Merge” gratis?

Ja — hele teksten til “Join-algoritmer: Nested Loop, Hash, Merge” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af Forberedelse til SQL-interview-kurset, skal du opgradere til CoddyKit PRO. Forberedelse til SQL-interview-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Join-algoritmer: Nested Loop, Hash, Merge”?

Sådan udføres hvert join, og hvornår hver metode er det rigtige valg. Du øver dig i Forberedelse til SQL-interview med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Forberedelse til SQL-interview?

Der kræves ingen tidligere erfaring. Forberedelse til SQL-interview på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 3 af 4.

Hvor lang tid tager lektionen “Join-algoritmer: Nested Loop, Hash, Merge”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Forberedelse til SQL-interview-lektion?

Ja. Alle Forberedelse til SQL-interview-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Sådan læses en EXPLAIN-plan
  2. Seq Scan kontra Index Scan og Index-Only
  3. Join-algoritmer: Nested Loop, Hash, Merge
  4. Find og ret langsomme forespørgsler
← Tilbage til Forberedelse til SQL-interview