V některých mých předchozích článcích zde o ladění výkonu jsem diskutoval o několika typech čekání a o tom, jak naznačují různá úzká hrdla zdrojů. Začínám novou sérii o scénářích, kde je mechanismus synchronizace zvaný latch překážkou výkonu, a konkrétně nestránkové latch. V tomto úvodním příspěvku vysvětlím, proč jsou vyžadovány západky, co vlastně jsou a jak mohou být překážkou.
Proč jsou západky potřeba?
Základním principem informatiky je, že kdykoli existuje datová struktura ve vícevláknovém systému, musí být datová struktura nějakým způsobem chráněna. Tato ochrana poskytuje následující podmínky:
- (Zaručeno) Datovou strukturu nemůže vlákno změnit, když ji čte jiné vlákno
- (Zaručeno) Datovou strukturu nemůže vlákno číst, zatímco jiné vlákno ji mění
- (Zaručeno) Strukturu dat nelze změnit dvěma nebo více vlákny současně
- (Volitelné) Umožněte dvěma nebo více vláknům číst datovou strukturu současně
- (Volitelné) Umožněte vláknům seřadit do fronty v uspořádaném režimu pro přístup k datové struktuře
To lze provést několika způsoby, včetně:
- Mechanismus, který vždy umožňuje přístup k datové struktuře pouze jednomu vláknu současně. SQL Server implementuje tento mechanismus a nazývá jej spinlock. To umožňuje #1, #2 a #3 výše.
- Mechanismus, který umožňuje více vláknům číst datovou strukturu současně (tj. mají sdílený přístup), umožňuje jedinému vláknu získat výhradní přístup k datové struktuře (s vyloučením všech ostatních vláken) a implementuje spravedlivý způsob řazení do fronty na přístup. SQL Server implementuje tento mechanismus a nazývá jej latch. To umožňuje všech pět výše uvedených výhrad.
Proč tedy SQL Server používá spinlocky i latche? K některým datovým strukturám se přistupuje tak často, že latch je prostě příliš drahý, a tak se místo něj používá velmi lehký spinlock. Dva příklady takových datových struktur jsou seznam volných vyrovnávacích pamětí ve fondu vyrovnávacích pamětí a seznam zámků ve správci zámků.
Co je to západka?
Latch je synchronizační mechanismus, který chrání jedinou datovou strukturu a v SQL Serveru existují tři široké typy latch:
- Zablokuje ochranu stránky datového souboru při jejím čtení z disku. Tyto se zobrazují, když PAGEIOLATCH_XX čeká, a diskutoval jsem o nich v tomto příspěvku.
- Zámky chránící přístup ke stránce datového souboru, která je již v paměti (8 kB stránka ve fondu vyrovnávacích pamětí je ve skutečnosti jen datová struktura). Tyto se zobrazují, když PAGELATCH_XX čeká, a diskutoval jsem o nich v tomto příspěvku.
- Zámky chránící nestránkové datové struktury. Tyto se zobrazují jako LATCH_SH a LATCH_EX čekající.
V této sérii se zaměříme na třetí typ západek.
Latch je sama o sobě malá datová struktura a můžete si ji představit tak, že má tři složky:
- Popis zdroje (co chrání)
- Stavové pole udávající, ve kterých režimech je západka aktuálně zadržena, kolik vláken drží západku v tomto režimu a zda nějaká vlákna čekají (a další věci, o které se nemusíme starat)
- Fronta vláken, která čekají na přístup k datové struktuře a na které režimy přístupu čekají (tzv. fronta čekání)
U nestránkových latchů se omezíme pouze na zvážení přístupových režimů SH (share) pro čtení datové struktury a EX (exkluzivní) pro změnu datové struktury. Existují další exotičtější režimy, ale používají se zřídka a nebudou se objevovat jako sporné body, takže po zbytek této diskuse budu předstírat, že neexistují.
Někteří z vás možná vědí, že superlatche/podzámky a rozdělení západek kvůli škálovatelnosti jsou také hlubší komplikace, ale pro účely této série nemusíme zacházet do takové hloubky.
Získání západky
Když vlákno chce získat latch, podívá se na stav latch.
Pokud vlákno chce získat západku v režimu EX, může tak učinit pouze v případě, že v žádném režimu nejsou žádná vlákna držící západku. Pokud tomu tak je, vlákno získá západku v režimu EX a nastaví stav tak, aby to indikoval. Pokud již jedno nebo více vláken drží západku, vlákno nastaví stav tak, aby indikovalo, že existuje čekající vlákno, vstoupí na konec fronty čekajících a poté je pozastaveno (v seznamu čekatelů plánovače, na kterém je ) čeká na LATCH_EX.
Pokud chce vlákno získat západku v režimu SH, může tak učinit pouze v případě, že žádné vlákno nedrží západku nebo jediná vlákna držící západku jsou v režimu SH *a* neexistují žádná vlákna čekající na získání západky. Pokud tomu tak je, vlákno získá latch v režimu SH, nastaví stav tak, aby to indikovalo, a zvýší počet vláken držících latch. Pokud je západka podržena v režimu EX nebo existuje jedno nebo více čekajících vláken, vlákno nastaví stav tak, aby indikovalo, že existuje čekající vlákno, vstoupí na konec fronty čekajících a poté je pozastaveno čekání na LATCH_SH.
Kontrola čekajících vláken se provádí, aby byla zajištěna spravedlivost vůči vláknu čekajícímu na západku v režimu EX. Bude muset pouze čekat na vlákna držící západku v režimu SH, která získala západku, než začala čekat. Bez této kontroly může nastat počítačový termín nazvaný „hladovění“, kdy neustálý proud vláken získávajících západku v režimu SH brání tomu, aby vlákno v režimu EX bylo schopno získat západku.
Uvolnění západky
Pokud vlákno drží západku v režimu EX, zruší nastavení stavu, který ukazuje, že je západka držena v režimu EX, a poté zkontroluje, zda existují nějaká čekající vlákna.
Pokud vlákno drží západku v režimu SH, snižuje počet vláken v režimu SH. Pokud je počet nyní nenulový, uvolňovací vlákno se provede pomocí západky. Pokud je počet *je* nyní nulový, zruší nastavení stavu, který ukazuje, že západka je držena v režimu SH, a poté zkontroluje, zda existují nějaká čekající vlákna.
Pokud žádná vlákna nečekají, uvolnění vlákna se provede pomocí západky.
Pokud hlava čekající fronty čeká na režim EX, uvolňující vlákno provede následující:
- Nastaví stav, aby se ukázalo, že západka je držena v režimu EX
- Odebere čekající vlákno z hlavy fronty a nastaví jej jako vlastníka západky
- Signalizuje čekajícímu vláknu, že je vlastníkem a je nyní spustitelné (koncepčně přesunutím čekajícího vlákna ze seznamu čekatelů v jeho plánovači do spustitelné fronty v plánovači)
- A je to se západkou hotovo
Pokud hlava čekající fronty čeká v režimu SH (což může nastat pouze v případě, že uvolňující vlákno bylo v režimu EX), uvolňující vlákno provede následující:
- Nastaví stav, aby se ukázalo, že západka je držena v režimu SH
- Pro všechna vlákna v čekající frontě, která čekají na režim SH
- Odebere čekající vlákno z hlavy fronty
- Zvyšuje počet vláken držících západku
- Signalizuje čekajícímu vláknu, že je vlastníkem a nyní jej lze spustit
- A je to se západkou hotovo
Jak mohou být západky sporným bodem?
Na rozdíl od zámků jsou západky obecně drženy pouze po dobu operace čtení nebo změny, takže jsou docela lehké, ale kvůli nekompatibilitě SH vs. EX mohou být stejně velkým sporným bodem jako zámky. K tomu může dojít, když se mnoho vláken snaží získat západku v režimu EX (může to jen jedno najednou) nebo když se mnoho vláken pokouší získat západku v režimu SH a jiné vlákno drží západku v režimu EX.
Shrnutí
Čím více vláken v systému bojuje o „horké“ blokování, tím vyšší bude spor a tím negativnější bude vliv na propustnost zátěže. Pravděpodobně jste slyšeli o dobře známých problémech latch sporů, například kolem bitmap alokace tempdb, ale spory mohou nastat i u nestránkových latchů.
Nyní jsem vám poskytl dostatek informací k tomu, abyste porozuměli latchům a tomu, jak fungují, v několika následujících článcích prozkoumám některé skutečné problémy sporu o latch mimo stránky a vysvětlím, jak jim předcházet nebo jak je obejít.