sql >> Databáze >  >> RDS >> Database

Jaký dopad mohou mít různé možnosti kurzoru?

Několikrát jsem psal o používání kurzorů a o tom, jak je ve většině případů efektivnější přepisovat kurzory pomocí logiky založené na množinách.

Jsem však realista.

Vím, že existují případy, kdy jsou kurzory „vyžadovány“ – potřebujete zavolat jinou uloženou proceduru nebo poslat e-mail pro každý řádek, provádíte úlohy údržby pro každou databázi nebo spouštíte jednorázovou úlohu, která jednoduše nestojí za to investovat čas do převodu na sadu.

Jak to dnes (pravděpodobně) děláte

Bez ohledu na důvod, proč stále používáte kurzory, měli byste být přinejmenším opatrní, abyste nepoužívali poměrně drahé výchozí možnosti. Většina lidí začíná své kurzory takto:

DECLARE c CURSOR FOR 
  SELECT whatever FROM ...

Nyní znovu, pro ad-hoc, jednorázové úkoly, je to pravděpodobně v pořádku. Ale existují…

Další způsoby, jak to udělat

Chtěl jsem spustit některé testy pomocí výchozích hodnot a porovnat je s různými možnostmi kurzoru, jako je LOCAL , STATIC , READ_ONLY a FAST_FORWARD . (Existuje spousta možností, ale tyto jsou nejčastěji používané, protože jsou použitelné pro nejběžnější typy kurzorových operací, které lidé používají.) Nejen, že jsem chtěl otestovat hrubou rychlost několika různých kombinací, ale také dopad na tempdb a paměť, a to jak po studeném restartu služby, tak s teplou mezipamětí.

Dotaz, který jsem se rozhodl zadat kurzoru, je velmi jednoduchý dotaz na sys.objects , ve vzorové databázi AdventureWorks2012. To vrátí 318 500 řádků v mém systému (velmi skromný 2jádrový systém se 4 GB RAM):

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

Poté jsem tento dotaz zabalil do kurzoru s různými možnostmi (včetně výchozích hodnot) a provedl jsem několik testů, měření celkové paměti serveru, stránek přidělených tempdb (podle sys.dm_db_task_space_usage a/nebo sys.dm_db_session_space_usage ) a celkovou dobu trvání. Zkoušel jsem také pozorovat spory tempdb pomocí skriptů od Glenna Berryho a Roberta Davise, ale na svém mizerném systému jsem nemohl detekovat žádný spor. Samozřejmě jsem také na SSD a v systému neběží absolutně nic jiného, ​​takže toto mohou být věci, které budete chtít přidat do svých vlastních testů, pokud je pravděpodobnější, že tempdb bude překážkou.

Takže nakonec dotazy vypadaly asi takto, s diagnostickými dotazy zakomponovanými na příslušných místech:

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;

Výsledky

    Trvání

    Zcela pravděpodobně nejdůležitější a nejběžnější měřítko je, "jak dlouho to trvalo?" Spuštění kurzoru s výchozími možnostmi (nebo pouze s LOCAL) trvalo téměř pětkrát déle specifikováno), ve srovnání se zadáním buď STATIC nebo FAST_FORWARD :

    Paměť

    Chtěl jsem také změřit další paměť, kterou by SQL Server požadoval při splnění každého typu kurzoru. Takže jsem před každým testem studené mezipaměti jednoduše restartoval a změřil počítadlo výkonu Total Server Memory (KB) před a po každém testu. Nejlepší kombinace zde byla LOCAL FAST_FORWARD :

    Využití tempdb

    Tento výsledek mě překvapil. Protože definice statického kurzoru znamená, že zkopíruje celý výsledek do databáze tempdb a ve skutečnosti je vyjádřen v sys.dm_exec_cursors jako SNAPSHOT , očekával jsem, že zásah na stránkách tempdb bude vyšší se všemi statickými variantami kurzoru. Nebylo tomu tak; opět vidíme zhruba 5násobný zásah při použití databáze tempdb s výchozím kurzorem a kurzorem pouze s LOCAL specifikováno:

Závěr

Po léta zdůrazňuji, že pro vaše kurzory by měla být vždy uvedena následující možnost:

LOCAL STATIC READ_ONLY FORWARD_ONLY

Od tohoto okamžiku, dokud nebudu mít možnost otestovat další permutace nebo najít případy, kdy to není nejrychlejší možnost, budu doporučovat následující:

LOCAL FAST_FORWARD

(Mimochodem jsem také provedl testy s vynecháním LOCAL a rozdíly byly zanedbatelné.)

To však nemusí nutně platit pro *všechny* kurzory. V tomto případě mluvím pouze o kurzorech, kde pouze čtete data z kurzoru, pouze ve směru dopředu a neaktualizujete podkladová data (buď pomocí klíče nebo pomocí WHERE CURRENT OF ). To jsou testy na další den.


  1. Jak najít součet více sloupců v tabulce v SQL Server 2005?

  2. MySQL:Přidejte sloupec sekvence na základě jiného pole

  3. MySQL vytváří syntaxi uložené procedury s oddělovačem

  4. Co jsou závislosti na databázi?