Mam niezłego zonka z MSSQL (2014). Zapytanie (bardzo długa i brzydka procedura składowana używająca innych równie długich i brzydkich sp) zakleszcza mi się. Robi to konsekwentnie dla różnych zestawów danych tylko na moim komputerze. Debugując namierzyłem dokładnie jeden insert z zagnieżdżonym selectem, na którym sp się zakleszcza. I tu oczy otworzyły mi się tak szeroko, że aż pomarszczyła mi się skóra z tyłu szyi. Konkrety:
blokujący się kod:
INSERT INTO core.root_seats
SELECT
@se_id + ROW_NUMBER() OVER (ORDER BY se_id),
se_name,
se_description,
se_number,
se_x,
se_y,
se_quality,
se_capacity,
se_group,
se_flags,
r.r_id,
se_sv_id,
se_seat_size,
se_drawing_objects,
SYSDATETIMEOFFSET(),
se_id,
0 ------------
FROM
ss_events.core.se_seats AS se
INNER JOIN core.root_rows AS r ON (r.r_root_id = se.se_row_id)
INNER JOIN core.root_sectors AS ON (s.s_id = r.r_sector_id)
WHERE
(s.s_a_id = @a_id);
Można go uprościć do:
SELECT *
FROM
ss_events.core.se_seats AS se
INNER JOIN core.root_rows AS r ON r.r_root_id = se.se_row_id
INNER JOIN core.root_sectors AS s ON s.s_id = r.r_sector_id
WHERE
s.s_a_id = 12;
Powyższe uproszczone zapytanie blokuje się tak samo, jak oryginalny insert. Puszczam je w ten sposób, że pauzuję w debugerze wykonanie sp na linijce z blokującym insertem, idę do drugiego okienka i tamże uruchamiam ten prosty select. OK. Na razie jest prosto, myślę sobie, że dodam nolock i będzie śmigać. I tu się zdziwiłem. Zapytanie w poniższej wersji nadal się blokuje!
SELECT *
FROM
ss_events.core.se_seats AS se with (nolock)
INNER JOIN core.root_rows AS r with (nolock) ON r.r_root_id = se.se_row_id
INNER JOIN core.root_sectors AS s with (nolock) ON s.s_id = r.r_sector_id
WHERE
s.s_a_id = 12;
Postanowiłem zobaczyć, na której tabelce jest robiony przeszkadzający mi lock. Puściłem
- SELECT * FROM ss_events.core.se_seats, poszło bez wieszania się;
- SELECT * FROM core.root_rows - zwróciło 1400 rekordów i zablokowało się (jak widać lock na wierszu albo stronie)
- SELECT * FROM core.root_rows with (nolock) - zwróciło wszystkie rekordy z tabelki (1412 wierszy) bez blokowania się.
- na wszelki wypadek sprawdziłem jeszcze core.root_sectors, tam też był lock, po dodaniu
with (nolock)
poszło zwracając 123 wiersze.
Zrobiłem wielkie oczy i tak sobie myślę - WTF? Dlaczego zapytanieselect * from se join r join s
puszczone znolock
blokuje się? Jakim prawem się to dzieje, kiedy dopuszczony jest dirty read i żaden lock nie powinien zatrzymać tego zapytania? To samo dzieje się dla puszczenia tego zapytania w transakcji dla poziomu read uncommitted (no w sumie to logiczne).
Ale to nie koniec zdziwka. Kod
SELECT *
FROM
core.root_rows AS r with (nolock)
INNER JOIN core.root_sectors AS s with (nolock) ON s.s_id = r.r_sector_id
WHERE
s.s_a_id = 12;
nie blokuje się. A przecież wyłączyłem jedyną tabelkę, na której nie było locka (wiem, bo sprawdziłem SELECT * FROM ss_events.core.se_seats).
Kolejne zdziwko:
SELECT top 2489 *
FROM
ss_events.core.se_seats AS se with (nolock)
INNER JOIN core.root_rows AS r with (nolock) ON r.r_root_id = se.se_row_id
INNER JOIN core.root_sectors AS s with (nolock) ON s.s_id = r.r_sector_id
WHERE
s.s_a_id = 12;
blokuje się. Ale wykonane z top 2488 już się nie blokuje i zwraca 1 (słownie: jeden) wiersz.
Czy ktoś jest w stanie wytłumaczyć mi, co się tutaj dzieje? Z góry przepraszam za obrzydliwe nazewnictwo schematów, tabel i pól, niestety nie miałem na nie najmniejszego wpływu.
edit: doczytałem (http://sqlblog.com/blogs/linchi_shea/archive/2009/08/03/performance-impact-can-select-nolock-block-inserts.aspx), że operacje na strukturze bazy danych mogą być blokujące pomimo nolock'a, w sumie jest to dość logiczne. Jednak tutaj nie mam do czynienia ani ze zmianą struktury bazy, ani nawet z insertem.
edit2: zapytanie w poniższej postaci nie blokuje się, ale nie zwraca wszystkich danych, które są mi potrzebne, dlatego nie jest rozwiązaniem, poza chcę zrozumieć, dlaczego to tak dziwnie działało:
select * from ss_events.core.se_seats AS se with (nolock)
where se.se_row_id in (
SELECT r.r_root_id
FROM
core.root_rows AS r with (nolock),
core.root_sectors AS s with (nolock)
WHERE
s.s_a_id = 12 and s.s_id = r.r_sector_id
)
Poniżej screenshot wywołania sp_who2
zawierający sp na bazach events
, nie wiem czy coś wniesie, ale może nie zaszkodzi: