Dlaczego operator konkatenacji szacuje mniej wierszy niż dane wejściowe?

20

W poniższym fragmencie planu zapytania wydaje się oczywiste, że oszacowanie wiersza dla Concatenationoperatora powinno być ~4.3 billion rowslub suma oszacowań wiersza dla jego dwóch danych wejściowych.

Jednak szacunek ~238 million rowsjest generowany, co prowadzi do nieoptymalnej Sort/ Stream Aggregatestrategii, która przelewa setki GB danych do tempdb. Logicznie spójna ocena w tym przypadku spowodowałaby Hash Aggregate, usunęła wyciek i znacznie poprawiła wydajność zapytania.

Czy to błąd w SQL Server 2014? Czy istnieją uzasadnione okoliczności, w których oszacowanie niższe niż dane wejściowe może być uzasadnione? Jakie obejścia mogą być dostępne?

wprowadź opis zdjęcia tutaj

Oto pełny plan zapytań (anonimowy). Nie mam dostępu sysadmin do tego serwera w celu dostarczenia danych wyjściowych QUERYTRACEON 2363lub podobnych flag śledzenia, ale może być w stanie uzyskać te dane wyjściowe od administratora, jeśli byłyby pomocne.

Baza danych ma poziom zgodności 120 i dlatego używa nowego programu SQL Server 2014 Cardinality Estimator.

Statystyki są aktualizowane ręcznie za każdym razem, gdy dane są ładowane. Biorąc pod uwagę ilość danych, obecnie używamy domyślnej częstotliwości próbkowania. Możliwe, że wyższa częstotliwość próbkowania (lub FULLSCAN) może mieć wpływ.

Geoff Patterson
źródło

Odpowiedzi:

21

Aby zacytować Campbell Fraser w tym elemencie Connect :

Te „niespójności liczności” mogą wystąpić w wielu sytuacjach, w tym w przypadku użycia konkat. Mogą powstać, ponieważ oszacowanie określonego poddrzewa w ostatecznym planie mogło zostać dokonane w odmiennie skonstruowanym, ale logicznie równoważnym poddrzewie. Ze względu na statystyczny charakter estymacji liczności, nie można zagwarantować, że estymacja na różnych, ale logicznie równoważnych drzewach będzie taka sama. Tak więc ogólnie nie ma żadnych gwarancji oczekiwanej spójności.

Aby to nieco rozwinąć: chciałbym to wyjaśnić, mówiąc, że wstępne oszacowanie liczności (przeprowadzone przed rozpoczęciem optymalizacji opartej na kosztach) daje bardziej „spójne” oszacowanie liczności, ponieważ całe początkowe drzewo jest przetwarzane, z każdym kolejnym oszacowanie w zależności bezpośrednio od poprzedniego.

Podczas optymalizacji opartej na kosztach części drzewa planu (jeden lub więcej operatorów) można eksplorować i zastępować alternatywami, z których każdy może wymagać nowej oceny liczności. Nie ma ogólnego sposobu na stwierdzenie, która ocena będzie ogólnie lepsza od innej, więc całkiem możliwe jest, aby otrzymać ostateczny plan, który wydaje się „niespójny”. Jest to po prostu efekt połączenia „kawałków planów” w celu ostatecznego ustalenia.

To powiedziawszy, wprowadzono kilka szczegółowych zmian w nowym estymatorze liczności (CE) wprowadzonym w SQL Server 2014, który to sprawia nieco mniej powszechne niż w przypadku pierwotnego CE.

Oprócz uaktualnienia do najnowszej aktualizacji zbiorczej i sprawdzenia, czy poprawki optymalizatora w wersji 4199 są włączone, głównymi opcjami są próby zmiany statystyk / indeksów (zwracając uwagę na ostrzeżenia o brakujących indeksach) i aktualizacji lub wyrażenie zapytania w inny sposób. Celem jest uzyskanie planu, który wyświetla wymagane zachowanie. Można to na przykład zamrozić za pomocą przewodnika po planach.

Anonimowy plan utrudnia ocenę szczegółów, ale przyjrzałbym się również mapom bitowym, aby sprawdzić, czy są one w wariancie „zoptymalizowanym” (Opt_Bitmap) czy postoptymalizacyjnym (Bitmap). Jestem również podejrzliwy w stosunku do filtrów.

Jeśli liczba wierszy jest jakoś dokładna, wydaje się, że jest to zapytanie, które może skorzystać z magazynu kolumn. Oprócz zwykłych korzyści, możesz być w stanie skorzystać z dynamicznego przydzielania pamięci dla operatorów w trybie wsadowym ( może być wymagana flaga śledzenia 9389 ).

Paul White mówi GoFundMonica
źródło
7

Zbudowanie, co prawda dość prostego stanowiska testowego na SQL Server 2012 (11.0.6020) pozwala mi odtworzyć plan z dwoma połączonymi zapytaniami dopasowanymi, które są łączone za pośrednictwem UNION ALL. Moje stanowisko testowe nie wyświetla nieprawidłowego oszacowania, które widzisz. Być może jest to problem z SQL Server 2014 CE.

