Database
 sql >> Baza danych >  >> RDS >> Database

Jaki wpływ mogą mieć różne opcje kursora?

Kilka razy pisałem o używaniu kursorów i o tym, jak w większości przypadków bardziej wydajne jest przepisywanie kursorów przy użyciu logiki opartej na zbiorach.

Ale jestem realistą.

Wiem, że zdarzają się przypadki, w których kursory są „wymagane” – musisz wywołać inną procedurę składowaną lub wysłać e-mail dla każdego wiersza, wykonujesz zadania konserwacyjne dla każdej bazy danych lub uruchamiasz jednorazowe zadanie, które po prostu nie warto inwestować czasu na konwersję na zestaw.

Jak (prawdopodobnie) to dzisiaj robisz

Bez względu na powód, dla którego nadal używasz kursorów, powinieneś przynajmniej uważać, aby nie używać dość drogich opcji domyślnych. Większość ludzi zaczyna swoje kursory w ten sposób:

DECLARE c CURSOR FOR 
  SELECT whatever FROM ...

Teraz znowu, w przypadku doraźnych, jednorazowych zadań jest to prawdopodobnie w porządku. Ale są…

Inne sposoby na zrobienie tego

Chciałem uruchomić kilka testów przy użyciu ustawień domyślnych i porównać je z różnymi opcjami kursora, takimi jak LOKALNIE , STATYCZNE , TYLKO DO ODCZYTU i FAST_FORWARD . (Istnieje mnóstwo opcji, ale są to te najczęściej używane, ponieważ mają zastosowanie do najczęstszych typów operacji kursora, których ludzie używają.) Nie tylko chciałem przetestować surową prędkość kilku różnych kombinacji, ale również wpływ na tempdb i pamięć, zarówno po zimnym ponownym uruchomieniu usługi, jak i z ciepłą pamięcią podręczną.

Zapytanie, które postanowiłem podać do kursora, jest bardzo prostym zapytaniem przeciwko sys.objects , w przykładowej bazie danych AdventureWorks2012. Zwraca to 318 500 wierszy w moim systemie (bardzo skromny system 2-rdzeniowy z 4 GB pamięci RAM):

SELECT c1.[object_id] 
  FROM sys.objects AS c1
  CROSS JOIN (SELECT TOP 500 name FROM sys.objects) AS c2;

Następnie umieściłem to zapytanie w kursorze z różnymi opcjami (w tym wartościami domyślnymi) i przeprowadziłem kilka testów, mierząc całkowitą pamięć serwera, strony przydzielone do tempdb (zgodnie z sys.dm_db_task_space_usage i/lub sys.dm_db_session_space_usage ) oraz całkowity czas trwania. Próbowałem również obserwować rywalizację tempdb za pomocą skryptów Glenna Berry'ego i Roberta Davisa, ale w moim marnym systemie nie mogłem wykryć żadnej rywalizacji. Oczywiście jestem również na SSD i absolutnie nic innego nie działa w systemie, więc mogą to być rzeczy, które chcesz dodać do własnych testów, jeśli tempdb jest bardziej wąskim gardłem.

Ostatecznie więc zapytania wyglądały mniej więcej tak, z zapytaniami diagnostycznymi wstawionymi w odpowiednich punktach:

DECLARE @i INT = 1;
 
DECLARE c CURSOR
-- LOCAL
-- LOCAL STATIC
-- LOCAL FAST_FORWARD
-- LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
  SELECT c1.[object_id] 
    FROM sys.objects AS c1
    CROSS JOIN (SELECT TOP 500 name FROM sys.objects) AS c2
    ORDER BY c1.[object_id];
 
OPEN c;
FETCH c INTO @i;
 
WHILE (@@FETCH_STATUS = 0)
BEGIN
  SET @i += 1; -- meaningless operation
  FETCH c INTO @i;
END
 
CLOSE c;
DEALLOCATE c;

Wyniki

    Czas trwania

    Całkiem prawdopodobnie najważniejszą i najczęstszą miarą jest „jak długo to trwało?” Cóż, uruchomienie kursora z domyślnymi opcjami (lub tylko z LOCAL zajęło prawie pięć razy więcej czasu określony), w porównaniu do określenia STATIC lub FAST_FORWARD :

    Pamięć

    Chciałem również zmierzyć dodatkową pamięć, której SQL Server zażądałby podczas wypełniania każdego typu kursora. Dlatego po prostu uruchamiałem ponownie przed każdym testem zimnej pamięci podręcznej, mierząc licznik wydajności Całkowita pamięć serwera (KB) przed i po każdym teście. Najlepsza kombinacja tutaj to LOCAL FAST_FORWARD :

    użycie tempdb

    Ten wynik był dla mnie zaskakujący. Ponieważ definicja kursora statycznego oznacza, że ​​kopiuje on cały wynik do tempdb i jest on faktycznie wyrażony w sys.dm_exec_cursors jako ZDJĘCIE , spodziewałem się, że trafienie na stronach tempdb będzie wyższe przy wszystkich statycznych wariantach kursora. Tak nie było; ponownie widzimy około 5X trafienie na użycie tempdb z domyślnym kursorem i tym z tylko LOKALNY określono:

Wniosek

Od lat podkreślam, że dla twoich kursorów zawsze powinna być określona następująca opcja:

LOCAL STATIC READ_ONLY FORWARD_ONLY

Od tego momentu, dopóki nie będę miał szansy przetestować dalszych permutacji lub znaleźć przypadków, w których nie jest to najszybsza opcja, będę polecać następujące:

LOCAL FAST_FORWARD

(Na marginesie przeprowadziłem również testy z pominięciem LOKALNE opcja, a różnice były znikome.)

To powiedziawszy, niekoniecznie dotyczy to *wszystkich* kursorów. W tym przypadku mówię wyłącznie o kursorach, w których tylko odczytujesz dane z kursora, tylko w kierunku do przodu i nie aktualizujesz danych bazowych (albo za pomocą klucza, albo za pomocą WHERE CURRENT OF ). To są testy na kolejny dzień.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Elastyczne i łatwe w zarządzaniu projekty zestawień materiałowych (BOM)

  2. Wprowadzenie do auto_explain:Jak automatycznie rejestrować wolne plany zapytań Postgres

  3. Używanie Microsoft DiskSpd do testowania podsystemu pamięci masowej

  4. 3 najważniejsze wskazówki, które musisz znać, aby szybciej pisać widoki SQL

  5. Jak uporządkować wiersze według sumy grup w SQL