Øvingseksamen 04 · TDT4145

Øvingseksamen 04

Tidsramme: 3 timer · Hjelpemidler: Ingen · Total: 100 poeng

Hovedfokus: DBMS-komponenter, division, kategorier, sort-merge, hash join, MVCC, snapshot isolation, REDO-test.

Del 1 — Theodoros (~40 %)

Introduksjon, relasjonsalgebra, SQL, ER-modellering, normalformer.

Spørsmål 1 3 poeng Kap. 1

Hvilken komponent i et tradisjonelt RDBMS er hovedansvarlig for å oversette en SQL-tekst til en utførbar plan, gjennom parsing, omskriving (rewrite) og kostnadsbasert optimisering?

  • A Query processor (parser + optimizer + executor).
  • B Storage manager — den vet hvor blokkene ligger på disk.
  • C Transaction manager — den koordinerer låser og logger.
  • D Buffer manager — den styrer cachen og bestemmer hvilke blokker som ligger i RAM.
Vis fasit
Riktig svar: A

Query processor er paraply-betegnelsen for parser, omskriver, planner/optimizer og executor — alle stegene fra SQL-tekst til en plan av fysiske operatorer. Storage manager håndterer fysisk lagring, transaction manager isolasjon og atomisitet, buffer manager hvilke blokker som er i RAM. Disse fire er ofte de som listes i en typisk arkitekturdiagram fra Kap. 1.

B, C, D er ekte komponenter med ekte oppgaver, men ingen av dem oversetter SQL — de aktiveres via callbacks fra query-processorens eksekusjonsfase.

Pensum: Kap. 1 — Introduksjon

Spørsmål 2 3 poeng Kap. 2

I en RIGHT OUTER JOIN mellom L og R på L.k = R.k, hva skjer med rader fra høyre tabell som ikke har match i venstre?

  • A De forsvinner — bare matchede rader returneres (samme som inner join).
  • B De returneres, og L-kolonnene fylles med tomme strenger.
  • C De returneres dupliserte med hver L-rad — kryssprodukt.
  • D De returneres, og L-kolonnene fylles med NULL.
Vis fasit
Riktig svar: D

RIGHT OUTER JOIN beholder alle rader fra høyre tabell, og fyller på NULL i L-kolonner der det ikke finnes match. (Symmetrisk for LEFT OUTER JOIN.)

A beskriver inner join. B forveksler NULL og tom streng — i SQL er disse forskjellige verdier, og NULL er det som brukes. C er kryssproduktet, ikke en outer join.

Pensum: Kap. 2 — Relasjonsalgebra

Spørsmål 3 3 poeng Kap. 2

Tabellene:

A
xy
1p
1q
2p
3r
B
yz
p10
p20
q30

Hvor mange tupler returnerer A ⋈ B (naturlig join på y)?

  • A 4
  • B 6
  • C 5
  • D 12
Vis fasit
Riktig svar: C

Match på y:

  • (1, p) ⋈ {(p,10), (p,20)} → 2 tupler
  • (1, q) ⋈ {(q,30)} → 1 tuppel
  • (2, p) ⋈ {(p,10), (p,20)} → 2 tupler
  • (3, r) → ingen match → 0

Sum: 2 + 1 + 2 = 5.

A glemmer at (1, p) og (2, p) hver gir 2 matcher. B inkluderer (3, r) feilaktig. D er kryssproduktet (4 × 3 = 12).

Pensum: Kap. 2 — Relasjonsalgebra

Spørsmål 4 3 poeng Kap. 3

Vurder spørringen:

SELECT lonn * 1.05 AS ny_lonn
FROM Ansatt
WHERE ny_lonn > 800000;

Hva skjer ved utføring i et standard-konformt RDBMS?

  • A Spørringen fungerer som forventet — aliaset ny_lonn er tilgjengelig overalt ellers i spørringen.
  • B Syntaks- eller kjøretidsfeil: ny_lonn finnes ikke i WHERE-fasen siden WHERE evalueres før SELECT.
  • C Spørringen kjører, men returnerer alltid 0 rader fordi aliaset alltid er NULL ved evaluering av WHERE.
  • D Spørringen optimeres helt bort av planneren og returnerer hele Ansatt-tabellen ufiltrert som resultat.
Vis fasit
Riktig svar: B

SQL-evalueringsrekkefølgen er FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY. Når WHERE evalueres er SELECT-listen ennå ikke beregnet, og aliaset ny_lonn finnes ikke som en navngitt kolonne. Standarden krever at aliaset bare er synlig fra og med SELECT-fasen (og videre i ORDER BY). Riktig formulering: WHERE lonn * 1.05 > 800000 eller pakke inn i en subquery / WITH.

A er feil i strict SQL — visse DBMS-er tillater det som en utvidelse, men ikke standarden. C er fri fantasi. D er en grov misforståelse av planneren.

Pensum: Kap. 3 — DDL og spørringer

