dbsys.info

Løsningsforslag til kapittel 10

Oppgave 1

Registrering av ordre.

SQL-setningene for registrering av ordren blir som vist under (i forhold til tabelldefinisjonene i Vedlegg B hopper vi over noen av kolonnene).

Kolonnen OrdreNr ville egnet seg for autonummerering, men her bare velger vi 5005 som et nytt ordrenummer (og antar at dette er første ledige nummer).


-- I noen systemer (f.eks. MySQL) startes transaksjoner eksplisitt, slik:
START TRANSACTION;

INSERT INTO Ordre(OrdreNr, OrdreDato, KNr)
VALUES (5005, '2019-08-10', '5002');

INSERT INTO Ordrelinje(OrdreNr, VNr, PrisPrEnhet, Antall)
VALUES (5005, '12055', 40, 25);

INSERT INTO Ordrelinje(OrdreNr, VNr, PrisPrEnhet, Antall)
VALUES (5005, '33045', 50, 10);

INSERT INTO Ordrelinje(OrdreNr, VNr, PrisPrEnhet, Antall)
VALUES (5005, '25079', 60, 1);

UPDATE Vare
SET Antall = Antall-25
WHERE VNr = 12055;

UPDATE Vare
SET Antall = Antall-10
WHERE VNr = 33045;

UPDATE Vare
SET Antall = Antall-1
WHERE VNr = 25079;

COMMIT;

En mer praktisk måte å gjøre dette på vil være at kundebehandleren taster inn informasjonen i et skjema ("skjermbilde"). I skjemaet kan man tenke seg en "Lagre"-knapp som gjør at SQL-setningene over blir utført.

Oppgave 2

ROLLBACK

Erstatt COMMIT med ROLLBACK i løsningsforslaget til oppgave 1. (Hvis du allerede har kjørt eksempelkoden over med ordrenummer 5005, så bør du nå bytte til f.eks. 5006, slik at du ikke får feilmelding for brudd på primærnøkkelregler.) Kjør deretter utvalgsspørringer for å sjekke at tabellinnholdet er uendret.

Oppgave 3

Eksempler på samtidighetsproblemer. Vi tenker oss et system uten skikkelig samtidighetskontroll, og også at skriveoperasjoner blir utført direkte til disk (uten bruk av transaksjonslogg).

3a

Tapt oppdatering

T1 og T2 skal begge oppdatere antall enheter på lager for vare 12055. La X stå for adressen der denne verdien er lagret. Vi antar at det er 100 enheter på lager av denne varen. Problemet starter når T2 avleser X etter at T1 er i gang med oppdatering, men før T2 har skrevet resultatet tilbake. Oppdateringen til T1 går tapt når T2 overskriver.

Tidspunkt Lokal T1-kopi T1 Databasen T2 Lokal T2-kopi
1     100    
2 100 Les inn X 100    
3 75 Tell ned med 25 100 Les inn X 100
4 75 Skriv til disk 75 Tell ned med 5 95
5 75   95 Skriv til disk 95

3b

Angret oppdatering

Bruker igjen T1 og T2, og X står for antall enheter på lager av vare 12055 som over. T1 gjennomfører hele operasjonen inkludert oppdatering av databasen. T2 avleser ny verdi, før T1 angrer (rollback). T2 har dermed observert "sluttresultatet" til T1 som jo aldri vil bli registrert.

ROLLBACK skal altså angre operasjonen som T1 har utført, men det må i så fall bety at også T2 må angres. Oppgaven forutsetter at vi bruker et databasesystem uten skikkelig flerbrukerhåndtering, så vi nøyer oss med å antyde at lokal kopi i T2 ikke er gyldig.

Tidspunkt Lokal T1-kopi T1 Databasen T2 Lokal T2-kopi
1     100    
2 100 Les inn 100    
3 75 Tell ned med 25 100    
4 75 Skriv til disk 75    
5 75   75 Les inn 75
6 75   75 Tell ned med 5 70
7   ROLLBACK 100 Ugyldig lokal verdi 70

