Zablokowanie aktualizacji indeksu SQL Server


13

Mam 2 zapytania, które uruchamiane w tym samym czasie powodują impas.

Zapytanie 1 - zaktualizuj kolumnę zawartą w indeksie (indeks 1):

update table1 set column1 = value1 where id = @Id

Bierze X-Lock na stole 1, a następnie próbuje X-Lock na indeksie 1.

Zapytanie 2:

select columnx, columny, etc from table1 where {some condition}

Bierze S-Lock na indeks 1, a następnie próbuje S-Lock na table1.

Czy istnieje sposób na uniknięcie impasu przy zachowaniu tych samych zapytań? Na przykład czy mogę jakoś wziąć X-Lock do indeksu w transakcji aktualizacji przed aktualizacją, aby upewnić się, że dostęp do tabeli i indeksu jest w tej samej kolejności - co powinno zapobiec zakleszczeniu?

Poziom izolacji jest zatwierdzony do odczytu. Blokady wierszy i stron są włączone dla indeksów. Możliwe, że ten sam rekord bierze udział w obu zapytaniach - nie mogę stwierdzić na podstawie wykresu zakleszczenia, ponieważ nie pokazuje parametrów.

Wykres zakleszczenia

Odpowiedzi:


11

Czy istnieje sposób na uniknięcie impasu przy zachowaniu tych samych zapytań?

Wykres zakleszczenia pokazuje, że ten zakleszczenie było zakleszczeniem konwersji związanym z wyszukiwaniem zakładek (w tym przypadku wyszukiwanie RID):

Wykres zakleszczenia

Jak zauważa pytanie, ogólne ryzyko zakleszczenia powstaje, ponieważ zapytania mogą uzyskać niezgodne blokady tych samych zasobów w różnych zamówieniach. SELECTKwerenda musi mieć dostęp do indeksu przed tabeli ze względu na RID Lookup, natomiast UPDATEmodyfikuje kwerendy tabeli, potem indeks.

Eliminacja impasu wymaga usunięcia jednego ze składników impasu. Oto główne opcje:

  1. Unikaj wyszukiwania RID, tworząc indeks nieklastrowany. Prawdopodobnie nie jest to praktyczne w twoim przypadku, ponieważ SELECTzapytanie zwraca 26 kolumn.
  2. Unikaj wyszukiwania RID, tworząc indeks klastrowany. Wymagałoby to utworzenia indeksu klastrowego w kolumnie Proposal. Warto to rozważyć, choć wydaje się, że ta kolumna jest typu uniqueidentifier, co może, ale nie musi być dobrym wyborem dla indeksu klastrowego, w zależności od szerszych kwestii.
  3. Unikaj dzielonych blokad podczas czytania, włączając opcje READ_COMMITTED_SNAPSHOTlub SNAPSHOTbazy danych. Wymagałoby to starannego przetestowania, szczególnie w odniesieniu do wszelkich zaprojektowanych zachowań blokujących. Kod wyzwalający wymagałby również przetestowania, aby upewnić się, że logika działa poprawnie.
  4. Unikaj dzielonych blokad podczas czytania, używając READ UNCOMMITTEDpoziomu izolacji dla SELECTzapytania. Obowiązują wszystkie zwykłe zastrzeżenia.
  5. Unikaj równoczesnego wykonywania dwóch zapytań, używając wyłącznej blokady aplikacji (patrz sp_getapplock ).
  6. Użyj wskazówek dotyczących blokowania tabeli, aby uniknąć współbieżności. Jest to większy młotek niż opcja 5, ponieważ może wpływać na inne zapytania, nie tylko na dwa wymienione w pytaniu.

Czy mogę w jakiś sposób wziąć X-Lock na indeks w transakcji aktualizacji przed aktualizacją, aby zapewnić dostęp do tabeli i indeksu w tej samej kolejności

Można spróbować, owijając aktualizację w jawnej transakcji i wykonując SELECTz XLOCKodrobiną na nieklastrowany wartości indeksu przed aktualizacją. To zależy od tego, czy wiesz na pewno, jaka jest bieżąca wartość w indeksie nieklastrowanym, czy poprawnie wykonałeś plan wykonania i poprawnie przewidziałeś wszystkie skutki uboczne przyjęcia tej dodatkowej blokady. Polega również na tym, że silnik blokujący nie jest wystarczająco inteligentny, aby uniknąć przejęcia blokady, jeśli zostanie uznany za zbędny .

Krótko mówiąc, chociaż jest to w zasadzie wykonalne, nie polecam tego. Zbyt łatwo jest przeoczyć coś lub przechytrzyć się w twórczy sposób. Jeśli naprawdę musisz unikać tych zakleszczeń (zamiast ich wykrywania i ponownej próby), zachęcam do spojrzenia na bardziej ogólne rozwiązania wymienione powyżej.


Patrząc dalej na ten problem, myślę, że pozostawienie go bez zmian jest prawdopodobnie najlepszym rozwiązaniem. To bardziej powszechny problem, który pierwotnie sobie uświadomiłem.
Dale K

1

Mam podobny problem, który pojawia się sporadycznie i oto moje podejście.

  1. Dodaj set deadlock priority low;do wybranych. Spowoduje to, że to zapytanie będzie ofiarą zakleszczenia, gdy nastąpi zakleszczenie.
  2. Skonfiguruj logikę ponownych prób w aplikacji, aby automatycznie ponawiała wybór, jeśli nie powiedzie się z powodu zakleszczenia (lub przekroczenia limitu czasu), po odczekaniu / uśpieniu przez krótki okres czasu, aby umożliwić zakończenie zapytań blokujących.

Uwaga: jeśli Twoja selecttransakcja jest częścią jawnej transakcji zawierającej wiele wyciągów, musisz spróbować całej transakcji, a nie tylko wyciągu, który się nie powiódł, w przeciwnym razie możesz uzyskać nieoczekiwane wyniki. Jeśli jest to singiel, selectwszystko jest w porządku, ale jeśli jest to wyciąg xz ntransakcji, upewnij się, że ponawiasz wszystkie nwyciągi podczas ponownej próby.


Dzięki - zapytania są automatycznie domyślnie ofiarami zakleszczenia. I tak, mamy już solidny mechanizm ponownych prób.
Dale K
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.