W SQL Server, jak działają blokady odczytu?

17

Załóżmy, że mam następujące długo działające zapytanie

UPDATE [Table1]
SET [Col1] = 'some value'
WHERE [Col2] -- some clause which selects thousands of rows

i załóżmy, że następujące zapytanie jest wykonywane podczas działania powyższego zapytania

SELECT *
FROM [Table1]

Czy pierwsze zapytanie uniemożliwia uruchomienie drugiego zapytania do momentu wykonania pierwszego zapytania? Jeśli tak, to czy pierwsze zapytanie uniemożliwia uruchomienie drugiego zapytania na wszystkich wierszach, czy tylko wierszach objętych klauzulą ​​WHERE?

EDYTOWAĆ:

Załóżmy, że drugie zapytanie to

SELECT [Col1], [Col2]
FROM [Table1]
WHERE [Col2] -- some clause whose matching elements overlap those from
             -- the clause in the first query and which has additional matching elements
cm007
źródło

Odpowiedzi:

14

Zalecam przeczytanie Zrozumienie, w jaki sposób SQL Server wykonuje zapytanie , zawiera wyjaśnienie, jak działa odczyt i zapis oraz jak działa blokowanie.

Widok 10000 stóp wygląda następująco:

  • operatorzy odczytu uzyskują wspólne blokady danych, które czytają, przed odczytem danych
  • operatorzy zapisu uzyskują wyłączne blokady danych, które modyfikują przed modyfikacją danych
  • blokady danych to tylko ciągi znaków, np. skrót klucza odczytywanego według bazy danych i obiektu.
  • menedżer blokad utrzymuje listę wszystkich przyznanych blokad i wykrywa niezgodności zgodnie z macierzą kompatybilności blokad
  • niekompatybilne żądania są zawieszane, dopóki niekompatybilny grant blokujący je nie zostanie zwolniony
  • operatorzy używają hierarchii blokady, aby zadeklarować zamiar odczytu lub aktualizacji danych na wyższym poziomie (na poziomie strony lub tabeli, ignorując opcje na poziomie partycji). Umożliwia to operatorom blokowanie całych tabel bez blokowania każdego pojedynczego rzędu
  • Żywotność zamka i zamki zasięgu służą do wymuszenia wyższych poziomów izolacji

To naprawdę tylko wierzchołek lodowej góry. Temat jest obszerny. W twoim przykładzie nikt nie może odpowiedzieć na twoje pytanie o to, co jest faktycznie zablokowane, ponieważ będzie to zależeć od wielu czynników. Oczywiście żadna aplikacja nie powinna wydać a, SELECT * FROM Table1 ponieważ nie ma w niej klauzuli WHERE i używa* . Są to złe praktyki, ponieważ między innymi doprowadzą dokładnie do zablokowania sporu.

Jeśli napotkasz blokady odczytu i zapisu, musisz przyjrzeć się wersjonowaniu wierszy i izolacji migawki. Przeczytaj Zrozumienie poziomów izolacji opartych na wersjach wierszy .

Remus Rusanu
źródło
Co jeśli potrzebuję całej zawartości tabeli (powiedzmy, że mam w niej tylko 14 wierszy)? Jak to źle, SELECT * FROM Table1jeśli dokładnie tego potrzebuję?
Azymut
1
*samo w sobie jest złą praktyką, ponieważ gdy zmienia się struktura tabeli, aplikacja zwykle się psuje (w rezultacie pojawiają się nieoczekiwane kolumny).
Remus Rusanu,
3

Edycja: Jak wskazuje @MaxVernon , poniższe stwierdzenie nie jest w żaden sposób sugestią użycia NOLOCK , a ja bardzo dobrze powinienem wspomnieć o ustawieniu poziomu transakcji READ UNCOMMITEDi pozostawieniu negatywnej konotacji tam, niż NOLOCKo tym wspominając . Tak jak pierwotnie napisano:

Szybka i prosta odpowiedź brzmi: „Tak, pierwsze zapytanie zablokuje drugie zapytanie, chyba że zostanie określona konkretna wskazówka dotycząca indeksu ( NOLOCK , czasami nazywana„ nieczytelnym odczytem ”) lub poziom izolacji transakcji drugiego zapytania jest ustawiony na READ UNCOMMITED(który działa identycznie), nie."

W odpowiedzi na dodatkowe szczegóły przedstawione w pytaniu, które wymagają włączenia WITHklauzuli drugiej SELECT, wzajemnie się wykluczającej lub w inny sposób, interakcje między tymi dwoma zapytaniami będą w dużej mierze takie same.