Spørsmål 5 4 poeng Kap. 3

Tabellen Anmeldelse:

aidkidstjerner
1105
210NULL
3114
4NULL3
5NULLNULL

Hva returnerer:

SELECT kid, COUNT(*), COUNT(stjerner)
FROM Anmeldelse
GROUP BY kid;
  • A Tre grupper: (10, 2, 1), (11, 1, 1), (NULL, 2, 1).
  • B To grupper: (10, 2, 1), (11, 1, 1) — NULL-grupper utelates.
  • C Tre grupper: (10, 2, 2), (11, 1, 1), (NULL, 2, 2) — COUNT teller NULL.
  • D En enkelt rad med samtlige tellet sammen — GROUP BY funker ikke når NULL er til stede.
Vis fasit
Riktig svar: A

Standarden behandler NULL-er som en egen gruppe i GROUP BY. Vi får tre grupper: kid=10 (rader 1, 2), kid=11 (rad 3), kid=NULL (rader 4, 5). For hver gruppe: COUNT(*) teller alle rader; COUNT(stjerner) teller bare ikke-NULL stjerner.

  • (10): 2 rader; én har stjerner ≠ NULL → (10, 2, 1)
  • (11): 1 rad med stjerner = 4 → (11, 1, 1)
  • (NULL): 2 rader; én med stjerner = 3 → (NULL, 2, 1)

B feilaktig utelater NULL-gruppen. C teller NULL i COUNT(stjerner) — feil. D er grov misforståelse av GROUP BY.

Pensum: Kap. 3 — DDL og spørringer

Spørsmål 6 3 poeng Kap. 3

Skjema:

Bestilling(bid, kid, total)
Kunde(kid, navn, by)

Hvilken av disse spørringene er ikke ekvivalent med de andre? (Alle skal finne navn på kunder som har lagt inn minst én bestilling med total > 1000.)

  • A SELECT DISTINCT k.navn FROM Kunde k JOIN Bestilling b ON k.kid = b.kid WHERE b.total > 1000;
  • B SELECT navn FROM Kunde WHERE kid IN (SELECT kid FROM Bestilling WHERE total > 1000);
  • C SELECT navn FROM Kunde k WHERE EXISTS (SELECT 1 FROM Bestilling b WHERE b.kid = k.kid AND b.total > 1000);
  • D SELECT k.navn FROM Kunde k JOIN Bestilling b ON k.kid = b.kid WHERE b.total > 1000;
Vis fasit
Riktig svar: D

D mangler DISTINCT. Hvis en kunde har to bestillinger over 1000, returneres navnet to ganger — det er ikke ekvivalent med A/B/C som hver returnerer navnet maks én gang. A bruker DISTINCT på joinet, B bruker IN på en kid-mengde (per definisjon én rad per kunde), C bruker EXISTS som returnerer én rad per kunde uavhengig av antall matchende bestillinger.

Detalj: hvis det er garantert maks én bestilling per kunde over 1000 (f.eks. på grunn av en UNIQUE-constraint), ville D være ekvivalent — men generelt er den det ikke.

Pensum: Kap. 3 — Joins og subqueries

Spørsmål 7 3 poeng Kap. 3

Skjema:

Student(sid, navn)
Kurs(kid, tittel)
Tar(sid, kid)

Vi vil finne studenter som tar alle kurs (relasjonsalgebra-divisjon). Hvilken SQL-spørring oppnår dette?

  • A SELECT navn FROM Student s JOIN Tar t ON s.sid = t.sid GROUP BY s.sid, s.navn HAVING COUNT(*) = (SELECT COUNT(*) FROM Tar);
  • B SELECT DISTINCT s.navn FROM Student s WHERE s.sid IN (SELECT t.sid FROM Tar t GROUP BY t.sid HAVING COUNT(*) >= 1);
  • C SELECT navn FROM Student s WHERE NOT EXISTS (SELECT 1 FROM Kurs k WHERE NOT EXISTS (SELECT 1 FROM Tar t WHERE t.sid = s.sid AND t.kid = k.kid));
  • D SELECT DISTINCT s.navn FROM Student s JOIN Kurs k ON 1 = 1 GROUP BY s.sid, s.navn ORDER BY s.navn;
Vis fasit
Riktig svar: C

Klassisk division-via-double-NOT-EXISTS: "studenter for hvilke det ikke finnes et kurs som studenten ikke tar". Det er nøyaktig spesifikasjonen for "tar alle kurs".

A bruker COUNT — men COUNT(*) FROM Tar teller alle Tar-rader på tvers av alle studenter, ikke antall distinkte kurs. En riktig COUNT-versjon ville brukt (SELECT COUNT(*) FROM Kurs) som referanse. B finner studenter som har tatt minst ett kurs. D produserer kryssproduktet — meningsløst som svar.

Pensum: Kap. 3 — Joins og subqueries

