Kwerenda działa inaczej w SQL 2005 niż SQL 2008R2


9

W moim biurze mamy zapytanie, które jest dość brzydkie, ale działa całkiem dobrze w środowisku produkcyjnym i programistycznym (odpowiednio 20 sekund i 4 sekundy). Jednak w naszym środowisku testowym trwa 4 godziny. SQL2005 (+ najnowsze łaty) działa podczas produkcji i rozwoju. SQL2008R2 działa podczas testowania.

Rzuciłem okiem na plan zapytań i pokazuje on, że SQL2008R2 używa TempDB za pomocą buforu tabel (opóźnionego buforowania) do przechowywania zwróconych wierszy z połączonego serwera. Następnym krokiem jest pokazanie zagnieżdżonych pętli (lewe anty semi-złączenie) jako zjedzenie 96,3% zapytania. Linia między dwoma operatorami wynosi 5 398 MB!

Plan zapytań dla SQL 2005 nie pokazuje użycia tempdb i lewy anty semi-join.

Poniżej znajduje się zdezynfekowany kod, a plany wykonania zawierają plan z 2005 roku na górze, a 2008R2 na dole.

Co powoduje drastyczne spowolnienie i zmianę? Spodziewałem się zobaczyć inny plan wykonania, więc mi to nie przeszkadza. Niepokoi mnie dramatyczne spowolnienie czasu zapytania.

Czy muszę patrzeć na podstawowy sprzęt, ponieważ wersja 2008R2 korzysta z tempdb, muszę się zastanowić, jak zoptymalizować wykorzystanie tego?

Czy istnieje lepszy sposób na napisanie zapytania?

Dzięki za pomoc.

    INSERT INTO Table1_GroupLock (iGroupID, dLockedDate)
SELECT 
 Table1.iGroupID,
 GETDATE()
FROM Table1
WHERE 
 NOT EXISTS (
  SELECT 1
  FROM LinkedServer.Database.Table2 Alias2
  WHERE 
   (
    Alias2.FirstName + Alias2.LastName = dbo.fnRemoveNonLetter(Table1.FullName)
    AND NOT dbo.fnRemoveNonLetter(Table1.FullName) IS NULL
    AND NOT Alias2.FirstName IS NULL 
    AND NOT Alias2.LastName  IS NULL
   ) OR (
    Alias2.FamilyName = dbo.fnRemoveNonLetter(Table1.FamilyName)
    AND Alias2.Child1Name = dbo.fnRemoveNonLetter(Table1.Child1Name)
    AND NOT dbo.fnRemoveNonLetter(Table1.FamilyName) IS NULL
    AND NOT dbo.fnRemoveNonLetter(Table1.Child1Name) IS NULL
    AND NOT Alias2.Familyname IS NULL
    AND NOT Alias2.Child1Name IS NULL
   ) OR (
    Alias2.StepFamilyName = dbo.fnRemoveNonLetter(Table1.StepFamilyName)
    AND Alias2.StepFamilyNameChild1 = dbo.fnRemoveNonLetter(Table1.StepFamilyNameChild2)
    AND NOT Alias2.StepFamilyName IS NULL
    AND NOT Alias2.StepFamilyNameChild1 IS NULL
    AND NOT dbo.fnRemoveNonLetter(Table1.StepFamilyName) IS NULL
    AND NOT dbo.fnRemoveNonLetter(Table1.StepFamilyNameChild2) IS NULL
   )  
 ) AND NOT EXISTS (
  SELECT 1
  FROM Table3
  INNER JOIN Table4
   ON Table4.FirstNameType = Table3.FirstNameType 
  INNER JOIN table5
   ON table5.LastNameType = Table3.LastNameType 
  WHERE 
   Table3.iGroupID = Table1.iGroupID
   AND Table3.bIsClosed = 0
   AND Table4.sNameTypeConstant = 'new_lastname'
   AND table5.sFirstNameConstant = 'new_firstname'
 )

SQL-2005


SQL2008R2

:: EDIT :: Wykonano zapytanie z innej instancji SQL2005, prawie taki sam plan wykonania jak „dobry”. Nadal nie jestem pewien, w jaki sposób dwie wersje 2005 działają lepiej na połączonym serwerze 2008R2, niż instancje 2008R2 z instancjami 2008R2.