IF NOT EXISTS ( SELECT  1
                FROM    sys.objects
                WHERE   name = 'Foo'
                    AND type = 'U' )
BEGIN
    --DROP TABLE dbo.Foo;
    CREATE TABLE dbo.Foo
    (
        Foo_PK          BIGINT IDENTITY( 1, 1 ) NOT NULL,
                            PRIMARY KEY ( Foo_PK ),
        Bar             BIT,
        x               BIT,
        y               BIT,
        z               BIT
    );

    CREATE NONCLUSTERED INDEX IX_Foo_x
        ON  dbo.Foo ( x );

    INSERT INTO dbo.Foo ( Bar, x, y, z )
    VALUES ( 1, 1, 1, 1 ), ( 0, 0, 0, 0 );
END;    
GO

BEGIN TRANSACTION;

UPDATE  dbo.Foo
    SET y = 0
WHERE   x = 1;

-- COMMIT TRANSACTION;

W osobnej sesji uruchom następujące polecenie:

SELECT  *
FROM    dbo.Foo WITH ( NOLOCK );
GO

SELECT  *
FROM    dbo.Foo;

Możesz sprawdzić blokady, które są obecnie sp_lockw użyciu, najlepiej podczas kolejnej osobnej sesji:

EXECUTE dbo.sp_lock;

Powinieneś zobaczyć KEYblokadę typu trzymaną przez pająka wykonującego transakcję wstawiania w Xtrybie (wyłącznym), aby nie pomylić jej z innymi IXblokadami (celowo-wyłącznymi). Dokumentacja blokady wskazuje, że chociaż KEYblokada jest specyficzna dla zakresu, zapobiega ona również wstawianiu lub aktualizowaniu odpowiednich kolumn przez inne transakcje, zmieniając zawarte w nich dane, tak aby mogła mieścić się w tym zakresie pierwotnego zapytania. Ponieważ sama blokada jest wyłączna, pierwsze zapytanie uniemożliwia dostęp do zasobu z dowolnego innej transakcji równoległej. W efekcie wszystkie wiersze kolumny są zablokowane, niezależnie od tego, czy mieszczą się w zakresie określonym przez pierwsze zapytanie.

SZamek jest w posiadaniu drugiej sesji będzie więc WAITXbezbarwnych zamek, uniemożliwiających innym X(lub U) Blokada przed podjęte na tego zasobu z innego jednoczesnego spid przed druga sesja kończy operację odczytu, uzasadniających istnienieS zamka.

Teraz edycja dla jasności: Chyba że się mylę z tym, co jest brudnym odczytem z krótkiego opisu wspomnianych tutaj zagrożeń ... Edytuj 3 : Właśnie zdałem sobie sprawę, że nie rozważam wpływu punktu kontrolnego w tle, który zapisuje jako jeszcze niezaangażowanej transakcji na dysk, więc tak, moje wyjaśnienie było mylące.

W drugim zapytaniu pierwsza partia może (iw tym przypadku zwróci) nieprzydzielone dane. Druga partia, działająca na domyślnym poziomie izolacji transakcji wynoszącymREAD COMMITED zwróci się dopiero po zakończeniu zatwierdzenia lub wycofania w pierwszej sesji.

Stąd można patrzeć na swoich planów kwerend i związanych z nimi poziomów blokady, ale jeszcze lepiej, można przeczytać o zamki w SQL Server tutaj .

Avarkx
źródło
1
W WITH (NOLOCK)tym przypadku pomocne byłoby słowo ostrzeżenia o używaniu . Więcej informacji można znaleźć na stronach brentozar.com/archive/2011/11/… i brentozar.com/archive/2013/02/ ...
Max Vernon
3
Och, WITH (NOLOCK)podpowiedź nie zwraca brudnych stron z pamięci, które nie zostały zatwierdzone . W rzeczywistości odczytuje wiersze z tabeli (na dysku lub w pamięci podręcznej) bez blokowania autorom aktualizacji i dodawania wierszy do stron używanych przez tabelę.
Max Vernon,
2
Jestem zmieszany. Jeśli odpowiedź na pytanie „czy pierwsze zapytanie uniemożliwia uruchomienie drugiego?” brzmi „nie”, jak odpowiedź na drugie pytanie brzmi „tak”? Czy możesz wyjaśnić, na które pytania odpowiadasz, i rozszerzyć swoje odpowiedzi?
Jon of All Trades
Wiele edycji, przepraszam ludzi! Daj mi znać, jeśli jest coś jeszcze niejasnego!
Avarkx