Spørsmål 8 3 poeng Kap. 3

Skjema:

CREATE TABLE Avdeling (
    aid  INT PRIMARY KEY,
    navn VARCHAR(50)
);
CREATE TABLE Prosjekt (
    pid  INT PRIMARY KEY,
    aid  INT NOT NULL,
    FOREIGN KEY (aid) REFERENCES Avdeling(aid) ON DELETE CASCADE
);
CREATE TABLE Oppgave (
    oid  INT PRIMARY KEY,
    pid  INT NOT NULL,
    FOREIGN KEY (pid) REFERENCES Prosjekt(pid) ON DELETE CASCADE
);

Hva skjer hvis vi sletter en rad fra Avdeling som har 2 prosjekter, der hvert prosjekt har 5 oppgaver?

  • A Slettingen avvises fordi Prosjekt.aid er NOT NULL.
  • B Avdelingsraden, de 2 prosjektene og alle 10 oppgavene slettes — kaskaderingen følger FK-kjeden.
  • C Bare avdelingen og de 2 prosjektene slettes; oppgavene blir foreldreløse fordi kaskadering ikke krysser to nivåer.
  • D Bare avdelingen slettes; prosjektene får aid satt til NULL.
Vis fasit
Riktig svar: B

ON DELETE CASCADE forplanter seg så langt FK-kjeden går. Slettingen av avdelingen utløser sletting av begge prosjektene; hver prosjekt-sletting utløser sletting av sine 5 oppgaver. Totalt slettes 1 + 2 + 10 = 13 rader.

A misforstår NOT NULL — den hindrer at vi setter inn en rad uten aid, men kaskadering tar bort hele raden. C er feil — kaskadering er rekursiv. D beskriver ON DELETE SET NULL, en annen handling, og ville uansett brutt NOT NULL-constraint på Prosjekt.aid.

Pensum: Kap. 3 — Views, transaksjoner, integritet

Spørsmål 9 3 poeng Kap. 3

Tabellen Forelder(barn, foreldre):

barnforeldre
AnnaBerit
BeritCathrine
CathrineDiana
DianaEva

Spørringen:

WITH RECURSIVE Aner(person, aner) AS (
    SELECT barn, foreldre FROM Forelder WHERE barn = 'Anna'
  UNION
    SELECT a.person, f.foreldre FROM Aner a
    JOIN Forelder f ON a.aner = f.barn
)
SELECT aner FROM Aner;

Hvor mange rader returnerer den?

  • A 4 (Berit, Cathrine, Diana, Eva).
  • B 1 (bare Berit).
  • C 5 (Anna selv pluss alle aner).
  • D Spørringen termimerer ikke (uendelig løkke).
Vis fasit
Riktig svar: A

Anker: (Anna, Berit). Iterasjon 1: join Aner(person='Anna', aner='Berit') med Forelder(barn='Berit', foreldre='Cathrine') → (Anna, Cathrine). Iterasjon 2: → (Anna, Diana). Iterasjon 3: → (Anna, Eva). Iterasjon 4: ingen ny rad — Eva har ingen forelder i tabellen → terminerer. Aner = {(Anna, Berit), (Anna, Cathrine), (Anna, Diana), (Anna, Eva)}. Resultat på SELECT aner: Berit, Cathrine, Diana, Eva — 4 rader.

B teller bare anker. C inkluderer Anna selv — men hun er person, ikke aner, så hun er ikke i SELECT-resultatet. D ignorerer at UNION termintaerer hierarkisk traversering så lenge grafen er endelig.

Pensum: Kap. 3 — Prosedyrer, triggere, rekursjon

Spørsmål 10 3 poeng Kap. 3

SQL definerer flere kategorier av rettigheter som kan tildeles via GRANT. Hvilken liste viser kun klassiske SQL-rettigheter på tabellnivå?

  • A CONNECT, DISCONNECT, AUTHENTICATE, AUTHORIZE.
  • B ENCRYPT, DECRYPT, SIGN, VERIFY.
  • C START_TX, COMMIT_TX, ROLLBACK_TX.
  • D SELECT, INSERT, UPDATE, DELETE, REFERENCES, TRIGGER.
Vis fasit
Riktig svar: D

SQL-standarden definerer rettigheter som SELECT, INSERT, UPDATE, DELETE (datamanipulering), REFERENCES (lov til å lage en FK som peker til en gitt tabell) og TRIGGER (lov til å definere triggere). I tillegg finnes USAGE og EXECUTE for andre objekttyper. WITH GRANT OPTION lar mottakeren videre-grante.

A og B er ikke SQL-rettigheter (skikkelig autentisering håndteres av roller / login-mekanisme). C er ikke en GRANT-able rettighet — alle bruker kan starte og committe transaksjoner per default.

Pensum: Kap. 3 — Views, transaksjoner, integritet

Spørsmål 11 3 poeng Kap. 4