3c

Inkonsistent analyse

T2 legger til 2 rader i Ordrelinje. T3 summerer salg pr. kunde og vil gi forskjellige rersultat avhengig av om den blir utført før eller etter T2. La oss si at salgsverdiene av de to ordrelinjene øker salgssummen for kunde 5091 med henholdsvis 50 og 100 kroner, og at total salgssum for kunde 5091 forut for T2 var 1200 kroner. Hvis T3 blir utført før T2 får vi svaret 1200. Hvis T3 blir utført etter T2 får vi svaret 1350. Følgende forløp gir salgssum lik 1250 kroner - som er feil:

Tidspunkt T2 T3 Salgsverdi
      1200
1 Skriv ordrelinje 1   1250
2   Les tabeller og beregn salgssum 1250
3 Skriv ordrelinje 2   1350

 

Ingen av disse problemene kan oppstå dersom ingen transaksjoner blir utført samtidig. Problemer kan heller ikke oppstå for to transaksjoner som opererer på ulike dataområder (selv om de blir utført samtidig).

Oppgave 4

Forløp med låser

Forløpet under viser hvordan problemet med tapt oppdatering kan unngås ved hjelp av låser (T2 må vente). Begge transaksjoner må bruke skrivelåser ettersom de skal oppdatere X. Uansett om transaksjonene låser en enkelt rad, hele tabeller, eller hele databasen får vi i dette eksemplet venting, fordi transaksjonene skal oppdatere samme rad.

Tidspunkt T1 T2 X
1 Skrivelås X   100
2 Les inn X Skrivelås X 100
3 Tell ned X med 25 Vent 100
4 Skriv X til disk Vent 75
5 Lås opp X Vent 75
6   Les inn X 75
7   Tell ned X med 5 75
8   Skriv X til disk 70
9   Lås opp X 70

 

Når T1 låser X må T2 vente, og det er dette som gjør at problemet unngås. Tilsvarende venting inntreffer for de to andre situasjonene (angret oppdatering og inkonsistent analyse) når vi bruker låser. Låser er altså en måte å forhindre "farlig" samtidighet.

Oppgave 5

Serialiserbart forløp

Et forløp er serialiserbart hvis det har samme effekt som et sekvensielt forløp (som er et forløp der transaksjoner blir utført etter hverandre - en av gangen). Vi ønsker så mye samtidighet som mulig, men samtidig insisterer vi på serialiserbare forløp.

Oppgave 6

Ikke-serialiserbart forløp

Denne deloppgaven gikk ut på å lage et forløp som ikke er serialiserbart, og altså gir galt svar. Anta både X og Y har startverdi 10. Hvis man utfører T4 før T5 vil både X og Y få verdien 40. Hvis man utfører T5 før T4 vil både X og Y få verdiene 30. Dette er altså de to korrekte forløpene. Ved å la T4 operere først på X og la T5 operere først på Y får X verdien 40 og Y verdien 30, som altså er galt. Merk at både T4 og T5 bryter med to-faselåsing, ettersom de låser data-elementer etter at de har låst opp et annet.

Tidspunkt T4 T5 X Y
      10 10
1 Skrivelås X Skrivelås Y 10 10
2 Les inn X Les inn Y 10 10
3 Øk X med 10 Gang Y med 2 10 10
4 Skriv X til disk Skriv Y til disk 20 20
5 Lås opp X Lås opp Y 20 20
6 Skrivelås Y Skrivelås X 20 20
7 Les inn Y Les inn X 20 20
8 Øk Y med 10 Gang X med 2 20 20
9 Skriv Y til disk Skriv Y til disk 40 30
10 Lås opp Y Lås opp X 40 30

Oppgave 7

Tofaselåsing

