Dlaczego drugie INSERTstwierdzenie jest ~ 5 razy wolniejsze niż pierwsze?
Biorąc pod uwagę ilość wygenerowanych danych dziennika, uważam, że drugi nie kwalifikuje się do minimalnego rejestrowania. Jednak dokumentacja w Przewodniku wydajności ładowania danych wskazuje, że oba wkładki powinny mieć możliwość minimalnego logowania. Jeśli więc minimalne rejestrowanie jest kluczową różnicą w wydajności, dlaczego drugie zapytanie nie kwalifikuje się do minimalnego rejestrowania? Co można zrobić, aby poprawić sytuację?
Zapytanie nr 1: Wstawianie wierszy 5 mm za pomocą polecenia WSTAW ... Z (TABLOCK)
Rozważ następujące zapytanie, które wstawia wiersze 5 mm do sterty. To zapytanie wykonuje 1 secondi generuje 64MBdane dziennika transakcji zgłoszone przez sys.dm_tran_database_transactions.
CREATE TABLE dbo.minimalLoggingTest (n INT NOT NULL)
GO
INSERT INTO dbo.minimalLoggingTest WITH (TABLOCK) (n)
SELECT n
-- Any table/view/sub-query that correctly estimates that it will generate 5MM rows
FROM dbo.fiveMillionNumbers
-- Provides greater consistency on my laptop, where other processes are running
OPTION (MAXDOP 1)
GO
Zapytanie nr 2: Wstawienie tych samych danych, ale SQL nie docenia liczby wierszy
Rozważmy teraz bardzo podobne zapytanie, które działa na dokładnie tych samych danych, ale zdarza się, że rysuje z tabeli (lub złożonej SELECTinstrukcji z wieloma złączeniami w moim rzeczywistym przypadku produkcyjnym), gdzie oszacowanie liczności jest zbyt niskie. To zapytanie wykonuje się 5.5 secondsi generuje 461MBdane dziennika transakcji.
CREATE TABLE dbo.minimalLoggingTest (n INT NOT NULL)
GO
INSERT INTO dbo.minimalLoggingTest WITH (TABLOCK) (n)
SELECT n
-- Any table/view/sub-query that produces 5MM rows but SQL estimates just 1000 rows
FROM dbo.fiveMillionNumbersBadEstimate
-- Provides greater consistency on my laptop, where other processes are running
OPTION (MAXDOP 1)
GO
Pełny skrypt
Zobacz tę Pastebin, aby uzyskać pełny zestaw skryptów do generowania danych testowych i wykonywania jednego z tych scenariuszy. Pamiętaj, że musisz użyć bazy danych, która jest w SIMPLE modelu odzyskiwania .
Kontekst biznesowy
Częściowo poruszamy się wokół milionów wierszy danych i ważne jest, aby te operacje były jak najbardziej wydajne, zarówno pod względem czasu wykonania, jak i obciążenia dysku we / wy. Początkowo mieliśmy wrażenie, że utworzenie tabeli stosu i użycie INSERT...WITH (TABLOCK)było dobrym sposobem na zrobienie tego, ale teraz jesteśmy mniej pewni, biorąc pod uwagę, że zaobserwowaliśmy sytuację pokazaną powyżej w rzeczywistym scenariuszu produkcyjnym (choć przy bardziej złożonych zapytaniach, a nie wersja uproszczona tutaj).
SELECTinstrukcja z licznymi złączeniami, które generują zestaw wyników dlaINSERT.UPDATE STATISTICSPołączenia te dają słabe oszacowania liczności dla operatora wstawiania ostatecznej tabeli (który symulowałem w skrypcie repro poprzez złe wywołanie), a zatem nie jest to tak proste jak wydanieUPDATE STATISTICSpolecenia naprawienia problemu. Zgadzam się całkowicie, że uproszczenie zapytania, aby łatwiej było zrozumieć Cardinality Estimator, może być dobrym podejściem, ale wdrożenie określonej złożonej logiki biznesowej nie jest trywialne.