2012-11-17 15 views
5

Piszę procedurę, która będzie uzgadnianie transakcji finansowych w żywej bazie danych. Praca, którą wykonuję, nie może być wykonana jako operacja ustawiona, więc używam dwóch zagnieżdżonych kursorów.Prawidłowy sposób na wyłączny blokadę

Potrzebuję wziąć wyłączną blokadę na stole transakcji, podczas gdy ja uzgadniam na klienta, ale chciałbym zwolnić blokadę i pozwolić innym ludziom uruchomić ich zapytania pomiędzy każdym klientem, którego przetwarzam.

chciałbym zrobić wyłącznej blokady na poziomie wiersza zamiast poziomie tabeli, ale what I have read so far mówi, że nie może zrobić with (XLOCK, ROWLOCK, HOLDLOCK) jeśli inne transakcje są uruchomione na READCOMMITED poziom izolacji (co to jest dla mnie).

Czy przyjmuję wyłączną blokadę na poziomie stołu i czy jest jakiś sposób na serwerze 2008 R2, aby zamki wyłączne na poziomie wiersza działały tak, jak chcę, bez modyfikowania innych zapytań uruchomionych w bazie danych?

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

Próbuję dowiedzieć się, dlaczego potrzebujesz wyłącznej blokady. Czy inni ludzie mogą wstawiać rekordy? Inne osoby aktualizujące rekordy? Martwisz się, że inne osoby uzyskują niespójny widok danych? – Laurence

+0

@ Laurence Martwię się, że inne osoby uzyskały niespójny stan wyświetlania. Próbuję poprawić błąd, który wpłynął na mały% klientów, jednak proces korygujący pozostawia kilka współzależnych wierszy w kilku tabelach (faktycznie będę blokował na 5 tabelach, ale uprościłem przykład mojego kodu do jednej tabeli) w niespójnym stanie podczas proces korekty. Ta niespójność jest izolowana na klienta, ale wykonanie "SUMIU SELECT (ColA) z trnHistory" nad tym jednym klientem zwróci nieprawidłową wartość podczas procesu "korekty". Muszę więc wybrać wyłączny zamek, aby zapobiec odczytowi. –

+0

Nie rozumiem, dlaczego transakcja nie ochroniłaby Cię przed tym, chyba że masz ludzi robiących read_uncommitted. – Laurence

Odpowiedz

3

Jeśli tylko martwi się o innych czytelników, to nie powinno być potrzeby wyłączne zamki, wzór

Begin Transaction 

    Make Data Inconsistent 

    Make Data Consistent 

Commit Transaction 

powinno być dobrze. Jedynymi sesjami, które zobaczą niespójne dane, są te, które używają nolock lub Read Uncommitted lub te, które oczekują wielokrotnych spójnych odczytów bez używania Repeatable Rows lub Serializable.

W odpowiedzi na pytanie, prawidłowym sposobem na wyłączenie blokady jest, moim zdaniem, uporządkowanie rzeczy tak, aby silnik zrobił to za Ciebie.

9

Nie mogłem uwierzyć, że nie będzie blokować XLOCK współbieżne czytelnika w read committed więc po prostu powielać go: To prawda. Scenariusz:

Sesja 1:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED 
BEGIN TRAN 

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

Sesja 2:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED 
BEGIN TRAN 

SELECT * FROM T WHERE ID = 123 

Wtyczka w jakiejś nazwy tabeli, że masz pod ręką. Sesja 2 nie jest blokowana.

Próbowałem również używać PAGLOCK, ale to też nie działa. Następnie próbowałem TABLOCKX, ale to też nie działało!

Twoja strategia blokowania tabel nie działa. Myślę, że będziesz musiał zmodyfikować czytelników tak, że albo

  1. izolacja użycie migawki, aby uzyskać spójny obraz (na dzień przed jakichkolwiek zapisów)
  2. użyć wyższy poziom izolacji zostać zablokowany przez pisarza

Oczywiście istnieje nieprzyjemne obejście, naprawdę zablokuj stół: zmień jego schemat. To zajmie blokadę Sch-M, która jest w konflikcie z praktycznie dowolnym dostępem do stołu. Przechowuje nawet niektóre operacje odczytu metadanych. Może wyglądać tak:

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

Przetestowałem to do pracy.


Czy program SQL Server nie jest zgodny z XLOCK? Czy jest to wada produktu? Myślę, że to prawda, ponieważ jest zgodne z udokumentowanymi właściwościami READ COMMITTED. Ponadto, nawet przy użyciu SERIALIZABLE są przypadki, w których jedna transakcja może zablokować tylko wiersz, a inna może odczytać ten sam wiersz! Może się to zdarzyć w obecności indeksów. Jedna transakcja może blokować X dla nieklastrowego indeksu IX_T_SomeCol, podczas gdy inna szczęśliwie odczyta klastrowany indeks PK_T.

Jest więc całkiem normalne, że transakcje mogą być wykonywane niezależnie, nawet w przypadku wyłącznego blokowania.

+2

Znalazłem, że tylko jeśli używam REPEATABLE READ LUB SERIALIZABLE poziomów izolacji podczas czytania sql blokuje go (jeśli uncommited rowlock + xlock istnieje) – SalientBrain

+1

Tak, tylko poziom izolacji REPEATABLE READ OR SERIALIZABLE może zablokować sesję 2, ponieważ nawet jeśli sesja uzyskuje XLOCK i nawet jeśli nie dokonujesz transakcji, sesja 1 już zwolniła XLOCK po zakończeniu odczytu. To już nie wytrzymało. – Xin

+0

@Xin trzymał go z powodu 'HOLDLOCK'. Jest to identyczne z "SERIALIZABLE" i przechowuje wszystkie blokady do końca transakcji. – usr

Powiązane problemy