Wskazówka NOLOCK zmienia kolejność zwracanych rekordów

11

Na Clientpolu tabeli znajduje się indeks klastrowany LastName.

Kiedy po prostu zrzucam wszystkie rekordy z tabeli, pojawiają się one w kolejności alfabetycznej, chyba że (nolock)podpowiedź jest użyta jak w zapytaniu. Ta wskazówka zmienia kolejność rekordów. Powinien? Jestem przekonany, że żadna inna sesja nie zawiera otwartej transakcji ze zmianami w tej tabeli (przynajmniej sp_who2mnie nie pokazuje).

Jak wyjaśnić różnicę w kolejności?

Dodatkowe informacje pochodzą z komentarzy:

  1. Nie ma zamówienia według. Czy indeks nieklastrowany powinien egzekwować zamówienie?

  2. Zapytania nadal zwracają inną kolejność, nawet gdy używana jest wskazówka dotycząca indeksu określająca indeks klastrowany. Powinni? Zastanawiam się, dlaczego nolockzmienia kolejność zwracanych rekordów bez widocznej zmiany planów.

  3. Zrobiłem na nich WinDiff - to samo, z wyjątkiem [the] (nolock)[wskazówka do zapytania].

ajeh
źródło
21
Wskazówka nie zmienia kolejności - ponieważ nie ma kolejności, która mogłaby zostać zmieniona
a_horse_w_na_name

Odpowiedzi:

47

Pojawienie się uporządkowanego zestawu wyników bez ORDER BYklauzuli często wynika ze skanu pobierającego wiersze w kolejności indeksu. Jednym z powodów, dla których skanowanie kolejności indeksów jest zwykle wybierane poniżej domyślnego READ COMMITTEDpoziomu izolacji, jest to, że zmniejsza ryzyko niepożądanych anomalii współbieżności, takich jak wielokrotne napotkanie tego samego wiersza lub całkowite pominięcie niektórych wierszy. Jest to szczegółowo opisane w kilku miejscach, w tym w tej serii artykułów o poziomach izolacji.

Dzięki NOLOCKpodpowiedzi do tabeli zachowanie to jest złagodzone, a dostęp do tabeli odbywa się na bardziej tolerancyjnym READ UNCOMMITTEDpoziomie izolacji, który może skanować dane w kolejności alokacji zamiast w kolejności indeksu. Jak opisano w tym łączu, decyzja o tym, czy użyć skanowania kolejności alokacji, czy skanowania kolejności indeksu, należy do silnika pamięci masowej. Ten wybór może zmieniać się między wykonaniami bez zmiany planu zapytań .

Może to zabrzmieć bardzo abstrakcyjnie, ale można to łatwiej zademonstrować za pomocą niektórych zapytań wykorzystujących nieudokumentowane funkcje w bazie danych AdventureWorks2012 .

USE AdventureWorks2012;
GO
-- Appears to be ordered by BusinessEntityID
-- File:Page:Slot goes up and down several times
-- Show physical locations with sys.fn_PhysLocFormatter (undocumented)
SELECT
    P.BusinessEntityID,
    [(File:Page:Slot)] =
        sys.fn_PhysLocFormatter(%%physloc%%)
FROM Person.Person AS P;

-- Same query with TABLOCK or NOLOCK
-- Allocation-order (IAM) scan
-- Now appears to be ordered by File:Page:Slot instead of BusinessEntityID
SELECT P.BusinessEntityID,
    [(File:Page:Slot)] =
        sys.fn_PhysLocFormatter(%%physloc%%)
FROM Person.Person AS P WITH (NOLOCK);

Wyniki zapytania

Zapytania zostały zapożyczone z niewielką modyfikacją od Paula White'a .

Wreszcie, dla jasności, ta odpowiedź dotyczy wyglądu uporządkowanego zestawu wyników. Nie ma gwarantowanej kolejności prezentacji bez najwyższego poziomu ORDER BY.

Skanowanie kolejności alokacji może wystąpić w różnych innych okolicznościach, na przykład w momencie uzyskania blokady na poziomie tabeli lub gdy baza danych jest w trybie tylko do odczytu. Równoległość może również wpływać na kolejność zwracania danych. Kluczową kwestią jest to, że bez ORDER BYtego zwracane dane mogą się zmieniać w czasie w zależności od projektu.

James L.
źródło
7
Miło, o wiele bardziej konkretnie niż moja niedoceniana próba. Sedno jest oczywiście w twoich dwóch ostatnich akapitach. „Dlaczego” nie ma tak naprawdę znaczenia. Może będziesz miał więcej szczęścia, uderzając w ten akord niż ja.
Aaron Bertrand
12

Kiedy po prostu zrzucam wszystkie rekordy ze stołu

... nie powinieneś oczekiwać żadnego zamówienia. W rzeczywistości to samo zapytanie uruchomione wiele razy może powrócić w innej kolejności bez ostrzeżenia. Powodem jest to, że twoje dwa zapytania - które są „różnymi” zapytaniami najprawdopodobniej z powodu innego tekstu zapytania, a nie z powodu podpowiedzi - mają różne plany wykonania (a różnica może być subtelna, na przykład iterator, który wygląda tak samo w oba plany, ale ma ordered: truetylko jeden lub znacznie inną liczbę szacowanych wierszy, ponieważ jeden został skompilowany przed poważną zmianą danych). Może być również tak, że przeprowadzany jest skan zlecenia alokacji, jak słusznie nakreślił James w swojej odpowiedzi.

W każdym razie, ponieważ uruchamiasz kwerendę bez ORDER BY, SQL Server wnioskuje, że nie obchodzi cię porządek, a więc wyłącza się i określa najbardziej efektywny sposób zwracania wierszy (nie przejmuje się porządkiem, jeśli nie ).

Wyjaśnię to bardzo jasno:

Jeśli chcesz lub oczekujesz określonego zamówienia, dodaj klauzulę ORDER BY

Stało się to wcześniej:

I napisałem o tym na blogu:

Oto cytat z tego ostatniego, który przypomina to, co powiedziałem, aby odpowiedzieć na twój komentarz na temat używania podpowiedzi indeksu zamiast ORDER BYklauzuli:

Nawet w przypadku indeksu z podpowiedziami zostanie użyty indeks (jeśli to możliwe, w przeciwnym razie błąd w większości przypadków), ale sam fakt, że indeks jest używany, nie oznacza, że ​​wyniki zostaną zwrócone posortowane według klucza (-ów) w ten indeks. Nadal potrzebujesz ORDER BYsortowania według kluczy indeksu.

Conor Cunningham - całkiem sprytny facet, bezpośrednio odpowiedzialny za większość tego, co robi SQL Server, gdy przetwarza dla ciebie zapytanie - napisał o tym tutaj:

Przeczytaj trochę, a następnie dodaj ORDER BYklauzulę do swoich zapytań, zanim narzekasz na inne lub nieoczekiwane sortowanie.

Aaron Bertrand
źródło
11

James dobrze wyjaśnił, jak to działa, ale chciałbym powtórzyć jedną rzecz: jeśli nie użyjesz funkcji porządkowania, kolejność wierszy w zestawie wyników jest niezdefiniowana . Jeśli potrzebujesz danego zamówienia, użyj wyraźnej order byklauzuli - jeśli go nie określisz, w zasadzie mówisz „W ogóle nie obchodzi mnie to zamówienie”, a nie „Zamów przez indeks klastrowany”.

Luaan
źródło