Med tofaselåsing vil enhver transaksjon følge tre faser - låsing, behandling og deretter opplåsing. Man tillater altså ikke transaksjoner som låser, behandler, låser opp, og deretter låser nye data. Hvis man har to transaksjoner som følger tofaselåsing, og som jobber med de samme dataene, blir forløpet serialiserbart. En enkel strategi for å gjøre forløpet i punkt 4 serialiserbart er å flytte WRITELOCK-operasjonene først og UNLOCK-operasjonene til slutt.

Tidspunkt T4 T5 X Y
      10 10
1 Skrivelås X   10 10
2 Skrivelås Y   10 10
3 Les inn X Skrivelås Y 10 10
4 Øk X med 10 Vent 10 10
5 Skriv X til disk Vent 20 10
6 Lås opp X Vent 20 10
7 Les inn Y Vent 20 10
8 Øk Y med 10 Vent 20 10
9 Skriv Y til disk Vent 20 20
10 Lås opp Y Vent 20 20
11   Vent 20 20
12   Skrivelås X 20 20
13   Les inn Y 20 20
14   Gang Y med 2 20 20
15   Skriv Y til disk 20 40
16   Lås opp Y 20 40
17   Les inn X 20 40
18   Gang X med 2 20 40
19   Skriv X til disk 40 40
20   Lås opp X 40 40

Oppgave 8

Vranglås

Hvis vi bruker samme forløp som i punkt 5, men lar T5 starte litt før oppstår det vranglås. Viser her kun operasjonene ned til tidspunktet da gjensidig venting oppstår:

Tidspunkt T4 T5
1 Skrivelås X Skrivelås Y
2 Skrivelås Y Skrivelås X
3 Vent Vent
... ... ...

Oppgave 9

Transaksjonslogg

Når strømmen er tilbake startes databasesystemet opp igjen. Problemet er at det kan ha vært transaksjoner halvveis utført på tidspunktet da strømbruddet inntraff, og uten transaksjonslogg er det ikke mulig å vite hvilke transaksjoner dette gjelder. På loggen er det skrevet før- og etter-verdier for alle deloperasjoner som er utført, og alle oppdateringer blir skrevet til loggen før selve databasen blir oppdatert. Ved gjenoppretting av databasen kan loggen brukes for å gjenskape databasen til tilstanden like før strømbruddet.

Oppgave 10

Eksperiment: Oppdatering fra to transaksjoner

Hvis en transaksjon T1 allerede har startet oppdatering av en bestemt rad, vil transaksjon T2 bli satt "på vent" ved forsøk på oppdatering av samme rad (hvis systemet er satt opp med vanlig pessimistisk låsing). Vi kan prøve å "lage" en vranglås ved å starte en transaksjon T1 som først oppdaterer tabell A og en transaksjon T2 som først oppdaterer tabell B. Deretter lar vi T1 forsøke å oppdatere tabell B, og T2 forsøke å oppdatere tabell A. DBHS vil avbryte en av transaksjonene, enten umiddelbart eller etter en kort stund.

Oppgave 10

Eksperiment: Isolasjonsnivåer

Vi definerer to transaksjoner: T1 oppdaterer Vare-tabellen, mens T2 bare avleser (gjentatte ganger). Her er rekkefølgen som kommandoene blir utført:


T1: SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
T2: SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
T1: SELECT * FROM Vare;
T2: SELECT * FROM Vare;
T1: UPDATE Vare SET Pris=Pris+10 WHERE VNr=12055;
T1: SELECT * FROM Vare;
T2: SELECT * FROM Vare;
T1: COMMIT;
T2: SELECT * FROM Vare;
T2: COMMIT;

T1 observerer sin egen oppdatering umiddelbart. For T2 vil derimot Vare-tabellen se lik ut gjennom hele forløpet. Oppdateringen som T2 gjør blir ikke synlig for T1, selv ikke etter at T1 utfører COMMIT. Endre så de to første kommandoene til:


T1: SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
T2: SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

Nå vil T2 observere prisendringen på vare 12055 etter at T1 har utført COMMIT.