Korzystamy z programu SQL Server 2008 R2 i mamy bardzo dużą tabelę (ponad 100 milionów wierszy) z podstawowym indeksem id oraz datetime
kolumnę z indeksem nieklastrowanym. Obserwujemy bardzo nietypowe zachowanie klient / serwer w oparciu o użycie order by
klauzuli konkretnie w indeksowanej kolumnie daty i godziny .
Przeczytałem następujący post: /programming/1716798/sql-server-2008-ordering-by-datetime-is-too-slow, ale z klientem / serwerem dzieje się więcej niż to, co jest zacznij opisywać tutaj.
Jeśli uruchomimy następujące zapytanie (edytowane w celu ochrony niektórych treści):
select *
from [big table]
where serial_number = [some number]
order by test_date desc
Limit czasu zapytania za każdym razem. W SQL Server Profiler wykonane zapytanie wygląda tak:
exec sp_cursorprepexec @p1 output,@p2 output,NULL,N'select * .....
Teraz, jeśli zmodyfikujesz zapytanie, powiedz to:
declare @temp int;
select * from [big table]
where serial_number = [some number]
order by test_date desc
SQL Server Profiler pokazuje, że wykonane zapytanie wygląda tak na serwerze i DZIAŁA natychmiast:
exec sp_prepexec @p1 output, NULL, N'declare @temp int;select * from .....
W rzeczywistości możesz nawet wstawić pusty komentarz („-;”) zamiast nieużywanej instrukcji deklaracji i uzyskać ten sam wynik. Tak więc początkowo wskazywaliśmy na preprocesor sp jako główną przyczynę tego problemu, ale jeśli to zrobisz:
select *
from [big table]
where serial_number = [some number]
order by Cast(test_date as smalldatetime) desc
Działa również natychmiastowo (możesz rzucić jak każdy inny datetime
typ), zwracając wynik w milisekundach. Profiler wyświetla żądanie do serwera jako:
exec sp_cursorprepexec @p1 output, @p2 output, NULL, N'select * from .....
To w pewien sposób wyklucza sp_cursorprepexec
procedurę z pełnej przyczyny problemu. Dodaj do tego fakt, że sp_cursorprepexec
wywoływany jest również wtedy, gdy nie jest używane polecenie „sortuj według”, a wynik jest natychmiast zwracany.
Rozglądaliśmy się za tym problemem dość często i widzę podobne problemy publikowane przez innych, ale żadne z nich nie rozkłada go na ten poziom.
Czy inni widzieli takie zachowanie? Czy ktoś ma rozwiązanie lepsze niż umieszczanie bezsensownego kodu SQL przed instrukcją select w celu zmiany zachowania? Ponieważ SQL Server powinien wywoływać kolejność po zebraniu danych, z pewnością wygląda na to, że jest to błąd w serwerze, który utrzymuje się przez długi czas. Odkryliśmy, że takie zachowanie jest spójne w wielu naszych dużych tabelach i jest powtarzalne.
Edycje:
Powinienem również dodać, że wstawienie forceseek
spowoduje, że problem zniknie.
Powinienem dodać, aby pomóc wyszukiwarkom, zgłoszony błąd przekroczenia limitu czasu ODBC to: [Microsoft] [ODBC SQL Server Driver] Operacja anulowana
Dodano 10/12/2012: Nadal szukam przyczyny źródłowej (wraz z zbudowaniem próbki do przekazania firmie Microsoft, opublikuję tutaj wszelkie wyniki po przesłaniu). Kopie do pliku śledzenia ODBC między działającym zapytaniem (z dodaną instrukcją komentarza / deklaracji) a niedziałającym zapytaniem. Podstawowa różnica w śladach została zamieszczona poniżej. Występuje w wywołaniu wywołania SQLExtendedFetch po zakończeniu wszystkich dyskusji SQLBindCol. Wywołanie kończy się niepowodzeniem z kodem powrotu -1, a następnie wątek nadrzędny wchodzi w SQLCancel. Ponieważ jesteśmy w stanie wyprodukować to zarówno ze sterownikami Native Client, jak i starszymi sterownikami ODBC, nadal wskazuję na pewien problem ze zgodnością po stronie serwera.
(clip)
MSSQLODBCTester 1664-1718 EXIT SQLBindCol with return code 0 (SQL_SUCCESS)
HSTMT 0x001EEA10
UWORD 16
SWORD 1 <SQL_C_CHAR>
PTR 0x03259030
SQLLEN 51
SQLLEN * 0x0326B820 (0)
MSSQLODBCTester 1664-1718 ENTER SQLExtendedFetch
HSTMT 0x001EEA10
UWORD 1 <SQL_FETCH_NEXT>
SQLLEN 1
SQLULEN * 0x032677C4
UWORD * 0x032679B0
MSSQLODBCTester 1664-1fd0 ENTER SQLCancel
HSTMT 0x001EEA10
MSSQLODBCTester 1664-1718 EXIT SQLExtendedFetch with return code -1 (SQL_ERROR)
HSTMT 0x001EEA10
UWORD 1 <SQL_FETCH_NEXT>
SQLLEN 1
SQLULEN * 0x032677C4
UWORD * 0x032679B0
DIAG [S1008] [Microsoft][ODBC SQL Server Driver]Operation canceled (0)
MSSQLODBCTester 1664-1fd0 EXIT SQLCancel with return code 0 (SQL_SUCCESS)
HSTMT 0x001EEA10
MSSQLODBCTester 1664-1718 ENTER SQLErrorW
HENV 0x001E7238
HDBC 0x001E7B30
HSTMT 0x001EEA10
WCHAR * 0x08BFFC5C
SDWORD * 0x08BFFF08
WCHAR * 0x08BFF85C
SWORD 511
SWORD * 0x08BFFEE6
MSSQLODBCTester 1664-1718 EXIT SQLErrorW with return code 0 (SQL_SUCCESS)
HENV 0x001E7238
HDBC 0x001E7B30
HSTMT 0x001EEA10
WCHAR * 0x08BFFC5C [ 5] "S1008"
SDWORD * 0x08BFFF08 (0)
WCHAR * 0x08BFF85C [ 53] "[Microsoft][ODBC SQL Server Driver]Operation canceled"
SWORD 511
SWORD * 0x08BFFEE6 (53)
MSSQLODBCTester 1664-1718 ENTER SQLErrorW
HENV 0x001E7238
HDBC 0x001E7B30
HSTMT 0x001EEA10
WCHAR * 0x08BFFC5C
SDWORD * 0x08BFFF08
WCHAR * 0x08BFF85C
SWORD 511
SWORD * 0x08BFFEE6
MSSQLODBCTester 1664-1718 EXIT SQLErrorW with return code 100 (SQL_NO_DATA_FOUND)
HENV 0x001E7238
HDBC 0x001E7B30
HSTMT 0x001EEA10
WCHAR * 0x08BFFC5C
SDWORD * 0x08BFFF08
WCHAR * 0x08BFF85C
SWORD 511
SWORD * 0x08BFFEE6
(clip)
Dodano sprawę Microsoft Connect 10/12/2012:
Powinienem również zauważyć, że sprawdziliśmy plany zapytań zarówno dla zapytań funkcjonujących, jak i niedziałających. Oba są ponownie wykorzystywane odpowiednio na podstawie liczby wykonania. Opróżnianie planów w pamięci podręcznej i ponowne uruchamianie nie zmienia powodzenia zapytania.
sp_executesql
i sprawdź, co się stanie.
select id, test_date from [big table] where serial_number = ..... order by test_date
- Zastanawiam się tylko, czySELECT *
ma to negatywny wpływ na Twoją wydajność. Jeśli masz nieklastrowany indekstest_date
i klastrowego indeksuid
(zakładając, że to, co się nazywa), ta kwerenda powinny być pokryte przez ten indeks nieklastrowany, a zatem powinien wrócić dość szybko