2012-11-17 13 views
5

Ich schreibe eine Prozedur, die Finical-Transaktionen in einer Live-Datenbank abgleicht. Die Arbeit, die ich mache, kann nicht als Set-Operation ausgeführt werden, daher verwende ich zwei verschachtelte Cursor.Korrekter Weg, eine exklusive Sperre zu übernehmen

Ich muss eine exklusive Sperre für die Transaktionstabelle nehmen, während ich pro Client abgleiche, aber ich möchte die Sperre freigeben und andere Personen ihre Abfragen zwischen jedem Client ausführen lassen, den ich verarbeite.

Ich würde gerne eine exklusive Sperre auf Zeilenebene anstelle einer Tischebene zu tun, aber what I have read so far sagt, dass ich nicht with (XLOCK, ROWLOCK, HOLDLOCK) tun können, wenn die anderen Transaktionen auf READCOMMITED Isolationsstufe ausgeführt wird (was es für mich ist).

Nehme ich eine Exclusionssperre auf Tabellenebene korrekt an, und gibt es in Server 2008 R2 eine Möglichkeit, Extensions auf Zeilenebene so einzurichten, wie ich möchte, ohne die anderen in der Datenbank ausgeführten Abfragen zu ändern?

declare client_cursor cursor local forward_only for 
    select distinct CLIENT_GUID from trnHistory 
open client_cursor 

declare @ClientGuid uniqueidentifier 
declare @TransGuid uniqueidentifier 

fetch next from client_cursor into @ClientGuid 
WHILE (@@FETCH_STATUS <> -1) 
BEGIN 
    IF (@@FETCH_STATUS <> -2) 
    BEGIN 
     begin tran 

     declare @temp int 

     --The following row will not work if the other connections are running READCOMMITED isolation level 
     --select @temp = 1 
    --from trnHistory with (XLOCK, ROWLOCK, HOLDLOCK) 
    --left join trnCB with (XLOCK, ROWLOCK, HOLDLOCK) on trnHistory.TRANS_GUID = trnCB.TRANS_GUID 
    --left join trnClients with (XLOCK, ROWLOCK, HOLDLOCK) on trnHistory.TRANS_GUID = trnClients.TRANS_GUID 
    --(Snip) --Other tables that will be "touched" during the reconcile 
    --where trnHistory.CLIENT_GUID = @ClientGuid 

     --Works allways but locks whole table. 
    select top 1 @temp = 1 from trnHistory with (XLOCK, TABLOCK) 
    select top 1 @temp = 1 from trnCB with (XLOCK, TABLOCK) 
    select top 1 @temp = 1 from trnClients with (XLOCK, TABLOCK) 
    --(Snip) --Other tables that will be "touched" during the reconcile 

     declare trans_cursor cursor local forward_only for 
       select TRANS_GUID from trnHistory where CLIENT_GUID = @ClientGuid order by TRANS_NUMBER 
     open trans_cursor 

     fetch next from trans_cursor into @TransGuid 
     WHILE (@@FETCH_STATUS <> -1) 
     BEGIN 
      IF (@@FETCH_STATUS <> -2) 
      BEGIN 

       --Do Work here 

      END 
      fetch next from trans_cursor into @TransGuid 
     END 

     close trans_cursor 
     deallocate trans_cursor 

      --commit the transaction and release the lock, this allows other 
      -- connections to get a few queries in while it is safe to read. 
     commit tran 
    END 

    fetch next from client_cursor into @ClientGuid 
END 

close client_cursor 
deallocate client_cursor 
+0

Ich versuche herauszufinden, warum Sie eine exklusive Sperre benötigen. Werden wahrscheinlich andere Personen Datensätze einfügen? Andere Leute aktualisieren Datensätze? Sind Sie besorgt, dass andere Menschen die Daten nicht einheitlich sehen? – Laurence

+0

@Laurence Ich mache mir Sorgen, dass andere Leute einen inkonsistenten Blickwinkel bekommen. Ich versuche, einen Fehler zu korrigieren, der einen kleinen Prozentsatz der Clients betrifft, aber dieser Korrekturprozess hinterlässt mehrere voneinander abhängige Zeilen in mehreren Tabellen (ich werde tatsächlich 5 Tabellen sperren, aber mein Codebeispiel auf eine Tabelle vereinfachen) in einem inkonsistenten Zustand Korrekturprozess. Die Inkonsistenz ist pro Client isoliert, aber wenn man einen 'SELECT SUM (ColA) FROM trnHistory' über diesen einen Client ausgibt, würde während des "Korrektur" -Prozesses ein falscher Wert zurückgegeben werden. Also muss ich eine exklusive Sperre nehmen, um Lesevorgänge zu verhindern. –

