2012-09-24 10 views
8

Ich führe mehrere lang andauernde SQL-Abfragen als Teil eines Berichtsmoduls aus. Diese Abfragen werden zur Laufzeit dynamisch erstellt. Abhängig von der Eingabe des Benutzers können sie Einzel- oder Mehrfachanweisungen sein, einen oder mehrere Parameter haben und an einer oder mehreren Datenbanktabellen arbeiten - mit anderen Worten, ihre Form kann nicht leicht vorhergesehen werden.Verwenden Sie SqlTransaction & IsolationLevel für längere Lesevorgänge?

Derzeit bin ich gerade diese Aussagen auf einer gewöhnlichen SqlConnection Ausführung, dh

using (SqlConnection cn = new SqlConnection(ConnectionString)) { 
    cn.Open(); 
    // command 1 
    // command 2 
    // ... 
    // command N 
} 

Da diese Abfragen (wirklich Chargen abfragen) kann eine Weile dauern auszuführen, ich bin besorgt über Sperren für Tabellen liest hält/schreibt für andere Benutzer. Es ist kein Problem, wenn sich die Daten für diese Berichte während der Ausführung des Stapels ändern. Die Berichtsabfragen sollten niemals Vorrang vor anderen Operationen in diesen Tabellen haben und sie auch nicht sperren.

Für die meisten Operationen mit langer Laufzeit und mehreren Anweisungen, bei denen Daten geändert werden müssen, würde ich Transaktionen verwenden. Der Unterschied besteht darin, dass diese Berichtsabfragen keine Daten ändern. Würde ich diese Berichtsabfragen korrekt in eine umhüllen, um ihre Isolationsstufe zu kontrollieren?

heißt:

using (SqlConnection cn = new SqlConnection(ConnectionString)) { 
    cn.Open(); 

    using (SqlTransaction tr = cn.BeginTransaction(IsolationLevel.ReadUncommitted)) { 
     // command 1 
     // command 2 
     // ... 
     // command N 

     tr.Commit(); 
    } 
} 

Wäre dies mein gewünschtes Ergebnis erreichen? Ist es richtig, eine Transaktion zu begehen, obwohl keine Daten geändert wurden? Gibt es einen anderen Ansatz?

+0

Haben die Berichte sein müssen [richtig] (http://blogs.msdn.com/b/sqlcat/archive/2007/02/01/previously-committed-rows-might- be-missed-if-nolock-Hinweis-is-used.aspx) oder nicht? –

+0

@RemusRusanu Die Berichte liefern eine Momentaufnahme der Daten über einen langen Zeitraum hinweg. Sie müssen Änderungen, die während der Transaktion auftreten können, nicht berücksichtigen. Die zugrunde liegenden Abfragen werden vom Benutzer wahrscheinlich mehrere Male hintereinander ausgeführt, und der Benutzer erwartet nicht, dass die Ergebnisse jedes Mal identisch sind. –

+1

Dies ist genau die Art von Berichten, die (schlecht) von Dirty Reads betroffen sind. Zählungen und Aggregate springen zufällig nach oben und unten und Daten innerhalb eines einzelnen Laufs (eines Berichts) sind nicht konsistent (z. B. Summenbelastung! = Summengutschrift). Ihre Benutzer werden das Vertrauen in den Bericht verlieren, da sie zufällige Daten zu produzieren scheinen. Haben Sie stattdessen ['SNAPSHOT'] (http://msdn.microsoft.com/en-us/library/ms345124 (v = sql.90) .aspx) in Betracht gezogen? –

Antwort

5

Ein weiterer Ansatz könnte zur Ausgabe sein, gegen die Verbindung:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; 

, die die gleiche Absicht erreicht, ohne mit einer Transaktion durcheinander. Oder Sie könnten den WITH(NOLOCK) Hinweis auf die Tabellen in Ihrer Abfrage verwenden, was den Vorteil hat, die Verbindung überhaupt nicht zu ändern.

Wichtig ist zu beachten, dass (ungewöhnlich): jedoch es geändert wird (Transaktion, Transaktions-scope, explizite SET, etc), wobei die Isolationsstufe nicht Reset zwischen Verwendungen der gleichen zugrundeliegenden Verbindung ist wenn es aus dem Pool geholt wird. Dies bedeutet, dass, wenn Ihr Code die Isolationsstufe (direkt oder indirekt) ändert, dann keine Ihrer Code weiß, was die Isolationsstufe einer neuen Verbindung ist:

using(var conn = new SqlConnection(connectionString)) { 
    conn.Open(); 
    // isolation level here could be **ANYTHING**; it could be the default 
    // if it is a brand new connection, or could be whatever the last 
    // connection was when it finished 
} 

Welches ist die WITH(NOLOCK) sehr verlockend macht.

+0

.... zu kurze Erklärungen Ihrer Vorschläge. – ulrichb

+1

Wow, danke für die Warnung über die Einstellung der Isolationsstufe ... das könnte gefährlich sein. –

+0

Durch das Festlegen der Isolationsstufe durch BeginTransaction vermeiden wir das von Ihnen erwähnte Problem "Isolationsstufe nicht zwischen den Verbindungen zurückgesetzt"? Wenn ja, gibt es einen Nachteil bei der Verwendung einer Transaktion? – digEmAll

1

ich mit Marc einverstanden, aber alternativ können Sie den NOLOCK-Abfragehinweis auf den betroffenen Tabellen verwenden. Dies gibt Ihnen die Möglichkeit, es auf Tabellenebene auf Tabellenebene zu steuern.

Das Problem bei allen Fragen läuft ohne gemeinsame Sperren zu nehmen, dass Sie sich offen für „nicht-deterministisch“ Ergebnisse und Geschäftsentscheidungen gemacht werden sollten nicht auf diese Daten zu verlassen.

Ein besserer Ansatz könnte darin bestehen, die Isolationsstufen SNAPSHOT oder READ_COMMITED_SNAPSHOT zu untersuchen. Diese bieten Ihnen Schutz vor Transaktionsanomalien, ohne dass Sie Schlösser nehmen müssen. Der Kompromiss besteht darin, dass sie IO gegenüber TempDB erhöhen. Jede dieser Ebenen kann entweder an der Sitzung angewendet werden, wie Marc vorgeschlagen oder die Tabelle als ich.

hoffe, das hilft

Verwandte Themen