Sdílet prostřednictvím


Řešení potíží s indexy hodnot hash pro tabulky Memory-Optimized

platí pro:SQL ServerAzure SQL Databaseazure SQL Managed Instance

Předpoklad

Důležité kontextové informace pro pochopení tohoto článku najdete tady:

Praktická čísla

Při vytváření indexu hash pro tabulku optimalizovanou pro paměť je potřeba zadat počet kbelíků v době vytvoření. Ve většině případů by počet kbelíků byl v ideálním případě mezi 1 a 2násobky počtu jedinečných hodnot v klíči indexu.

I když je ale BUCKET_COUNT mírně nižší nebo vyšší než upřednostňovaný rozsah, bude výkon vašeho hash indexu pravděpodobně přijatelný nebo tolerovatelný. Alespoň zvažte, že váš index hash bude mít BUCKET_COUNT přibližně roven počtu řádků, které předpokládáte, že tabulka optimalizovaná pro paměť bude mít.
Předpokládejme, že vaše rostoucí tabulka má 2 000 000 řádků, ale předpovídá se, že se tento počet zvětší 10krát na 20 000 000 řádků. Začněte počtem kbelíků, který je 10krát větší než počet řádků v tabulce. Tím získáte prostor pro větší množství řádků.

  • V ideálním případě byste počet kbelíků zvýšili, když množství řádků dosáhne počátečního počtu kbelíků.
  • I když počet řádků roste na pětkrát větší než počet kbelíků, je výkon ve většině situací stále dobrý.

Předpokládejme, že index hash má 10 000 000 jedinečných hodnot klíče.

  • Počet kbelíků 2 000 000 by byl přibližně nejméně, co byste mohli akceptovat. Stupeň snížení výkonu může býtlerovatelný.

Příliš mnoho duplicitních hodnot v indexu?

Pokud mají indexované hodnoty hash vysokou míru duplicit, kontejnery hash trpí delšími řetězy.

Předpokládejme, že máte stejnou tabulku SupportEvent ze staršího bloku kódu syntaxe T-SQL. Následující kód T-SQL ukazuje, jak můžete najít a zobrazit poměr všech hodnot k jedinečných hodnot:

-- Calculate ratio of:  Rows / Unique_Values.  
DECLARE @allValues float(8) = 0.0, @uniqueVals float(8) = 0.0;  
  
SELECT @allValues = Count(*) FROM SupportEvent;  
  
SELECT @uniqueVals = Count(*) FROM  
  (SELECT DISTINCT SupportEngineerName  
      FROM SupportEvent) as d;  
  
    -- If (All / Unique) >= 10.0, use a nonclustered index, not a hash.   
SELECT Cast((@allValues / @uniqueVals) as float) as [All_divby_Unique];  
go  
  • Poměr 10,0 nebo vyšší znamená, že hash není vhodná jako typ indexu. Zvažte místo toho použití neclusterovaného indexu.

Řešení potíží s počtem kontejnerů indexu hash

Tato část popisuje, jak řešit potíže s počtem kontejnerů pro index hash.

Monitorování statistik pro řetězy a prázdné kontejnery

Statistický stav indexů hash můžete monitorovat spuštěním následujícího příkazu T-SQL SELECT. Select používá zobrazení správy dat (DMV) s názvem sys.dm_db_xtp_hash_index_stats.

SELECT  
  QUOTENAME(SCHEMA_NAME(t.schema_id)) + N'.' + QUOTENAME(OBJECT_NAME(h.object_id)) as [table],   
  i.name                   as [index],   
  h.total_bucket_count,  
  h.empty_bucket_count,  
    
  FLOOR((  
    CAST(h.empty_bucket_count as float) /  
      h.total_bucket_count) * 100)  
                            as [empty_bucket_percent],  
  h.avg_chain_length,   
  h.max_chain_length  
FROM  
        sys.dm_db_xtp_hash_index_stats  as h   
  JOIN sys.indexes                     as i  
          ON h.object_id = i.object_id  
          AND h.index_id  = i.index_id  
JOIN sys.memory_optimized_tables_internal_attributes ia ON h.xtp_object_id=ia.xtp_object_id
JOIN sys.tables t on h.object_id=t.object_id
WHERE ia.type=1
ORDER BY [table], [index];  

Porovnejte výsledky SELECT s následujícími statistickými pokyny:

  • Prázdné kontejnery:
    • 33% je dobrá cílová hodnota, ale větší procento (i 90%) je obvykle v pořádku.
    • Pokud se počet kbelíků rovná počtu jedinečných hodnot klíče, je přibližně 33% kbelíků prázdných.
    • Hodnota nižší než 10% je příliš nízká.
  • Řetězy v kontejnerech:
    • Průměrná délka řetězce 1 je ideální v případě, že neexistují žádné duplicitní hodnoty klíče indexu. Délky řetězů až do 10 jsou obvykle přijatelné.
    • Pokud je průměrná délka řetězu větší než 10 a procento prázdného kbelíku je větší než 10%, data mají tolik duplicit, že index hash nemusí být nejvhodnějším typem.

Ukázka řetězů a prázdných kbelíků

Následující blok kódu T-SQL poskytuje snadný způsob, jak otestovat SELECT * FROM sys.dm_db_xtp_hash_index_stats;. Blok kódu se dokončí za 1 minutu. Tady jsou fáze následujícího bloku kódu:

  1. Vytvoří tabulku optimalizovanou pro paměť s několika indexy hash.
  2. Naplní tabulku tisíci řádků.
    a. Operátor modulo slouží ke konfiguraci rychlosti duplicitních hodnot ve sloupci StatusCode.
    b. Smyčka vloží 262,144 řádků přibližně za 1 minutu.
  3. PRINT zobrazí zprávu s výzvou ke spuštění předchozího příkazu SELECT z sys.dm_db_xtp_hash_index_stats.
