wtorek, 17 stycznia 2012

T-SQL od podstaw cz. 2

W poprzednim wpisie poruszyłem temat transakcji i jak sie zabezpieczyć przed sytuacją taką, że wiecej danych zostanie zaktualizowanych niż oczekiwano.

Blokowanie zasobów

Pozostawienie otwartej transakcji nawet na chwile ma negatywny wpływ na inne operacje na bazie danych.
W momencie kiedy transakcja jest otwarta i tabela jest aktualizowana inne zapytanie domyślnie nie mogą odczytać danych dopóki transakcja aktualizująca dane nie zostanie zakończona. Ma to zapobiedz sytuacji takiej, że odczytywane danych w innych transakcjach mogą uledz zmianie w przypadku gdy aktualizacja się nie powiedzie z jakiegoś powodu.
Są przypadki, w których nie zależy nam na aptekarskim podejściu do zwróconych danych i pewien stopień błędu jest akceptowalny. Dodatkowo samo odczytywanie danych także ma wpływ na operacje aktualizujące te same dane w innej sesji.
W przypadku analizy danych na środowisku produkcyjnym w zdecydowanej większości przypadków zależy nam na tym by uniknąć sytuacji, w której to co robimy ma negatywny wpływ na pracę systemu.

Hint NOLOCK.

Wykonując polecenie SELECT z hintem NOLOCK nie blokujemy zasobów ale także potęcjalnie możemy mieć taką sytuację, że odczytywane dane niekoniecznie będą takie same przy ponownym uruchomieniu zapytania.
Załóżmy, że mamy tabelę MyTable, z której chcemy pobrać wszystkie rekordy z wartością 1 w kolumnie val
   1: create table MyTable (
   2:     id uniqueidentifier not null,
   3:     val char(1) not null
   4: )
   5:  
   6: declare @i int
   7: set @i = 0
   8: while @i<=100
   9: begin
  10:     insert into MyTable values(newid(), @i%9)
  11:     set @i = @i + 1
  12: end
Rezultat ten możemy uzyskać uruchamiając poniższe zapytanie
   1: select * from MyTable where val = 2
Jednakże w sytuacji, kiedy w innym procesie dane w tej samej tabeli są aktualizowane na odpowiedź może nam przyjść trochę poczekać.

Załóżmy następującą sytuację. W transakcji nr 1 wykonujemy następujące zapytanie aktualizujące wartości w kolumnie val z 1 na 3
   1: begin tran
   2: update MyTable
   3: set val = 3
   4: where val = 1

Teraz wykonując ponownie zapytanie, w transakcji nr 2, pobierające rekordy z wartością 2 w kolumnie val będzie czekać aż pierwsza transakcja się zakończy. Może to trochę potrwać, szczególnie w sytuacji gdy ktoś rozpoczął transakcje a zapomniał ją zakończyć. Taka sytuacja już mi się kilka razy zdarzyła pracując na bazie danych dzielonej z innymi deweloperami.

Z racji tego, że potrzebuje rezultat natychmiast i akceptowanla jest dla mnie sytuacja taka, że jakieś dane mogą zostać zmienione w międzyczasie w innej transakcji, zapytanie SELECT wykonuję z hintem NOLOCK.

Zapytanie uruchamiam w transakcji nr 3.
   1: select * from MyTable WITH (NOLOCK) where val = 2

Wynik w tym przypadku otrzymuję natychmiast, podczas, gdy drugie zapytanie ciągle czeka aż zasoby zostaną zwolnione.

Teraz wracając do pierwszej transakcji zatwierdzam zapytanie
   1: commit tran
W tym samym momencie w transakcji nr 2 dostaję wynik, taki sam jak w transakcji nr 3.

Prześlij komentarz