W ramach serii Quest Database Training Days Fall, Brent Ozar, Microsoft Certified Master, przedstawił samouczek „Unikanie zakleszczeń dzięki dostrajaniu zapytań”. Program skupił się na trzech problemach ze współbieżnością, które występują w SQL Server, trzech sposobach ich naprawy i jednym sposobie, który wydaje się je naprawiać, ale tak naprawdę nie jest.
Problemy ze współbieżnością:blokowanie, blokowanie i zakleszczenia w SQL Server
Czym są problemy ze współbieżnością? Zdarzają się, gdy zapytania próbują uniknąć konfliktów dotyczących obiektów bazy danych, takich jak tabele. Są to:
- Blokowanie – Zapytania robią to cały czas, aby inne zapytania nie korzystały z tabeli w tym samym czasie. To jest normalna operacja bazy danych.
- Blokowanie – Dzieje się tak, gdy jedno zapytanie ma normalną blokadę, ale inne zapytanie próbuje uzyskać tę samą blokadę. Drugie zapytanie musi czekać tak długo, jak to konieczne, aby pierwsze zapytanie zwolniło blokadę. W zależności od charakteru pierwszego zapytania, drugie może czekać bardzo krótko lub bardzo długo. To te długie oczekiwanie naprawdę wpływa na wydajność.
- Zakleszczenie – Zakleszczenia pojawiają się, gdy jedno zapytanie przyjmuje blokadę, inne zapytanie przyjmuje inną blokadę, a następnie każde z nich chce uzyskać blokadę drugiego. SQL Server rozwiązuje ten problem, wyznaczając jedno z zapytań jako ofiarę i zabijając je, aby przerwać impas. Nawet jeśli jedno z zapytań jest w stanie kontynuować, ma to również wpływ na wydajność.
Naprawianie problemów ze współbieżnością
Niezależnie od tego, czy w programie SQL Server występują blokady, czy zakleszczenia, istnieją sposoby rozwiązania problemów ze współbieżnością. Brent przedstawił te trzy metody i spędził większość pozostałej części sesji, skupiając się na drugiej – naprawianiu złego kodu.
- Miej wystarczająco dużo indeksów, by Twoje zapytania były szybkie, ale nie tak dużo, by spowalniały działanie, powodując, że zapytania utrzymywały więcej blokad przez dłuższy czas
- Dostosuj swój kod transakcyjny, aby zapytania działały za każdym razem przez tabele w tej samej przewidywalnej kolejności
- Użyj odpowiedniego poziomu izolacji dla potrzeb Twojej aplikacji
Przechodząc do praktycznej części programu, Brent skomentował użycie instrukcji NOLOCK do blokowania i zakleszczania. Ostrzegł, że NOLOCK tak naprawdę nie rozwiązuje tych problemów, ponieważ opiera się na „brudnych odczytach” – zasadniczo ignoruje blokady wierszy innych zapytań.
Demonstrując to za pomocą bazy danych Stack Overflow, stworzył proste zapytanie, które szukało i liczyło osoby o imieniu „Alex”. Następnie utworzył kolejne zapytanie, które uruchomi aktualizację dla osób, które nie o imieniu Alex — bez wstawiania lub usuwania rekordów. Jedno zapytanie nie powinno mieć nic wspólnego z drugim. Ale prowadzenie ich razem prowadzi do różnych wyników w liczbie osób o imieniu Alex. Dzieje się tak, ponieważ NOLOCK pozwala zobaczyć dane, które nie zostały zatwierdzone, co prowadzi do losowych wyników, których nie można przewidzieć. Dzieje się to tylko w przypadku współbieżności.
Oczywiście potrzebna jest lepsza poprawka do blokowania i zakleszczania w SQL Server, która nie prowadzi do losowych i nieprzewidywalnych wyników.
Lepsze rozwiązanie zakleszczeń SQL
Brent następnie zademonstrował, jak naprawić zakleszczenie, zmieniając kod, który go powoduje. Jego pierwsze demo pokazało prostą sytuację z udziałem dwóch stołów, dzięki czemu publiczność mogła zobaczyć impas w zwolnionym tempie. Ponieważ SQL Server szuka zakleszczeń co 5 sekund i zabija zapytanie, które jest najłatwiejsze do wycofania, mogliśmy zobaczyć, jak wyłania się ofiara zakleszczenia.
W prostej sytuacji obowiązuje najogólniejsza rada, a mianowicie dotykanie tabel w tej samej kolejności za każdym razem podczas konstruowania zapytań. Dzięki temu zapytania nie będą się wzajemnie blokować.
A co z bardziej złożonymi zapytaniami? W tym scenariuszu Brent wykorzystał bardziej realistyczną sytuację, która może łatwo zaistnieć na Stack Overflow, gdzie dwie osoby głosują nawzajem nad swoimi pytaniami. Ponieważ w obu transakcjach biorą udział ci sami użytkownicy, powoduje to impas.
Tutaj nie wystarczy przejrzeć każdy stół za każdym razem w tej samej kolejności, ale konieczne jest również zminimalizowanie liczby dotykania każdego stołu. Jak wyjaśnił Brent, poprawka może obejmować brzydki kod, który powoduje blokowanie zapytań, ale przynajmniej nie zakleszczenie. W takim przypadku blok o krótkim czasie trwania, który umożliwia wykonanie obu zapytań do końca, jest lepszy niż impas, który kończy jedno z nich.
Nikt nie chce zmieniać kodu w setkach zapytań, więc skup się na tych, które ciągle się zacinają, usuń niepotrzebne linie z transakcji i nie bój się wprowadzać blokady, aby uniknąć impasu.