Om mijn korte serie artikelen over vergrendelingen af te ronden, ga ik deze keer een paar andere vergrendelingen in SQL Server bespreken die u af en toe tegenkomt, maar die op zichzelf geen volledig artikel verdienen. Zoals gewoonlijk raad ik je ten zeerste aan om de eerste post in de serie voor deze te lezen, zodat je alle algemene achtergrondkennis over vergrendelingen hebt.
De LOG_MANAGER-vergrendeling
De LOG_MANAGER-latch wordt gebruikt voor synchronisatie tijdens sommige bewerkingen waarbij het transactielogboek betrokken is, en er is één LOG_MANAGER-latch per database (omdat elke database zijn eigen logmanager heeft). Het kan alleen worden verkregen in de exclusieve modus en kan een knelpunt zijn tijdens de groei van transactielogboekbestanden. Het scenario waarin het als een probleem duidelijk wordt, is:
- Het logbestand heeft een kleine autogrowth-set
- Er zijn veel gelijktijdige verbindingen die transactielogboekrecords genereren
- Het logbestand moet steeds groter worden
Wanneer het logbestand onvoldoende ruimte heeft, moet het groeien. De eerste thread om te realiseren dat er meer logruimte nodig is, verwerft de LOG_MANAGER-grendel in EX-modus en gaat verder met het uitbreiden van het logbestand. Veel andere threads blijven proberen logrecords te genereren en in de wachtrij te komen voor de LOG_MANAGER-latch, zodat ze het logbestand kunnen laten groeien. Wanneer de eerste draad de grendel loslaat, krijgt de volgende deze en realiseert zich dat de stam al is gegroeid, dus laat hem vallen en gaat verder. En ga zo maar door. Overigens wordt dit knelpuntpatroon een latch-konvooi genoemd .
Je kunt het zien als precies hetzelfde knelpunt als bij de FGCB_ADD_REMOVE-grendel die ik eerder in de serie heb besproken, maar met groei van logbestanden in plaats van groei van gegevensbestanden. Met de FGCB_ADD_REMOVE-vergrendeling heeft de instantie echter meestal directe bestandsinitialisatie ingeschakeld, dus de bestandsgroei is erg snel, maar met de LOG_MANAGER-vergrendeling moet het logboek *moet* worden geïnitialiseerd op nul en de tijd die wordt verspild aan de vergrendelingswachtrij is langer .
De oplossing voor dit knelpunt bestaat uit drie delen:
- Stel het logbestand autogrowth correct in, zodat het log niet vaak groeit
- Grootte van het logboek correct voor de werkbelasting, zodat het logboek helemaal niet groeit
- Zorg ervoor dat het logboek correct wordt gewist, zodat het logboek niet hoeft te groeien
Als deze allemaal aanwezig zijn, zou je de LOG_MANAGER-vergrendeling niet als een normaal knelpunt moeten zien, en ik vertel hier meer over in mijn post hier.
De ACCESS_METHODS_DATASET_PARENT vergrendeling
Wanneer een heap of een index wordt geopend, is er intern een object met de naam HeapDataSetSession of IndexDataSetSession. Wanneer een parallelle scan wordt uitgevoerd, hebben de threads die het eigenlijke werk van de scan doen elk een "kind"-gegevensset (een ander exemplaar van de twee objecten die ik zojuist heb beschreven), en de hoofdgegevensset, die de scan echt bestuurt, wordt genoemd de "ouder".
Wanneer een van de scanwerkthreads de reeks rijen heeft uitgeput die het zou moeten scannen, moet het een nieuw bereik krijgen door toegang te krijgen tot de bovenliggende dataset, wat betekent dat de ACCESS_METHODS_DATASET_PARENT-vergrendeling in de exclusieve modus moet worden verkregen. Hoewel dit een knelpunt kan lijken, is het dat niet echt, en u kunt niets doen om te voorkomen dat de threads die een parallelle scan uitvoeren, af en toe een LATCH_EX laten zien die op deze vergrendeling wacht.
De vraag die je jezelf moet stellen is:moet deze query in de eerste plaats een parallelle scan uitvoeren? Het is heel goed mogelijk dat er iets is gebeurd waardoor het queryplan is gedwongen een parallelle scan op te nemen, terwijl dat misschien niet de meest efficiënte manier is om de query uit te voeren. Voorbeelden van zaken die ertoe kunnen leiden dat een plan verandert in een parallelle scan zijn:
- Verouderde statistieken
- Een ontbrekende of weggevallen niet-geclusterde index
- Nieuwe code dwingt een scan af vanwege een impliciete conversie - een gegevenstype dat niet overeenkomt tussen een kolom en een variabele/parameter, waardoor het gebruik van een niet-geclusterde index wordt uitgesloten
- Nieuwe code dwingt een scan af omdat rekenkunde wordt uitgevoerd op een tabelkolom in plaats van een variabele/parameter, wat wederom het gebruik van een niet-geclusterde index uitsluit
- Er vindt datagroei plaats en een scan is echt het meest efficiënte plan
Of het kan zijn dat deze zoekopdracht een scan vereist, in welk geval LATCH_EX wacht tot ACCESS_METHODS_DATASET_PARENT slechts een deel van uw omgeving is.
De ACCESS_METHODS_HOBT_VIRTUAL_ROOT-vergrendeling
Elke instantie van deze vergrendeling beschermt een item in de Storage Engine-metadata voor een b-tree, met name de pagina-ID van de hoofdpagina van de b-tree (de pagina bovenaan de driehoek die we over het algemeen beschouwen als een index) . Ik zeg specifiek b-tree en niet index , aangezien een index meerdere partities kan hebben, die elk een b-tree hebben (in wezen een deel van de algemene index, maar met sleutelbeperkingen van lage waarde en hoge waarde).
Elke keer dat een thread een b-tree moet doorlopen, moet deze beginnen bij de hoofdpagina en zich een weg banen naar het bladniveau. Om de metadata met de pagina-ID van de hoofdpagina te lezen, moet de thread de ACCESS_METHODS_HOBT_VIRTUAL_ROOT-vergrendeling in SH-modus verwerven om ervoor te zorgen dat de pagina-ID niet wordt gewijzigd. Wanneer een thread de pagina-ID van de hoofdpagina moet wijzigen, moet deze de vergrendeling verkrijgen in de EX-modus.
Waarom zou de hoofdpagina van een b-tree ooit veranderen? Naarmate het aantal indexrecords op de hoofdpagina groeit, zal deze uiteindelijk vol raken en zal er een paginasplitsing plaatsvinden. Wanneer dat gebeurt, worden de huidige hoofdpagina en de pagina waarin deze wordt opgesplitst een nieuw niveau in de b-tree, en wordt een geheel nieuwe hoofdpagina gemaakt, met twee indexrecords erin, wijzend naar de oude hoofdpagina en de pagina die het splitsen in. De nieuwe rootpagina-ID moet in metadata worden ingevoerd, zodat de vergrendeling in EX-modus wordt verkregen. Dit zal een paar keer snel gebeuren omdat een index op een lege tabel gevuld begint te worden met invoegingen, maar dit is niet iets dat je zult zien als een voortdurend prestatieprobleem.
Samenvatting
Zoals u ongetwijfeld uit deze serie hebt opgemaakt, betekent het begrijpen van vergrendelingen en bottlenecks van vergrendelingen dat u iets meer moet weten over wat er in de Storage Engine gebeurt dan voor de analyse van algemene wachtstatistieken.
Ik adviseer mensen meestal *niet* om prestatieproblemen op te lossen door naar vergrendelingsstatistieken te kijken (via sys.dm_os_latch_stats ) maar om in plaats daarvan altijd te beginnen met wachtstatistieken (zie mijn bericht hier) en alleen in grendels te duiken als LATCH_EX of LATCH_SH een van de tophandjes is van wachttijden op de SQL Server-instantie.
Als je vragen hebt over vergrendelingen, neem dan gerust contact met me op.