SQL: Co spowalnia INSERT, jeśli nie CPU lub IO?


19

Mamy bazę danych dla produktu, który wymaga dużego zapisu. Właśnie kupiliśmy nowy serwer z dyskiem SSD, aby pomóc. Ku naszemu zdziwieniu wstawki nie były szybsze niż na naszej starej maszynie z dużo wolniejszym przechowywaniem. Podczas testów porównawczych zauważyliśmy, że wskaźnik operacji we / wy wykazywany przez proces SQL Server był bardzo niski.

Na przykład uruchomiłem skrypt znaleziony na tej stronie , z tą różnicą, że dodałem BEGIN TRAN i COMMIT wokół pętli. W najlepszym wypadku mogłem zobaczyć, że użycie dysku osiąga 7 Mb / s, a procesor ledwo dotknął 5%. Serwer ma zainstalowaną 64 Gb i używa 10. Całkowity czas działania wyniósł 2 minuty 15 sekund dla pierwszego połączenia i około 1 minuty dla kolejnych połączeń. Baza danych jest na prostym odzyskiwaniu i była bezczynna podczas testu. Upuściłem stolik między każdą rozmową.

Dlaczego tak prosty skrypt jest tak wolny? Sprzęt w ogóle nie jest używany. Zarówno narzędzia do analizy porównawczej dysków, jak i SQLIO wskazują, że dysk SSD działa poprawnie z prędkościami do 500 Mb / s zarówno w przypadku odczytu, jak i zapisu. Rozumiem, że zapisy losowe są wolniejsze niż zapisy sekwencyjne, ale oczekiwałbym, że taka prosta wstawka do tabeli bez indeksowania klastrowego będzie znacznie szybsza.

Ostatecznie nasz scenariusz jest znacznie bardziej złożony, ale uważam, że najpierw muszę zrozumieć prosty przypadek. W skrócie nasza aplikacja usuwa stare dane, a następnie używa SqlBulkCopy do kopiowania nowych danych do tabel pomostowych, wykonuje pewne filtrowanie, a na koniec używa MERGE i / lub INSERT INTO w zależności od przypadków, aby skopiować dane do ostatecznych tabel.

-> EDYCJA 1: Postępowałem zgodnie z procedurą powiązaną przez Martina Smitha i otrzymałem następujący wynik:

[Wait Type]  [Wait Count] [Total Wait (ms)] [T. Resource Wait (ms)] [T. Signal Wait (ms)]
NETWORK_IO          5008              46735                 46587        148
LOGBUFFER           901               5994                  5977         17
PAGELATCH_UP        40                866                   865          1
SOS_SCHEDULER_YIELD 53279             219                   121          98
WRITELOG            5                 145                   145          0
PAGEIOLATCH_UP      4                 58                    58           0
LATCH_SH            5                 0                     0            0

Uważam, że to dziwne NETWORK_IO zajmuje większość czasu, biorąc pod uwagę, że nie ma żadnych wyników do wyświetlenia i żadnych danych do przesłania gdziekolwiek poza plikami SQL. Czy typ NETWORK_IO obejmuje wszystkie operacje wejścia / wyjścia?

-> EDYCJA 2: Utworzyłem dysk RAM 20 Gb i stamtąd zamontowałem bazę danych. Najlepszy czas, jaki miałem na dysku SSD to 48s, z dyskiem RAM spadł do 37 sekund. NETWORK_IO jest nadal największym oczekiwaniem. Maksymalna prędkość zapisu na dysku RAM wynosiła około 250 Mb / s, podczas gdy jest on w stanie wykonać wiele gigabajtów na sekundę. Nadal nie zużywał dużo procesora, więc co powstrzymuje SQL?



3
NETWORK_IOmoże być od 3 mln „1 row (s) affected” wiadomości są odsyłane. Czy próbowałeś dodać SET NOCOUNT ONdo skryptu?
Martin Smith

Tak, dodałem NOCOUNT.
Djof

2
Dziwne. Nie spodziewałbym się wtedy w ogóle zbyt dużej aktywności w sieci. Czy usunąłeś stare pliki zdarzeń rozszerzonych między uruchomieniami? Skrypt, który je odczytuje, używa dzikiej karty, EE_WaitStats*.xelwięc stare zepsują twoje wyniki.
Martin Smith

Dobry telefon, jutro zaktualizuję wyniki.
Djof

Odpowiedzi:


9

Wiem, że to stare pytanie, ale wciąż może pomóc wyszukiwarkom i jest to problem, który pojawia się co jakiś czas.

