5

Mam za zadanie poprawić wydajność () i jest to mój pierwszy rzeczywisty tuning wydajności) raportowania przechowywanej procedury, która jest wywoływana przez front-end SSRS i procedura przechowywana trwa obecnie około 30 sekund do uruchom na największej ilości danych (w oparciu o filtry ustawione z frontendu raportu).Ulepszanie czasu wykonywania przechowywanych procedur - dostrajanie tabel tymczasowych?

Ta procedura składowana zawiera zestawienie 19 zapytań, z których większość przekształca dane z początkowego (tradycyjnego) formatu z tabel podstawowych do znaczącego zestawu danych, który ma być wyświetlany po stronie biznesowej.

Utworzyłem zapytanie oparte na kilku DMV-ach, aby dowiedzieć się, które są najbardziej zasobochłonne zapytania z procedury składowanej (mały fragment poniżej) i znalazłem jedno zapytanie, które trwa około 10 sekund, średnia, do uzupełnienia.

select 
    object_name(st.objectid)                 [Procedure Name] 
    , dense_rank() over (partition by st.objectid order by qs.last_elapsed_time desc)   [rank-execution time] 
    , dense_rank() over (partition by st.objectid order by qs.last_logical_reads desc)   [rank-logical reads] 
    , dense_rank() over (partition by st.objectid order by qs.last_worker_time desc)   [rank-worker (CPU) time] 
    , dense_rank() over (partition by st.objectid order by qs.last_logical_writes desc)   [rank-logical write] 
     ... 
from sys.dm_exec_query_stats as qs 
    cross apply sys.dm_exec_sql_text (qs.sql_handle) as st 
    cross apply sys.dm_exec_text_query_plan (qs.plan_handle, qs.statement_start_offset, qs.statement_end_offset) as qp 
where st.objectid in (object_id('SuperDooperReportingProcedure')) 
    , [rank-execution time] 
    , [rank-logical reads] 
    , [rank-worker (CPU) time] 
    , [rank-logical write] desc 

Teraz ta kwerenda jest nieco dziwne w tym sensie, że plan wykonania pokazuje, że pokazuje, że większość prac (~ 80%) odbywa się podczas wstawiania danych do lokalnej tabeli tymczasowej, a nie wtedy, gdy odpytywanie innych tabel, z których pobierane są dane źródłowe, a następnie manipulowane. (Poniżej zrzut ekranu jest z programu SQL Sentry Plan Explorer)

enter image description here

Również pod względem szacunków rzędu, plan wykonania ma daleko szacunki dotyczące tego, w tym sensie, że istnieją tylko 4218 wiersze wstawione do lokalnej tabela tymczasowa w przeciwieństwie do wierszy ~ 248k, które plan wykonania uważa za przeniesienie do lokalnej tabeli tymczasowej. Tak więc, ze względu na to, myślę "statystyki", ale czy nadal ma to znaczenie, jeśli ~ 80% pracy jest rzeczywistą wstawić do stołu?

Jednym z moich pierwszych zaleceń było ponowne napisanie całego procesu i procedury przechowywanej, tak aby nie uwzględniać przenoszenia i przekształcania danych do przechowywanej procedury składowania i przeprowadzania transformacji danych co noc w niektórych utrwalonych tabelach (dane w czasie rzeczywistym nie są wymagane, tylko istotne dane do końca poprzedniego dnia). Ale strona biznesowa nie chce zainwestować czasu i zasobów w przeprojektowanie tego, a zamiast tego "sugeruje", że robię dostrajanie wydajności w sensie znajdowania gdzie i jakie indeksy mogę dodać, aby przyspieszyć to.

Nie sądzę, że dodanie indeksów do tabel podstawowych poprawi wydajność raportu, ponieważ większość czasu potrzebnego do uruchomienia kwerendy polega na zapisaniu danych do tabeli tymczasowej (co z mojej wiedzy spowoduje trafienie tempdb, co oznacza, że ​​zostaną zapisane na dysk -> zwiększony czas z powodu opóźnienia we/wy).

Ale mimo to, jak już wspomniałem, jest to moje pierwsze zadanie polegające na dostrajaniu osiągów i starałem się czytać jak najwięcej w związku z tym w ciągu ostatnich kilku dni i są to moje dotychczasowe wnioski, ale Chciałbym poprosić o radę szerszej publiczności i mam nadzieję, że zdobędę więcej informacji i zrozumienie, co mogę zrobić, aby ulepszyć tę procedurę.

