Kontynuując moją serię artykułów na temat zatrzasków, tym razem omówię zatrzask DBCC_OBJECT_METADATA i pokażę, jak może być głównym wąskim gardłem dla sprawdzania spójności przed SQL Server 2016 w określonych okolicznościach. Problem dotyczy DBCC CHECKDB, DBCC CHECKTABLE i DBCC CHECKFILEGROUP, ale dla jasności będę odwoływał się do DBCC CHECKDB w dalszej części tego postu.
Możesz się zastanawiać, dlaczego piszę o problemie, który dotyczy starszych wersji, ale wciąż istnieje ogromna liczba SQL Server 2014 i starszych instancji, więc jest to ważny temat dla mojej serii.
Gorąco polecam przeczytanie pierwszego posta z serii przed tym, aby mieć ogólną wiedzę na temat zatrzasków.
Co to jest zatrzask DBCC_OBJECT_METADATA?
Aby wyjaśnić ten zatrzask, muszę trochę wyjaśnić, jak działa DBCC CHECKDB.
Wśród ogromnej liczby kontroli spójności, które wykonuje DBCC CHECKDB, jest sprawdzanie poprawności indeksów nieklastrowanych. W szczególności DBCC CHECKDB zapewnia:
- Dla każdego rekordu indeksu nieklastrowego w każdym indeksie nieklastrowym istnieje dokładnie jeden „pasujący” rekord danych w tabeli podstawowej (sterta lub indeks klastrowy)
- Dla każdego rekordu danych w tabeli istnieje dokładnie jeden „pasujący” rekord indeksu nieklastrowego w każdym indeksie nieklastrowym zdefiniowanym dla tabeli, z uwzględnieniem indeksów filtrowanych
Nie zagłębiając się zbytnio w szczegóły, jak to się robi, dla każdego rekordu danych w tabeli DBCC CHECKDB konstruuje każdy rekord indeksu nieklastrowego, który powinien istnieć dla każdego indeksu nieklastrowego i upewnia się, że skonstruowany rekord indeksu nieklastrowanego dokładnie odpowiada rzeczywistemu rekord indeksu nieklastrowanego. Jeśli indeks nieklastrowy zawiera kolumnę wyliczaną (jako część klucza indeksu nieklastrowego lub jako kolumnę INCLUDEd), DBCC CHECKDB musi określić wartość kolumny obliczonej do użycia podczas konstruowania rekordów indeksu.
Oprócz sprawdzania poprawności indeksu nieklastrowanego, jeśli istnieje utrzymujący się obliczoną kolumnę w definicji tabeli, a następnie dla każdego rekordu danych w tabeli DBCC CHECKDB musi sprawdzić, czy utrwalona wartość jest poprawna, niezależnie od tego, czy ta kolumna jest częścią indeksu nieklastrowego, czy nie.
Jak więc oblicza wartości obliczonych kolumn?
Procesor zapytań udostępnia mechanizm obliczania obliczonych wartości kolumn, nazywany „ewaluatorem wyrażeń”. DBCC CHECKDB wywołuje tę funkcję, dostarczając odpowiednie informacje o metadanych i rekord danych, a oceniający wyrażenia używa zapisanej definicji kolumny wyliczanej w metadanych oraz wartości z rekordu danych i zwraca wartość kolumny wyliczonej do użycia przez DBCC CHECKDB . Wewnętrzne działanie ewaluatora wyrażeń jest poza kontrolą kodu DBCC, ale aby móc korzystać z ewaluatora wyrażeń, najpierw należy nabyć zatrzask; zatrzask DBCC_OBJECT_METADATA.
Jak zatrzask staje się wąskim gardłem?
Oto problem:istnieje tylko jeden akceptowalny tryb, w którym zatrzask DBCC_OBJECT_METADATA można uzyskać przed użyciem ewaluatora wyrażeń, i jest to tryb EX (wyłączny). A jak wiesz, czytając wpis wprowadzający do serii, tylko jeden wątek na raz może utrzymać zatrzask w trybie EX.
Zebranie wszystkich tych informacji razem:gdy baza danych ma utrwalone kolumny obliczone lub indeksy nieklastrowane, które zawierają kolumny obliczone, należy użyć ewaluatora wyrażeń. Jeśli wersja SQL Server to Enterprise, DBCC CHECKDB może korzystać z równoległości, a więc ma wiele wątków wykonujących różne kontrole. A gdy tylko masz wiele wątków próbujących uzyskać zatrzask w trybie EX, zatrzask ten staje się wąskim gardłem. To, jak duże staje się wąskie gardło, zależy od tego, jak dużo trzeba użyć ewaluatora wyrażeń, więc im więcej jest utrwalonych kolumn obliczanych lub indeksów nieklastrowanych używających kolumn obliczanych, a im większa jest liczba wierszy w tych tabelach, tym większe wąskie gardło staje się zatrzask DBCC _OBJECT_METADATA.
Należy jednak pamiętać, że to wąskie gardło występuje tylko w wersjach programu SQL Server wcześniejszych niż SQL Server 2016. W programie SQL Server 2016 firma Microsoft zdecydowała się „naprawić” wąskie gardło, wyłączając sprawdzanie indeksów nieklastrowanych przy użyciu domyślnie kolumn obliczanych i wykonując je tylko wtedy, gdy Używana jest opcja EXTENDED_LOGICAL_CHECKS.
Pokazuje wąskie gardło
Możesz łatwo odtworzyć wąskie gardło dla siebie, uruchamiając DBCC CHECKDB w bazie danych, która ma utrwalone kolumny obliczane lub indeksy nieklastrowane z kolumnami obliczanymi, a baza danych AdventureWorks dostarczona przez firmę Microsoft jest doskonałym przykładem. Tutaj możesz pobrać kopie zapasowe AdventureWorks dla swojej wersji programu SQL Server. Przeprowadziłem kilka testów przy użyciu bazy danych AdventureWorks2014 na instancji SQL Server 2014 (na 32-rdzeniowym Dell R720) i powiększyłem bazę danych do kilkuset GB za pomocą skryptów Jonathana.
Kiedy uruchomiłem DBCC CHECKDB, z serwerem MAXDOP ustawionym na 0, uruchomienie zajęło ponad 5 godzin. Typ oczekiwania LATCH_EX stanowił około 30% czasów oczekiwania, przy czym każde oczekiwanie nie przekraczało 1 milisekundy, a 99% czekań LATCH_EX dotyczyło zatrzasku DBCC_OBJECT_METADATA.
Szukałem indeksów nieklastrowych zawierających kolumny obliczane za pomocą następującego kodu:
SELECT [s].[name] AS [Schema], [o].[name] AS [Object], [i].[name] AS [Index], [c].[name] AS [Column], [ic].* FROM sys.columns [c] JOIN sys.index_columns [ic] ON [ic].[object_id] = [c].[object_id] AND [ic].[column_id] = [c].[column_id] JOIN sys.indexes [i] ON [i].[object_id] = [ic].[object_id] AND [i].[index_id] = [ic].[index_id] JOIN sys.objects [o] ON [i].[object_id] = [o].[object_id] JOIN sys.schemas [s] ON [o].[schema_id] = [s].[schema_id] WHERE [c].[is_computed] = 1;
Ten kod znalazł sześć indeksów nieklastrowanych w bazie danych AdventureWorks2014. Wyłączyłem wszystkie sześć indeksów (używając ALTER INDEX … DISABLE) i ponownie uruchomiłem DBCC CHECKDB i zakończyło się to w około 18 minut. Tak więc wąskie gardło DBCC_OBJECT_METADATA było głównym czynnikiem powodującym, że DBCC CHECKDB działał ponad 16 razy wolniej!
Podsumowanie
Niestety wyłączenie indeksów nieklastrowanych przy użyciu kolumn obliczanych (a następnie ponowne ich włączenie przy użyciu ALTER INDEX… REBUILD) jest *jedynym* sposobem na usunięcie wąskiego gardła DBCC_OBJECT_METADATA w wersjach wcześniejszych niż SQL Server 2016 przy jednoczesnym zachowaniu wszystkich pozostałych funkcji DBCC CHECKDB. Wyłączenie indeksów nieklastrowych prawdopodobnie nie jest czymś, co chcesz robić w środowisku produkcyjnym, chyba że masz okno konserwacji o zerowej aktywności. Oznacza to, że prawdopodobnie wyłączysz te indeksy nieklastrowane tylko w celu usunięcia wąskiego gardła, jeśli kontrole spójności zostaną przeniesione na inny serwer przy użyciu metody backup-copy-restore-CHECKDB.
Innym sposobem na zrobienie tego jest użycie opcji WITH PHYSICAL_ONLY podczas uruchamiania DBCC CHECKDB, ale wtedy tracisz wszystkie dogłębne kontrole logiczne, więc nie jestem wielkim fanem polecania tego jako rozwiązania.