W tej kontynuacji mojej serii „dostrajania wydajności pod kątem szarpnięcia kolanem” chciałbym omówić cztery typowe problemy, które widzę podczas używania tabel tymczasowych. Każdy z tych problemów może sparaliżować obciążenie pracą, dlatego warto o nich wiedzieć i szukać w swoim środowisku.
Problem 1:Używanie tymczasowych tabel tam, gdzie nie są potrzebne
https://www.flickr. com/photos/tea_time/3890677277/Tabele tymczasowe mają wiele zastosowań (prawdopodobnie najczęstszym jest przechowywanie pośredniego zestawu wyników do późniejszego wykorzystania), ale należy pamiętać, że wprowadzając do zapytania tabelę tymczasową, przerywasz przepływ danych przez procesor zapytań.
Pomyśl o populacji tabeli tymczasowej jako o twardym zatrzymaniu, ponieważ istnieje zapytanie (nazwijmy je producentem) w celu wygenerowania pośredniego zestawu wyników, który jest następnie przechowywany w tabeli tymczasowej w tempdb, a następnie następne zapytanie (nazwijmy się konsument) musi ponownie odczytać dane z tabeli tymczasowej.
Często stwierdzam, że niektóre części obciążenia faktycznie działają lepiej, gdy tabela tymczasowa jest całkowicie usunięta, więc dane przepływają z części zapytania producenta do części zapytania dla konsumenta bez konieczności utrwalania w tempdb, a optymalizator zapytań może stworzyć bardziej optymalny ogólny plan.
Możesz teraz pomyśleć:„dlaczego ktoś miałby używać tymczasowego stołu, jeśli to spowalnia sprawę?” – i słusznie! Odkryłem, że w takich przypadkach korzystanie z tymczasowego stołu stało się zinstytucjonalizowane w zespole programistów; ktoś odkrył, że używanie tabeli tymczasowej wiele lat temu zwiększyło wydajność, więc tabele tymczasowe stały się domyślnym wyborem projektowym.
To może być trudne do zmiany, zwłaszcza jeśli masz starszego programistę lub menedżera, który jest przekonany, że zawsze należy używać tabel tymczasowych. Prostą rzeczą do wypróbowania jest wybranie kosztownego zapytania (na przykład długotrwałego lub wykonywanego wiele razy na sekundę) i usunięcie jednej lub więcej tabel tymczasowych, aby sprawdzić, czy bez nich wydajność wzrośnie. A jeśli tak, masz dowód, aby pokazać nieustępliwym!
Problem 2:Brak filtrowania podczas wypełniania tabel tymczasowych
Nawet jeśli nie możesz usunąć tabeli tymczasowej, możesz znacznie poprawić wydajność, upewniając się, że kod wypełniający tabelę tymczasową prawidłowo filtruje dane pobierane z tabel źródłowych.
Straciłem rachubę, ile razy widziałem tabelę tymczasową zapełnioną kodem zaczynającym się od SELECT *
, zawiera kilka nieograniczających łączeń i nie ma klauzuli WHERE, a późniejsze zapytanie, które używa tabeli tymczasowej, używa tylko kilku kolumn i zawiera klauzulę WHERE, która znacznie zmniejsza liczbę wierszy.
Pamiętam jeden przypadek, w którym tymczasowa tabela w procedurze składowanej agregowała dane z 15 lat z głównej bazy danych, a następnie używane były tylko dane z bieżącego roku. To wielokrotnie powodowało wzrost tempdb, aż zabrakło miejsca na woluminie dysku, a procedura składowana nie powiodła się.
Za każdym razem, gdy wypełniasz tabelę tymczasową, używaj tylko niezbędnych kolumn tabeli źródłowej i używaj tylko niezbędnych wierszy — tj. przesuń predykaty filtru w górę do kodu wypełnienia tabeli tymczasowej. To nie tylko zaoszczędzi miejsce w tempdb, ale także zaoszczędzi dużo czasu, ponieważ nie będzie trzeba kopiować niepotrzebnych danych z tabeli źródłowej (i potencjalnie usunie przede wszystkim potrzebę odczytywania stron źródłowej bazy danych z dysku).
Problem 3:Nieprawidłowe tymczasowe indeksowanie tabeli
Podobnie jak w przypadku zwykłych tabel, należy tworzyć tylko te indeksy, które będą faktycznie używane przez późniejszy kod zapytania, aby zwiększyć wydajność zapytania. Widziałem wiele przypadków, w których istnieje indeks nieklastrowy na kolumnę tabeli tymczasowej, a indeksy jednokolumnowe, które są wybierane bez analizy późniejszego kodu, są często zupełnie bezużyteczne. Teraz połącz bezużyteczne indeksy nieklastrowe z brakiem filtrowania podczas zapełniania tabeli tymczasowej, a otrzymasz przepis na ogromne rozdęcie tempdb.
Ponadto, ogólnie rzecz biorąc, szybsze jest tworzenie indeksów po wypełnieniu tabeli. Daje to dodatkową korzyść, że indeksy będą miały dokładne statystyki, co może dodatkowo pomóc w zapytaniu, ponieważ optymalizator zapytań będzie w stanie wykonać dokładne oszacowanie kardynalności.
Posiadanie wielu nieklastrowanych indeksów, które nie są używane, marnuje nie tylko miejsce na dysku, ale także czas potrzebny na ich utworzenie. Jeśli jest to w kodzie, który jest często wykonywany, usunięcie tych niepotrzebnych indeksów, które są tworzone przy każdym uruchomieniu kodu, może mieć znaczący wpływ na ogólną wydajność.
Problem 4:rywalizacja o blokadę tempdb
Często zdarza się, że w tempdb występuje wąskie gardło blokujące, które można prześledzić do tymczasowego użycia tabeli. Jeśli istnieje wiele jednoczesnych połączeń z uruchomionym kodem, który tworzy i usuwa tymczasowe tabele, dostęp do bitmap alokacji bazy danych w pamięci może stać się znaczącym wąskim gardłem.
Dzieje się tak, ponieważ tylko jeden wątek na raz może zmieniać mapę bitową alokacji, aby oznaczyć strony (z tabeli tymczasowej) jako przydzielone lub cofnięte, więc wszystkie inne wątki muszą czekać, zmniejszając przepustowość obciążenia. Mimo że istnieje tymczasowa pamięć podręczna tabeli od SQL Server 2005, nie jest ona bardzo duża i istnieją ograniczenia dotyczące tego, kiedy tymczasowa tabela może być przechowywana w pamięci podręcznej (np. tylko wtedy, gdy jej rozmiar jest mniejszy niż 8 MB).
Tradycyjne sposoby obejścia tego problemu polegają na użyciu flagi śledzenia 1118 i wielu plików danych tempdb (więcej informacji można znaleźć w tym poście na blogu), ale należy też rozważyć całkowite usunięcie tabel tymczasowych!
Podsumowanie
Tabele tymczasowe mogą być bardzo przydatne, ale są bardzo łatwe i często niepoprawnie używane. Za każdym razem, gdy piszesz (lub przeglądasz kod), który korzysta z tabeli tymczasowej, rozważ następujące kwestie:
- Czy ta tymczasowa tabela naprawdę jest potrzebna ?
- Czy kod wypełnia tabelę przy użyciu prawidłowego filtrowania? ograniczyć tymczasowy rozmiar tabeli?
- Czy indeksy są tworzone po wypełnieniu tabeli? (ogólnie) i czy są używane indeksy przez późniejszy kod?
Paul White ma kilka świetnych postów (tu i tutaj) na temat tymczasowego używania obiektów i buforowania, które również polecam przeczytać.
I ostatnia rzecz, jeśli zdecydujesz się nie używać tabeli tymczasowej, nie zamieniaj jej po prostu na zmienną tabeli, wspólne wyrażenie tabeli lub kursor (z których wszystkie są typowymi sposobami, w jakie ludzie próbują „zoptymalizować” tabela tymczasowa) – znajdź najbardziej efektywny sposób (prze)pisania kodu – nie ma odpowiedzi „jeden rozmiar dla wszystkich”.
Miłego rozwiązywania problemów do następnego razu!