W kilku jasnych pytań byłbym wdzięczny, gdyby mógł odpowiedzieć to:

  • Czy jest coś błędnie, co powiedziałem powyżej (w moim zrozumieniu dB lub moich założeń)?
  • Czy to prawda, że ​​dodanie indeksu do tabeli tymczasowej spowoduje zwiększenie czasu wykonania, ponieważ tabela (i powiązany z nią indeks (y) są/są odbudowywane po każdym wykonaniu)?
  • Czy w tym scenariuszu może być coś jeszcze, bez konieczności ponownego pisania procedury/zapytań i można to zrobić tylko za pomocą indeksów lub innych metod strojenia? (Czytałem kilka nagłówków artykułów, które można również "dostroić tempdb", ale jeszcze nie dostałem szczegółów).

Każda pomoc jest bardzo doceniana i jeśli potrzebujesz więcej informacji, chętnie opublikuję.

Update (02 sierpnia 2016)

Zapytanie o którym mowa, jest (częściowo) poniżej. Brakuje jeszcze kilka kruszywa kolumny i odpowiadające im linie w sekcji GROUP BY:

select 
    b.ProgramName 
    ,b.Region 
    ,case when b.AM IS null and b.ProgramName IS not null 
     then 'Unassigned' 
     else b.AM 
    end as AM 
    ,rtrim(ltrim(b.Store)) Store 
    ,trd.Store_ID 
    ,b.appliesToPeriod 
    ,isnull(trd.countLeadActual,0) as Actual 
    ,isnull(sum(case when b.budgetType = 0 and b.budgetMonth between @start_date and @end_date then b.budgetValue else 0 end),0) as Budget 
    ,isnull(sum(case when b.budgetType = 0 and b.budgetMonth between @start_date and @end_date and (trd.considerMe = -1 or b.StoreID < 0) then b.budgetValue else 0 end),0) as CleanBudget 
    ... 
into #SalvesVsBudgets 
from #StoresBudgets b 
    left join #temp_report_data trd on trd.store_ID = b.StoreID and trd.newSourceID = b.ProgramID 
where (b.StoreDivision is not null or (b.StoreDivision is null and b.ProgramName = 'NewProgram')) 
    group by 
     b.ProgramName 
     ,b.Region 
     ,case when b.AM IS null and b.ProgramName IS not null 
      then 'Unassigned' 
      else b.AM 
     end 
    ,rtrim(ltrim(b.Store)) 
    ,trd.Store_ID 
    ,b.appliesToPeriod 
    ,isnull(trd.countLeadActual,0) 

Nie jestem pewien, czy jest to rzeczywiście pomocne, ale ponieważ @kcung o to, dodałem informację.

Ponadto, aby odpowiedzieć na jego pytania:

  • tabele tymczasowe nie mają indeksy na nich
  • RAM rozmiar: 32 GB

Update (03 sierpnia 2016):

Próbowałem sugerować @ kcung, aby przenieść instrukcje CASE z kwerendy generowania agregatów i niefortunne ogólnie rzecz biorąc, czas procedury nie poprawił się, zauważalnie, ponieważ wciąż waha się w zakresie od ± 0,25 do ± 1,0 sekundy (tak, zarówno niższy, jak i wyższy czas, niż oryginalna wersja procedury przechowywanej - ale zgaduję, że to jest spowodowane zmiennym obciążeniem na moim komputerze).

plan wykonania dla tego samego zapytania, ale zmodyfikowane w celu usuwania warunki CASE, pozostawiając tylko agregaty SUM jest teraz:

enter image description here

Odpowiedz

0

jakaś szansa widzę kwerendy? i indeksy na obu tabelach? Jak duży jest twój baran? jak duży jest rząd w każdej tabeli (w przybliżeniu)? Czy można zaktualizować statystyki dla obu tabel i ponownie wysłać planer zapytań?

Aby odpowiedzieć na to pytanie:

  1. jesteś głównie prawo, z wyjątkiem części dodanie indeksów. Dodanie indeksów pomoże zapytaniu wykonać wyszukiwanie. Daje także szansę na to, że planista zapytań rozważy zagnieżdżony plan łączenia pętli zamiast planu łączenia hasha. Niestety, nie mogę odpowiedzieć na więcej, dopóki moje pytanie nie zostanie udzielone.
  2. Nie należy dodawać indeksu do tabeli tymczasowej. Dodanie indeksu do tej tabeli temp (lub dowolnej tabeli docelowej wstawiania) zwiększy czas zapisu, ponieważ wstawka będzie musiała zaktualizować ten indeks. Wyobraź sobie indeks jako kopię tabeli z mniejszą ilością informacji, który znajduje się na szczycie stołu i musi być zsynchronizowany z twoją tabelą. Każdy zapis (wstawianie, aktualizowanie, usuwanie) wymaga aktualizacji tego indeksu.
  3. Patrząc na obie sumy wierszy w tabelach, zapytanie to powinno przebiegać szybciej niż 10 sekund, chyba że masz komputer z cytryną, to jest inna historia.

