© 2005 MAH – Datavetenskap LdP Transaktioner Distribuerade system PV7110
Innehåll Transaktioner Seriell ekvivalens Nästlade transaktioner Seriell ekvivalens med lås Samtidighetskontroll (concurrency control)
Transaktioner I vissa situationer kräver klienter att: 1.De inte störs av andra samtidiga klienter 2.Alla eller ingen av operationerna lyckas ACID –Atomicity –Consistency –Isolation –Durability
Transaktioner Det är önskvärt att maximera samtidighet Seriellt ekvivalenta transaktioner tillåts exekvera samtidigt –de har samma effekt som om de exekverades seriellt Varje transaktion ges ett unikt ID (TID) Lyckade transaktioner kallas ”commited” Vad gör servern och klienten vid krascher?
Transaktioner Tre vanliga problem –”Lost update” –”Inconsistent retrievals” –”Conflicting operations”
Lost update Båda transaktionerna skall öka b med 10% T’s uppdatering blir förlorat TransactionT: balance = b.getBalance(); b.setBalance(balance*1.1); a.withdraw(balance/10) TransactionU: balance = b.getBalance(); b.setBalance(balance*1.1); c.withdraw(balance/10) balance = b.getBalance(); $200 balance = b.getBalance(); $200 b.setBalance(balance*1.1); $220 b.setBalance(balance*1.1); $220 a.withdraw(balance/10) $80 c.withdraw(balance/10) $280
Inconsistent retrievals V överför $100 från A till B medan W beräknar ”totalt” Transaction V: a.withdraw(100) b.deposit(100) Transaction W: aBranch.branchTotal() a.withdraw(100); $100 total = a.getBalance() $100 total = total+b.getBalance() $300 total = total+c.getBalance() b.deposit(100) $300
Seriell ekvivalens Vi inför nu begreppet seriell ekvivalens En seriellt ekvivalent parallell exekvering: –En exekvering som ger samma resultat som om exekveringen hade skett seriellt Med samma resultat menas: –Läsoperationer ger samma värden –Instansvariabler har samma värden i slutet Transaktionerna schemaläggs för att undvika samtidig access till samma konton
Funkar det nu då? Om den ena kör före den andra funkar det Samma gäller för en seriellt ekvivalent ordning Transaction T: balance = b.getBalance() b.setBalance(balance*1.1) a.withdraw(balance/10) Transaction U: balance = b.getBalance() b.setBalance(balance*1.1) c.withdraw(balance/10) balance = b.getBalance()$200 b.setBalance(balance*1.1)$220 balance = b.getBalance()$220 b.setBalance(balance*1.1)$242 a.withdraw(balance/10) $80 c.withdraw(balance/10)$278
Den andra då? Det kan aldrig bli fel om alla läsningar görs före eller efter en uppdatering Läsningar och skrivningar till olika resurser påverkar inte varandra Vilka två operationer skulle kunna byta plats? Transaction V: a.withdraw(100); b.deposit(100) Transaction W: aBranch.branchTotal() a.withdraw(100); $100 b.deposit(100) $300 total = a.getBalance() $100 total = total+b.getBalance() $400 total = total+c.getBalance()... Svar: den översta I W med den andra i V
Conflicting operations Två operationer sägs var i konflikt om deras effekt beror på i vilken ordning de körs Enklaste exemplet: read och write Operations of different transactions Conflict Reason read No Because the effect of a pair ofread operations does not depend on the order in which they are executed readwriteYes Because the effect of aread and a write operation depends on the order of their execution write Yes Because the effect of a pair ofwrite operations depends on the order of their execution
Definition –”För att två transaktioner skall vara seriellt ekvivalenta är det nödvändigt och tillräckligt att alla operationer i konflikt utförs i samma ordning på alla objekt som båda använder” Om vi har följande: –T:x = read(i); write(i, 10); write(j, 20); –U:y = read(j); write(j, 30); z = read (i); Seriell ekvivalens kräver då att: –T accessar i före U och T accessar j före U, eller –U accessar i före T och U accessar j före T Var är konflikten? Seriell ekvivalens… T’s write(i) med U’s read (i) U’s read (j) och write(j) med T’s write(j)
Detta funkar således ej Observera att detta inte är seriellt ekvivalent Operationerna görs inte i samma ordning TransactionT: U: x = read(i) write(i, 10) y = read(j) write(j, 30) write(j, 20) z = read (i)
Återhämtning från avbrott Om en transaktion avbryts måste servern se till att andra samtidiga transaktioner inte ”ser” dess effekter Vi tittar på två problem –”Dirty reads” –”Premature writes” Enligt tidigare: –getBalance() är en read-operation –setBalance() är en write-operation
TransactionT: a.getBalance() a.setBalance(balance + 10) TransactionU: a.getBalance() a.setBalance(balance + 20) balance = a.getBalance()$100 a.setBalance(balance + 10)$110 balance = a.getBalance()$110 a.setBalance(balance + 20) $130 commit transaction abort transaction Dirty read Exekveringarna är seriellt ekvivalenta Vad är problemet? U har gjort en “dirty read” U kan inte avbryta eftersom den har gjort “commit”
Återhämtning Om en transaktion (jämför med U) blir ”commited” efter att den ”sett” effekterna av en transaktion som senare blir avbruten, kan den inte återhämtas Vad kan vi göra? –En ”commit” fördröjs tills alla observerade transaktioner gör ”commit” Fortfarande problem –Detta leder till kedjeavbrott på väntande transaktioner
Återhämtning Kedjeavbrott (Cascading aborts) Tänk att U väntar tills T avbryter –Då måste även U avbryta –Då måste även alla andra som ”sett” U avbryta –O.s.v. Vi undviker genom: –Transaktioner tillåts endast läsa objekt skrivna av ”commited” transaktioner –Varje read operation måste alltså fördröjas U väntar t.ex. med getBalance() tills T gör ”commit”
Premature write Vissa databassystem håller ”before-images” och återställer dessa efter avbott $105 är ”before-image” för U, om U avbryter får vi rätt värde Blir det rätt i alla fall med ”before-images”? TransactionT: a.setBalance(105) TransactionU: a.setBalance(110) $100 a.setBalance(105)$105 a.setBalance(110) $110 Interaktion mellan write-operationer när en transaktion avbryter
Åtgärder Premature writes –Om vi har ”before-images” Köa write-operationer tills tidigare transaktioner som har uppdaterat samma objekt antingen avbryter eller gör ”commit” Strikt exekvering av transaktioner –Undvika både dirty read och premature write Köa både read- och write-operationer En exekvering kallar strikt om både read och write på ett objekt köas tills alla transaktioner som tidigare uppdaterat objektet antingen avbrutits eller gjort ”commit” Strikt exevering ger den önskvärda egenskapen isolation Tentative versions –Objektuppdateringar sparas tillfälligt i temporärt minne
Nästlade transaktioner En transaktion kan starta en annan transaktion som kan starta en annan transaktion som kan starta… Top level Sub transaction A1Sub transaction B1 Sub transaction A2Sub transaction B2
Nästlade transaktioner Transaktioner på samma nivå kan exekvera samtidigt om de uppfyller seriell ekvivalens En undertransaktion kan fallera oberoende av andra
Fördelar Samtidighet mellan undertransaktioner –Jämför med summering av konton (branchTotal) –Kan implementeras i olika servrar Robusthet –Undertransaktioner kan fallera oberoende –Föräldern väljer vad som skall göras vid eventuellt avbrott
Några viktiga krav En transaktion måste se till att alla dess undertransaktioner är färdiga innan den avbryter eller gör ”commit” En undertransaktion väljer själv om den vill avbryta eller göra ”provisional commit”, beslutet är fast Om en förälder avbryts, avbryts även alla undertransaktioner Om huvudtransaktionen gör ”commit” kan även alla undertransaktioner göra ”commit”
Sammanfattning OBS! Transaktioner i en server Atomiska vid samtidighet –Genom seriellt ekvivalenta exekveringar Atomiska vid serverkrascher –Sparar ”commited”-tillstånd –Använder strikt exekvering –Tentative versions för att kunna uppdatera och avbryta Nästlade transaktioner –Samtidig exekvering av undertransaktioner –Oberoende återhämtning av undertransaktioner
Lås/Uteslutning Med lås/uteslutning kan seriell ekvivalens garanteras
Strikt 2-fas låsning 1. Vad är “dirty reads”? 2. Hur kan de förhindras? 1.En transaktion läser ett värde som uppdaterats av en annan transaktion som senare avbryter 2.Fördröj read-operationer tills transaktioner som uppdaterat värden avbryter eller gör ”commit” Strikt exekvering förhindrade ”dirty reads” och ”premature writes” Allt som blivit låst under en transaktion måste hållas låst tills transaktionen avbryter eller ”commits” Detta kallas strikt 2-fas låsning För att dessutom säkerställa återhämtning efter krasch, hålls låsen tills uppdateringar har skrivits till permanent lagring
Read/Write konfliktregler Read-operationer i olika transaktioner blir inte i konflikt –Exklusiva lås på dessa påverkar samtidigheten negativt Readers/writer - flera läsare, en skrivare –Flera transaktioner kan läsa ett värde samtidigt –En transaktion i taget kan skriva –Båda ovanstående kan inte göras samtidigt –Använd read- och write-lås –Read-lås kallas ibland för ”shared locks” (delade) 3. Vad avgör om 2 operationer är i konflikt? 3.Deras gemensamma effekt är beroende på i vilken ordning de körs
Konfliktregler Operationer i konflikt: –1. En transaktion T har läst ett värde. Då kan inte en annan uppdatera värdet innan T avbryter eller gör ”commit” –2. En transaktion T har skrivit ett värde. Då kan inte en annan läsa eller skriva värdet innan T avbryter eller gör ”commit” For one objectLock requested readwrite Lock already set noneOK readOKwait writewait
Upp/Ner-gradering av lås En transaktion sätter ett read-lås när den skall läsa ett värde Låset ”uppgraderas” till ett write-lås vid behov till att skriva –OBS! Bara samma transaktion kan uppgradera det Andra transaktioner måste då vänta Eng: Lock promotion Varning! – Ökar risken för deadlock!
Sammanfattning Access till ett objekt i en transaktion –Om objektet inte är låst, lås det och fortsätt –Om objektet är ”write”-låst, vänta –Om objektet är ”read”-låst, dela låset och fortsätt –Om samma transaktion redan låst det, uppgradera vid behov och fortsätt När en transaktion avbryts eller gör ”commit” –Öppna alla lås som transaktionen låst Hur gör vi med risken för deadlock?
Kan vi förhindra deadlock? Ja, men är det realistiskt? Två sätt att göra det: –Låsa alla objekt som en transaktion kommer att behöva access till Stora prestandaförluster Kan vara omöjligt att avgöra vilka objekt som kommer att behövas –Lås begärs i en fördefinierad ordning Stora prestandaförluster Går det i stora system?
Implementation av lås Ett separat objekt sköter om låshanteringen - Lock manager Lock managern måste hantera deadlocks Vi introducerar väntegrafer (wait-for graphs) för att visualisera relationerna mellan samtidiga transaktioner B A Waits for Held by T U U T Waits for
Wait-for graphs T, U och V delar ett ”read”-lås på C W håller ett ”write”-lås på B (som V väntar på) T och W begär uppgradering av låset på C deadlock (V väntar i 2 cykler) C T U V Held by T U V W W B Waits for
Upptäcka deadlock Leta efter cykler i väntegrafer Om ett deadlock upptäcks kan en transaktion avbrytas för att lösa upp en cykeln Kan byggas in i Lock managern Väntegrafen måste representeras Vilken transaktion skall avbrytas? –Den ”äldsta”, eller ”nyaste”, eller den i flest cykler?
Timeout på lås Kan vi inte ha timeout på låsen istället? –Ett lås blir ”sårbart” efter en viss tid –Låset kan fortgå i sårbart tillstånd tills en annan transaktion begär inträde, då bryts låset –Transaktioner som har fått ett lås brutet avbryts Vilka problem har vi då? –Lås bryts trots att vi inte har deadlock –Vid tider med stor belastning straffas långa transaktioner eftersom många lås då bryts –Svårt att välja tidsintervall
Optimistisk kontroll Eng: Optimistic Concurrency Control Behöver vi tvunget låsa allt för att vara säkra? Nej! Inte i system med få konflikter –Transaktioner tillåts exekvera utan hinder –I slutet kollas om transaktionen har varit i konflikt –Om en konflikt har uppstått avbryts en transaktion Transaktioner delas in i 3 faser –Arbete(Working) –Validering(Validating) –Uppdatering(Updating)
3 olika faser En koordinator behövs Arbete –Transaktionen använder ”tentativa” versioner av objekt den accessar (”dirty reads” kan förekomma) –Read- och write-operationer ”spelas in” av koordinatorn Validering –Vid slutet av en transaktion kollas efter eventuella konflikter –Om inga påträffas görs ”commit” –Vid konflikt måste en av transaktionerna avbryta Uppdatering –Vid lyckad validering skrivs de ”tentativa” värdena permanent –”read-only”-transaktioner kan göra ”commit” direkt Här får vi alltså inte deadlock. Istället lyckas den transaktion som först går igenom valideringen. De andra avbryts om konflikter hittas
Validering av transaktioner Vi använder read/write-konfliktregler –Då har vi seriell ekvivalens mellan transaktioner Varje transaktion ges ett nummer vid start. Numret behålls vid ”commit” Reglerna nedan säkerställer serialiserbarhet för transaktion T v m.a.p T i TvTv TiTi Rule writeread1.TiTi must not read objects written byTvTv readwrite2.TvTv must not read objects written byTiTi write 3.TiTi must not write objects written byTvTv and TvTv mustnot write objects written byTiTi
Validering av transaktioner Backward validation –T v ’s ”reads” kollas mot föregående överlappande transaktioner –Transaktioner utan read-operationer behöver inte kollas Earlier committed transactions WorkingValidationUpdate T 1 T v Transaction being validated T 2 T 3 Later active transactions active 1 2
Backward validation boolean valid = true; for (int Ti = startTn+1; Ti <= finishTn; Ti++){ if (read set of Tv intersects write set of Ti) valid = false; } startTn - Det största transaktionsnumret hos en tidigare avslutad transaktion när T v startade finishTn – Det största transaktionsnumret hos en tidigare transaktion när T v påbörjar validering Vid eventuell konflikt måste T v avbrytas 4. Vad behövs för valideringen? 4.Vi behöver lagra ”write”-operationer från tidigare transaktioner
Validering av transaktioner Forward validation –T v ’s ”writes” kollas mot överlappande aktiva transaktioner –Transaktioner utan ”write”-operationer går alltid igenom Earlier committed transactions WorkingValidationUpdate T 1 T v Transaction being validated T 2 T 3 Later active transactions active 1 2
Forward validation boolean valid = true; for (int Tid = active1; Tid <= activeN; Tid++){ if (write set of Tv intersects read set of Tid) valid = false; } Algoritmen måste kunna hantera det faktum att aktiva transaktioners ”read”-operationer kan ändras under valideringen 5. Vilken transaktion avbryter vi vid fel? 5.Vi kan välja, men tänk på att: Om vi avbryter T v på grund av T i kan det vara förgäves om T i senare också avbryts
Jämförelse Vid konflikt –Forwdard tillåter olika val vid avbrytning av transaktion –Backward kan endast avbryta den validerande transaktionen Normalt är ”read”-operationer > ”write”-operationer –Backward Kan behöva kontrollera en stor mängd operationer Kan behöva lagra många gamla ”write”-operationer –Forward Kollar en ett mindre antal ”write”-operationer Behöver däremot hantera förändring i transaktioners ”read”- operationer och nystartade transaktioner