In de afgelopen 18 maanden heb ik me gefocust op reflexmatige reacties om statistische analyse en andere prestatie-afstemming gerelateerde onderwerpen af te wachten, en in dit bericht ga ik daar verder op in en bespreek de PAGELATCH_XX wacht. De XX aan het einde van de wachttijd betekent dat er meerdere soorten PAGELATCH . zijn wacht, en de meest voorkomende voorbeelden zijn:
- PAGELATCH_SH – ( SH zijn) wachten op toegang tot een gegevensbestand pagina in het geheugen zodat de pagina-inhoud kan worden gelezen
- PAGELATCH_EX of PAGELATCH_UP – (EX inclusief of UP datum) wachten op toegang tot een gegevensbestand pagina in het geheugen zodat de pagina-inhoud kan worden gewijzigd
Wanneer een van deze typen wacht het meest voorkomt op een server, is de reflexmatige reactie dat het probleem iets te maken heeft met I/O (d.w.z. verwarring met de PAGEIOLATCH_XX type wacht, dat ik in 2014 in een bericht heb behandeld) en iemand probeert meer geheugen toe te voegen of het I/O-subsysteem aan te passen. Geen van deze reacties zal enig effect hebben, aangezien de gegevensbestandpagina's die worden betwist zich al in het geheugen in de bufferpool bevinden!
In alle gevallen kun je zien of je een probleem hebt met PAGELATCH_XX stelling met behulp van de sys.dm_os_waiting_tasks script op mijn blog of met behulp van een tool zoals Performance Advisor, zoals gedemonstreerd (voor een ander type wacht) in dit bericht.
Dus wat is de bron van de stelling? Eerst zal ik de achtergrond van deze soorten wachten uitleggen, en daarna zal ik de twee meest voorkomende oorzaken van PAGELATCH_XX bespreken twist.
Achtergrond:vergrendelingen
Voordat ik inga op enkele van de oorzaken van PAGELATCH_XX wacht, ik wil uitleggen waarom ze bestaan.
In elk systeem met meerdere threads moeten gegevensstructuren die kunnen worden geopend en gemanipuleerd door meerdere threads worden beschermd om scenario's te voorkomen zoals:
- Twee threads die gelijktijdig een gegevensstructuur bijwerken, en sommige updates gaan verloren
- Een thread die een gegevensstructuur bijwerkt terwijl een andere thread de gegevensstructuur leest, zodat de leesthread een combinatie van oude en nieuwe gegevens ziet
Dit is elementaire informatica en SQL Server is niet anders, dus alle gegevensstructuren binnen SQL Server moeten toegangscontrole met meerdere threads hebben.
Een van de mechanismen die SQL Server gebruikt om dit te doen, wordt een vergrendeling genoemd, waarbij het vasthouden van de vergrendeling in de exclusieve modus voorkomt dat andere threads toegang krijgen tot de gegevensstructuur, en het vasthouden van de vergrendeling in de gedeelde modus voorkomt dat andere threads de gegevensstructuur wijzigen. SQL Server gebruikt ook spinlocks voor sommige gegevensstructuren en ik heb deze in 2014 in dit bericht besproken.
Maar waarom wordt een gegevensbestandpagina in het geheugen beschermd door een vergrendeling, vraagt u zich misschien af? Welnu, een gegevensbestandspagina is slechts een gegevensstructuur, zij het voor een speciaal doel, en heeft dus dezelfde toegangscontrole nodig als elke andere gegevensstructuur. Dus wanneer een thread een gegevensbestandspagina moet wijzigen, moet deze een exclusieve of update-vergrendeling op de pagina krijgen, en als deze niet kan en moet wachten, het wachttype PAGELATCH_EX of PAGELATCH_UP resultaten.
Klassieke tempdb-conflict
PAGELATCH conflict in tempdb is meestal op toewijzingsbitmaps en komt voor bij workloads met veel gelijktijdige verbindingen die kleine tijdelijke tabellen maken en laten vallen (die worden opgeslagen in tempdb).
Wanneer de eerste rij in een tijdelijke tabel wordt ingevoegd, moeten twee pagina's worden toegewezen (een gegevenspagina en een IAM-pagina, die de gegevenspagina volgt). Deze pagina's moeten worden gemarkeerd als toegewezen op een speciale toewijzingspagina, een PFS-pagina genaamd, en worden standaard toegewezen vanuit speciale gegevensbereiken die worden bijgehouden door een andere toewijzingspagina, een SGAM-pagina genaamd (details hiervan zijn te vinden in mijn oude blogpost hier). Wanneer de tijdelijke tabel wordt verwijderd, moeten deze pagina's opnieuw worden vrijgegeven, waardoor er meer wijzigingen in de PFS- en SGAM-pagina's nodig zijn.
Als de tijdelijke tabellen klein zijn en de cumulatieve grootte van alle gelijktijdig gemaakte tijdelijke tabellen kleiner is dan 64 MB, dan worden al deze bitmapwijzigingen voor de toewijzing gecentreerd op de allereerste PFS- en SGAM-pagina's in het tempdb-gegevensbestand (met pagina-ID (1:):1) en (1:3) respectievelijk). Het bijwerken van een van deze toewijzingspagina's vereist het vergrendelen van de pagina, en slechts één thread tegelijk kan de pagina wijzigen, dus alle andere threads moeten wachten - met wachttype PAGELATCH_UP .
Vanaf SQL Server 2005 kunnen tijdelijke tabellen worden opgeslagen in de cache wanneer ze worden verwijderd, zolang ze maar kleiner zijn dan 8 MB (en in SQL Server 2014 niet worden gemaakt in een opgeslagen procedure die ook DDL-instructies op de tijdelijke tabel heeft). Dit betekent dat de volgende thread die hetzelfde queryplan uitvoert, de tijdelijke tabel uit de cache kan halen en niet met de initiële toewijzingen hoeft te werken. Dit vermindert de strijd op de toewijzingsbitmaps, maar de tijdelijke tabelcache is niet erg groot, dus workloads met honderden gelijktijdige tijdelijke tabelcreaties/drops zullen nog steeds veel strijd opleveren.
Het is triviaal om de onenigheid op de SGAM-pagina's in tempdb te voorkomen door gedocumenteerde traceringsvlag 1118 op de server in te schakelen, waarvan ik zeg dat dit op alle servers over de hele wereld moet worden ingeschakeld, en is eigenlijk het onveranderlijke standaardgedrag in SQL Server 2016.
Het voorkomen van controverse op de PFS-pagina's in tempdb is iets moeilijker. Ervan uitgaande dat de tijdelijke tabellen nodig zijn voor prestaties, is de truc om meerdere gegevensbestanden voor tempdb te hebben, zodat de toewijzingen round-robin tussen de bestanden worden gedaan, de stelling wordt verdeeld over meerdere PFS-pagina's en dus de algemene stelling verdwijnt. Er is helaas geen juist antwoord op hoeveel databestanden je zou moeten hebben. U kunt meer lezen over de algemeen aanvaarde richtlijnen hierover in KB-artikel 2154845 en in deze blogpost.
Hotspot invoegen
In gebruikersdatabases, een veelvoorkomende oorzaak van een hoog aantal PAGELATCH_EX waits is een invoeghotspot.
Dit kan gebeuren wanneer een tabel een geclusterde index heeft met een int- of bigint-clustersleutel en een rijgrootte die klein genoeg is zodat vele tientallen of meer tabelrijen op een gegevenspagina op bladniveau van de geclusterde index passen.
Als voor een dergelijke tabel de werkbelasting vele tientallen of honderden gelijktijdige threads omvat die in de tabel worden ingevoegd, genereren veel van de threads rijen met identiteitswaarden (en dus clustersleutels) die op dezelfde gegevenspagina op leaf-niveau moeten worden ingevoegd .
Onthoud nu dat het maken van een wijziging aan een gegevensbestandpagina in het geheugen een exclusieve vergrendeling vereist, dus elk van de threads die op dezelfde pagina proberen in te voegen, moet exclusief de vergrendeling van de pagina krijgen. Terwijl elke thread de exclusieve vergrendeling vasthoudt, wachten de andere threads op PAGELATCH_EX voor die pagina, waardoor de gelijktijdige invoegingen in wezen een synchroon proces met enorme knelpunten worden.
Er zijn een paar mogelijke oplossingen voor dit probleem:
- Gebruik een meer willekeurige sleutel en besef dat dit zal leiden tot indexfragmentatie, dus maak ook gebruik van een indexvulfactor om paginasplitsingen te voorkomen
- Verspreid de inzetstukken in de tafel met behulp van een soort kunstmatig scheidingsmechanisme
- Gebruik een langere tabelrijgrootte (dit is duidelijk de minst smakelijke optie)
Ik heb een invoeg-hotspot zoals deze zien opduiken toen iemand probeerde indexfragmentatieproblemen op te lossen door een willekeurige GUID-clustersleutel te veranderen in een int- of bigint-identiteitsclustersleutel, maar het nieuwe tabelschema niet onder productiebelastingen testte.
Samenvatting
Net als bij andere soorten wachten, precies begrijpen wat PAGELATCH_XX waits mean is de sleutel om te begrijpen hoe u ze kunt oplossen.
Wat algemene wachtstatistieken betreft, kunt u meer informatie vinden over het gebruik ervan voor het oplossen van problemen met de prestaties in:
- Mijn serie SQLskills-blogposts, te beginnen met Wachtstatistieken, of vertel me alsjeblieft waar het pijn doet
- Mijn bibliotheek met wachttypes en vergrendelingsklassen hier
- Mijn online Pluralsight-trainingscursus SQL Server:prestatieproblemen oplossen met behulp van wachtstatistieken
- SQL Sentry Performance Advisor
Tot de volgende keer, veel plezier met het oplossen van problemen!