ADO.NET Transaktions Isolation Level
-
Moin!
Ich habe gerade festgestellt, das der Isolation-Level nur in einer Transaktion übergeben werden kann (DbConnection.BeginTransaction). Was mache ich aber, wenn ich einfach nur Daten selektieren will, ohne diese zu bearbeiten? Trotzdem eine Transaktion starten?!
Folgendes Problem:
Session A schickt folgendes Statement in einer Transaktion ab:update MEINETABELLE set FELD = wert
Session B selektiert nun diese Tabelle, bevor Session A commited.
select * from MEINETABELLE
Ergebnis: Session B hängt fest, bis Session A commit oder rollback abschickt. Dies lässt sich vermeiden, wenn man den Transaktions-Isolationslevel auf "Read Commited" setzt. Dann würde Session B die Daten ohne die Änderungen von Session A lesen (was in meinem Fall auch richtig ist). Seit ADO.NET ist dieses Flag aber nicht mehr Verbindungsglobal sondern wird pro Transaktion übergeben. Ist das korrekt? Muss ich für ein einfaches select dann auch eine extra Transaktion starten?
-
Kommt darauf an welches DBMS Du einsetzt. In Postgres beispielsweise wird MVCC eingesetzt (Multi-Version-Control) so daß Readers nicht auf Writers warten und umgekehrt. Wenn Dein DBMS das nicht unterstützt mußt Du eben den SELECT auch in einer Transaktion kapseln.
-
@witte:
Er.
MVCC (PostgreSQL) ist wenn ich das richtig verstehe SNAPSHOT ISOLATION (MSSQL).
Was nu aber mit den einzelnen SELECTs vs. Transaktionen nix zu tun hat.Normalerweise macht das DBMS ein implizites BEGIN/.../COMMIT wenn man einzelne Statements ohne aktuelle Transaktion rausknallt.
Und deine Antwort hat auch mit der Frage von Cpp_Junky nicht wirklich was zu tun. Und ganz davon abgesehen, dass SNAPSHOT ISOLATION nicht immer die Beste Wahlt (tm) ist, weil es vermehrt dazu führt dass Transaktionen fehlschlagen. Wenn eine Anwendung damit schlecht klarkommt, ist READ (UN)COMMITTED sicherlich besser.
@Cpp_Junky:
Check nochmal die Doku zu ADO .NET. Für ein einfaches SELECT muss man normalerweise nirgends eine eigene Transaktion aufmachen, das passiert implizit. Und da es implizit passiert, muss man auch irgendwo irgendwie den Isolation Level umstellen können. Wenn es in ADO .NET wirklich nix geben sollte (was ich bezweifle), hilft u.U. noch der Connection-String -- da kann man normalerweise auch das Default Isolation Level setzen.
-
hustbaer schrieb:
@witte:
Er.
MVCC (PostgreSQL) ist wenn ich das richtig verstehe SNAPSHOT ISOLATION (MSSQL).
Was nu aber mit den einzelnen SELECTs vs. Transaktionen nix zu tun hat.Es geht simpel darum dass Postgres für einzelne Änderungen verschiedene Rowversionen vorhält, sodaß der beschriebene Lock dort nicht auftritt. Das hat m.E. nichts mit den Isolierungsebenen zu tun, außer das READ UNCOMMITTED automatisch auf READ COMMITTED hochgestuft werden muß/wird.
Wenn ich versuche dass auf einen SQL 2005-Server in zwei Shells nachzuvollziehen, hängt die Shell mit der SELECT-Anweisung tatsächlich, bis ein Commit oder Rollback der anderen Transaktion stattfindet. Dabei habe ich jedoch Deinen beschriebenen SNAPSHOT_level nicht ausprobieren können sondern nur READ COMMITTED. Es geht mir nicht darum welches System besser ist -ich setze selber beide ein- will damit nur sagen dass ich dem OP nicht besser helfen kann wenn er nicht angibt welches DBMS er verwendet.hustbaer schrieb:
Normalerweise macht das DBMS ein implizites BEGIN/.../COMMIT wenn man einzelne Statements ohne aktuelle Transaktion rausknallt.
Klar, Autocommit halt.
hustbaer schrieb:
Und deine Antwort hat auch mit der Frage von Cpp_Junky nicht wirklich was zu tun. Und ganz davon abgesehen, dass SNAPSHOT ISOLATION nicht immer die Beste Wahlt (tm) ist, weil es vermehrt dazu führt dass Transaktionen fehlschlagen. Wenn eine Anwendung damit schlecht klarkommt, ist READ (UN)COMMITTED sicherlich besser.
Kein Mensch hat ihm eine Isolierungsebene vorgeschlagen, er wollte selber READ COMMIT einsetzen.
@Cpp_Junky:
Zur Not kannst Du das SELECT in einer Transaktion kapseln um die Isolierungsebene festzulegen. Alternativ unterstützen die meisten System ein SET TRANSACTION [ISOLATION] LEVEL <Isolierungsebene>; was Du innerhalb der Verbindung vor dem Lesen setzen könntest. Aber wie gesagt bei MS SQL 2005 hat es in zwei verschiedenen Shells nicht geholfen, vllt solltest Du erstmal sagen welches DBMS Du einsetzt.
-
Momentan handelt es sich um einen Firebird 2.1 Server. Allerdings sollte die Lösung nichts DB spezifisches sein, da die Schnittstelle u.a auch für eine Oracle 10 DB verwendet wird.
Früher war es unter ADO ganz einfach. Da konnte man den Isolations-Level global über das Connection-Objekt setzenpConnection->PutIsolationLevel(ADO::adXactReadCommitted);
... und alle geöffneten Recordsets wurden automatisch "ReadCommitted" behandelt. Wenn das nichts anderes war, als das der Treiber im Hintergrund eine Transaktion mit den entsprechenden Parametern gestartet hat, dann kann ich das jetzt natürlich genauso gut "zu Fuss" machen. Mir war jedoch der Ansatz nicht bekannt, das man ein SELECT in einer Transaktion ausführen muss.
-
So ich habe das mal auf einer Oracle 10.0.2EE getestet. Da läuft es auch ohne Verklemmung durch. Firebird kann ich nicht testen, habe keinen Server. Es scheint so dass man bei Firebird und Ado.Net eine Transaktion öffnen muß um die Isolierungsebene festlegen zu können:
http://www.firebirdsql.org/dotnetfirebird/transaction-isolation-levels.html
Es ist aber unklar was Du vorhast, der READ UNCOMMITTED wird von Firebird nicht unterstützt, dann müßte doch READ COMMITTED die Standardebene sein oder? Warum mußt Du sie dann erst einschalten?
Was Du Dir alternativ noch anschauen könntest wäre das Entity Framework, was von Firebird seit Anfang dieses Jahres (und Oracle sowieso) unterstützt wird. Dort arbeitest Du unabhängig von einem spezifischen Datenbanksystem was die späteren Betrieb unter Oracle erleichtert. Allerdings steckt EF noch in den Kinderschuhen, Du mußt erst prüfen ob es für Dich in der Produktion einsetzbar ist.
-
So, ich habe das Rätsel inzwischen einigermaßen gelöst. Ich benötige für meine Zwecke folgende Konfiguration:
- Iso Level READ COMMITTED
- NO WAIT (nicht auf Änderungen fremder Transaktionen warten)
- Rec Version (Letzte committete Version des Records lesen)Diese Parameter lassen sich unter ADO.NET jedoch nur beim Starten einer Transaktion setzen. Das heisst, ich bin gezwungen jedes SELECT Statement in einer Transaktion zu kapseln. Das finde ich ziemlich unschön, da diese Transaktion mindestens bestehenbleiben muss, bis ich alle Ergebnis-Records ausgelesen habe. Wenn ich jetzt zwischendurch Änderungen mache, werden diese erst wirksam wenn die Übergeordnete "SELECT-Transaktion" abgeschlossen wurde, oder? Irgendwie ist das doch alles Quark