+0

Ich verstehe nicht, warum eine Transaktion Sie nicht davor schützt, es sei denn, Sie haben Leute, die read_uncommitted tun. – Laurence

Antwort

3

Wenn Sie nur Sorgen um andere Leser sind, dann müssen Sie sollten nicht exklusive Sperren, um das Muster

Begin Transaction 

    Make Data Inconsistent 

    Make Data Consistent 

Commit Transaction 

sollte in Ordnung sein. Die einzigen Sitzungen, die inkonsistente Daten anzeigen, sind die, die nolock oder Read Uncommitted verwenden, oder solche, die mehrere konsistente Lesevorgänge ohne Verwendung von Repeatable Rows oder Serializable erwarten.

Als Antwort auf die Frage, ist der richtige Weg, eine exklusive Sperre zu nehmen, meiner Meinung nach, Dinge so zu arrangieren, dass die Engine es für Sie erledigt.

9

Ich konnte nicht glauben, dass ein XLOCK keine gleichzeitige Leser bei read committed blockieren würde, damit ich es gerade wiedergegeben: Es ist wahr. Drehbuch:

Session 1:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED 
BEGIN TRAN 

SELECT * FROM T WITH (ROWLOCK, XLOCK, HOLDLOCK /*PAGLOCK, TABLOCKX*/) WHERE ID = 123 

Session 2:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED 
BEGIN TRAN 

SELECT * FROM T WHERE ID = 123 

Plug-in einigen Tabellennamen, die Sie zur Hand haben. Sitzung 2 wird nicht blockiert.

Ich habe auch versucht, eine PAGLOCK, aber das hat auch nicht funktioniert. Als nächstes habe ich versucht TABLOCKX aber das hat auch nicht funktioniert!

Ihre Tabelle-Lock-basierte Strategie funktioniert nicht. Ich denke, Sie werden die Leser zu ändern, so dass sie entweder

  1. Verwendung Snapshot-Isolation eine konsistente Sicht zu erhalten (Stand vor irgendwelchen schreibt)
  2. eine höhere Isolationsstufe verwenden, um durch den Autor blockiert

Natürlich gibt es einen bösen Workaround, um wirklich, wirklich die Tabelle zu sperren: Ändern Sie sein Schema. Dies wird eine Sch-M Sperre, die im Grunde jeden Zugriff auf die Tabelle widerspricht. Es gilt sogar für einige Metadaten-Leseoperationen. Es könnte so aussehen:

--just change *any* setting in an idempotent way 
ALTER TABLE T SET (LOCK_ESCALATION = AUTO) 

Ich habe dies getestet, um zu arbeiten.


Ist SQL Server nicht richtig gehorchen XLOCK? Oder ist das ein Fehler im Produkt? Ich denke, es ist richtig, weil es den dokumentierten Eigenschaften von READ COMMITTED entspricht. Auch unter Verwendung von SERIALIZABLE gibt es Fälle, in denen eine Transaktion eine Zeile exklusiv sperren kann und eine andere dieselbe Zeile lesen kann! Dies kann bei Vorhandensein von Indizes passieren. Eine Transaktion blockiert möglicherweise den nicht gruppierten Index IX_T_SomeCol, während ein anderer glücklicherweise den Clustered-Index PK_T ausliest.

So ist es eigentlich ganz normal, dass Transaktionen selbst in Anwesenheit von exklusiven Sperren unabhängig voneinander ausgeführt werden können.

+2

Ich habe festgestellt, dass nur wenn ich REPEATABLE READ ODER SERIALIZABLE Isolationsstufen beim Lesen sql Blöcke verwendet (wenn nicht commomed rowlock + xlock existiert) – SalientBrain

+1

Ja, nur Isolationsstufe REPEATABLE READ ODER SERIALIZABLE könnte die Sitzung 2 blockieren, weil obwohl Sitzung XLOCK erhält , und obwohl Sie die Transaktionen nicht festlegen, wird in Sitzung 1 XLOCK bereits freigegeben, nachdem das Lesen abgeschlossen ist. Es hielt es nicht länger aus. – Xin

+0

@Xin hielt es wegen 'HOLDLOCK'. Dies ist identisch mit 'SERIALIZABLE' und enthält alle Sperren bis zum Ende der Transaktion. – usr

Verwandte Themen