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

Přístup k ladění indexu – 2. část

Ve svém posledním příspěvku jsem začal popisovat proces, kterým procházím při ladění dotazů – konkrétně když zjistím, že potřebuji přidat nový index nebo upravit existující. Do této chvíle jsme identifikovali problematický dotaz, index, který potřebuji, jaké indexy aktuálně existují v tabulce a zda se tyto indexy používají, či nikoli. Jakmile máme tato data, můžeme přejít k dalším krokům v procesu.

Krok 5:Co používá index

Kromě toho, že uvidíte, jak často se index používá (či nikoli), je užitečné vědět, jaké dotazy použít index, zejména pokud ho chci sloučit s jiným indexem. Naštěstí Jonathan Kehayias již napsal dotaz, který má pomoci určit, které plány používají konkrétní index. Jeho verzi lze použít pro mezipaměť plánu – jedinou výzvou je, že informace je přechodná, takže nemusíte zachytit každý dotaz, který používá konkrétní index. Query Store s tím může pomoci – upravil jsem jeho dotaz tak, aby získal stejné informace z plánů v Query Store:

  SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
  DECLARE @IndexName AS NVARCHAR(128) = N'[IX_Sales_OrderLines_AllocatedStockItems]',
          @lb AS nchar(1) = N'[', @rb AS nchar(1) = N']';
 
  -- Make sure the name passed is appropriately quoted
  IF (LEFT(@IndexName, 1) <> @lb AND RIGHT(@IndexName, 1) <> @rb) SET @IndexName = QUOTENAME(@IndexName);
 
  --Handle the case where the left or right was quoted manually but not the opposite side
  IF LEFT(@IndexName, 1)  <> @lb SET @IndexName = @rb + @IndexName;
  IF RIGHT(@IndexName, 1) <> @rb SET @IndexName = @IndexName + @rb;
 
  ;WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')   
  SELECT
    stmt.value('(@StatementText)[1]', 'varchar(max)') AS SQL_Text,
    obj.value('(@Database)[1]', 'varchar(128)') AS DatabaseName,
    obj.value('(@Schema)[1]', 'varchar(128)') AS SchemaName,
    obj.value('(@Table)[1]', 'varchar(128)') AS TableName,
    obj.value('(@Index)[1]', 'varchar(128)') AS IndexName,
    obj.value('(@IndexKind)[1]', 'varchar(128)') AS IndexKind,
    query_plan
  FROM 	
  (
    SELECT query_plan
    FROM
    (
      SELECT TRY_CONVERT(XML, [qsp].[query_plan]) AS [query_plan]
      FROM sys.query_store_plan [qsp]
    ) tp
  ) AS tab (query_plan)
  CROSS APPLY query_plan.nodes('/ShowPlanXML/BatchSequence/Batch/Statements/StmtSimple') AS batch(stmt)
  CROSS APPLY stmt.nodes('.//IndexScan/Object[@Index=sql:variable("@IndexName")]') AS idx(obj)
  OPTION(MAXDOP 1, RECOMPILE);

Stojí za zmínku, že toto je další bod, kde se mohu ocitnout velmi hluboko v králičí noře, v závislosti na počtu indexů, které přezkoumávám, a počtu dotazů, které je používají. Pokud to bude možné, zvážím také počty provedení (z Query Store nebo mezipaměti plánu), abych nejen pochopil, co dotaz používá index, ale jak často se dotaz provádí. Zde se ladění indexů stává uměním. Dokážu shromáždit směšné množství dat... ale nemám nekonečně mnoho času na analýzu, takže musím posoudit, kolik dotazů zkontroluji.

Krok 6:Testování

Testování indexu ve své nejjednodušší podobě znamená vzít problematický dotaz a zachytit data plánu a výkonu (trvání, IO, CPU atd.) a poté vytvořit index, znovu spustit dotaz a zachytit stejné informace. Pokud se výkon zlepší, můžete začít!

Málokdy je to tak jednoduché.

Pro začátek mám často alespoň dvě varianty indexu, které chci otestovat, někdy i více. Začnu se svým základním plánem, pak vytvořím všechny varianty indexu, vyčistím mezipaměť plánu a uvidím, co SQL Server vybere. Poté procházím a vynucuji každý index s nápovědou, přičemž zachycuji plán a metriky výkonu pro každé provedení. Poznámka:předpokládá se, že mám dostatek místa na disku pro všechny indexy... pokud ne, vytvořím je jeden po druhém a otestuji. Nakonec čísla porovnám. Pokud právě přidávám nový index, jsem téměř hotový. Pokud ale upravuji index nebo slučuji pár dohromady, může se to zkomplikovat.