I et ER-diagram er relasjonen "leder" mellom Avdeling og Ansatt 1:1. Total deltakelse fra Avdeling og partial fra Ansatt. Hva betyr dette i den konkrete databasen?

  • A Hver ansatt må lede én avdeling, men en avdeling trenger ikke ha en leder.
  • B Hver avdeling må ha en leder, og hver ansatt må lede en avdeling.
  • C Hver avdeling må ha nøyaktig én leder, men en ansatt trenger ikke å være leder for noen avdeling.
  • D Verken avdeling eller ansatt har krav om å delta — relasjonen er rent valgfri.
Vis fasit
Riktig svar: C

Total deltakelse fra Avdeling betyr at hver avdelings-rad må delta i relasjonen → hver avdeling har en leder. Partial deltakelse fra Ansatt betyr at en ansatt kan, men ikke trenger, lede en avdeling. Sammen med 1:1: nøyaktig én leder per avdeling, og hver leder leder maksimalt én avdeling.

A bytter om totalisiteten. B krever at alle ansatte er ledere — det blir 1:N med total fra Ansatt. D ignorerer total fra Avdeling.

Pensum: Kap. 4 — ER-modellen

Spørsmål 12 3 poeng Kap. 4

En kategori (også kalt union type) i utvidet ER er forskjellig fra en vanlig spesialisering (ISA). Hvordan?

  • A En kategori har én superklasse og flere subklasser, mens en spesialisering har omvendt struktur i diagrammet.
  • B En kategori er en subklasse hvis instanser er union fra flere ulike superklasser; spesialisering har én superklasse.
  • C En kategori er det samme som en svak entitet i ER-modellen — det er bare to forskjellige notasjoner for begrepet.
  • D En kategori er en ISA-relasjon der alle subklassene er disjunkte, mens spesialisering tillater overlapp mellom dem.
Vis fasit
Riktig svar: B

Eksempel: EierAvKjøretøy kan være enten Person, Bedrift eller Bank. Disse tre er uavhengige superklasser med ulik PK og attributter, men instanser av kategorien EierAvKjøretøy er unionen av instanser fra dem alle. En vanlig spesialisering har derimot kun én superklasse (f.eks. Bil ISA Kjøretøy), og subklassens instanser er en delmengde.

A snur strukturen. C blander begreper. D gjelder disjoint vs overlap-spesialiseringer, ikke kategorier.

Pensum: Kap. 4 — ER-modellen

Spørsmål 13 3 poeng Kap. 4

Tabellen Bestilling(bnr, kid, kby, totalpris) har FDs:

F = { bnr → kid,   bnr → totalpris,   kid → kby }

Kandidatnøkkelen er {bnr}. Hvilken normalform er Bestilling brutt mot?

  • A 3NF — bnr → kid → kby er transitiv avhengighet av PK via en ikke-prim-attributt.
  • B 2NF — kid → kby er en delvis avhengighet av en del av primærnøkkelen.
  • C 1NF — kby er ikke atomær fordi feltet kan inneholde strukturerte data.
  • D Ingen normalform — tabellen er allerede på BCNF og trenger ingen normalisering.
Vis fasit
Riktig svar: A

Vi har kjeden bnr → kid (PK → ikke-prim) og kid → kby (ikke-prim → ikke-prim). Det er en klassisk transitiv avhengighet: kby avhenger av PK via kid. 3NF forbyr nettopp dette med mindre høyresiden er prim, og kby er ikke prim. Det er derfor et 3NF-brudd.

B er feil — PK er én attributt (bnr), så det finnes ingen "delvis avhengighet" å bryte. C er feil — kby er en atomær by-streng. D er feil — ja, tabellen bryter også BCNF, men det spesifikke bruddet er 3NF (transitiv avhengighet).

Pensum: Kap. 4 — Normalformer

Del 2 — Svein Erik (~60 %)

Lagring, indekser, queries, transaksjoner og recovery.

Spørsmål 14 4 poeng Kap. 6

Hvorfor implementerer DBMS-er sin egen buffer manager i stedet for å stole utelukkende på operativsystemets filcache?

  • A Operativsystemer har ikke filcache, og DBMS-et må derfor implementere all caching helt selv fra bunnen.
  • B Bare for å spare RAM på maskinen — DB-bufferen er alltid mindre i størrelse enn operativsystemets cache i praksis.
  • C DBMS-et trenger fin kontroll over evictions, prefetching basert på spørringsplanen og garantert tilstedeværelse av pinned sider.
  • D Fordi OS-cachen lager ekstra kopier av data og bryter dermed med ACID-egenskapen Atomicity for transaksjoner.
Vis fasit
Riktig svar: C

OS-cachen er generell og vet ingenting om spørringsplanen. DBMS-et derimot vet for eksempel at den skal scanne en stor tabell sekvensielt (MRU-eviction er bedre enn LRU) eller at en bestemt index-blokk vil bli aksessert mange ganger (pin den). DBMS-et trenger også å pinne sider mens en transaksjon holder en lås på dem, og må kunne flushe spesifikke sider deterministisk for å implementere WAL og force-log-at-commit.