DROP TABLE IF EXISTS SalesOrder_Mem;  
go  
  
  
CREATE TABLE SalesOrder_Mem  
(  
  SalesOrderId   uniqueidentifier  NOT NULL  DEFAULT newid(),  
  OrderSequence  int               NOT NULL,  
  OrderDate      datetime2(3)      NOT NULL,  
  StatusCode     tinyint           NOT NULL,  
  
  PRIMARY KEY NONCLUSTERED  
      HASH (SalesOrderId)  WITH (BUCKET_COUNT = 262144),  
  
  INDEX ix_OrderSequence  
      HASH (OrderSequence) WITH (BUCKET_COUNT = 20000),  
  
  INDEX ix_StatusCode  
      HASH (StatusCode)    WITH (BUCKET_COUNT = 8),  
  
  INDEX ix_OrderDate       NONCLUSTERED (OrderDate DESC)  
)  
  WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA)  
go  
  
--------------------  
  
SET NOCOUNT ON;  
  
-- Same as PK bucket_count.  68 seconds to complete.  
DECLARE @i int = 262144;  
  
BEGIN TRANSACTION;  
  
WHILE @i > 0  
BEGIN  
  
  INSERT SalesOrder_Mem  
      (OrderSequence, OrderDate, StatusCode)  
    Values  
      (@i, GetUtcDate(), @i % 8);  -- Modulo technique.  
  
  SET @i -= 1;  
END  
COMMIT TRANSACTION;  
  
PRINT 'Next, you should query:  sys.dm_db_xtp_hash_index_stats .';  
go  

Předchozí smyčka INSERT provede následující kroky:

  • Vloží jedinečné hodnoty pro index primárního klíče a pro ix_OrderSequence.
  • Vloží několik stovek tisíc řádků, které představují pouze osm jedinečných hodnot pro StatusCode. Proto existuje vysoká míra duplikace hodnot v indexu ix_StatusCode.

Při řešení potíží, pokud počet kbelíků není optimální, prozkoumejte následující výstup příkazu SELECT z sys.dm_db_xtp_hash_index_stats. Pro tyto výsledky jsme přidali WHERE Object_Name(h.object_id) = 'SalesOrder_Mem' do SELECT zkopírovaného z části D.1.

Naše výsledky SELECT jsou zobrazeny za kódem a pro lepší zobrazení jsou uměle rozděleny do dvou užších tabulek.

  • Zde jsou výsledky pro počet věder .
Název indexu Počet_celkových_kbelíků počet_prázdných_kbelíků EmptyBucketPercent
ix_OrderSequence 32768 13 0
ix_StatusCode 8 4 50
PK_SalesOrd_B14003... 262144 96525 36
  • Dále jsou výsledky pro délku řetězu .
Název indexu průměrná délka řetězce maximální_délka_řetězce
ix_OrderSequence 8 26
ix_StatusCode 65536 65536
PK_SalesOrd_B14003... 1 8

Pojďme interpretovat předchozí tabulky výsledků pro tři indexy hash:

ix_StatusCode:

  • 50% kbelíků je prázdných, což je dobré.
  • Průměrná délka řetězu je však velmi vysoká při 65536.
    • To označuje vysokou míru duplicitních hodnot.
    • Proto použití indexu hash není v tomto případě vhodné. Místo toho by se měl použít neclusterovaný index.

ix_OrderSequence:

  • 0% kbelíků je prázdný, což je příliš malé množství.
  • Průměrná délka řetězu je 8, i když jsou všechny hodnoty v tomto indexu jedinečné.
    • Proto by se měl zvýšit počet kbelíků, aby se snížila průměrná délka řetězu blíž ke 2 nebo 3.
  • Vzhledem k tomu, že klíč indexu má 262144 jedinečné hodnoty, měl by být počet kbelíků alespoň 262144.
    • Pokud se očekává budoucí růst, měl by být počet kbelíků vyšší.

index primárního klíče (PK_SalesOrd_...):

  • 36% kbelíků jsou prázdné, což je dobré.
  • Průměrná délka řetězu je 1, což je také dobré. Není nutná žádná změna.

Vyvážení kompromisu

Úlohy OLTP se zaměřují na jednotlivé řádky. Úplná skenování tabulek obvykle nejsou kritickou cestou z hlediska výkonu pro úlohy OLTP. Kompromis, který musíte vyvážit, je mezi množstvím využití paměti a výkonem testů rovnosti a operací vkládání.

Pokud je využití paměti větší problém:

  • Zvolte počet kontejnerů blízko počtu jedinečných hodnot klíče indexu.
  • Počet kontejnerů by neměl být výrazně nižší než počet jedinečných hodnot klíče indexu, protože to má vliv na většinu operací DML i dobu potřebnou k obnovení databáze po restartování serveru.

Pokud je výkon testů rovnosti větší problém:

  • Je vhodné mít počet kbelíků ve výši dvojnásobku nebo trojnásobku počtu jedinečných hodnot indexu. Vyšší počet znamená:
    • Rychlejší načítání při hledání jedné konkrétní hodnoty
    • Zvýšené využití paměti.
    • Zvýšení doby potřebné pro úplnou kontrolu indexu hash.

Další čtení

indexy hodnot hash pro tabulky Memory-Optimized
neclusterované indexy pro tabulky Memory-Optimized