V pokračování mé série článků o latch budu tentokrát diskutovat o APPEND_ONLY_STORAGE_INSERT_POINT latch a ukážu, jak může představovat hlavní překážku pro velké aktualizační úlohy, kde se používá kterákoli z forem izolace snímků.
Důrazně doporučuji, abyste si přečetli první příspěvek v sérii před tímto, abyste měli všechny obecné základní znalosti o západkách.
Co je to západka APPEND_ONLY_STORAGE_INSERT_POINT?
Abych vysvětlil tuto západku, musím trochu vysvětlit, jak funguje izolace snímků.
Když povolíte jednu ze dvou forem verzování, SQL Server používá mechanismus nazvaný verzování pro zachování verzí před změnou záznamu v úložišti verzí v tempdb. To se provádí následovně:
- Záznam je identifikován jako záznam, který se má teprve změnit.
- Aktuální záznam se zkopíruje do úložiště verzí.
- Záznam je změněn.
- Pokud záznam již neměl 14bajtovou značku pro správu verzí , jedna se přidá na konec záznamu. Značka obsahuje časové razítko (ne v reálném čase) a ukazatel na předchozí verzi záznamu v úložišti verzí.
- Pokud záznam již měl značku pro správu verzí, aktualizuje se pomocí nového časového razítka a ukazatele úložiště verzí.
Časové razítko verzování pro celou instanci se zvýší vždy, když začne nový příkaz nebo dávka nebo se vytvoří nová verze záznamu v jakékoli databázi, kde je povolena kterákoli forma izolace snímků. Toto časové razítko se používá k zajištění toho, aby dotaz zpracoval správnou verzi záznamu.
Představte si například databázi, která měla povolený snímek potvrzeného čtení, takže je zaručeno, že každý příkaz uvidí záznamy v okamžiku spuštění příkazu. Časové razítko verzování je nastaveno na začátek příkazu, takže jakýkoli záznam, na který narazí a který má vyšší časové razítko, je „nesprávná“ verze, a proto je třeba načíst „správnou“ verzi s časovým razítkem před časovým razítkem příkazu. úložiště verzí. Mechanika tohoto procesu není pro účely tohoto příspěvku relevantní.
Jak jsou tedy verze fyzicky uloženy v úložišti verzí? Celý záznam před změnou, včetně sloupců mimo řádek, se zkopíruje do úložiště verzí, rozdělený na 8 000 bajtů, které mohou v případě potřeby zahrnovat dvě stránky (např. 2 000 bajtů na konci jedné stránky a 6 000 bajtů na začátek dalšího). Toto speciální úložiště se skládá z alokačních jednotek, které lze pouze připojit a používá se pouze pro operace úložiště verzí. Nazývá se to proto, že nová data mohou být vždy připojena pouze bezprostředně po skončení poslední zadané verze. Každou chvíli se vytváří nová alokační jednotka, což umožňuje velmi efektivní pravidelné čištění úložiště verzí – protože nepotřebnou alokační jednotku lze jednoduše vypustit. Mechanika toho opět přesahuje rozsah tohoto příspěvku.
A nyní se dostáváme k definici latch:každé vlákno, které potřebuje zkopírovat záznam před změnou do úložiště verzí, musí vědět, kde je v aktuální alokační jednotce pouze pro připojení bod vložení. Tyto informace jsou chráněny západkou APPEND_ONLY_STORAGE_INSERT_POINT.
Jak se západka stane úzkým hrdlem?
Zde je problém:existuje pouze jeden přijatelný režim, ve kterém lze získat západku APPEND_ONLY_STORAGE_INSERT_POINT:režim EX (exkluzivní). A jak víte z přečtení úvodního příspěvku k sérii, v režimu EX může západku držet vždy pouze jedno vlákno.
Shrnutí všech těchto informací dohromady:když má jedna nebo více databází povolenou izolaci snímků a existuje dostatečně velké souběžné pracovní zatížení těchto databází, bude různými připojeními generováno mnoho verzí a tato západka se stane trochu úzké místo, přičemž velikost úzkého místa se zvyšuje s rostoucí zátěží aktualizací tam, kde se jedná o verzování.
Zobrazení úzkého místa
Úzké místo si můžete snadno reprodukovat sami. Udělal jsem to následovně:
- Vytvořili tabulku se spoustou celočíselných sloupců s názvem cXXX, kde XXX je číslo a seskupený index ve sloupci identity int s názvem DocID
- Vloženo 100 000 záznamů s náhodnými hodnotami pro všechny sloupce
- Vytvořil skript s nekonečnou smyčkou pro výběr náhodného DocID v rozsahu 1 až 10 000, výběr náhodného názvu sloupce a zvýšení hodnoty sloupce o 1 (proto byla vytvořena verze)
- Vytvořeno devět identických skriptů, ale každý z nich vybírá z jiného rozsahu klastrových klíčů 10 000 hodnot
- Nastavte DELAYED_DURABILITY na FORCED, abyste zkrátili čekání na WRITELOG (sice byste to dělali jen zřídka, ale pomáhá to prohloubit překážku pro účely ukázky)
Poté jsem spustil všech deset skriptů současně a změřil jsem počítadlo Access Methods:Index Searches/s, abych sledoval, kolik aktualizací probíhalo. Nemohl jsem použít Databases:Batch Requests/sec, protože každý skript měl pouze jednu dávku (nekonečnou smyčku), a nechtěl jsem používat Transactions/s, protože by mohl počítat interní transakce stejně jako ty, které obalují každou aktualizaci.
Když izolace snímků nebyla povolena, na mém notebooku s Windows 10 se systémem SQL Server 2019 jsem během deseti připojení dostával asi 80 000 aktualizací za sekundu. Když jsem pak zapnul nastavení READ_COMMMITED_SNAPSHOT pro databázi na ON a znovu spustil test, propustnost zátěže klesla na přibližně 60 000 aktualizací za sekundu (25% pokles propustnosti). Z pohledu statistik čekání bylo 85 % všech čekání LATCH_EX a podle statistik blokování bylo 100 % na APPEND_ONLY_STORAGE_INSERT_POINT.
Mějte na paměti, že jsem nastavil scénář tak, aby ukázal úzké hrdlo v nejhorším. V reálném prostředí se smíšenou pracovní zátěží je obecně přijímaným vodítkem pro pokles propustnosti při použití izolace snímků 10–15 %.
Shrnutí
Další potenciální oblastí, která by mohla být tímto úzkým hrdlem ovlivněna, jsou sekundární skupiny čitelné skupiny dostupnosti. Pokud je replika databáze nastavena tak, aby byla čitelná, všechny dotazy proti ní automaticky používají izolaci snímků a každé přehrání záznamů protokolu z primárního záznamu vygeneruje verze. S dostatečně velkým aktualizačním vytížením pocházejícím z primární a mnoha databází nastavenými tak, aby byly čitelné, a s paralelním opakováním, které je normou pro sekundární skupiny dostupnosti, se západka APPEND_ONLY_STORAGE_INSERT_POINT může stát úzkým hrdlem i na sekundární čitelné skupině dostupnosti, což by mohlo vést k sekundární zaostávající za primární. Netestoval jsem to, ale je to přesně stejný mechanismus, který jsem popsal výše, takže se to zdá pravděpodobné. V takovém případě je možné zakázat paralelní opakování pomocí příznaku trasování 3459, ale to může vést k horší celkové propustnosti na sekundárním serveru.
Ponecháním scénáře skupiny dostupnosti stranou, bohužel, nepoužívání izolace snímků je jediný způsob, jak se tomuto úzkému hrdlu úplně vyhnout, což není schůdná možnost, pokud vaše pracovní zatížení závisí na sémantice poskytované izolací snímků nebo ji potřebujete ke zmírnění problémů s blokováním. (protože izolace snímku znamená, že čtené dotazy nezískávají zámky sdílení, které blokují dotazy na změnu).
Upravit:z níže uvedených komentářů *můžete* odstranit úzké hrdlo latch pomocí ADR v SQL Server 2019, ale pak je výkon mnohem horší kvůli režii ADR. Scénář, kdy se latch stane úzkým hrdlem kvůli velkému vytížení aktualizací, absolutně není platným případem použití pro ADR.