A er feil — OS-er har filcache (Linux page cache osv.). B er en for snever forklaring. D er fri fantasi — atomicity opprettholdes av loggen, ikke ved å unngå caching.

Pensum: Kap. 6 — Lagring

Spørsmål 15 4 poeng Kap. 6

I extendible hashing har en blokk lokal depth d. Hva betyr dette eksakt?

  • A d angir antall datapostene som passer inn i blokken før den må splittes pga. plassmangel og overflyt mot directory.
  • B d angir hvor mange splits denne spesifikke blokken har vært gjennom siden filen ble opprettet i utgangspunktet av systemet.
  • C d angir antall directory-entries som peker direkte på nettopp denne blokken i hash-strukturen for filen totalt.
  • D d angir antall hash-bits som trengs for å skille blokken fra andre — nøkler i blokken har samme prefiks av lengde d.
Vis fasit
Riktig svar: D

Lokal depth d betyr at blokken inneholder nøkler hvis hash-prefiks (eller -suffiks) av lengde d er det samme. Når GD > d peker flere directory-entries (nøyaktig 2^(GD−d) av dem) på blokken — det er disse vi kan splitte uten directory-doubling. Når d = GD peker bare én entry på blokken; en splitt krever da doubling.

A er en helt annen ting (blokk-kapasitet). B er et historisk teller, ikke en lagret verdi. C er nesten korrekt for tilfellet d = GD, men generelt feil — antall pekere er 2^(GD−d), ikke d selv.

Pensum: Kap. 6 — Lagring

Spørsmål 16 4 poeng Kap. 6

En post med flere variabel-lengde felter kan lagres med en delimiter mellom feltene (f.eks. NUL-byte). Hva er hovedulempen ved denne strategien sammenlignet med en offset-vektor?

  • A Delimiter-strategien er treig fordi den krever zip-komprimering før hver eneste skriving til disk.
  • B Vi må skanne posten lineært for å finne et bestemt felt — direkte aksess i konstant tid er ikke mulig.
  • C Delimiter-strategien kan ikke representere felter som har variabel lengde i datapostene på disk.
  • D Posten må padded til en fast størrelse, og dette unødig sløser med diskplass i datafilen.
Vis fasit
Riktig svar: B

Delimiter krever lineær søking gjennom posten for å finne start på det N-te feltet (skann fram til N delimiters er funnet). Med en offset-vektor i header har vi konstant-tids aksess: vektor[N] gir starten direkte. Tidskostnaden ved delimiter er typisk ikke betydelig per post, men kan dominere for breie tabeller med mange felter.

A er fri fantasi (ingen komprimering implisitt). C er motsatt av riktig — delimiter er nettopp laget for variabel lengde. D beskriver fast-lengde-strategi.

Pensum: Kap. 6 — Lagring

Spørsmål 17 4 poeng Kap. 6

Et clustered B+-tre lagrer 5 millioner poster på 200 byte. Blokker er 4 KiB med 2/3 fyllgrad. (key, BlockId)-poster på indre nivåer er 16 byte. Hvor mange nivåer (inkl. løv = level 0) har treet?

  • A 2
  • B 4
  • C 5
  • D 3
Vis fasit
Riktig svar: B

Tilgjengelig plass per blokk: 4096 · 2/3 ≈ 2730. Poster per løvblokk: floor(2730/200) = 13. Antall løvblokker: ceil(5 000 000 / 13) ≈ 384 616. Fan-out på indre nivåer: floor(2730/16) = 170. Level 1: ceil(384616 / 170) ≈ 2263. Level 2: ceil(2263/170) ≈ 14. Level 3: ceil(14/170) = 1 (rot). Treet har 4 nivåer (0, 1, 2, 3).

A er for grunt — ville krevd over 30 millioner poster passer i én blokk. C ville krevd at level 3 også hadde flere blokker. D undervurderer høyden ved 1.

Pensum: Kap. 6 — B+-trær

Spørsmål 18 4 poeng Kap. 6

Hvorfor regnes B+-tre-blokker ofte med en 2/3 fyllgrad, og ikke 100 %?

  • A Det er fordi DBMS-et alltid reserverer akkurat 1/3 av blokken til intern WAL-logging i seg selv.
  • B Fordi en B+-tre-blokk har inntil 1/3 av plassen brukt opp til peker-hodet (parent, neste og prev pekere).
  • C Empirisk gjennomsnittlig fyllgrad etter mange innsettinger og slettinger — splits gir 50/50, men over tid jevner det seg til ca. 67 %.
  • D Fordi 1/3 av blokken alltid reserveres til neste forventede splits, slik at innsettinger blir raskere senere i bruk.
Vis fasit
Riktig svar: C

