11. september 2006 - 13:39Der er
22 kommentarer og 1 løsning
Isolation level - Serializable
Hej Eksperter.
Jeg forsøger at forhindre læsninger i min database, når jeg skriver til den. Dette skulle være muligt ved at sætte ISOLATION LEVEL til SERIALIZABLE. Dermed skulle alle transaktioner løbe serielt.
Jeg har forsøgt mig med nedenstående eksempel, der sætter isolations-niveauet globalt. Desværre kan jeg stadig læse fra databasen under en transaktion. Jeg tester dette ved at afvikle nedenstående kode i debugging/step-by-step mode, så jeg kan udføre én linje kode af gangen. Når jeg er nået ind i min transaktion har jeg præcis lige så gode muligheder for at læse som jeg havde før jeg startede den.
Ideen er naturligvis at sikre, at jeg ikke kommer til at opdatere på baggrund af forældet data. Hvis vi forestiller os ovenstående kode eksekveret 100 gange samtidig, så er risikoen for at flere transaktioner læser det samme versions-nummer ret stor. Dermed skrives den samme værdi (+1) tilbage til databasen flere gange. Værdien skal incrementeres med 1 HVER gang.
Der er noget der tyder på at gamle forbindelser, der var etableret FØR det nye serialiserings-niveau blev sat, skal reconnectes. Så det prøver jeg lige at lege med.
Ja, det var åbenbart dér problemet lå. Men nu står jeg med et andet problem.
Jeg kan åbenbart starte flere transaktioner samtidig (START TRANSACTION). Og det resulterer i DeadLocks. Jeg havde da forventet at det kørte 100% serielt, og at Applikation 2 måtte vente på, at Applikation 1's transaktion blev færdigt (COMMIT).
Den fejl jeg får :
Error while execeuting query: UPDATE `settings` SET `value` = @variable + 1 WHERE `setting` = 'version': Deadlock found when trying to get lock; try restarting transaction (errno: 1213) Click 'Ignore' if you'd like to have this error ignored until the end of the script.
Faktisk ser det ud som om, at transaktionen først bliver opsat i det øjeblik UPDATE statementet eksekveres. Indtil da kan jeg starte nye transaktioner.
Okay. Jeg finder det bare underligt at jeg overhovedet får LOV til at starte endnu en transaktion, "parrallelt" med den første.
Faktisk viser det sig, at starter jeg Transaktion 1, og sætter et breakpoint ved UPDATE, og efterfølgende starter Transaktion 2, som får lov at færdiggøre sig, og efterfølgende resume'r Transaktion 1, så får jeg ingen fejl - men Transaktion 1 bliver ikke gemt i databasen.
Men kan denne fejl skyldes tiden det tager mig, at steppe og trykke mig videre igennem mine test cases? For det ville da være rart hvis jeg bare kunne stole på at MySQL gør som den skal, og ikke det som mine tests viser.
Jeg sidder og bliver forvirret nu, for i bogen "Database Systems - A practical approach to Design, Implementation, and Management" beskrives transaktioner i Serializable mode som fuldstændig serielle, mens forfatterne alligevel beskriver forskellige algoritmer til at time transaktioner, så der ikke opstår deadlocks. Men der kan naturligvis være en idé i at time transaktioner, frem for at afvikle alle transaktioner serielt (globalt serializable level). Sidstnævnte må unægteligt reducere samtidigheden hvis der udføres mange transaktioner i systemet.
noget af det smarte ved transaktioner er at de laves så de generer minimalt
selv med serializable, så garanterer den kun at transaktionerne får samme resultat som hvis de var udført serielt ikke at de faktisk bliver kørt serielt
to transaktioner der ikke berører samme data kan køre fuldt parallelt
derfor kan du selvfølgelig starte en anden transaktion selvom der er en igang
der er normal indbygget deadlock checks i database for at undgå situationen at connection 1 låser række A, connection 2 låser række B, connection 1 venter så på at få lov til at låse række B, connection 1 venter så på række A
de kan vente meget længe
i det hele taget er transaktioner beregnet til at gå hurtigt - vi snakker millisekunder
det er iøvrigt mest almindeligt at: - sætte transaction isolation level for connection i app (ikke globalt) - starte og commite transaktioner i app og ikke i SQL
Tak for de ekstra svar, og referencen til din fine artikel. Mht. isolationsniveauet, så har jeg også ændret det til at være session-baseret. Det kræver SUPER user privilegier at sætte det globalt, og så gælder det for samtlige databaser og forbindelser.
Jeg kan dog undre mig over, at nedenstående variabler alligevel afslører det globale isolationsniveau (REPEATABLE READ), og ikke det som jeg sætter i den aktuelle session (SERIALIZABLE):
Ja, det kan der være noget om. I øvrigt ser det ud til at virke som det skal, selv om tx_isolation indeholder en forkert værdi. Men tak for bug-linket :)
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.