Archiwa blogu

Statystyki w SQL Server: GEEK LEVEL BONUS

Jak zapewne pamiętacie kilka dni temu prowadziłem prelekcję na spotkaniu Trójmiejskiej Zawodowej Grupy .Net. W ramach tego spotkania pokazałem demo, które jest odpowiedzią na bardzo specyficzny problem 🙂

Definicja problemu

Wyobraźmy sobie sytuację, w której jesteście osobami z działu developmentu i dostajecie zgłoszenie odnośnie problemów wydajnościowych związanych z działaniem waszej aplikacji. Standardowym sposobem byłoby podłączenie się do instancji i stworzenie odpowiedniego trace-a. Tym razem jest niestety nieco inaczej – dane klienta są tajne i nie możecie w żaden sposób ich podejrzeć. Oczywiście możecie prosić o różnego rodzaju zrzuty wydajnościowe, jednak działanie „po omacku” i przy pomocy osób trzecich jest czasem problematyczne …

Co możemy w takim razie zrobić?

I tutaj do akcji wchodzi mój GEEK LEVEL BONUS :). Może by oszukać optymalizator i sprawić by „myślał”, że jest u naszego klienta? Tylko jak to zrobić …

Na początek imitacja bazy klienta.

Załóżmy, że naszą bazą klienta jest baza AdventureWorks2012, którą można pobrać z witryny Codeplex, a problematyczna instrukcja SELECT wygląda następująco:

SELECT *
fROM AdventureWorks2012.Sales.SalesOrderDetail sod
JOIN AdventureWorks2012.[Sales].[SalesOrderHeader] soh
ON soh.SalesOrderID = sod.SalesOrderID
WHERE sod.CarrierTrackingNumber = '4911-403C-98'

A teraz nasza baza developerska

Jako bazę developerską użyjemy również bazy AdventureWorks2012. Jedyne co, to odzyskamy ją z kopii zapasowej pod inną nazwą (np. AdventureGeek) i usuniemy informacje z tabel , tak aby zasymulować brak danych na serwerze developerskim:

DELETE AdventureGeek.Sales.SalesOrderDetail

DELETE AdventureGeek.Sales.SalesOrderHeader

Gdy odpalimy nasz selekt na bazie developerskiej zobaczymy następujący plan zapytania:

SELECT *
fROM AdventureGeek.Sales.SalesOrderDetail sod
JOIN AdventureGeek.[Sales].[SalesOrderHeader] soh
ON soh.SalesOrderID = sod.SalesOrderID
WHERE sod.CarrierTrackingNumber = '4911-403C-98'

GEEK_LEVEL_BONUS_01

No tak … ale co w tym planie jest nie tak? Oczywiście mamy pewne ostrzeżenie optymailizatora, ale mimo wszystko plan zapytania na pierwszy rzut oka wygląda w miarę OK (no, może cześć z was zauważy niuans naprowadzający na rozwiązanie). Dlaczego tak się stało – po prostu mamy inne dane niż nasz klient (a dokładnie nie mamy danych). Diagnostyka, co może być nie tak przeprowadzona w ten sposób może okazać się dość kłopotliwa.

No dobrze, ale wspomniałem coś o oszukiwaniu optymalizatora 🙂

Na początek użyjemy instrukcji DBCC SHOW_STATISTICS, która służy do pokazania informacji na temat rozkładu danych w tabeli. Instrukcja ta zawiera nie do końca udokumentowaną opcję o nazwie STATS_STREAM:

DBCC SHOW_STATISTICS ('AdventureWorks2012.Sales.SalesOrderDetail', 'PK_SalesOrderDetail_SalesOrderID_SalesOrderDetailID')
WITH STATS_STREAM

GEEK_LEVEL_BONUS_02

Gdy zajrzymy w SQL Server Books Online możemy o tej opcji przeczytać następujące zdanie:

STATS_STREAM is Identified for informational purposes only. Not supported. Future compatibility is not guaranteed.

Jeżeli namówimy osobę po stronie klienta by skopiowała i udostępniła nam jej wyniki, to może być tylko lepiej 🙂

W drugim kroku użyjemy instrukcji UPDATE STATISTICS (już na naszej bazie), przepisując wartości ROWCOUNT, PAGECOUNT I STATS_STREAM na zgodne z wypisanymi we wcześniejszym kroku:

UPDATE STATISTICS AdventureGeek.Sales.SalesOrderDetail PK_SalesOrderDetail_SalesOrderID_SalesOrderDetailID
WITH ROWCOUNT = 121317, PAGECOUNT = 1237,
STATS_STREAM = -- tutaj wklejamy dane STATS_STREAM z poprzedniego kroku :)

No i oczywiście odpalmy nasze zapytanie w bazie AdventureGeek. Dla łatwiejszego porównania wyników zamieszczam obie instrukcje SELECT:

