Próbujemy zaktualizować / usunąć dużą liczbę rekordów w wielomiliardowej tabeli wierszy. Ponieważ jest to popularny stół, w różnych jego częściach jest dużo aktywności. Wszelkie duże działania związane z aktualizacją / usuwaniem są blokowane przez dłuższy czas (ponieważ czeka na uzyskanie blokad wszystkich wierszy lub blokad strony lub tabeli), co powoduje przekroczenie limitu czasu lub wykonanie wielu dni.
Tak więc zmieniamy podejście do usuwania małych partii wierszy jednocześnie. Ale chcemy sprawdzić, czy wybrane (powiedzmy 100 lub 1000 lub 2000 wierszy) są obecnie zablokowane przez inny proces, czy nie.
- Jeśli nie, kontynuuj usuwanie / aktualizację.
- Jeśli są zablokowane, przejdź do następnej grupy rekordów.
- Na koniec wróć na początek i spróbuj zaktualizować / usunąć pominięte.
Czy to jest wykonalne?
Dzięki, ToC
Odpowiedzi:
Jeśli dobrze rozumiem żądanie, celem jest usunięcie partii wierszy, a jednocześnie operacje DML występują w wierszach w całej tabeli. Celem jest usunięcie partii; jeśli jednak wszystkie leżące poniżej wiersze mieszczące się w zakresie określonym przez tę partię są zablokowane, musimy pominąć tę partię i przejść do następnej partii. Następnie musimy powrócić do partii, które nie zostały wcześniej usunięte, i ponowić próbę naszej oryginalnej logiki usuwania. Musimy powtarzać ten cykl, aż wszystkie wymagane partie wierszy zostaną usunięte.
Jak już wspomniano, uzasadnione jest użycie podpowiedzi PRZECZYTAJ WSTĘP i poziomu izolacji PRZECZYTAJ ZAANGAŻOWANY (domyślny), aby pominąć zakresy przeszłości, które mogą zawierać zablokowane wiersze. Pójdę o krok dalej i zalecę użycie SERIALIZABLE poziomu izolacji i skubanie.
SQL Server używa blokad zakresu klucza do ochrony zakresu wierszy domyślnie zawartych w zestawie rekordów odczytywanym przez instrukcję Transact-SQL podczas korzystania z poziomu izolacji transakcji możliwego do serializacji ... więcej informacji tutaj: https://technet.microsoft.com /en-US/library/ms191272(v=SQL.105).aspx
W przypadku usuwania skubania naszym celem jest wyodrębnienie zakresu wierszy i dopilnowanie, aby żadne wiersze nie wystąpiły podczas ich usuwania, to znaczy, nie chcemy odczytywania fantomów ani wstawiania. Szeregowy poziom izolacji ma rozwiązać ten problem.
Zanim zademonstruję swoje rozwiązanie, chciałbym dodać, że ani nie zalecam przełączania domyślnego poziomu izolacji bazy danych na SERIALIZABLE, ani nie zalecam, aby moje rozwiązanie było najlepsze. Chcę tylko to przedstawić i przekonać się, dokąd możemy się udać.
Kilka notatek dotyczących utrzymania domu:
Aby rozpocząć eksperyment, utworzę testową bazę danych, przykładową tabelę i wypełnię tabelę 2 000 000 wierszy.
W tym momencie będziemy potrzebować jednego lub więcej indeksów, na które mogą działać mechanizmy blokujące SERIALIZABLE poziom izolacji.
Teraz sprawdźmy, czy utworzono nasze 2 000 000 wierszy
Mamy więc naszą bazę danych, tabelę, indeksy i wiersze. Ustawmy więc eksperyment usuwania skubań. Najpierw musimy zdecydować, jak najlepiej stworzyć typowy mechanizm usuwania skubania.
Jak widać, umieściłem jawną transakcję w pętli while. Jeśli chcesz ograniczyć opróżnianie logów, umieść go poza pętlą. Ponadto, ponieważ jesteśmy w modelu odzyskiwania PEŁNYM, możesz częściej tworzyć kopie zapasowe dziennika transakcji podczas wykonywania operacji usuwania skubania, aby zapobiec nadmiernemu wzrostowi dziennika transakcji.
Tak więc mam kilka celów z tym ustawieniem. Najpierw chcę moje zamki z kluczem; więc staram się, aby partie były jak najmniejsze. Nie chcę również negatywnie wpływać na współbieżność na moim „gigantycznym” stole; więc chcę wziąć moje zamki i zostawić je tak szybko, jak to możliwe. Dlatego zalecam, aby twoje partie były małe.
Teraz chcę podać bardzo krótki przykład tej procedury usuwania. Musimy otworzyć nowe okno w SSMS i usunąć jeden wiersz z naszej tabeli. Zrobię to w ramach transakcji niejawnej przy użyciu domyślnego poziomu izolacji PRZECZYTAJ ZOBOWIĄZANIE.
Czy ten wiersz rzeczywiście został usunięty?
Tak, zostało usunięte.
Teraz, aby zobaczyć nasze zamki, otwórzmy nowe okno w SSMS i dodajmy fragment kodu lub dwa. Używam sp_whoisactive Adama Mechanica, który można znaleźć tutaj: sp_whoisactive
Teraz jesteśmy gotowi do rozpoczęcia. W nowym oknie SSMS zacznijmy wyraźną transakcję, która spróbuje ponownie wstawić jeden wiersz, który usunęliśmy. W tym samym czasie uruchomimy naszą operację usuwania skubania.
Wstaw kod:
Rozpocznijmy obie operacje, zaczynając od wstawki, a następnie usuwając. Widzimy zamki z kluczem i zamki ekskluzywne.
Wstawka wygenerowała następujące blokady:
Nibbling delete / select trzyma te blokady:
Nasza wstawka blokuje nasze usuwanie zgodnie z oczekiwaniami:
Teraz zatwierdź transakcję wstawiania i zobacz, co jest grane.
I zgodnie z oczekiwaniami wszystkie transakcje zostały zakończone. Teraz musimy sprawdzić, czy wstawka była fantomem, czy też usunęła ją również operacja usuwania.
W rzeczywistości wstawka została usunięta; więc nie było dozwolone wstawianie fantomów.
Podsumowując, uważam, że prawdziwym celem tego ćwiczenia nie jest próba śledzenia każdego wiersza, strony lub blokady na poziomie tabeli i próba ustalenia, czy element partii jest zablokowany, a zatem wymagałaby naszej operacji usuwania, aby czekać. Taka mogła być intencja pytających; zadanie to jest jednak herkulesowe i zasadniczo niepraktyczne, jeśli nie niemożliwe. Prawdziwym celem jest dopilnowanie, aby nie powstały żadne niepożądane zjawiska, gdy odizolujemy zakres naszej partii za pomocą własnych blokad, a następnie poprzedzimy usunięcie partii. SERIALIZABLE poziom izolacji osiąga ten cel. Kluczem do sukcesu jest utrzymywanie małych skubków, kontrolowanie dziennika transakcji i eliminowanie niepożądanych zjawisk.
Jeśli chcesz prędkości, nie buduj gigantycznie głębokich tabel, których nie można podzielić na partycje, a zatem nie można używać przełączania partycji w celu uzyskania najszybszych wyników. Kluczem do szybkości jest podział i równoległość; kluczem do cierpienia jest gryzienie i blokowanie życia.
Proszę daj mi znać co myślisz.
Stworzyłem kilka dalszych przykładów SERIALIZABLE poziomu izolacji w działaniu. Powinny być dostępne pod poniższymi linkami.
Usuń operację
Wstaw operację
Operacje na rzecz równości - blokady kluczowego zakresu przy kolejnych kluczowych wartościach
Operacje na rzecz równości - pobieranie istniejących danych w singletonie
Operacje na rzecz równości - pobieranie singleton nieistniejących danych
Nierówności Operacje - Blokowanie kluczowych zakresów przy zakresie i kolejnych kluczowych wartościach
źródło
Jest to bardzo dobry pomysł, aby usunąć w małych partiach starannych lub kawałki . Chciałbym dodać mały
waitfor delay '00:00:05'
iw zależności od modelu odzyskiwania bazy danych - jeśliFULL
, to zrobićlog backup
i jeśliSIMPLE
wtedy zrobićmanual CHECKPOINT
, aby uniknąć wzdęcia z dziennika transakcji - pomiędzy partiami.To, co mówisz, nie jest całkowicie możliwe od razu po wyjęciu z pudełka (pamiętając o 3 punktorach). Jeśli powyższa sugestia -
small batches + waitfor delay
nie działa (pod warunkiem, że wykonasz odpowiednie testy), możesz użyćquery HINT
.Nie używaj
NOLOCK
- patrz kb / 308886 , Problemy dotyczące spójności odczytu serwera SQL autorstwa Itzika Ben-Gana , Umieszczanie NOLOCK wszędzie - autor Aaron Bertrand i SQL Server Wskazówka NOLOCK i inne słabe pomysły .READPAST
podpowiedź pomoże w twoim scenariuszu. IstotąREADPAST
podpowiedzi jest - jeśli istnieje blokada na poziomie wiersza, to serwer SQL jej nie przeczyta.Podczas moich ograniczonych testów znalazłem naprawdę dobrą przepustowość podczas używania
DELETE from schema.tableName with (READPAST, READCOMMITTEDLOCK)
i ustawiania poziomu izolacji sesji zapytania naREAD COMMITTED
używanie,SET TRANSACTION ISOLATION LEVEL READ COMMITTED
które jest domyślnym poziomem izolacji.źródło
Podsumowując inne podejścia pierwotnie oferowane w komentarzach do pytania.
Użyj,
NOWAIT
jeśli pożądane zachowanie ma zakończyć się niepowodzeniem całego fragmentu, gdy tylko napotkamy niezgodną blokadę.Z
NOWAIT
dokumentacji :Użyj,
SET LOCK_TIMEOUT
aby osiągnąć podobny wynik, ale z konfigurowalnym limitem czasu:Z
SET LOCK_TIMEOUT
dokumentacjiźródło
Załóżmy, że mamy 2 zapytania równoległe:
connect / session 1: zablokuje wiersz = 777
connect / session 2: zignoruje zablokowany wiersz = 777
LUB połącz / sesja 2: wyrzuci wyjątek
źródło
Spróbuj przefiltrować coś takiego - może się to skomplikować, jeśli chcesz naprawdę, bardzo konkretnie. Poszukaj w BOL opisu sys.dm_tran_locks
źródło
Możesz użyć NoLOCK podczas usuwania, a jeśli wiersze są zablokowane, nie zostaną usunięte. To nie jest idealne, ale może załatwić sprawę.
źródło
Msg 1065, Level 15, State 1, Line 15 The NOLOCK and READUNCOMMITTED lock hints are not allowed for target tables of INSERT, UPDATE, DELETE or MERGE statements.
, nieaktualnych od 2005