Głównym powodem, dla którego osiągasz pułap wydajności, nie zauważając wąskiego gardła zasobów, jest to, że osiągnąłeś limit możliwości przetwarzania jednego wątku w ramach jednej sesji. Pętla nie jest przetwarzana równolegle, ale wszystkie wstawki są wykonywane szeregowo.

W moim przypadku wstawienie 3 milionów wierszy zajmuje 36 sekund. Oznacza to 36/30000000 = 0,000012 sekund na rząd. To dość szybko. W moim systemie wystarczy 0,000012, aby przejść wszystkie niezbędne kroki.

Jedynym sposobem, aby to zrobić szybciej, jest rozpoczęcie drugiej sesji równolegle.

Jeśli rozpocznę 2 sesje równolegle, obie zrobią 15 milionów wstawek. Oba kończą w 18 sekund. Mógłbym zwiększyć skalę, ale moja obecna konfiguracja testowa osiąga 95% procesora w dwóch równoległych sesjach, więc wykonanie 3 zmieniłoby wyniki, ponieważ trafiłbym na wąskie gardło procesora.

Jeśli rozpocznę 2 równoległe sesje, oba wstawią 3 miliony wierszy, oba zakończą się w 39 sekund. więc jest to teraz 6 milionów wierszy w 39 sekund.

Okej, to wciąż pozostawia nam pojawienie się NETWORK_IO.

Oczekiwania NETWORK_IO są dodawane przez fakt, że do ich śledzenia używasz zdarzeń rozszerzonych. W moim przypadku wstawka zajmuje 36 sekund (średnio). Podczas korzystania z metody rozszerzonego zdarzenia (z linku powyżej w pierwszym komentarzu) rejestruje się:

Wait Type             Wait Count  Total Wait Time (ms) Total Resource Wait Time (ms) Total Signal Wait Time (ms)
NETWORK_IO            3455        68808                68802                         6
PAGEIOLATCH_SH        3           64                   64                            0
PAGEIOLATCH_UP        12          58                   58                            0
WRITE_COMPLETION      8           15                   15                            0
WRITELOG              3           9                    9                             0
PAGELATCH_UP          2           4                    4                             0
SOS_SCHEDULER_YIELD   32277       1                    0                             1
IO_COMPLETION         8           0                    0                             0
LATCH_SH              3           0                    0                             0
LOGBUFFER             1           0                    0                             0

Widać, że 68 sekund NETWORK_IO jest zarejestrowanych. Ale ponieważ pętla wstawiania jest pojedynczym wątkiem, który zajął 36 sekund, nie może tak być. (Tak, używanych jest wiele wątków, ale operacje są szeregowe, nigdy równoległe, więc nie można skumulować więcej czasu oczekiwania niż całkowity czas trwania zapytania)

Jeśli nie używam zdarzeń rozszerzonych, a tylko statystyki DMV czekania w cichym wystąpieniu (tylko z uruchomioną wstawką), otrzymuję to:

Wait Type                   Wait Count  Total Wait Time (ms)  Total Resource Wait Time (ms) Signal Resource Wait Time (ms)
SOS_SCHEDULER_YIELD             8873                 0.21                                    0.01                                    0.20
PAGEIOLATCH_UP                  3                    0.02                                    0.02                                    0.00
PREEMPTIVE_OS_AUTHENTICATIONOPS 17                   0.02                                    0.02                                    0.00
PAGEIOLATCH_SH                  1                    0.00                                    0.00                                    0.00

NETWORK_IO, które widziałeś w rozszerzonym dzienniku zdarzeń, nie było związane z twoją pętlą wstawiania. (Gdybyś nie włączył nocount, miałbyś masową asynchroniczną sieć IO czeka, +1 Martin)

Jednak nie wiem, dlaczego NETWORK_IO pojawia się w rozszerzonym śledzeniu zdarzeń. Upewnij się, że zapisywanie w celu pliku asynchronicznego zdarzeń gromadzi ASYNC_NETWORK_IO, ale na pewno wszystko to odbywa się na innym SPID niż ten, na którym filtrujemy. Mogę zadać to jako nowe pytanie)


1
„osiągasz pułap wydajności, nie zauważając wąskiego gardła zasobów, ponieważ osiągnąłeś limit możliwości przetwarzania w ramach pojedynczego wątku jednej sesji”: opisujesz 100% wąskie gardło procesora (na jednym rdzeniu). Jeśli nie ma wąskiego gardła, system będzie działał szybciej, więc coś innego musi być w grze.
Remus Rusanu,