Når en blokk splittes deler vi typisk innholdet 50/50 mellom de to nye blokkene — så de halvfylte blokkene fyller seg opp igjen ved nye innsettinger. Empirisk havner dynamisk vedlikeholdte B+-trær på rundt 65–70 % gjennomsnittlig fyllgrad, derav antagelsen 2/3. Det er ikke en fast reservert plass, men et gjennomsnittlig tall man bruker for å estimere størrelse.

A er fabrikert. B beskriver header-overhead, som er liten — ikke 1/3. D ville vært en explicit reserveringspolicy, men den brukes ikke i klassiske B+-trær.

Pensum: Kap. 6 — B+-trær

Spørsmål 19 4 poeng Kap. 6

Hvilken påstand om "write amplification" i LSM-trær er korrekt?

  • A Write amplification er null i LSM-trær — det er nettopp dette som er en av de vesentligste fordelene med dem.
  • B Write amplification er nøyaktig 1 i alle tilfeller: hver post skrives kun én gang når memtable flushes til disk.
  • C Write amplification i LSM er per definisjon høyere enn i B+-trær fordi compaction-prosessen ikke eksisterer i B+-trær.
  • D Write amplification er hvor mange ganger samme post skrives totalt — i en N-nivå LSM med leveled compaction skrives en post N ganger.
Vis fasit
Riktig svar: D

Write amplification = totale bytes skrevet til disk / totale bytes som applikasjonen logisk skrev. I LSM-trær flyttes en post nedover nivåene gjennom compaction; med leveled compaction og en faktor-T-mellom-nivåer-ratio er WA ca. T × (antall nivåer). Dette er prisen for høy skrive-throughput. B+-trær har derimot lavere WA fordi skriv går direkte til riktig blokk (men flere små random skriv).

A og B er begge for optimistiske. C er feil — B+-trær har sin egen form for WA via blokkomskriving og buffer-flusher.

Pensum: Kap. 6 — B+-trær og LSM

Spørsmål 20 4 poeng Kap. 7

Tabellen Salg har 200 000 rader, lagret i en heap (4000 blokker). Det finnes et unclustered B+-tre på belop med 3 nivåer og 800 løvblokker. Vi kjører:

SELECT * FROM Salg WHERE belop > 5000;

Anta at 70 % av radene oppfyller predikatet. Hvilken plan er optimal?

  • A Full table scan av heapen — 4000 blokker.
  • B Index range scan: traversér til riktig løv (3 blokker), skann 70 % av løvnivået (560 blokker), slå opp 140 000 RID-er i heap (140 000 blokker worst case).
  • C Bitmap index scan med 70 % bitset, deretter 4000 heap-blokker — 4003 totalt.
  • D Index seek til startposisjon (3 blokker) og deretter scan av 800 løvblokker.
Vis fasit
Riktig svar: A

Når selektiviteten er høy (her 70 %) blir et unclustered indeks-oppslag dyrere enn en full table scan, fordi hver rad krever en separat heap-aksess (worst case én blokk per rad). Optimizer velger derfor full scan: 4000 blokker. Tommelregelen er at unclustered index-scan blir lønnsomt først når selektiviteten er svært lav (vanligvis < 5–10 %).

B beskriver det optimizer ville fått hvis vi tvang indeks-bruk — 140 000+ blokker, mye verre enn full scan. C miks-up bitmap (et avansert index-format ofte sett i analytiske DBMS-er) men endrer ikke konklusjonen for høy selektivitet. D leser hele indeksens løvnivå men hopper over heap-aksessene — feil hvis vi trenger SELECT *.

Pensum: Kap. 7 — Spørringsutføring

Spørsmål 21 4 poeng Kap. 7

Når lønner sort-merge join (J3) seg sammenlignet med hash join (J4) eller nested loop?

  • A Når begge tabellene er små nok til å kunne passe samtidig i hovedminnet på maskinen som kjører spørringen.
  • B Når join-attributtet er en lang streng, fordi sortering generelt håndterer kollisjoner bedre enn hashing gjør.
  • C Når begge inputene allerede er sortert på join-attributtet (eller sorteringen kan brukes til ORDER BY) — kun lineær merge.
  • D Sort-merge er alltid raskere enn hash join for store tabeller med svært mange rader på disken i praksis.
Vis fasit
Riktig svar: C

Sort-merge er attraktivt når sorteringen kan brukes igjen — f.eks. når en av inputene er resultatet av en index-scan over en clustered index på join-attributtet, eller når det ytre spørringsplanen uansett trenger sortert output (ORDER BY, GROUP BY, DISTINCT). Da er J3 sin marginale ekstrakostnad bare merge-fasen.

A — for små tabeller velger man typisk hash join eller nested loop. B er fri fantasi. D er overgenerelt — hash join er ofte raskere når input er usortert og hashtabellen passer i RAM.

Pensum: Kap. 7 — Join-algoritmer