Chociaż nie zaprzeczam, że kod może przydać się do pracy, to gdyby to był problem, czyż nie widziałbym tych samych planów wykonania we wszystkich moich testach? Niezależnie od wersji SQL?

:: EDYCJA :: Zastosowałem SP1 i CU3 do obu instancji 2008R2, wciąż nie ma kości. Specjalnie ustawiłem kolokację na połączonym serwerze, bez kości. Specjalnie ustawiłem uprawnienia mojego acct użytkownika na sysadmin w obu przypadkach, bez kości. Pamiętam również moje wewnętrzne funkcje SQL Server 2008 i rozwiązywanie problemów, zobaczymy, czy uda mi się to jakoś wyśledzić.

Dziękujemy wszystkim za pomoc i wskazówki.

:: EDYCJA :: Wprowadziłem różne zmiany uprawnień do połączonego serwera. Używałem loginów SQL, loginów domen, podszywałem się pod użytkowników, korzystałem z opcji „bądź przy użyciu tego kontekstu bezpieczeństwa”. Utworzyłem użytkowników po obu stronach połączonego serwera, którzy mają uprawnienia sysadmin na serwerze. Nie mam pomysłów.

Wciąż chciałbym wiedzieć, dlaczego SQL2005 wykonuje zapytanie tak bardzo różni się od SQL2008R2. Gdyby to zapytanie było złe, widziałbym czas działania 4+ zarówno na SQL2005, jak i SQL2008R2.

Odpowiedzi:


5

Chciałbym, żebyś przerobił zapytanie.

Masz problemy z podatnością na zranienia, a nawet używasz tam wywołań funkcji skalarnych, co również będzie szkodzić zapytaniu. Możesz utworzyć kolumnę obliczeniową FullName na Table2 i nałożyć na nią indeks, upewniając się, że indeks ZAWIERA FirstName i LastName. Powinieneś także dodać indeksy, które pomagają drugiemu

Zrób również wbudowaną funkcję o wartościach tabelarycznych, aby wykonać swoją funkcję „RemoveNonLetter”, i przerób zapytanie, aby z niego skorzystać, prawdopodobnie używając ZASTOSUJ tak jak tutaj.

I zdecydowanie sprawdź ten błąd, do którego odnosi się odpowiedź Paula .

INSERT INTO Table1_GroupLock (iGroupID, dLockedDate)
SELECT 
 Table1.iGroupID,
 GETDATE()
FROM Table1
OUTER APPLY (SELECT NonLettersRemoved FROM dbo.ifnRemoveNonLetter(Table1.FullName)) AS fn (FullName)
OUTER APPLY (SELECT NonLettersRemoved FROM dbo.ifnRemoveNonLetter(Table1.FamilyName)) AS famn (FamilyName)
OUTER APPLY (SELECT NonLettersRemoved FROM dbo.ifnRemoveNonLetter(Table1.Child1Name)) AS c1n (Child1Name)
OUTER APPLY (SELECT NonLettersRemoved FROM dbo.ifnRemoveNonLetter(Table1.StepFamilyName)) AS sfn (StepFamilyName)
OUTER APPLY (SELECT NonLettersRemoved FROM dbo.ifnRemoveNonLetter(Table1.StepFamilyNameChild2)) AS sfnc2 (StepFamilyNameChild2)
WHERE 
 NOT EXISTS (
  SELECT 1
  FROM LinkedServer.Database.Table2 Alias2
  WHERE Alias2.FullName = fn.FullName
  UNION ALL
  SELECT 1
  FROM LinkedServer.Database.Table2 Alias2
  WHERE Alias2.FamilyName = famn.FamilyName AND Alias2.Child1Name = c1n.Child1Name
  UNION ALL
  SELECT 1
  FROM LinkedServer.Database.Table2 Alias2
  WHERE Alias2.StepFamilyName = sfn.StepFamilyName AND Alias2.StepFamilyNameChild1 = sfnc2.StepFamilyNameChild2
 ) 
 AND NOT EXISTS (
  SELECT 1
  FROM Table3
  INNER JOIN Table4
   ON Table4.FirstNameType = Table3.FirstNameType 
  INNER JOIN table5
   ON table5.LastNameType = Table3.LastNameType 
  WHERE 
   Table3.iGroupID = Table1.iGroupID
   AND Table3.bIsClosed = 0
   AND Table4.sNameTypeConstant = 'new_lastname'
   AND table5.sFirstNameConstant = 'new_firstname'
 )