Twoja odpowiedź jest bardzo pouczająca, Edwardzie. Wygląda na to, że równoległość jest rozwiązaniem naszego problemu, nad którym już pracujemy, choć wymaga zmian w układzie naszej bazy danych. Podobnie jak Remus, wciąż jestem ciekawy, dlaczego maszyna nie wykorzystała wszystkich (jednego) zasobów procesora lub dysku.
Djof

9

Zazwyczaj można rozpocząć patrząc sys.dm_exec_requests, szczególnie u wait_time, wait_typea wait_resourcena życzenie wkładka (ów). To da wyraźne wskazanie, co blokuje WSTAW. Wyniki wskażą, czy jest to rywalizacja o blokadę, zdarzenia wzrostu plików, oczekiwanie na opróżnienie dziennika, rywalizacja o alokację (manifestuje się jako rywalizacja o zatrzask strony PFS) itp. Itd. Po zmierzeniu odpowiednio zaktualizuj swoje pytanie. Gorąco zachęcam do zatrzymania się teraz i zapoznania się z metodologią rozwiązywania problemów z kolejkami i kolejkami przed kontynuowaniem.


3

Uruchomiłem skrypt testowy na stronie połączonej w OP z BEGIN TRAN / COMMIT wokół pętli. Na moim komputerze ukończenie pierwszego razu zajęło 1:28.

Następnie przeniosłem te dwa polecenia poza pętlę:

SELECT @Random = ROUND(((@Upper - @Lower -1) * RAND() + @Lower), 0)
SET @InsertDate = DATEADD(dd, @Random, GETDATE())

Ukończono po 28 sekundach.

Nie wiem na pewno, co się dzieje, ale domyślam się, że w RAND()kodzie może być jakiś sen , być może jako część algorytmu, którego używają do generowania entropii (lepszych liczb losowych).

FWIW, dyski SSD nie zawsze są najlepszą technologią dla aplikacji wymagających dużego zapisu. Aby uzyskać najlepszą wydajność, upewnij się, że dziennik DB znajduje się na innej literze dysku niż dane DB, plik dziennika jest wstępnie powiększony do maksymalnego rozmiaru i nigdy nie obcinaj dziennika.


Dziękujemy za Twój wkład RickNZ. Nie uzyskałem szybszych wyników, przenosząc kod z pętli. Zaczekałem, zauważyłem, że jeśli uruchomisz go wiele razy, robi się szybciej, to może być to, czego doświadczyłeś. Wiem, że dyski SSD nie są srebrnymi kulami, ale nadal mam wrażenie, że wydajność nie jest taka, jak mogłaby być.
Djof

1

Innym DMV, którego używam do identyfikowania powolności, jest sys.dm_os_waiting_tasks . Jeśli twoje zapytanie nie wymaga dużego procesora, możesz znaleźć więcej informacji na temat oczekiwań z tego DMV.


0

Sprawdzam listę zdarzeń oczekiwania dla SQL 2008 i nie widzę na liście NETWORK_IO: http://technet.microsoft.com/en-us/library/ms179984(v=sql.100).aspx

Myślałem, że NETWORK_IO jest teraz na liście ASYNC_NETWORK_IO, więc chciałem zapytać, czy możesz ponownie sprawdzić swoją wersję SQL, ponieważ jestem po prostu ciekawy, jak / dlaczego to zdarzenie oczekiwania pojawia się dla tej wersji.

Jeśli chodzi o pojawienie się sieci, tak, może się to zdarzyć, nawet jeśli pracujesz na samodzielnym serwerze. Czy sprawdziłeś ustawienia swoich kart sieciowych? Zastanawiam się, czy to problem.

Na koniec dnia możliwe jest tylko kilka wąskich gardeł zasobów: pamięć, procesor, dyskowe operacje we / wy, sieć i blokowanie. Wskazałeś, że nie ma problemu z CPU i I / O, i masz zdarzenie oczekiwania NETWORK_IO, więc sugeruję, abyś najpierw spojrzał na te karty sieciowe.


1
NETWORK_IOPokazano ponieważ OP używa rozszerzonych zdarzeń. Nigdy nie został zaktualizowany wsys.dm_xe_map_values
Martin Smith

Myślę o tej samej SQLRockstar, tylko o tym, co może się dziać. Próbowałem całkowicie wyłączyć karty sieciowe. Martin zwrócił uwagę, że niektóre stare pliki mogą nadal tam być, zaktualizuję jutro wyniki, aby zobaczyć, czy coś to zmieni.
Djof

może również pomóc, jeśli zobaczymy plany wykonania instrukcji.
SQLRockstar
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.