Spørsmål 22 4 poeng Kap. 7

Hva er kjernemekanismen i partition-hash join (J4)?

  • A Begge tabellene sorteres først etter join-attributtet, før de slås sammen med en lineær merge i en separat fase.
  • B Join-attributtet hashes for å partisjonere begge tabellene; deretter bygges en hashtabell på minste partisjon som den større probes mot.
  • C En hashtabell over join-attributtet vedlikeholdes permanent på disk og oppdateres ved hver innsetting i en av tabellene.
  • D Tabellene leses parallelt fra disk og kombineres i en intern hash-bitmap som indikerer matchede par av rader mellom dem.
Vis fasit
Riktig svar: B

Hash join går i to faser: (1) partition — begge tabellene hashes på join-attributtet og partisjoneres til disk; matchende rader havner i samme partisjons-bucket. (2) build/probe — for hver bucket bygges en hashtabell på den mindre sidens rader (build), og den større sidens rader probes mot den for matcher. Kravet er at en build-partisjon må passe i RAM; ellers må man rekursivt partisjonere.

A er sort-merge, ikke hash join. C er en separat indeksstruktur, ikke en algoritme. D er fri fantasi.

Pensum: Kap. 7 — Join-algoritmer

Spørsmål 23 4 poeng Kap. 8

Recoverability-hierarkiet rangerer schedule-klasser fra mest til minst restriktiv. Hva er den korrekte strikt-til-svak rekkefølgen?

  • A ACA ⊂ Strict ⊂ Recoverable ⊂ Alle.
  • B Recoverable ⊂ ACA ⊂ Strict ⊂ Alle.
  • C Strict ⊂ ACA ⊂ Alle ⊂ Recoverable.
  • D Strict ⊂ ACA ⊂ Recoverable ⊂ Alle.
Vis fasit
Riktig svar: D

Strict (S) er den mest restriktive: ingen lese eller skrive på et objekt som har en uncommitted skriv. ACA (Avoid Cascading Aborts) er bredere: ingen lese på en uncommitted skriv (men skriv-skriv-konflikter er tillatt). Recoverable er enda bredere: tillater dirty reads, men krever at en transaksjon committer etter alle den har lest fra. Alle schedules er supersettet.

A bytter Strict og ACA. B reverserer hierarkiet. C plasserer Recoverable utenfor Alle — umulig.

Pensum: Kap. 8 — Transaksjoner: teori

Spørsmål 24 4 poeng Kap. 8

Vurder schedulen:

w1(X, 200); r2(X); w2(Y); a1; c2;

(a1 = T1 aborterer.) Hva er problemet?

  • A T2 har lest en verdi T1 skrev (dirty read) og committet egen state. Når T1 aborterer kan ikke T2 rulles tilbake.
  • B Lost update: T2 sin skriv overskriver T1 sin før T1 er ferdig committed, slik at endringen forsvinner.
  • C Phantom: T2 ser en rad som T1 senere fjerner i sin egen pågående transaksjon før commit-tidspunkt.
  • D Ingen problem — schedulen er allerede strict og dermed rekoverable og fri for anomalier her.
Vis fasit
Riktig svar: A

r2(X) leser den verdien T1 har skrevet (men ikke committet) — dirty read. T2 committer (c2) før T1 er ferdig, og deretter aborterer T1. Vi kan ikke lenger rulle tilbake T2 (den er per definisjon irreversibel etter commit), så X-verdien T2 så har "lekket" ut til andre transaksjoner. Schedulen er ikke recoverable.

B krever to skriv på X uten å lese hverandres mellomstate. C er om range-spørringer og insertions. D er feil — strict ville forbudt r2(X) før c1.

Pensum: Kap. 8 — Transaksjoner: teori

Spørsmål 25 4 poeng Kap. 8

Hva er den kjerneideen i MVCC (Multi-Version Concurrency Control) sammenlignet med 2PL?

  • A MVCC bruker både låser og versjoner samtidig — alt fra 2PL pluss en versjonskjede som vedlikeholdes per rad i tabellen i basen.
  • B MVCC lagrer flere versjoner av hver rad — lesere ser konsistent snapshot uten lås, og bare skriv-skriv-konflikter krever låsing.
  • C MVCC fjerner alt av låsing helt fra DBMS-et; konflikter løses utelukkende ved retry på applikasjonsnivå over i klienten.
  • D MVCC og 2PL er bare to forskjellige navn på den samme grunnleggende mekanismen i mange moderne DBMS-er som finnes i dag.
Vis fasit
Riktig svar: B

MVCC sin store gevinst er at readers don't block writers, and writers don't block readers. Hver skriv lager en ny versjon av raden; lesere ser den versjonen som var commit-et ved sin start (snapshot). Kun skriv-skriv-konflikter krever koordinering (typisk førstemann vinner / siste mann aborterer, eller en skrive-lås på selve raden).

