2009-04-27 3 views
15

Staramy się mieć tabelę transakcyjną, która pobiera tylko nowe rekordy wstawiane regularnie.Dlaczego SQL Server 2008 blokuje SELECT przy długich transakcjach INSERT?

Ta prosta tabela wymaga od nas ciągłego dodawania nowych rekordów w miarę upływu czasu. Oczekuje się, że wielkość transakcji w tej tabeli będzie dość wysoka, a także może wystąpić okresowy import transakcji (> 1000), który może potrwać kilka sekund.

Na podstawie tych danych wykonujemy zestaw instrukcji select grupujących różne kolumny, aby zwrócić wymagane wartości.

Z naszych wstępnych testów znaleźliśmy wąskie gardło, które należy powiązać z SQL Server, który blokuje nasze SELECT, gdy jest w trakcie transakcji INSERTS.

Poniżej znajduje się prosty przykład, który można uruchomić w celu zilustrowania problemu.

- prosty DB Tabela

create table LOCK_TEST (
LOCK_TEST_ID int identity , 
AMOUNT int); 

- Uruchamia się w oknie 1 zapytania

begin tran 
insert into LOCK_TEST (AMOUNT) values (1); 
WAITFOR DELAY '00:00:15' ---- 15 Second Delay 
insert into LOCK_TEST (AMOUNT) values (1); 
commit 

- Zapytania 2. przebieg ten równolegle

select SUM(AMOUNT) 
from LOCK_TEST; 

by się spodziewać Zapytanie 2 zwraca od razu, z 0, dopóki nie zakończy się zapytanie 1, a następnie pokaże 2. Nigdy nie chcemy widzieć 1 zwróconego z drugiego zapytania.

Odpowiedź, którą przyjrzeliśmy, odnosi się do WITH (NOLOCK) w instrukcji select. Ale to narusza granice transakcji, a zwrócone informacje mogą mieć charakter finansowy i nie chcemy widzieć żadnych niepowiązanych szczegółów w naszych zapytaniach.

Mój problem wydaje się być po stronie INSERT ...
Dlaczego INSERT zablokować SELECT mimo to nie modyfikujące istniejące dane?

Pytanie dodatkowe: Czy jest to "funkcja" programu SQL Server, czy też znajdziemy to w innych smakach bazy danych?

UPDATE Mam teraz czas, aby znaleźć lokalną bazę danych oracle i uruchomić ten sam prosty test. Ta karta testowa jest taka, jak się spodziewałam.

Ie mogę uruchomić kwerendę tak często, jak chcę, i to zwróci null aż 1st transakcja zobowiązuje, a następnie zwraca 2.

Czy istnieje sposób, aby SQL Server pracy takiego? czy musimy przenieść się do Oracle?

+0

jakie indeksy posiadasz na tych stołach? masz regularną konserwację indeksu? –

+0

Brak obecnie indeksów. Zobacz prosty przykład wymieniony w tym poście, do odtworzenia tego problemu służą tylko prosta tabela 2 kolumn, podstawowa wstawka i wybierz. – stevemac

Odpowiedz

12

to zachowanie blokujące jest cechą programu SQL Server. Z wersją 2005 i nowszą możesz użyć row level versioning (która jest używana domyślnie w Oracle), aby osiągnąć ten sam wynik. & Nie blokuj swoich wyborów. To dodatkowo obciąża tempdb, ponieważ tempdb utrzymuje wersję na poziomie wiersza, więc upewnij się, że to pomieścisz. Aby SQL zachowują się tak, jak chcesz go uruchomić to:

ALTER DATABASE MyDatabase 
SET ALLOW_SNAPSHOT_ISOLATION ON 

ALTER DATABASE MyDatabase 
SET READ_COMMITTED_SNAPSHOT ON 
+0

http://stackoverflow.com/a/4118803/2319909 –

4

Jest to całkowicie standardowe zachowanie w SQL Server i Sybase (przynajmniej).

Zmiany danych (wstawianie, aktualizacja, usuwanie) wymagają exclusive locks.powoduje to blokowanie czytników:

Z wyłączną (X) blokadą żadne inne transakcje nie mogą modyfikować danych; odczytanie operacji może odbywać się tylko przy użyciu korzystania z podpowiedzi NOLOCK lub odczytu niezatwierdzonego poziomu izolacji .

W SQL Server 2005 i nowszych wersjach wprowadzono izolację migawek (inaczej wersje wierszy). Z MS BOL: Understanding Row Versioning-Based Isolation Levels. Oznacza to, że czytnik ma ostatnie zatwierdzone dane, ale jeśli zechcesz je później napisać, może się okazać, że dane są błędne, ponieważ zostały zmienione w transakcji blokującej.

Właśnie dlatego najlepszą praktyką jest utrzymywanie krótkich transakcji. Na przykład, tylko przetwarzać co trzeba między BEGIN/COMMIT, nie wysyłać e-maile z wyzwalaczy itp

Edit:

Blokowanie jak to dzieje się cały czas w SQL Server. Jednak blokowanie zbyt długo staje się blokowaniem, które zmniejsza wydajność. Zbyt długi jest oczywiście subiektywny.

+0

Transakcja jest krótka, ale jej objętość powoduje blokowanie, które w ogóle nie powinno być wymagane. Zobacz komentarz na pytanie dotyczące Oracle. – stevemac

+0

Przejdź do Oracle, jeśli chcesz. Jeśli możesz przenieść się do Oracle po prostu wtedy podejrzewam, że twój SQL nie jest optymalny, jesteś w obie strony od klienta do serwera lub coś, co powoduje zbyt duże blokowanie. – gbn

+1

Jak taki prosty przykład może nie być optymalny? Z prostego przykładu pod warunkiem, że nadal blokuje. Czy możesz zasugerować, jak zmienić podany przykład, aby był optymalny? Należy pamiętać, że NIGDY nie będzie aktualizacji tych danych, tylko nowe wstawki. – stevemac

1

To również istnieje w MySQL. Oto logika stojąca za tym: jeśli wykonasz SELECT na niektórych danych, spodziewasz się, że będzie działać na aktualnym zestawie danych. Właśnie dlatego INSERT powoduje blokadę na poziomie tabeli (to znaczy, że silnik nie jest w stanie wykonać blokowania na poziomie wiersza, jak w przypadku innodb). Istnieją sposoby wyłączenia tego zachowania, aby można było wykonywać "brudne odczyty", ale wszystkie one są oprogramowaniem (lub nawet silnikiem bazy danych).

+1

Zgadzam się, ale chcę też, aby potwierdził transakcje. Z mojego punktu widzenia nowe zapisy (te w transakcji) nie istnieją i prawdopodobnie nigdy nie będą. Dlaczego baza danych blokuje dane, które (z mojego punktu widzenia) nie istnieją, wydaje się błędne. Rozumiem, że jeśli aktualizowałem dane w jednym miejscu i WYBIERAŁEM je z innych źródeł, mógłbym poczekać na aktualizację, ale chcę mieć kontrolę nad tym ... Zwłaszcza z INSERTING. Wygląda na to, że Oracle robi tak, jak się spodziewam. Nadal próbuję się dowiedzieć, czy SQL Server może. – stevemac

1

ja też wpadłem na ten temat. Po przeprowadzeniu dochodzenia wydawało się, że to nie dane zostały zablokowane per se, ale indeks klastrowany, który był modyfikowany w wyniku wstawienia. Ponieważ indeks klastrowy znajduje się na stronach danych, skojarzone wiersze danych są również blokowane.