-- baza developerska
SELECT *
FROM AdventureGeek.Sales.SalesOrderDetail sod
JOIN AdventureGeek.[Sales].[SalesOrderHeader] soh
ON soh.SalesOrderID = sod.SalesOrderID
WHERE sod.CarrierTrackingNumber = '4911-403C-98'

-- baza klienta
SELECT *
FROM AdventureWorks2012.Sales.SalesOrderDetail sod
JOIN AdventureWorks2012.[Sales].[SalesOrderHeader] soh
ON soh.SalesOrderID = sod.SalesOrderID
WHERE sod.CarrierTrackingNumber = '4911-403C-98'

GEEK_LEVEL_BONUS_03

Jak widać podpowiedź optymalizatora w obu przypadkach jest taka sama – utworzyć indeks na kolumnie CarrierTrackingNumber. Oczywiście oba zaprezentowane plany są nieco odmienne. Na bazie developerskiej obie tabele nie są wypełnione wartościami, a my podmieniliśmy tylko jedną ze statystyk. Pozostałe kolumny nadal pozostają dla SQL Server-a puste.  

Mimo wszystko, na powyższym przykładzie widać, że nasza idea jak działa. Podmieniając kolejne statystyki dla obu tabel w bazie AdventureGeek moglibyśmy doprowadzić do wygenerowania identycznych planów.

Podsumowanie

Mam nadzieję, że moje rozwiązanie związane z podmianą statystyk wam się podoba. Jedynie co, to proszę – nie róbcie tego typu „sztuczek” na serwerach „u klienta”. Oczywiście chcielibyśmy, aby działały ładnie jak developerskie u nas, lecz nie jestem przekonany, czy ta sztuczka będzie działać w obie strony 🙂

PS. Pamiętajcie, że te rozwiązanie jest totalnie niewspierane !!!! (nawet przez mnie :))

Materiały z sesji o statystykach już dostępne

Prezentacja z sesji „Statystyki w SQL Server: od zera do totalnego geeka :)”, którą poprowadziłem w ramach środowego spotkania Trójmiejskiej Zawodowej Grupy .NET jest już dostępna w materiałach do pobrania.

Jako, że GEEK LEVEL BONUS wyraźnie wam się spodobał, to w kolejnym wpisie wykonam go jeszcze raz, wyjaśniając krok po kroku co i jak 🙂

Statystyki w SQL Server – od zera do totalnego GEEKa na spotkaniu TZG.NET :)

big_logo_tzg

13 listopada 2013 o godzinie 18:00 obędzie się kolejne spotkanie Trójmiejskiej Zawodowej Grupy .NET, podczas którego będę prelegentem. 

Tym razem opowiem na temat statystyk w SQL Server. Sesję rozpoczniemy łagodnym wprowadzeniem do tematu,  a następnie zaczniemy podkręcać level, aż do uzyskania poziomu totalnego GEEKa 🙂 

Agenda spotkania przedstawia się następująco:

18:00 – 19:00: Statystyki w SQL Server – od zera do totalnego GEEKa

Prelegent:  Cezary Ołtuszyk (BEST S.A.)

19:15 – 20:00: Wprowadzenie do knockout.js

Prelegent: Wojciech Gomoła (Atena Usługi Informatyczne i Finansowe S.A)

Link do rejestracji znajdziecie po tym adresem: rejestracja !!! :)

Materiały z wczorajszej sesji na temat planów zapytań

Slajdy z wczorajszej sesji na temat planów zapytań prowadzonej przeze mnie w ramach spotkania Trójmiejskiej Zawodowej Grupy .NET są już dostępne w zakładce materiały do pobrania mojego bloga.

Tych z was, którzy są zainteresowani tematem zapraszam do lektury 🙂

Kolejna prelekcja – tym razem w Gdańsku :)

big_logo_tzgPo powrocie z Torunia przyszedł czas na prelekcję w Gdańsku 🙂

12 marca 2013 o godzinie 18:00 obędzie się kolejne spotkanie Trójmiejskiej Zawodowej Grupy .NET, podczas którego będę prelegentem. Agenda spotkania przedstawia się nastepująco:

18:00 – 18:45: Dotacja nie tylko unijna, zastrzyk na założenie działalności lub rozwój firmy IT

Prelegent: Artur Rorzkowski (Europejskie Doradztwo Biznesowe)

18:45 – 19:45: SQL Server 2012 i nie tylko: Wprowadzenie do planów zapytań

Prelegent: Cezary Ołtuszyk (BEST S.A.)

19:45 – 19:45: ASP .NET MVC 4.0 – nowości

Prelegent: Rafał Gordon (Banqsoft)

Zachęcam do wzięcia udziału i rejestracji !!! 🙂