A overdriver — moderne MVCC-systemer (PostgreSQL, Oracle) tar typisk ikke leselås. C overser at MVCC fortsatt bruker noe låsing for skrive-konflikter. D er falsk — 2PL gir SERIALIZABLE per default; MVCC gir snapshot isolation som ikke er fullt serializable.

Pensum: Kap. 8 — Samtidighet og låser

Spørsmål 26 4 poeng Kap. 8

Under snapshot isolation kan en kjent anomali oppstå: write skew. Hva karakteriserer den?

  • A To transaksjoner overskriver hver sin verdi av samme rad uten å se hverandres skriv først, og siste skriv vinner.
  • B En transaksjon leser samme rad to ganger og får forskjellige verdier mellom de to lesingene i samme transaksjon.
  • C To transaksjoner leser et felles datasett, hver oppdaterer en annen del basert på lesingen, og effekten bryter en invariant som hver enkelt sjekket.
  • D En transaksjon committer en skriv mens en annen er midt i en lengre lesing — dette er det samme som en dirty read.
Vis fasit
Riktig svar: C

Det klassiske eksempelet: to leger T1 og T2 vil hver melde seg av vakt. Hver leser rader for begge legene og ser at den andre fortsatt er på vakt — så de oppdaterer kun sin egen status. Begge committer parallelt. Hver enkelt så lovlig invariant ("minst én på vakt"), men kombinasjonen bryter den. Snapshot isolation oppdager ikke dette fordi de oppdaterer forskjellige rader, så det er ingen skriv-skriv-konflikt.

A er lost update — fanges av MVCC sin write-write-deteksjon. B er unrepeatable read — ikke aktuell under snapshot. D er dirty read.

Pensum: Kap. 8 — Samtidighet og låser

Spørsmål 27 4 poeng Kap. 8

Etter analyse-fasen i ARIES er DPT:

(P1, recLSN=80), (P2, recLSN=85), (P3, recLSN=90)

REDO-fasen vurderer loggpost LSN=82 som oppdaterer P2. På disk har P2 pageLSN=78. Skal loggposten redoes?

  • A Nei, fordi DPT.recLSN(P2) = 85 > LSN = 82 — endringen er allerede kjent å være flushed til disk.
  • B Ja, fordi transaksjonen fortsatt er aktiv etter at analyse-fasen er ferdig med sin skanning.
  • C Ja, fordi pageLSN er mindre enn loggens LSN, og endringen er derfor ikke synlig på disk ennå.
  • D Nei, fordi selve loggposten ligger før checkpointet og dermed allerede må være flushed til disk.
Vis fasit
Riktig svar: A

ARIES sin REDO-test: hopp over en loggpost dersom (i) PageID ikke er i DPT, (ii) DPT.recLSN > LSN, eller (iii) blokkens pageLSN ≥ LSN. Her er DPT.recLSN(P2) = 85 og LSN = 82 → 85 > 82, så vi vet at endringen ved LSN=82 ble flushed før blokken ble dirty igjen. Vi hopper over.

C er forvirret — pageLSN < LSN ville faktisk antydet at vi skulle redoe, men kriterium (ii) overstyrer her. B forveksler analyse-fasens transaksjonsstatus med REDO-testen; ARIES gjentar historien for både winners og losers. D forveksler checkpoint-grensen med REDO-grensen.

Pensum: Kap. 8 — Recovery

Spørsmål 28 4 poeng Kap. 8

Buffer-management policies for transaksjoner kombineres i en 2 × 2-matrise: steal/no-steal × force/no-force. Hvilken kombinasjon bruker ARIES, og hvorfor?

  • A No-steal + force: enkleste politikk å implementere, krever ikke logging og er enkel i daglig drift.
  • B No-steal + no-force: gir best ytelse for små og raske transaksjoner som er korte i tid totalt.
  • C Steal + force: kombinerer god fleksibilitet med Durability uten å trenge en egen REDO-logg.
  • D Steal + no-force: maksimal fleksibilitet — buffer-manager kan flushe uncommitted endringer og trenger ikke flushe ved commit.
Vis fasit
Riktig svar: D

ARIES bruker steal + no-force. Steal lar buffer-managen evicte vilkårlige sider (selv uncommitted) når RAM-en blir trang — det krever UNDO-logg for å rulle tilbake. No-force betyr at vi ikke trenger å flushe alle dirty pages ved commit; bare loggen flushes (force-log-at-commit). Det krever REDO-logg for å gjenta committet skriving etter krasj. Kombinasjonen gir høy ytelse: korte commits (bare logg-flush), god buffer-utnyttelse (kan flushe uansett).

A er enkelt, men sløser med disk-I/O ved commit (force) og krever stor buffer (no-steal). B er fortsatt no-steal — fordrer mye RAM. C kombinerer steal med force, sjeldent brukt fordi force allerede koster det meste.

Pensum: Kap. 8 — Recovery

Slutt på øvingseksamen 04.