;

6

Dodając do poprzednich odpowiedzi, przyczyną regresji planu może być znany błąd oszacowania liczności, gdy plan obejmuje antyprzyłączenie. Zobacz KB 2222998

Zakładając, że plan z 2005 r. Zapewnił akceptowalną wydajność, może się okazać, że podniesienie serwera do wersji zawierającej tę poprawkę (i umożliwienie TF4199 aktywacji) spowoduje powrót do „dobrego” planu.

To powiedziawszy, istnieje wiele innych możliwości ulepszenia tego zapytania, więc może to być dobry moment, aby skoncentrować się na zrobieniu tego.


5

Sugeruję, że zdalne dane są buforowane lokalnie, ponieważ jeden z nich

  1. Ustawienia serwera połączonego (takie jak sortowanie) nie są takie same
  2. Lokalne sortowanie nie jest tym samym co zdalne, pomimo ustawień połączonego serwera
  3. Zapytanie nie może działać poprawnie zdalnie z powodu uprawnień

Dla punktu 1, patrz sp_serveroption
I dla punktu 2, ale także sprawdź sortowanie serwer / db.

W punkcie 3 zobacz te z Linchi Shea:

Pytasz SQL Servera o przetwarzanie wszystkich danych lokalnie, zgodnie z moją odpowiedzią tutaj: Implikacje wydajności użycia OPENQUERY w widoku

Edytować

Przy drugim spojrzeniu widzę 2 zdalne połączenia na „dobrym” planie zamiast jednego. Potwierdza to, co tu mówię


Przepraszam za duże opóźnienie. 1: Sprawdziłem ustawienia połączonego serwera, są one takie same 2: Sprawdziłem również i domyślnie ustawiłem zestawianie właściwości połączonego serwera na serwerze zdalnym. 3: Kontekst bezpieczeństwa Nadal pracuję nad testowaniem A / B. Nadal pracuję nad logiką stojącą za drastycznie różnymi planami wykonawczymi. Z serwera R2 na serwer R2 uruchamia tylko select (wciąga ponad 150 000 wierszy), a następnie wykonuje połączenia. Gdzie, od 2005 do R2, dokonuje selekcji i dołącza do zdalnego serwera. Kontekst bezpieczeństwa dla obu scenariuszy jest taki sam.
RateControl

3

+1 na Spróbuj Przepisać komentarz do zapytania z datagod.

Zastanawiam się również, czy napotykasz problem z uprawnieniami po stronie serwera połączonego, co prowadzi do tego spowolnienia. Jakiś czas temu pisałem na blogu o spowolnieniu tego połączonego serwera . Być może warto zweryfikować perms (czy to serwer połączony z SQL? A może to kolejny DBMS? Jeśli ten drugi, to i tak nie dostaniesz świetnych statystyk dla wszystkich)

Czy masz SQL Server 2005 w środowisku testowym, aby wypróbować to zapytanie i wykluczyć środowisko?

Czy przebudowałeś statystyki od czasu aktualizacji?


3

Z tym porównaniem jest tyle problemów ... Po prostu nie wiem od czego zacząć.

  1. Uzyskaj dokładne specyfikacje dla swoich maszyn produkcyjnych i testowych.

  2. Określ połączenia sieciowe między różnymi połączonymi serwerami w obu środowiskach. Czy mają taką samą prędkość? Czy serwery znajdują się obok siebie w obu środowiskach?

  3. Czy jest jakiś sposób na ponowne napisanie zapytania, aby NIE używać połączonych serwerów? Łączenie tabel między serwerami naraża cię na zmiany topologii, a jego okropne spowolnienie w większości przypadków.

  4. Użycie NOT i OR zwykle prowadzi do pełnego skanowania tabeli. Spróbuj przepisać zapytanie.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.