Ist es korrekt, bei StoredProcedures, in denen ausschließlich etwas gelesen wird, die zugeordnete Transaction mit einem Rollback zu beenden oder ist immer ein Commit vorzuziehen?
Ich bin bisher immer davon ausgegangen, dass ich nur dann ein Commit benötige, wenn Daten irgendwie verändert wurden.
Und bei StoredProcedures, die -ohne visuelle Komponenten- nur bspw. einen Wert lesen, benutze ich kein Retaining, gleich ob Rollback oder Commit.
Transactions für StoredProcedures
Moderator: martin.koeditz
Innerhalb von Function/Proceduren ist Commit/Rollback verboten.
Soviel erst mal dazu.
Vom Client aus muss man tatsächlich immer eine neue Transaktion starten, dann Daten lesen und anschließend committen.
Dies liegt an der grundsätzlichen Versionierung von Daten in der Firebird.
Wenn Daten verändert werden (Update/Delete/Insert), wird eine neue Satzversion mit der aktuellen Transaktionsid angelegt.
Die vorherigen Daten werden nur als ungültig markiert, zumal ggf. noch ein Rollback möglich ist.
Sollte dein Clientprozess eine offene Verbindung mit einer offenen Transaktion haben, erhält nun dieser noch immer die alten Daten, da deine Transaktionsid älter ist, als die der neu veränderten Daten.
Mit jedem Start einer neuen Transaktion, können die alten Daten dann automatisch entfernt werden.
Hat nur ein Prozess eine Transaktion ewig lange offen, führt dies ebenso zu einem starken Anwachsen der Datenbank sowie zu z.T. massiven Performanceeinbrüchen, da die vorherigen Satzversionen einer Tabelle immer überlesen werden müssen. Firebird führt da keinen Index um die Daten schneller zu finden.
Beim Überlesen wird geprüft, ob die Transaktionsid der markierten Daten älter ist, als die älteste offene Transaktion, und gibt diese Datenblöcke dann auch tatsächlich für die Wiederverwendung frei.
Parallel dazu wird auch ab und an ein Sweeper-Prozess gestartet, der alle alten Daten sucht um an Hand der ältesten Transationsid diese dann aufzuräumen, was allerdings auch nicht immer klappt.
Dazu sollte man sich regelmäßig die MON$DATABASE ansehen. Wenn die Differenz der ältesten und nächsten Transaktionsid dei 100.000 übersteigt, ist ein Backup/Restore erforderlich um die Datenbank mal wieder gründlich aufzuräumen.
Soviel erst mal dazu.
Vom Client aus muss man tatsächlich immer eine neue Transaktion starten, dann Daten lesen und anschließend committen.
Dies liegt an der grundsätzlichen Versionierung von Daten in der Firebird.
Wenn Daten verändert werden (Update/Delete/Insert), wird eine neue Satzversion mit der aktuellen Transaktionsid angelegt.
Die vorherigen Daten werden nur als ungültig markiert, zumal ggf. noch ein Rollback möglich ist.
Sollte dein Clientprozess eine offene Verbindung mit einer offenen Transaktion haben, erhält nun dieser noch immer die alten Daten, da deine Transaktionsid älter ist, als die der neu veränderten Daten.
Mit jedem Start einer neuen Transaktion, können die alten Daten dann automatisch entfernt werden.
Hat nur ein Prozess eine Transaktion ewig lange offen, führt dies ebenso zu einem starken Anwachsen der Datenbank sowie zu z.T. massiven Performanceeinbrüchen, da die vorherigen Satzversionen einer Tabelle immer überlesen werden müssen. Firebird führt da keinen Index um die Daten schneller zu finden.
Beim Überlesen wird geprüft, ob die Transaktionsid der markierten Daten älter ist, als die älteste offene Transaktion, und gibt diese Datenblöcke dann auch tatsächlich für die Wiederverwendung frei.
Parallel dazu wird auch ab und an ein Sweeper-Prozess gestartet, der alle alten Daten sucht um an Hand der ältesten Transationsid diese dann aufzuräumen, was allerdings auch nicht immer klappt.
Dazu sollte man sich regelmäßig die MON$DATABASE ansehen. Wenn die Differenz der ältesten und nächsten Transaktionsid dei 100.000 übersteigt, ist ein Backup/Restore erforderlich um die Datenbank mal wieder gründlich aufzuräumen.
Seit ich im Firebird-Buch folgendes gelesen habe:
"Transactions in the rolled-back state are not garbage-collected. They remain interesting until a database sweep tags them as “committed” and releases them for garbage collection. In systems with low contention, a periodic manual sweep may be all that’s needed to deal with them."
commite ich auch alle Transaktionen, die nur lesen (und setze bei denen explizit das "read only" Flag).
"Transactions in the rolled-back state are not garbage-collected. They remain interesting until a database sweep tags them as “committed” and releases them for garbage collection. In systems with low contention, a periodic manual sweep may be all that’s needed to deal with them."
commite ich auch alle Transaktionen, die nur lesen (und setze bei denen explizit das "read only" Flag).
--
Joachim
Joachim
Wobei das Buch wohl noch nicht aktualisiert wurde.
https://firebirdsql.org/manual/gfix-housekeeping.html
Wichtig dabei ist das "cooperative" Garbage. Sobald also eine Tabelle mit alten Transaktionsdaten gelesen wird und die älteste Transaktionsid ist jünger, werden diese Daten entfernt.
Aber wie bereits geschrieben, ein regelmäßiger Backup/Restore wirkt oft wahre Wunder.
Eine ca. 70GB Datenbank wird dadurch am Wochenende in 2,5 Stunden (ca. 40 Minuten Backup, Rest Restore) wieder auf ca. 45GB reduziert.
https://firebirdsql.org/manual/gfix-housekeeping.html
Wichtig dabei ist das "cooperative" Garbage. Sobald also eine Tabelle mit alten Transaktionsdaten gelesen wird und die älteste Transaktionsid ist jünger, werden diese Daten entfernt.
Aber wie bereits geschrieben, ein regelmäßiger Backup/Restore wirkt oft wahre Wunder.
Eine ca. 70GB Datenbank wird dadurch am Wochenende in 2,5 Stunden (ca. 40 Minuten Backup, Rest Restore) wieder auf ca. 45GB reduziert.
Erst einmal vielen Dank für Eure Hinweise!
Meine Fragestellung war wohl etwas ungenau. Deshalb konkretisiere ich das noch mal.
Ich habe im Server eine SP, die bspw. nur einen Wert liest: select id from tabelle where wert=:pWert
Im Client (bspw. Delphi oder Lazarus) benutze ich eine dynamisch erzeugte Query, die genau diese SP einmal ausführt:
1. Create Query und Transaction
2. Besetze SQL-Statement
3. Besetze Input-Parameter
4. Öffnen der Query
5. Lesen der Output-Felder
6. Query schließen
7. Transaction Commit oder Rollback
8. Query und Transaction freigeben
Ich möchte nun gern wissen, ob in diesem rein-lesenden Fall ein Rollback dem Commit vorzuziehen ist. Sobald ich irgend etwas Schreibe oder Update oder Lösche ist mir natürlich klar, dass ich Committen muss.
Die Transaction ist im Übrigen wirklich nicht lange offen, nur für diesen einen Lesevorgang.
Meine Fragestellung war wohl etwas ungenau. Deshalb konkretisiere ich das noch mal.
Ich habe im Server eine SP, die bspw. nur einen Wert liest: select id from tabelle where wert=:pWert
Im Client (bspw. Delphi oder Lazarus) benutze ich eine dynamisch erzeugte Query, die genau diese SP einmal ausführt:
1. Create Query und Transaction
2. Besetze SQL-Statement
3. Besetze Input-Parameter
4. Öffnen der Query
5. Lesen der Output-Felder
6. Query schließen
7. Transaction Commit oder Rollback
8. Query und Transaction freigeben
Ich möchte nun gern wissen, ob in diesem rein-lesenden Fall ein Rollback dem Commit vorzuziehen ist. Sobald ich irgend etwas Schreibe oder Update oder Lösche ist mir natürlich klar, dass ich Committen muss.
Die Transaction ist im Übrigen wirklich nicht lange offen, nur für diesen einen Lesevorgang.
Beim Lesen ist Rollback oder Commit vollkommen egal.
In beiden Fällen wird die Transaktion abgeschlossen.
Zu kontrollieren i.Ü. über MON$TRANSACTIONS.
In beiden Fällen wird die Transaktion abgeschlossen.
Zu kontrollieren i.Ü. über MON$TRANSACTIONS.