Dostaję szacunkową wartość 133,785 wierszy dla zapytania, które faktycznie zwraca 280 wierszy, ale należy się tego spodziewać, jak zobaczymy poniżej:

IF OBJECT_ID('dbo.Union1') IS NOT NULL
DROP TABLE dbo.Union1;
CREATE TABLE dbo.Union1
(
    Union1_ID INT NOT NULL
        CONSTRAINT PK_Union1
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , Union1_Text VARCHAR(255) NOT NULL
    , Union1_ObjectID INT NOT NULL
);

IF OBJECT_ID('dbo.Union2') IS NOT NULL
DROP TABLE dbo.Union2;
CREATE TABLE dbo.Union2
(
    Union2_ID INT NOT NULL
        CONSTRAINT PK_Union2
        PRIMARY KEY CLUSTERED
        IDENTITY(2,2)
    , Union2_Text VARCHAR(255) NOT NULL
    , Union2_ObjectID INT NOT NULL
);

INSERT INTO dbo.Union1 (Union1_Text, Union1_ObjectID)
SELECT o.name, o.object_id
FROM sys.objects o;

INSERT INTO dbo.Union2 (Union2_Text, Union2_ObjectID)
SELECT o.name, o.object_id
FROM sys.objects o;
GO

SELECT *
FROM dbo.Union1 u1
    INNER HASH JOIN sys.objects o ON u1.Union1_ObjectID = o.object_id
UNION ALL
SELECT *
FROM dbo.Union2 u2
    INNER HASH JOIN sys.objects o ON u2.Union2_ObjectID = o.object_id;

Myślę, że powodem jest brak statystyk dla dwóch powstałych złączeń, które są UNIONed. W większości przypadków SQL Server musi zgadywać, co do selektywności kolumn w obliczu braku statystyk.

Joe Sack ma ciekawą poczytać na ten temat tutaj .

Dla UNION ALLbezpieczeństwa można powiedzieć, że zobaczymy dokładnie całkowitą liczbę wierszy zwróconych przez każdy składnik unii, jednak ponieważ SQL Server używa oszacowań wierszy dla dwóch składników UNION ALL, widzimy, że dodaje całkowitą szacunkową liczbę wierszy z obu zapytania mające na celu oszacowanie dla operatora konkatenacji.

W moim przykładzie powyżej szacunkowa liczba wierszy dla każdej części UNION ALLwynosi 66,8927, co po zsumowaniu wynosi 133,785, co widzimy dla szacunkowej liczby wierszy dla operatora konkatenacji.

Rzeczywisty plan wykonania powyższego zapytania dotyczącego unii wygląda następująco:

wprowadź opis zdjęcia tutaj

Możesz zobaczyć „szacunkową” a „faktyczną” liczbę wierszy. W moim przypadku dodanie „szacunkowej” liczby wierszy zwróconych przez dwóch operatorów dopasowania mieszającego dokładnie równa się liczbie pokazanej przez operator konkatenacji.

Spróbowałbym uzyskać dane wyjściowe ze śledzenia 2363 itp., Jak zalecono w poście Paula White'a, który pokazałeś w swoim pytaniu. Alternatywnie możesz spróbować użyć OPTION (QUERYTRACEON 9481)zapytania, aby przywrócić wersję 70 CE, aby sprawdzić, czy to „rozwiązuje” problem.

Max Vernon
źródło
1
Dzięki. Zdecydowanie widziałem, że „przyczyną jest brak statystyk dla dwóch wynikowych złączeń, które są UNIONed”, mają duży wpływ na kolejne złączenia lub agregacje (występujące po UNII). Z mojego doświadczenia wynika, że ​​SQL 2014 obsługuje to lepiej niż SQL 2012. Oto prosty skrypt testowy, którego używałem w przeszłości, na przykład: gist.github.com/anonymous/1497112d8b25ab8fb782a04569959c68 Jednak nie sądzę, że operator konkatenacji potrzebowałby tego samego rodzaju informacji o rozkładzie wartości, które łączy może potrzebować.
Geoff Patterson
Zgadzam się z tobą, że konkatenacja nie powinna wymagać statystyk, aby działać poprawnie. Powinien po prostu być w stanie niezawodnie dodawać szacunkowe przychodzące wiersze, aby dobrze zorientować się, ile wierszy zostanie wygenerowanych. Jak pokazuje @PaulWhite w swojej odpowiedzi, nie zawsze jest to zaskakujące. Dla mnie na wynos może to wyglądać prosto, ale w rzeczywistości może nie być. Bardzo się cieszę, że zadałeś pytanie tak, jak to zrobiłeś, żałuję tylko, że nie musiałeś anonimizować planu - byłoby interesujące zobaczyć aktualne zapytanie.
Max Vernon