Ve svém předchozím příspěvku jsem diskutoval o čekání LCK_M_XX, ASYNC_NETWORK_IO a OLEDB a jejich reakcích. V tomto příspěvku budu pokračovat v tématu statistiky čekání a budu diskutovat o čekání SOS_SCHEDULER_YIELD.
Když je na serveru nejrozšířenější SOS_SCHEDULER_YIELD, je běžné vidět trvalé vysoké využití CPU. Prudká reakce je taková, že server musí být pod tlakem CPU nebo že problémem je spinlock.
K pochopení těchto dvou reakcí potřebujeme trochu pozadí.
Plánování vláken
Plánování vláken v SQL Server je spravováno samotným SQL Serverem, nikoli Windows (tj. není preemptivní). Část operačního systému SQL Storage Engine poskytuje funkce plánování a přechod vláken z běžící na procesoru (kde je stav vlákna RUNNING) na seznam čekatelů a čeká na zpřístupnění prostředku (stav je SUSPENDED) na provozovaný. Zařaďte do fronty, jakmile bude zdroj dostupný (stav je RUNNABLE) a čeká, až se dostanete na začátek fronty a znovu se vrátíte do procesoru (zpět do stavu RUNNING). Napsal jsem velká písmena Processor, Waiter List a Runnable Queue, abych je identifikoval jako součásti plánovače.
Kdykoli vlákno potřebuje zdroj, který nemůže okamžitě získat, bude pozastaveno a čeká na seznamu čekatelů, až mu bude sděleno (signalizováno), že jeho zdroj je dostupný. Čas strávený na seznamu čekatelů je doba čekání na zdroj a doba strávená na Runnable Queue je doba čekání na signál. Společně tvoří celkovou čekací dobu. SQL OS sleduje dobu čekání a dobu čekání na signál, takže musíme provést nějaké výpočty na výstupu ze sys.dm_os_wait_stats, abychom odvodili dobu čekání zdroje (viz můj skript zde).
Seznam čekatelů je neuspořádaný (jakékoli vlákno na něm může být kdykoliv signalizováno a přesunuto do Runnable Queue) a Runnable Queue je First-In-First-Out (FIFO) téměř 100 % času. Jedinou výjimkou z toho, že Runnable Queue je FIFO, je situace, kdy bylo ve stejném fondu zdrojů nakonfigurováno více skupin zátěže guvernéra prostředků, které mají vůči sobě různé priority. Nikdy jsem to neviděl úspěšně použit ve výrobě, takže to nebudu dále rozebírat.
Existuje další důvod, proč se vlákno může muset přesunout z procesoru – vyčerpává jeho kvanta. Kvantum vláken v SQL OS je pevně stanoveno na 4 milisekundy. Samotné vlákno je zodpovědné za určení, že jeho kvantum bylo vyčerpáno (voláním pomocných rutin v SQL OS) a dobrovolně se vzdalo procesoru (známé jako výnos). Když k tomu dojde, vlákno se přesune přímo na konec Runnable Queue, protože není na co čekat. SQL OS však musí zaregistrovat typ čekání pro tento přechod mimo procesor a zaregistruje SOS_SCHEDULER_YIELD.
Toto chování je často mylně považováno za tlak procesoru, ale není tomu tak – je to jen trvalé využití procesoru. Tlak CPU a jeho rozpoznání je úplně jiné téma pro budoucí příspěvek. Pokud jde o tento příspěvek, pokud je průměrná doba čekání na signál nízká (0-0,1-0,2 ms), je docela jisté, že tlak CPU není problém.
Spinlocks
Spinlock je velmi nízkoúrovňové synchronizační primitivum, které se používá k poskytování bezpečného přístupu k datovým strukturám na serveru SQL Server, které jsou extrémně horké (velmi nestálé a přístupné a měněné neuvěřitelně často více vlákny). Příklady takových struktur jsou seznam bez vyrovnávací paměti v každé části fondu vyrovnávacích pamětí a pole proporcionálních vah pro datové soubory ve skupině souborů.
Když vlákno potřebuje získat spinlock, podívá se, zda je spinlock volné, a pokud ano, okamžitě ho získá (pomocí propojeného primitiva jazyka montáže, jako je „test bit clear and set“). Pokud nelze spinlock získat, vlákno se jej okamžitě pokusí získat znovu a znovu a znovu, až po tisíc iterací, dokud neustoupí (na chvíli usne). Toto se neregistruje jako žádný typ čekání, protože vlákno jednoduše volá funkci spánku Windows (), ale může způsobit, že ostatní vlákna, která čekají, budou mít dlouhé čekací doby signálu (10–20 ms+), protože spící vlákno zůstane na procesoru, dokud dostane spinlock.
Proč mluvím o spinlockech? Protože mohou být také příčinou vysokého využití CPU a existuje mylná představa, že spinlocky jsou příčinou čekání SOS_SCHEDULER_YIELD. Nejsou.
Příčiny SOS_SCHEDULER_YIELD
Existuje tedy jeden důvod pro SOS_SCHEDULER_YIELD:vlákno vyčerpávající své plánovací kvantum a silně se opakující instance mohou vést k tomu, že SOS_SCHEDULER_YIELD je nejčastější čekání spolu s vysokým využitím CPU.
Ve výstupu z sys.dm_os_waiting_tasks neuvidíte čekání SOS_SCHEDULER_YIELD, protože vlákno nečeká. Můžete zjistit, který dotaz generuje čekání SOS_SCHEDULER_YIELD, dotazováním sys.dm_exec_requests a filtrováním ve sloupci last_wait_type.
To také znamená, že když ve výstupu sys.dm_os_wait_stats uvidíte SOS_SCHEDULER_YIELD, čekání na prostředek bude nulové, protože ve skutečnosti nečekal. Pamatujte však, že každé z těchto „čekání“ se rovná 4 ms času procesoru nashromážděného pro dotaz.
Jediným způsobem, jak prokázat, co způsobuje čekání SOS_SCHEDULER_YIELD, je zachytit zásobníky volání serveru SQL Server, když nastane tento typ čekání, pomocí rozšířených událostí a symbolů ladění od společnosti Microsoft. Mám příspěvek na blogu, který popisuje a ukazuje, jak toto vyšetřování provést, a existuje skvělý whitepaper o spinlockech a vyšetřování spinlocků, který stojí za to si přečíst, pokud vás zajímá tato hloubka vnitřností.
V případě kvantového vyčerpání to není hlavní příčina. Je to další symptom. Nyní musíme zvážit, proč může vlákno opakovaně vyčerpávat své kvantum.
Vlákno může vyčerpat své kvantum pouze tehdy, když může pokračovat ve zpracování kódu SQL Server po dobu 4 ms, aniž by potřebovalo zdroj, který vlastní jiné vlákno – žádné čekání na zámky, zámky stránek, stránky datových souborů na načtení z disku, alokace paměti, nárůst souborů, protokolování nebo nesčetné množství dalších zdrojů, které vlákno může potřebovat.
Nejběžnějším kusem kódu, kde může dojít ke kvantovému vyčerpání a nahromadit velké množství čekání SOS_SCHEDULER_YIELD, je skenování indexu/tabulky, kde jsou všechny potřebné stránky datových souborů v paměti a neexistuje žádný spor o přístup k těmto stránkám, a to je to, co Doporučuji vám hledat v plánech dotazů, když vidíte SOS_SCHEDULER_YIELD jako nejvyšší typ čekání – velké a/nebo opakované prohledávání indexů/tabulek.
To neznamená, že říkám, že velká skenování jsou špatná, protože nejúčinnějším způsobem, jak zpracovat vaši zátěž, je skenování. Pokud jsou však čekání SOS_SCHEDULER_YIELD nová a neobvyklá a jsou způsobena rozsáhlými skeny, měli byste prozkoumat, proč plány dotazů používají skenování. Možná někdo zahodil kritický neshlukovaný index nebo statistiky jsou zastaralé, a tak byl zvolen nesprávný plán dotazů, nebo možná byla do uložené procedury předána neobvyklá hodnota parametru a plán dotazu vyžadoval skenování nebo změnu kódu došlo bez podpory přidání indexu.
Shrnutí
Stejně jako u jiných typů čekání je klíčové přesně porozumět tomu, co SOS_SCHEDULER_YIELD znamená, k pochopení toho, jak problém odstranit a zda se dané chování očekává kvůli zpracovávané zátěži.
Pokud jde o obecné statistiky čekání, více informací o jejich použití pro odstraňování problémů s výkonem naleznete v:
- Moje série příspěvků na blogu SQLskills, počínaje statistikou čekání, nebo mi prosím řekněte, kde to bolí
- Moje knihovna typů čekání a tříd Latch zde
- Můj online školicí kurz Pluralsight SQL Server:Odstraňování problémů s výkonem pomocí statistiky čekání
- Poradce pro výkon SQL Sentry
V dalším článku ze série proberu další typ čekání, který je častou příčinou trhavých reakcí. Do té doby přejeme hodně štěstí při odstraňování problémů!