{"id":469,"date":"2011-01-12T18:34:18","date_gmt":"2011-01-12T17:34:18","guid":{"rendered":"https:\/\/d-mueller.de\/blog\/?p=469"},"modified":"2016-01-12T00:00:55","modified_gmt":"2016-01-11T23:00:55","slug":"datenbank-transaktionen-von-akademischer-seite-behind-the-scenes","status":"publish","type":"post","link":"https:\/\/d-mueller.de\/blog\/datenbank-transaktionen-von-akademischer-seite-behind-the-scenes\/","title":{"rendered":"Datenbank-Transaktionen von akademischer Seite: Behind the Scenes"},"content":{"rendered":"<h2>Disclaimer<\/h2>\n<p>An diejenigen mit ordentlichen Vorkenntnissen auf dem Gebiet der Transaktionen: Bitte nicht Abschrecken lassen. Es geht nach der kurzgehaltenen Einf\u00fchrung noch ordentlich in die Tiefe.<\/p>\n<h2>Was sind Transaktionen<\/h2>\n<p>Nach Definition ist eine Transaktion eine <b>logische Arbeitseinheit, die entweder ganz oder garnicht<\/b> durchgef\u00fchrt wird. Bei einem Fehler wird die Datenbank also in den Zustand <b>vor<\/b> Ausf\u00fchrung der Transaktion versetzt, als ob nie etwas geschehen w\u00e4re.<\/p>\n<p>\n<b>Beispiel<\/b>: Wo k\u00f6nnte man Transaktionen mehr ben\u00f6tigen als auf einem Gebiet, auf dem Fehler richtig weh tun? Die Bankenwelt! Man stelle sich folgendes ultrasimples Datenmodell vor:<\/p>\n<pre data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">\r\nCREATE TABLE  `konto` (\r\n`kontonr` INT NOT NULL PRIMARY KEY ,\r\n`betrag` INT NOT NULL DEFAULT  &#039;0&#039;,\r\n`kundenid` INT NOT NULL\r\n) ENGINE = INNODB;\r\n\r\nCREATE TABLE  `ueberweisung` (\r\n`ueberweisungsid` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,\r\n`from_kontonr` INT NOT NULL ,\r\n`to_kontonr` INT NOT NULL ,\r\n`betrag` INT NOT NULL\r\n) ENGINE = INNODB;\r\n<\/pre>\n<p>Man beachte InnoDB als Engine, da MyISAM keine Transaktionen unterst\u00fctzt. Folgendes Codebeispiel (bitte wirklich genauer anschauen &#038; verstehen, ist wichtig f\u00fcr den Ablauf):<\/p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\n$conn = new PDO(&quot;mysql:host=localhost;dbname=konto&quot;, &quot;root&quot;, &quot;&quot;);\r\n$conn-&gt;setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);\r\n\r\ntry\r\n{\r\n\t$conn-&gt;exec(&quot;START TRANSACTION&quot;);\r\n\t\r\n\t$conn-&gt;exec(&#039;INSERT INTO ueberweisung (from_kontonr,to_kontonr,betrag) VALUES (1,2,400)&#039;);\r\n\t$conn-&gt;exec(&quot;UPDATE konto SET betrag = betrag - 400 WHERE kontonr = 1&quot;);\r\n\t\r\n\t\/\/etwas geht schief...\r\n\t$conn-&gt;exec(&quot;Korruptes SQL Statement, das einen Datenbankfehler symbolisiert&quot;);\r\n\t\r\n\t$conn-&gt;exec(&quot;UPDATE konto SET betrag = betrag + 400 WHERE kontonr = 2&quot;);\r\n\t\r\n\t$conn-&gt;exec(&quot;COMMIT&quot;); \/\/Transaktion abgeschlossen\r\n}\r\ncatch (Exception $e)\r\n{\r\n\tprint &quot;Error: &quot;.$e-&gt;getMessage();\r\n\t$conn-&gt;exec(&quot;ROLLBACK&quot;); \/\/Zur\u00fcck zum Zustand vor &quot;START TRANSACTION&quot;\r\n}\r\n<\/pre>\n<p>Hierbei verwende ich bewusst die expliziten SQL-Kommandos <b>START TRANSACTION<\/b>, <b>COMMIT<\/b> und <b>ROLLBACK<\/b> und nicht das in PDO bereits verzahnte Transaktions-Handling. Dazu sp\u00e4ter mehr. Ohne Transaktionen w\u00fcrde in obigem Fall das Geld von erstem Konto abgebucht werden, ohne jemals auf dem Zielkonto anzukommen. Man stelle sich anstelle von meinem korrupten SQL-Statement einen beliebigen Fehler vor (Stromausfall, Headcrash, &#8230;).<\/p>\n<p>Okay, haben wir den Sinn von Transaktionen gekl\u00e4rt.<\/b>\n<\/p>\n<h2>Savepoints<\/h2>\n<p>Das Ganze l\u00e4sst sich nun noch etwas feingranularer gestalten:<\/p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\n$conn-&gt;exec(&quot;START TRANSACTION&quot;);\r\n\r\ntry\r\n{\r\n\t$conn-&gt;exec(&#039;INSERT INTO ueberweisung (from_kontonr,to_kontonr,betrag) VALUES (1,2,400)&#039;);\r\n\t$conn-&gt;exec(&quot;UPDATE konto SET betrag = betrag - 400 WHERE kontonr = 1&quot;);\r\n\t$conn-&gt;exec(&quot;UPDATE konto SET betrag = betrag + 400 WHERE kontonr = 2&quot;);\r\n\t$conn-&gt;exec(&quot;SAVEPOINT ueberweisung1&quot;);\r\n}\r\ncatch (Exception $e)\r\n{\r\n\tprint &quot;Error in \u00dcberweisung 1: &quot;.$e-&gt;getMessage();\r\n\t$conn-&gt;exec(&quot;ROLLBACK&quot;);\r\n}\r\n\r\ntry\r\n{\r\n\t$conn-&gt;exec(&#039;INSERT INTO ueberweisung (from_kontonr,to_kontonr,betrag) VALUES (3,4,3600)&#039;);\r\n\t$conn-&gt;exec(&quot;UPDATE konto SET betrag = betrag - 3600 WHERE kontonr = 3&quot;);\r\n\t\r\n\t$conn-&gt;exec(&quot;Hier passiert was bl\u00f6des&quot;);\r\n\t\r\n\t$conn-&gt;exec(&quot;UPDATE konto SET betrag = betrag + 3600 WHERE kontonr = 4&quot;);\r\n}\r\ncatch (Exception $e)\r\n{\r\n\tprint &quot;Error in \u00dcberweisung 2: &quot;.$e-&gt;getMessage();\r\n\t$conn-&gt;exec(&quot;ROLLBACK TO ueberweisung1&quot;);\r\n}\r\n\r\n$conn-&gt;exec(&quot;COMMIT&quot;);\r\n<\/pre>\n<p>Nach der erfolgreichen \u00dcberweisung im ersten Block wird ein <a href=\"http:\/\/dev.mysql.com\/doc\/refman\/5.0\/en\/savepoint.html\">Savepoint<\/a> definiert (<b>SAVEPOINT ueberweisung1<\/b>). Der Fehler in der zweiten \u00dcberweisung f\u00fchrt nun nicht zu einem kompletten Rollback, die erste \u00dcberweisung bleibt bestehen. Nat\u00fcrlich w\u00fcrde man in der Praxis jede \u00dcberweisung in eine eigene Transaktion verpacken, dann w\u00fcrde mein Beispiel aber nicht mehr klappen ;).<\/p>\n<h2>Die Probleme von Transaktionen<\/h2>\n<p>Okay, bis hierher war ja alles cool. Nun stelle man sich aber mal Mehrbenutzerbetrieb vor. Verschiedene Transaktionen laufen <b>parallel<\/b> auf der Datenbank und operieren mit den gleichen Daten. Dazu ist hier exemplarisch ein Vorgang so dargestellt, wie er Datenbank-intern ablaufen k\u00f6nnte. Herr M\u00fcller mit Kontonummer 1 kauft sich ein neues Fahrrad f\u00fcr 400 Euro und bekommt parallel in der selben Mikrosekunde sein Gehalt von 2000\u20ac \u00fcberwiesen<\/p>\n<p>\n<a href=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/lostupdate.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/lostupdate.png\" alt=\"\" title=\"lost-update\" width=\"573\" height=\"145\" class=\"alignnone size-full wp-image-470\" \/><\/a>\n<\/p>\n<p><b>Schlecht, oder?<\/b> Wenn der Betriebssystem-Scheduler fies ist und genau so die Prozesse wechselt, ist das Gehalt weg. Aber die Datenbanksystem-Entwickler sind ja nicht auf der Wurstpelle dahergeschwommen. Daher:<\/p>\n<h2>ACID to the rescue<\/h2>\n<p>\nACID steht f\u00fcr <b>A<\/b>tomicity, <b>C<\/b>onsistency, <b>I<\/b>solation und <b>D<\/b>urability.\n<\/p>\n<ul>\n<li><b>Atomar<\/b> bedeutet in diesem Zusammenhang, dass Transaktionen als kleinstm\u00f6gliche Einheit gesehen werden. Das war das mit dem &#8222;entweder alles oder garnichts&#8220; aus der Definition.<\/li>\n<li><b>Kosistent<\/b> bedeutet, dass die Datenbank von einem konsistenten in einen anderen konsistenten Zustand \u00fcberf\u00fchrt wird.<\/li>\n<li><b>Isolation<\/b> bedeutet, dass sich die Transaktionen nicht gegenseitig beeinflussen d\u00fcrfen. Jede Transaktion soll denken, sie sei die einzige<\/li>\n<li><b>Dauerhaftigkeit<\/b> bedeutet, dass die Daten nach einem Commit dauerhaft gespeichert sein sollen<\/li>\n<\/ul>\n<h2>Die Probleme der Isolation<\/h2>\n<p>Nun unterscheidet man 3 gro\u00dfe Probleme bei parallel laufenden Transaktionen (Mehrbenutzerbetrieb). Eines davon habe ich weiter oben bereits auf dem Bild angesprochen: Lost Update.<\/p>\n<h3>Lost Update<\/h3>\n<p>\n<a href=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/lostupdate.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/lostupdate.png\" alt=\"\" title=\"lost-update\" width=\"573\" height=\"145\" class=\"alignnone size-full wp-image-470\" \/><\/a>\n<\/p>\n<p>Eine andere Transaktion operiert auf den gleichen Daten und resetted eine durchgef\u00fchrte \u00c4nderung.<\/p>\n<p><b>Ablauf beim Lost Update:<\/b><br \/>\n<a href=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/lostupdate-graph.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/lostupdate-graph.png\" alt=\"\" title=\"lostupdate-graph\" width=\"307\" height=\"98\" class=\"alignnone size-full wp-image-475\" srcset=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/lostupdate-graph.png 307w, https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/lostupdate-graph-300x95.png 300w\" sizes=\"auto, (max-width: 307px) 100vw, 307px\" \/><\/a><\/p>\n<p><h3>Dirty Read<\/h3>\n<p>Dirty Read bezeichnet das Lesen von noch nicht freigegebenen (comitteden) Daten. In folgendem Beispiel soll Mitarbeiter 2 doppelt soviel Gehalt bekommt wie Mitarbeiter 1. Gleichzeitig l\u00e4uft noch eine andere Transaktion, die das Gehalt von Mitarbeiter 1 um 400\u20ac erh\u00f6ht &#8211; aber abgebrochen wird.<\/p>\n<p><a href=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/dirtyread.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/dirtyread.png\" alt=\"\" title=\"dirtyread\" width=\"579\" height=\"187\" class=\"alignnone size-full wp-image-471\" srcset=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/dirtyread.png 579w, https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/dirtyread-300x96.png 300w\" sizes=\"auto, (max-width: 579px) 100vw, 579px\" \/><\/a><\/p>\n<p>Durch das Lesen der &#8222;schmutzigen&#8220; Gehaltserh\u00f6hung von Mitarbeiter 1 bekommt Mitarbeiter 2 nun mehr, als ihm eigentlich zusteht. Die Gehaltserh\u00f6hung kam n\u00e4mlich durch den rollback nie zum Tragen.<\/p>\n<p>\n<b>Ablauf beim Dirty Read:<\/b><br \/>\n<a href=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/dirtyread-graph.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/dirtyread-graph.png\" alt=\"\" title=\"dirtyread-graph\" width=\"304\" height=\"99\" class=\"alignnone size-full wp-image-474\" srcset=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/dirtyread-graph.png 304w, https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/dirtyread-graph-300x97.png 300w\" sizes=\"auto, (max-width: 304px) 100vw, 304px\" \/><\/a>\n<\/p>\n<h3>Non Repeatable Read<\/h3>\n<p>Eine Bank (mit bisher nur 2 Kunden) m\u00f6chte ermitteln, wieviel Geld alle Kunden gemeinsam auf ihren Konten haben. Parallel dazu finden aber zwei Abbuchungen statt.<\/p>\n<p><a href=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/nonrepeatableread.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/nonrepeatableread.png\" alt=\"\" title=\"nonrepeatableread\" width=\"583\" height=\"256\" class=\"alignnone size-full wp-image-473\" srcset=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/nonrepeatableread.png 583w, https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/nonrepeatableread-300x131.png 300w\" sizes=\"auto, (max-width: 583px) 100vw, 583px\" \/><\/a><\/p>\n<p>Nun ist also der alte Betrag von Konto 1 und der neue Betrag von Konto 2 in die Gesamtsumme eingegangen. Deswegen auch <b>non repeatable<\/b>: F\u00fchre ich die Summen-Abfrage ein zweites mal aus, kommt was anderes heraus.<\/p>\n<p>\n<b>Ablauf beim Non Repeatable Read:<\/b><br \/>\n<a href=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/nonrepeatableread-graph.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/nonrepeatableread-graph.png\" alt=\"\" title=\"nonrepeatableread-graph\" width=\"305\" height=\"102\" class=\"alignnone size-full wp-image-476\" srcset=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/nonrepeatableread-graph.png 305w, https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/nonrepeatableread-graph-300x100.png 300w\" sizes=\"auto, (max-width: 305px) 100vw, 305px\" \/><\/a>\n<\/p>\n<h2>Was nun? Sperren!<\/h2>\n<p>Das sind ja keine guten Voraussetzungen f\u00fcr saubere Transaktionen. Doch Sperren machens m\u00f6glich. <\/p>\n<ul>\n<li><b>Lesesperre:<\/b> M\u00f6chte eine Transaktion einen Wert lesen, setzt sie eine Lesesperre auf ihn. Nun darf keine andere Transaktion diesen Wert schreiben, bis die erste Transaktion nicht die Sperre (durch den commit) wieder freigegeben hat. Paralleles Lesen ist allerdings m\u00f6glich, d.h. mehrere Transaktionen d\u00fcrfen eine Lesesperre auf den selben Wert setzen.<\/li>\n<li><b>Schreibsperre:<\/b> Beim Schreiben setzt die Transaktion (logischerweise) eine Schreibsperre auf den Wert. Das bedeutet, dass der nun weder von anderen Transaktionen gelesen, noch geschrieben werden darf. Erst beim commit ist das dann wieder m\u00f6glich.<\/li>\n<\/ul>\n<p><b>Also alles gut jetzt?<\/b> Nein, nicht ganz. Wenn eine Transaktion viele Sperren gesetzt hat und sehr lange ausgef\u00fchrt wird, m\u00fcssen alle anderen Transaktionen, die auch gern eine Sperre auf die Daten h\u00e4tten, bis zum commit (=Freigabe der Sperren) warten. Auch <b>Deadlocks<\/b> sind m\u00f6glich:<\/p>\n<p>Transaktion 1 hat einen WriteLock auf Wert A und m\u00f6chte Wert B beschreiben, auf den allerdings Transaktion 2 einen WriteLock hat. Transaktion 2 m\u00f6chte Wert A beschreiben, der ja von Transaktion 1 gelocked ist. Bl\u00f6d, oder? In beiden angesprochenen F\u00e4llen (Deadlock und \u00fcberm\u00e4\u00dfige Wartezeit) muss das DBMS eine Transaktion abbrechen, um das Problem zu l\u00f6sen. Wie das nun intern aber genau geregelt ist, ist von System zu System verschieden.<\/p>\n<h2>Und noch ein Problem: Das Phantom!<\/h2>\n<p>Mit Sperren kann man also Probleme mit Deadlocks und lange laufenden Transaktionen bekommen. Was von Sperren ebenfalls nicht gel\u00f6st werden kann, ist das sogenannte Phantom-Problem. Eine Bank ist gro\u00dfz\u00fcgig und m\u00f6chte auf alle Konten einen Bonus von 313373\u20ac verteilen. W\u00e4hrend dieser Prozess l\u00e4uft, wird ein neues Konto in die Datenbank eingetragen.<\/p>\n<p><a href=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/phantom.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/phantom.png\" alt=\"\" title=\"phantom\" width=\"595\" height=\"183\" class=\"alignnone size-full wp-image-477\" srcset=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/phantom.png 595w, https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/01\/phantom-300x92.png 300w\" sizes=\"auto, (max-width: 595px) 100vw, 595px\" \/><\/a><\/p>\n<p>F\u00fchrt nun also dazu, dass der Bonus ungerecht verteilt wird, weil in der Zwischenzeit ein Konto mehr angelegt wurde. Mit Sperren haben wir keine Chance, dieses Problem zu l\u00f6sen, da alles mit rechten Dingen zugeht. Hier muss das Datenbanksystem intern selbst Buch f\u00fchren, um solche Vorg\u00e4nge zu vermeiden.<\/p>\n<h2>Das Isolations-Level<\/h2>\n<p>So, das ganze theoretische Vorwissen war n\u00f6tig, um das <a href=\"http:\/\/dev.mysql.com\/doc\/refman\/5.1\/de\/innodb-transaction-isolation.html\">Innodb-Transaktions-Isolationslevel<\/a> zu verstehen.<\/p>\n<p>In der MySQL-Konfigurationsdatei <i>my.ini<\/i> l\u00e4sst sich im Abschnitt <i>mysqld<\/i> folgende Einstellung t\u00e4tigen:<\/p>\n<pre data-enlighter-language=\"enlighter\" class=\"EnlighterJSRAW\">\r\ntransaction-isolation = {READ-UNCOMMITTED | READ-COMMITTED | REPEATABLE-READ | SERIALIZABLE}\r\n<\/pre>\n<p>Dabei entsprechen die verschiedenen Level genau den oben besprochenen Problemen der Isolation. Klar ist, dass Sperren und andere Vorsichtsma\u00dfnahmen Performance kosten. Wenn es also in meiner Anwendung von vornherein ausgeschlossen ist, dass es zu Non Repeatable Read-Problemen kommen kann, dann kann ich dem Datenbanksystem eine gewisse Last abnehmen und damit mehr Performance erreichen. Aber kurz zur Erkl\u00e4rung der Optionen:\n<\/p>\n<ul>\n<li><b>SERIALIZABLE<\/b>: <u>Ausgeschlossen:<\/u> lost-update, dirty read, non repeatable read, phantom \/ <u>M\u00f6glich:<\/u> &#8211;<\/li>\n<li><b>REPEATABLE-READ<\/b> (im \u00fcbrigen die Standardeinstellung von mysql): <u>Ausgeschlossen:<\/u> lost-update, dirty read, non repeatable read \/ <u>M\u00f6glich:<\/u> phantom<\/li>\n<li><b>READ-COMMITTED<\/b>: <u>Ausgeschlossen:<\/u> lost-update, dirty read \/ <u>M\u00f6glich:<\/u> non repeatable read, phantom<\/li>\n<li><b>READ-UNCOMMITTED<\/b>: <u>Ausgeschlossen:<\/u> lost-update \/ <u>M\u00f6glich:<\/u> dirty read, non repeatable read, phantom. Eine Transaktion hat hier Zugriff auf noch nicht festgeschriebene Daten. In MySQL \u00e4u\u00dfert sich das so, dass keine Lesesperren (ReadLock) gesetzt werden, wenn eine Select-Abfrage durchgef\u00fchrt wird.<\/li>\n<\/ul>\n<p><b>Lost Update<\/b> ist dabei also immer ausgeschlossen. Man kann wie man sieht nun sehr gut Einfluss auf die interne Handhabung der genannten Probleme nehmen und so einiges an Performance gewinnen, wenn man gewisse Probleme anwendungsbedingt nicht f\u00fcrchten muss.<\/p>\n<p>Mittels folgender Query kann ich auch aus dem Programm selbst das Verhalten anpassen:<\/p>\n<pre data-enlighter-language=\"enlighter\" class=\"EnlighterJSRAW\">\r\nSET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}\r\n<\/pre>\n<p>Im MySQL-Handbuch steht dazu:<\/p>\n<blockquote><p>\nStandardm\u00e4\u00dfig wird die Isolationsebene immer f\u00fcr die n\u00e4chste (noch nicht begonnene) Transaktion eingestellt. Mit dem Schl\u00fcsselwort GLOBAL stellt die Anweisung die Standard-Transaktionsebene global f\u00fcr alle neuen Verbindungen ein, die ab diesem Punkt aufgebaut werden (aber nicht f\u00fcr die schon bestehenden Verbindungen).\n<\/p><\/blockquote>\n<p>Leider muss das Isolations-Level auf diese Art und Weise mit einer harten Abfrage ge\u00e4ndert werden, da PHP (weder mit PDO noch MySQLi) einen Wrapper daf\u00fcr anbietet. In Java kann man sowas beispielsweise ganz smooth erledigen (mit JDBC):<\/p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\r\ncon.setTransactionIsolation(TRANSACTION_SERIALIZABLE);\r\ncon.getDefaultTransactionIsolation();\r\ncon.supportsTransactions(); \r\ncon.supportsTransactionIsolationLevel( )\r\n<\/pre>\n<p>Um damit mal ein paar Funktionen aufzulisten.<\/p>\n<h2>Transaktionshandling mit PDO<\/h2>\n<p>Wenn man sich auf die &#8222;Basics&#8220; beschr\u00e4nkt, kann man auch mit PDO gut mit Transaktionen umgehen:<\/p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\ntry \r\n{\r\n\t$conn-&gt;exec(&quot;SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE&quot;); \/\/kein wrapper daf\u00fcr in pdo\r\n\t$conn-&gt;beginTransaction();\r\n  \r\n\t$conn-&gt;exec(&#039;INSERT INTO ueberweisung (from_kontonr,to_kontonr,betrag) VALUES (3,4,3600)&#039;);\r\n\t$conn-&gt;exec(&quot;UPDATE konto SET betrag = betrag - 3600 WHERE kontonr = 3&quot;);\r\n\t\r\n\t$conn-&gt;exec(&quot;Hier passiert was bl\u00f6des&quot;);\r\n\t\r\n\t$conn-&gt;exec(&quot;UPDATE konto SET betrag = betrag + 3600 WHERE kontonr = 4&quot;);\r\n\t\r\n\t$conn-&gt;commit();\r\n} \r\ncatch (Exception $e) \r\n{\r\n\tprint &quot;Error: &quot;.$e-&gt;getMessage();\r\n\t$conn-&gt;rollBack();\r\n}\r\n<\/pre>\n<p>W\u00fcrde ich auch vorziehen, verglichen mit den direkten Queries von &#8222;START TRANSACTION&#8220; etc.<\/p>\n<h2>Implicit Commit<\/h2>\n<p>Es gibt (speziell bei strukturver\u00e4ndernden Operationen) sogenannte <a href=\"http:\/\/dev.mysql.com\/doc\/refman\/5.0\/en\/implicit-commit.html\">implicit commits<\/a>. Bedeutet, dass automatisch ein Commit ausgel\u00f6st wird, auch wenn im Code kein &#8222;COMMIT;&#8220; steht. Eine kleine Auswahl (vollst\u00e4ndig auf der verlinkten MySQL-manpage dazu):<\/p>\n<pre data-enlighter-language=\"enlighter\" class=\"EnlighterJSRAW\">\r\nCREATE DATABASE\r\nALTER DATABASE\r\nDROP DATABASE\r\nCREATE TABLE\r\nALTER TABLE\r\nDROP TABLE\r\nRENAME TABLE\r\nTRUNCATE TABLE\r\nCREATE INDEX\r\nDROP INDEX\r\n<\/pre>\n<p>Hei\u00dft im konkreten Fall am Beispiel:<\/p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\ntry \r\n{\r\n\t$conn-&gt;beginTransaction();\r\n\t\r\n\t$conn-&gt;exec(&#039;INSERT INTO ueberweisung (from_kontonr,to_kontonr,betrag) VALUES (1,2,3600)&#039;);\r\n\t$conn-&gt;exec(&quot;UPDATE konto SET betrag = betrag - 3600 WHERE kontonr = 1&quot;);\r\n\t\r\n\t$conn-&gt;exec(&quot;ALTER TABLE  `konto` ADD UNIQUE (`kundenid`)&quot;); \/\/implicit commit\r\n\t\r\n\t$conn-&gt;exec(&quot;Hier passiert was bl\u00f6des&quot;);\r\n\t\r\n\t$conn-&gt;exec(&quot;UPDATE konto SET betrag = betrag + 3600 WHERE kontonr = 2&quot;);\r\n\t\r\n\t$conn-&gt;commit();\r\n} \r\ncatch (Exception $e) \r\n{\r\n\tprint &quot;Error: &quot;.$e-&gt;getMessage();\r\n\t$conn-&gt;rollBack();\r\n}\r\n<\/pre>\n<p>Die 3600\u20ac kommen nie auf Konto 2 an, werden aber abgebucht. Der total zusammenhanglos dazwischenstehende ALTER TABLE Befehl f\u00fchrt wie beschrieben einen commit aus.<\/p>\n<h2>Finished<\/h2>\n<p>Ich hoffe, ich bin nicht der einzige der sowas tierisch interessant findet und es hat jemand bis hier ausgehalten. Fragen und Erg\u00e4nzungen wie immer gern in den Kommentaren. Ach, und vielen Dank an <a href=\"http:\/\/www.fbi.h-da.de\/organisation\/personen\/stoerl-uta.html\">Frau Prof. St\u00f6rl<\/a> f\u00fcr die exzellenten Anregungen und das ein oder andere Bild aus ihrem Script ;).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Disclaimer An diejenigen mit ordentlichen Vorkenntnissen auf dem Gebiet der Transaktionen: Bitte nicht Abschrecken lassen. Es geht nach der kurzgehaltenen Einf\u00fchrung noch ordentlich in die Tiefe. Was sind Transaktionen Nach Definition ist eine Transaktion eine logische Arbeitseinheit, die entweder ganz &hellip; <a href=\"https:\/\/d-mueller.de\/blog\/datenbank-transaktionen-von-akademischer-seite-behind-the-scenes\/\">Weiterlesen <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,9,6,3],"tags":[],"class_list":["post-469","post","type-post","status-publish","format-standard","hentry","category-php","category-datenbanken","category-security","category-webdev"],"_links":{"self":[{"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/posts\/469","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/comments?post=469"}],"version-history":[{"count":0,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/posts\/469\/revisions"}],"wp:attachment":[{"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/media?parent=469"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/categories?post=469"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/tags?post=469"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}