V ideálním světě, pokud upravím existující index, najdu nejčastější/nejdůležitější dotazy, které používají aktuální index, a získám jejich plány a metriky výkonu (to je snadné s Query Store). Pak změním index, znovu spustím všechny tyto dotazy a uvidím, zda dostanu významné změny ve tvaru plánu a/nebo výkonu.

Pokud sloučím dva indexy, udělám to samé, ale se všemi dotazy, které používají kterýkoli index, a poté znovu otestuji se sloučeným indexem.

Pokud přidávám/měním/slučuji více indexů pro tabulku, pak potřebuji získat všechny relevantní dotazy a jejich plány a metriky, změnit indexy, pak znovu získat všechny informace a porovnat. To může být časově velmi náročné v závislosti na počtu různých dotazů. Zde se jedná o uměleckou formu a vy musíte určit, kolik dotazů skutečně potřebujete otestovat. Je to funkce frekvence provádění, důležitosti/relevance dotazu a času, který mám k dispozici/přiděleno.

A konečně, pokud přidám index do tabulky a neodeberu žádné existující, pak jsem přidal režii pro INSERT, DELETE a případně UPDATE. Testování výkonu tato změna je možná, ale potřebujete testovací prostředí a schopnost spustit zátěžový test a zachytit metriky před a po změně související s trváním, IO a CPU.

Je to hodně přátel, a proto je ironické, že jsem zpočátku přemýšlel o tom, že ladění indexu je snadné. Nemusí to být vždy jednoduché, ale je to možné. Je to otázka píle a sledování toho všeho.

Krok 7:Implementace

Poté, co co nejvíce prověřím nový index (y), jsme připraveni k výrobě. Přiznám se, že změny indexu považuji za nízkorizikové, zvláště ty nové. Pokud je to problém, můžete jej okamžitě zahodit a vrátit se do původního stavu. Ve scénáři upravit/sloučit/upustit chcete mít vše naskriptované, abyste mohli měnit a znovu vytvářet indexy podle potřeby k resetování indexů. Vždy doporučuji nejprve indexy deaktivovat namísto jejich vypouštění, protože pak se nemusíte starat o definici – pokud potřebujete index přidat zpět, jednoduše jej znovu sestavíte.

Shrnutí

Vaše metoda přidávání a/nebo konsolidace indexů se může lišit! Stejně jako ladění dotazů neexistuje dokonalý proces. Pro každého, kdo začíná s laděním indexu, to doufejme poskytne začátek položek ke kontrole a důležitých úvah. Není možné přidávat indexy, aniž byste přidali určité množství režijních nákladů – a opět zde přichází na řadu umění:musíte určit, zda přínos indexu převáží jeho náklady na úpravy.

Ladění indexu je neustálý, opakující se proces – nemyslím si, že jste někdy skončili, protože se přidávají změny kódu, nové tabulky nebo funkce a data v tabulkách se mění. Kimberly má dva příspěvky (https://www.sqlskills.com/blogs/kimberly/spring-cleaning-your-indexes-part-i/ a https://www.sqlskills.com/blogs/kimberly/spring-cleaning- your-indexes-part-ii/), které hovoří o vyčištění vašich indexů – nyní je čas začít jako každý jiný! A nakonec, kdykoli se někdo zeptá:„Kolik indexů by měla mít tabulka? Odpovídám něco jako:"co nejmenší počet potřebujete k uspokojení co největšího počtu dotazů." Žádné magické číslo neexistuje – viděl jsem tabulky s nulovými indexy a viděl jsem tabulky s více než 100 (jsem si jistý, že někteří z vás viděli vyšší počty). Ani nula, ani 100 není dobré, ale „správné“ číslo je takové, které musíte zjistit pomocí dostupných dat a svých zkušeností.


  1. Trezor TO_NUMBER()

  2. Zobrazení ALL_PROCEDURES nezobrazuje PROCEDURE_NAME

  3. PL/SQL ORA-01422:Přesné načtení vrátí více než požadovaný počet řádků

  4. Jak použít ANY místo IN v klauzuli WHERE s Rails?