MySQL und Transaktionen anwenden
Timo Kluse
- datenbank
0 Frank (no reg)2 Ilja0 Reiner0 Ilja0 Frank (no reg)0 Ilja
0 Ludger
Komme gerade an meinen Problemen nicht wirklich weiter. Lange Suche und stöpern in diversen Dokus brachte mich auch nicht auf den Weg der Erkenntnis, so lege ich meine Hoffnung nun mal hier auf meine geistige Rettung :)
Das Kernproblem liegt im "wie setzte ich Transaktionen richtig ein". Aber genau da versagen bisher alle Dokus die ich gefunden habe, ein konkretes Beispiel scheint keiner auf die Reihe zu bringen. Obwohl das doch "so einfach" ist. Ist der Vorgang ok, kommt ein COMMIT ansonsten ein ROLLBACK. Hmpf, entweder suche/finde ich falsch, oder ... ka.
So, mal (etwas) vereinfacht ein Problem an dem ich sitze.
Tabelle Warteliste und Teilnehmer, unter gewissen Kriterien kommt jmd nun aus der Warteliste in die Teilnehmer.
Als Transaktion ausgedrückt, wäre das ja:
START TRANSACTION;
SELECT FROM Warteliste ... WHERE ...
INSERT INTO Teilnehmer ...
DELETE FROM Warteliste ...
COMMIT;
Also bis hier ja noch recht einfach ^^. Nur, wie entscheide ich richtig ob ich nun wirklich ein COMMIT oder ein ROLLBACK setze.
Zunächst muss ja vielleicht mal klar werden, was schief gehen kann.
Also, gesetz dem Fall ich verstehe die Problematik richtig, könnte nun eine Transaction (1) den Eintrag herauslesen, welcher nun in die Teilnehmer übernommen wird, fast gleichzeitig liesst nun eine andere Transaction (2) auch diesen Eintrag. Mit dem Resultat, dass nun beide Transaktionen versuchen den Eintrag in Teilnehmer zu schreiben und anschliessend zu löschen.
Und (vielleicht hier) auch eine entscheidende Frage, brauch ich das überhaupt?
Für mich sieht das ja erstmal nach dem Sinn von Transaktionen aus, NUR wenn der Member sowohl in der einen Tabelle eingetragen wird als auch aus der anderen gelöscht wird, ist der Vorgang konsistent, aber ...
Da ich bei Teilnehmer eh ein UNIQUE auf Member_ID habe, _kann_ dieser keine 2x eingetragen werden, und gelöscht werden muss er ja auf alle Fälle (richtig?) egal ob er nun schon vorher in Teilnehmer stand, oder kein weiteres mal eingetragen werden kann (weil schon vorhanden).
Und, wie würde es dann wirklich in der Praxis aussehen?
Wie würde man auf Fehler prüfen?
START TRANSACTION;
SELECT FROM Warteliste ... WHERE ...
INSERT INTO Teilnehmer ...
DELETE FROM Warteliste ...
SELECT FROM Teilnehmer ... WHERE ... (als Kontrolle)
und dann falls eingetragen
COMMIT;
ansonsten
ROLLBACK?
Oder würde hier eine Transaktion sogar nach hinten losgehen?
D.h. eine 2 Transaction versucht nun fast gleichzeitig einzutragen, aber entscheidet, das ein Fehler aufgetraten ist und führt ein ROLLBACK durch, wird dann auch der gelöschte Eintrag auch wieder hergestellt? obwohl er ja in der ersten Transaktion stimmig gelöscht wurde?
Es gibt auch eine andere Stelle, wo ich auch am grübeln bin.
(Beim ersten Beispiel, quält mich ja die Frage, ob eine Transaktion überhaupt notwendig und sinnvoll ist, oder nicht eher eine "Integritätsprüfung" mit UNIQUE der richtige Weg ist)
Beispiel, ein Member bekommt TANs zum Werben von neunen Member. Jede TAN kann nur 1x verwendet werden und wird dann ungültig. Ein (korrekter) Vorgang wäre dann.
-> auslesen ob TAN noch gültig ist
-> TAN ungültig setzen
-> Provision gutschreiben
So, aber wie überprüfe ich ob _während_ ich festgestellt habe die TAN ist ok, bis zum ungültig setzen der TAN, nicht eine andere Transaktion das gleiche feststellt?. Mit dem Resultat, dass die Provision 2x gutgeschrieben wird.
gruss
Timo
Hi,
vielleicht hast du in der Doku bislang nicht 100% nach dem _richtigen_
gesucht.
Ich kann dir bei mySQL leider nicht sagen, welche Programmiermöglich-
keiten bei Batches (mehrere sequentiell ausgeführte SQL Statements)
bestehen (Werte in Variablen ablegen, Fehler abfangen, Fehler erzeugen ...).
Kurzum, schaffe Bedingungen, die zu einem Rollback oder zum Fort-
setzen des Batches führen als Bindeglied zwischen den einzelnen Statements.
Gruß, Frank
yo,
Das Kernproblem liegt im "wie setzte ich Transaktionen richtig ein". Aber genau da versagen bisher alle Dokus die ich gefunden habe, ein konkretes Beispiel scheint keiner auf die Reihe zu bringen. Obwohl das doch "so einfach" ist.
ein konkretes beispiel wäre zum beispiel die überweisung bei einer bank. dort muss sichergestellt werden, dass das geld nicht nur auf dem ein konto abgeht, sondern auch auf dem anderen konto landet. ansonsten würde ja geld vernichtet, bzw. im positiven falle geld erzeugt werden. das wäre eine typische anwendung von transaktionen. der auftrag an das dbms ist, führe die transaktion ganz oder gar nicht durch.
nun ist es aber so, dass man hier nicht immer aktiv eingreifen muß, sondern das dbms übernimmt für dich diese aufgabe. tritt ein fehler auf, dann rollt es die ganze transaktion zurück, auch im dem falle, wenn in dem Programm ein Commit am ende steht. wenn du also ein transaktion ausführen willst, kannst du am ende ohne einschlechtes gewissen ein commit eingeben, ja viele anweisungen an ein dbms haben sogar ein auto-commit.
rollback "per hand" eingeben macht man in aller regel dann, wenn man zum beispiel an einer konsole daten verändern will und feststellt, moment, da habe ich doch einen denkfehler gehabt. oder mit anderen worten, ein commit bedeutet, ja ich will dass diese transaktion ausgeführt wird, bzw. ein rollback heißt soviel wie, nein ich habe meine meinung geändert. und trotzdem wird das dbms die transaktion zurück-rollen, falls was schief läuft.
deswegen haben programme in aller regel programme ihr commit zu stehen. was aber noch zusätzlich geleistet werden muss ist, ob eben die transaktion sauber durchgelaufen ist oder nicht, sprich nach dem obigen beispiel die meldung, Überweisung erfolgreich durchgeführt, bzw. Überweisung gescheitert.
ich hoffe, damit ist es ein wenig klarer geworden, was rollback und commit bewirkt.
Ilja
Hi,
Das Kernproblem liegt im "wie setzte ich Transaktionen richtig ein". Aber genau da versagen bisher alle Dokus die ich gefunden habe, ein konkretes Beispiel scheint keiner auf die Reihe zu bringen. Obwohl das doch "so einfach" ist.
ein konkretes beispiel wäre zum beispiel die überweisung bei einer bank. dort muss sichergestellt werden, dass das geld nicht nur auf dem ein konto abgeht, sondern auch auf dem anderen konto landet. ansonsten würde ja geld vernichtet, bzw. im positiven falle geld erzeugt werden. das wäre eine typische anwendung von transaktionen. der auftrag an das dbms ist, führe die transaktion ganz oder gar nicht durch.
nun ist es aber so, dass man hier nicht immer aktiv eingreifen muß, sondern das dbms übernimmt für dich diese aufgabe. tritt ein fehler auf, dann rollt es die ganze transaktion zurück, auch im dem falle, wenn in dem Programm ein Commit am ende steht. wenn du also ein transaktion ausführen willst, kannst du am ende ohne einschlechtes gewissen ein commit eingeben, ja viele anweisungen an ein dbms haben sogar ein auto-commit.
nein, ich denke, das war nicht die Frage!
Wenn es innerhalb einer Transaktion zum Absturz des Rechners/der DBMS kommt, so wird die Transaktion beim erneuten Hochfahren einem Rollback unterzogen. Dies passiert automatisch und bedarf keines Eingriffs.
Das genannte Szenario war aber, so wie ich es verstanden hatte, ein anderes:
Ein Datensatz aus A soll nach B verschoben werden, d.h. lese Daten aus A, speichere sie in B und lösche aus A.
Wenn jetzt ein zweiter Prozeß das gleiche macht, so kann dieser aus A lesen, in B speichern aber u.U. nicht mehr aus A löschen, weil der Datensatz nicht mehr in A vorhanden ist. Er wurde schon von dem ersten Prozeß gelöscht.
Dies führt zu einem SQL-Fehler (das Löschen aus A schlägt fehl), aber die Transaktion sollte dennoch abgeschlossen werden.
Wenn ich das richtig sehe, sollte somit der Datensatz aus A gelöscht sein, aber es bestehen zwei Datensätze in B (sofern nicht unique).
Man muß dann wohl per Hand einen Rollback veranlassen, sobald ein SQL-Statement fehlschlägt oder man setzt ein Lock.
siehe auch http://dev.mysql.com/doc/mysql/en/innodb-transaction-model.html
Gruß
Reiner
yo,
nein, ich denke, das war nicht die Frage!
nun ja, es gab ja auch mehr als eine frage. und ich wollte ihm ein gutes beispiel für eine traskation geben. ob damit alle fragen abgedeckt sind, keine ahnung. aber vielleicht hilft es ihm ein wenig weiter.
Wenn es innerhalb einer Transaktion zum Absturz des Rechners/der DBMS kommt, so wird die Transaktion beim erneuten Hochfahren einem Rollback unterzogen. Dies passiert automatisch und bedarf keines Eingriffs.
es passiert nicht nur ein rollback beim absturz des dbms, sondern generell wenn die transaktion nicht vollständig durchgeführt werden konnte. dies kann unterschiedliche ursachen haben, rechnerabsturz ist sicherlich einer davon. unter Oracle sollte dan der SMON dafür sorgen, dass alles wieder im konsistenten zustand gebracht wird.
Wenn jetzt ein zweiter Prozeß das gleiche macht, so kann dieser aus A lesen, in B speichern aber u.U. nicht mehr aus A löschen, weil der Datensatz nicht mehr in A vorhanden ist. Er wurde schon von dem ersten Prozeß gelöscht.
Dies führt zu einem SQL-Fehler (das Löschen aus A schlägt fehl), aber die Transaktion sollte dennoch abgeschlossen werden.
der versuch, einen datensatz zu löschen, der gar nicht vorhanden ist, wird keine fehlermeldung nach sich ziehen und es wird somit auch nicht die transaktion zurück gerollt. ich kann auf eine leere tabelle einen delete statement absetzten und er wird erfolgreich ausgeführt, egal wieviele datensätze davon betroffen wurden.
Ilja
Hi,
nein, ich denke, das war nicht die Frage!
Die Frage vom OP war wohl am ehesten, dass er eine explizite Transaktion starten kann und auch mit einem Commit beenden kann, er aber keine Chance kennt auf den Programmfluß Einfluß zu nehmen. Er weiß einfach nicht, wo jetzt da ein Rollback ausgelöst werden könnte wenn es gemacht werden müsste.
unter Oracle sollte dan der SMON dafür sorgen, dass alles wieder im konsistenten zustand gebracht wird.
Was tut dieser Hinweis auf Oracle zur Sache?
Leider weiß ich zuwenig über die Transaktionfähigkeiten von mySQL, deswegen möchte ich auch gar keine technische Debatte erst anfangen.
Grundsätzlich möchte ich nur noch bemerken ... der Grund für ein Rollback ist nicht per se ein Systemabsturz, es kann bei deinem Beispiel (Bank/Geldtransaktion) auch vorkommen, dass...
... die Zielkontonummer nicht existiert (respektive falsches Bank-Ziel)
... das Quellkonto nicht ausreichend gedeckt ist
... das Zielkonto durch staatliche Organe gesperrt worden ist
... u.v.m.
Gerade bei diesen genannten Punkten muss eine Entscheidung über ein Rollback innerhalb der Transaktion selbst gefällt werden.
Gut nacht,
Frank
yo,
ein wenig verwirrend, galt das nun mir oder reiner ?
Ilja
Hi,
ein wenig verwirrend, galt das nun mir oder reiner ?
Was war denn an meinem Beitrag für dich so verwirrend? Ich sehe ...
Durch die vielen »» hab ich wohl einen Teil von Reiner noch mit-
geschleift (was denn die Frage war oder auch nicht war).
:)
Gut Macht, Frank
Hi,
ein konkretes beispiel wäre zum beispiel die überweisung bei einer bank. dort muss sichergestellt werden, dass das geld nicht nur auf dem ein konto abgeht, sondern auch auf dem anderen konto landet. ansonsten würde ja geld vernichtet, bzw. im positiven falle geld erzeugt werden.
es wuerde weder Geld vernichtet noch Geld erzeugt werden, es sei denn der Vorgang geschieht bei der europaeischen Zentralbank, aber auch dieser Einwand ist nicht ganz ueberzeugend. Was glaubst Du uebrigens wie oft solche oder aehnliche Fehler in der Vergangenheit bereits passiert sind?
das wäre eine typische anwendung von transaktionen. der auftrag an das dbms ist, führe die transaktion ganz oder gar nicht durch.
Ergaenzend koennte man noch auf das Vorhandesein expliziter und implizierter Transaktionen verweisen.
nun ist es aber so, dass man hier nicht immer aktiv eingreifen muß, sondern das dbms übernimmt für dich diese aufgabe. tritt ein fehler auf, dann rollt es die ganze transaktion zurück, auch im dem falle, wenn in dem Programm ein Commit am ende steht. wenn du also ein transaktion ausführen willst, kannst du am ende ohne einschlechtes gewissen ein commit eingeben, ja viele anweisungen an ein dbms haben sogar ein auto-commit.
Kann MySQL bereits explizite Transaktionen?
Gruss,
Ludger