EDIT: prostu chce zwrócić uwagę na pkt 2, nie zdawałem sobie sprawy, że jesteś tabela źródłowa jest tabela temp, jak również. Tabela tymczasowa jest niszczona po zakończeniu każdej sesji połączenia. Dodanie indeksu do tabeli tymczasowej oznacza, że ​​dodasz dodatkowy czas na utworzenie tego indeksu za każdym razem, gdy utworzysz tabelę tymczasową.

EDYTOWANIE: Niestety, używam teraz telefonu. Będę krótko. Więc zasadniczo 2 rzeczy:

  • dodać klucz podstawowy w momencie tworzenia tabeli temp więc to zrobić za jednym zamachem. Nie przejmuj się dodawaniem nieklastrowanego indeksu lub jakiegokolwiek indeksu pokrycia, aby ostatecznie poświęcić więcej czasu na ich tworzenie.

  • zobacz zapytanie, wszystkie przypadki, gdy instrukcja, zamiast robić to w tym zapytaniu, dlaczego nie dodasz ich jako innej kolumny w tabeli. Zasadniczo chcesz uniknąć kalkulacji w locie podczas robienia grupy przez. Możesz zostawić sumę() w zapytaniu, ponieważ jest to zapytanie zbiorcze, ale spróbuj zredukować obliczanie czasu wykonywania w miarę możliwości.

Próbka:

case when b.AM IS null and b.ProgramName IS not null 
    then 'Unassigned' 
    else b.AM 
end as AM 

Można utworzyć kolumnę o nazwie AM podczas tworzenia tabeli B. Także te rtrim i ltrim. Usuń te i wklej je w czas tworzenia tabeli. :)

+0

1. Zaktualizowano pytania, podając więcej szczegółów. –

+0

2. Brak indeksów w tabelach tymczasowych. –

+0

4. Czy myślisz o fragmentacji strony tutaj? –

0
  1. Dodanie indeksów do tabeli tymczasowej zdecydowanie poprawi wywołanie odczytu, ale spowolni zapisywanie wywołań do tabeli tymczasowej.
  2. Tutaj, jak wspomniano, w procedurze jest wykonywanych 19 zapytań, więc analiza tylko jednego zapytania z planem wykonania nie byłaby bardziej pomocna.
  3. Dodając więcej, jeśli to możliwe, wykonaj to zapytanie tylko, aby sprawdzić, ile czasu zajmuje (dotyczy wierszy).
  4. Inne podejście, które możesz spróbować, nie wiesz, jeśli to możliwe w twoim przypadku, spróbuj użyć zmiennej tabeli zamiast tabeli tymczasowej. Dzieje się tak dlatego, że użycie zmiennej tabeli w tabeli tymczasowej ma dodatkowe zalety, takie jak procedura jest wstępnie skompilowana, nie są przechowywane żadne dzienniki transakcyjne. & więcej, nie trzeba pisać tabeli upuszczania.
+0

2. Większość zapytań to "WYBIERZ" z jednej tabeli zastępczej do drugiej ("WYBIERZ DO FROMU"), z kilkoma modyfikacjami danych na podstawie kilku kolejnych sprzężeń/itd. Jest jeszcze kilka zapytań, które sumują się do około 15 sekund (ale są to 4-5 zapytań, a nie 1), więc staram się ogolić jak najwięcej czasu z tego jednego dużego zapytania przed przejściem na inne, które nie sądzę, że można je poprawić (jeszcze). –

+0

3. Nie sądzę, żebym mógł uzyskać bardziej szczegółowe informacje o te zapytania z innego miejsca niż DMV. Dodanie/przetestowanie więcej zapytań na raz nie pomoże mi znaleźć niczego nowego, ponieważ wiem już, ile czasu zajmuje i kim są moi "winowajcy". –

+0

4. Próbowałem wypróbować zmienne tabelowe, a czas wykonania poprawił się, ale tylko o 5-10% (0.25-1 sek.) Po około 25 powtórzeniach, średnio wynosi około 0,5 sekundy. I mogę "winić" ten zakres na obciążenie "serwera". Ponadto, z tego, co wiem, zmienne tabel nadal będą powodować tempdb, jeśli nie mieszczą się w pamięci, więc opóźnienie we/wy dodane ponownie. Zgadzam się co do dziennika transakcji, ale nie jest to teraz moja sprawa, a nie coś, co firma chce usłyszeć (jeśli nie może ** rozwiązać problemu **). Ponadto tabela drop nie jest wyraźnie określona, ​​tabele tymczasowe ulegają zniszczeniu podczas zamykania sesji. –