Impasses in Azure SQL Database analyseren en voorkomen
Van toepassing op: Azure SQL Database
In dit artikel wordt beschreven hoe u impasses in Azure SQL Database kunt identificeren, impassegrafieken en Query Store kunt gebruiken om de query's in de impasse te identificeren en wijzigingen te plannen en te testen om te voorkomen dat impasses zich opnieuw voordoen.
Dit artikel is gericht op het identificeren en analyseren van impasses vanwege conflicten met vergrendelingen. Meer informatie over andere soorten impasses in resources die een impasse kunnen vormen.
Hoe impasses optreden in Azure SQL Database
Voor elke nieuwe database in Azure SQL Database is de standaardinstelling voor het lezen vastgelegde momentopname (RCSI) ingeschakeld. Blokkeren tussen sessies die gegevens lezen en sessies die gegevens schrijven, wordt geminimaliseerd onder RCSI, waarbij gebruik wordt gemaakt van rijversiebeheer om de gelijktijdigheid te verhogen. Blokkeringen en impasses kunnen echter nog steeds optreden in databases in Azure SQL Database, omdat:
- Query's die gegevens wijzigen, kunnen elkaar blokkeren.
- Query's kunnen worden uitgevoerd onder isolatieniveaus die de blokkering verhogen. Isolatieniveaus kunnen worden opgegeven via clientbibliotheekmethoden, queryhints of SET-instructies in Transact-SQL.
- RCSI kan worden uitgeschakeld, waardoor de database gedeelde (S)-vergrendelingen gebruikt om SELECT-instructies te beveiligen die worden uitgevoerd op het niveau van de vastgelegde isolatie voor lezen. Dit kan leiden tot blokkeringen en impasses.
Een voorbeeld van een impasse
Er treedt een impasse op wanneer twee of meer taken elkaar permanent blokkeren, omdat elke taak een vergrendeling heeft op een resource die de andere taak probeert te vergrendelen. Een impasse wordt ook wel een cyclische afhankelijkheid genoemd: in het geval van een impasse met twee taken heeft transactie A een afhankelijkheid van transactie B en transactie B sluit de cirkel door een afhankelijkheid van transactie A te hebben.
Bijvoorbeeld:
- Sessie A begint een expliciete transactie en voert een update-instructie uit waarmee een updatevergrendeling (U) wordt verkregen op één rij in de tabel
SalesLT.Product
die wordt geconverteerd naar een exclusieve (X)-vergrendeling. - Sessie B voert een update-instructie uit waarmee de
SalesLT.ProductDescription
tabel wordt gewijzigd. De update-instructie wordt samengevoegd met deSalesLT.Product
tabel om de juiste rijen te vinden die moeten worden bijgewerkt.- Sessie B verkrijgt een updatevergrendeling (U) op 72 rijen in de
SalesLT.ProductDescription
tabel. - Sessie B heeft een gedeelde vergrendeling nodig voor rijen in de tabel
SalesLT.Product
, inclusief de rij die is vergrendeld door sessie A. Sessie B wordt geblokkeerd opSalesLT.Product
.
- Sessie B verkrijgt een updatevergrendeling (U) op 72 rijen in de
- Sessie A gaat verder met de transactie en voert nu een update uit voor de
SalesLT.ProductDescription
tabel. Sessie A wordt geblokkeerd door sessie B opSalesLT.ProductDescription
.
Alle transacties in een impasse wachten voor onbepaalde tijd, tenzij een van de deelnemende transacties wordt teruggedraaid, bijvoorbeeld omdat de sessie is beëindigd.
De impasse-monitor van de database-engine controleert periodiek op taken die zich in een impasse bevinden. Als de impassemonitor een cyclische afhankelijkheid detecteert, kiest deze een van de taken als slachtoffer en beëindigt de transactie met fout 1205, 'Transactie (proces-id N) is impasse op vergrendelingsbronnen met een ander proces en is gekozen als het impasseslachtoffer. Voer de transactie opnieuw uit. Door de impasse op deze manier te verbreken, kunnen de andere taken of taken in de impasse hun transacties voltooien.
Notitie
Meer informatie over de criteria voor het kiezen van een impasseslachtoffer in de lijst met impasses in dit artikel.
De toepassing met de transactie die als het slachtoffer van de impasse is gekozen, moet de transactie opnieuw proberen, die meestal wordt voltooid nadat de andere transactie of transacties die betrokken zijn bij de impasse zijn voltooid.
Het is een best practice om een korte, gerandomiseerde vertraging te introduceren voordat u opnieuw probeert te voorkomen dat dezelfde impasse opnieuw optreedt. Meer informatie over het ontwerpen van logica voor opnieuw proberen voor tijdelijke fouten.
Standaardisolatieniveau in Azure SQL Database
Voor nieuwe databases in Azure SQL Database is standaard rcsi (read committed snapshot) ingeschakeld. RCSI wijzigt het gedrag van het vastgelegde isolatieniveau voor lezen om rijversiebeheer te gebruiken om consistentie op instructieniveau te bieden zonder gebruik te maken van gedeelde (S)-vergrendelingen voor SELECT-instructies.
Met RCSI ingeschakeld:
- Instructies die gegevens lezen, blokkeren geen instructies voor het wijzigen van gegevens.
- Instructies die gegevens wijzigen, blokkeren geen instructies voor het lezen van gegevens.
Isolatieniveau voor momentopnamen is ook standaard ingeschakeld voor nieuwe databases in Azure SQL Database. Isolatie van momentopnamen is een extra isolatieniveau op basis van rijen dat consistentie op transactieniveau biedt voor gegevens en waarbij rijversies worden gebruikt om rijen te selecteren die moeten worden bijgewerkt. Als u isolatie van momentopnamen wilt gebruiken, moeten query's of verbindingen hun transactieisolatieniveau expliciet instellen op SNAPSHOT
. Dit kan alleen worden gedaan wanneer isolatie van momentopnamen is ingeschakeld voor de database.
U kunt bepalen of RCSI en/of momentopname-isolatie zijn ingeschakeld met Transact-SQL. Verbinding maken naar uw database in Azure SQL Database en voer de volgende query uit:
SELECT name, is_read_committed_snapshot_on, snapshot_isolation_state_desc
FROM sys.databases
WHERE name = DB_NAME();
GO
Als RCSI is ingeschakeld, retourneert de is_read_committed_snapshot_on
kolom de waarde 1. Als isolatie van momentopnamen is ingeschakeld, retourneert de snapshot_isolation_state_desc
kolom de waarde AAN.
Als RCSI is uitgeschakeld voor een database in Azure SQL Database, onderzoekt u waarom RCSI is uitgeschakeld voordat u deze opnieuw inschakelt. Toepassingscode is mogelijk geschreven waarin wordt verwacht dat query's die gegevens lezen, worden geblokkeerd door query's die gegevens schrijven, wat resulteert in onjuiste resultaten van racevoorwaarden wanneer RCSI is ingeschakeld.
Impasse-gebeurtenissen interpreteren
Er wordt een impasse-gebeurtenis verzonden nadat de impassemanager in Azure SQL Database een impasse detecteert en een transactie als slachtoffer selecteert. Met andere woorden, als u waarschuwingen instelt voor impasses, wordt de melding geactiveerd nadat een afzonderlijke impasse is opgelost. Er is geen actie van de gebruiker die moet worden uitgevoerd voor die impasse. Toepassingen moeten worden geschreven om logica voor opnieuw proberen op te nemen, zodat ze automatisch doorgaan na ontvangst van fout 1205, 'Transactie (proces-id N) was impasse op vergrendelingsresources met een ander proces en is gekozen als het impasseslachtoffer. Voer de transactie opnieuw uit.
Het is echter handig om waarschuwingen in te stellen, omdat er mogelijk impasses optreden. Met impassewaarschuwingen kunt u onderzoeken of er een patroon van herhalende impasses in uw database plaatsvindt. In dat geval kunt u ervoor kiezen om actie te ondernemen om te voorkomen dat impasses opnieuw optreden. Meer informatie over waarschuwingen in de sectie Bewaking en waarschuwingen over impasses van dit artikel.
Belangrijkste methoden om impasses te voorkomen
De laagste risicobenadering om te voorkomen dat impasses opnieuw optreden, is over het algemeen om niet-geclusterde indexen af te stemmen om query's te optimaliseren die betrokken zijn bij de impasse.
- Het risico is laag voor deze benadering omdat het afstemmen van niet-geclusterde indexen geen wijzigingen in de querycode zelf vereist, waardoor het risico van een gebruikersfout wordt verminderd bij het herschrijven van Transact-SQL, waardoor onjuiste gegevens naar de gebruiker worden geretourneerd.
- Effectieve niet-geclusterde indexafstemming helpt query's de gegevens te vinden die efficiënter kunnen worden gelezen en gewijzigd. Door de hoeveelheid gegevens te verminderen waartoe een query toegang nodig heeft, wordt de kans op blokkeren verminderd en kunnen impasses vaak worden voorkomen.
In sommige gevallen kan het maken of afstemmen van een geclusterde index blokkeren en impasses verminderen. Omdat de geclusterde index is opgenomen in alle niet-geclusterde indexdefinities, kan het maken of wijzigen van een geclusterde index een I/O-intensieve en tijdrovende bewerking zijn voor grotere tabellen met bestaande niet-geclusterde indexen. Meer informatie over ontwerprichtlijnen voor geclusterde indexen.
Wanneer het afstemmen van indexen niet lukt om impasses te voorkomen, zijn er andere methoden beschikbaar:
- Als de impasse alleen optreedt wanneer een bepaald plan wordt gekozen voor een van de query's die betrokken zijn bij de impasse, kan het afdwingen van een queryplan met Query Store verhinderen dat impasses opnieuw optreden.
- Het herschrijven van Transact-SQL voor een of meer transacties die zijn betrokken bij de impasse, kunnen ook helpen impasses te voorkomen. Het splitsen van expliciete transacties in kleinere transacties vereist zorgvuldig coderen en testen om ervoor te zorgen dat gegevens geldig zijn wanneer gelijktijdige wijzigingen plaatsvinden.
Meer informatie over elk van deze benaderingen vindt u in de sectie Voorkomen dat er een impasse optreedt in dit artikel.
Impasses bewaken en waarschuwen
In dit artikel gebruiken we de AdventureWorksLT
voorbeelddatabase om waarschuwingen in te stellen voor impasses, een voorbeeld van een impasse te veroorzaken, de impassegrafiek voor het voorbeeld van een impasse te analyseren en wijzigingen te testen om te voorkomen dat de impasse opnieuw optreedt.
In dit artikel gebruiken we de SSMS-client (SQL Server Management Studio), omdat deze functionaliteit bevat voor het weergeven van impassegrafieken in een interactieve visuele modus. U kunt andere clients zoals Azure Data Studio gebruiken om de voorbeelden te volgen, maar u kunt mogelijk alleen impassegrafieken weergeven als XML.
De AdventureWorksLT-database maken
Als u de voorbeelden wilt volgen, maakt u een nieuwe database in Azure SQL Database en selecteert u Voorbeeldgegevens als de gegevensbron.
Voor gedetailleerde instructies over het maken AdventureWorksLT
met Azure Portal, Azure CLI of PowerShell selecteert u de methode van uw keuze in quickstart: Een individuele Azure SQL Database-database maken.
Impassewaarschuwingen instellen in Azure Portal
Als u waarschuwingen wilt instellen voor impasse-gebeurtenissen, volgt u de stappen in het artikel Waarschuwingen maken voor Azure SQL Database en Azure Synapse Analytics met behulp van Azure Portal.
Selecteer Impasses als de signaalnaam voor de waarschuwing. Configureer de actiegroep om u op de hoogte te stellen met behulp van de methode van uw keuze, zoals het actietype E-mail/SMS/Push/Spraak .
Impassegrafieken verzamelen in Azure SQL Database met uitgebreide gebeurtenissen
Impassegrafieken zijn een rijke bron van informatie over de processen en vergrendelingen die betrokken zijn bij een impasse. Als u impassegrafieken wilt verzamelen met uitgebreide gebeurtenissen (XEvents) in Azure SQL Database, legt u de sqlserver.database_xml_deadlock_report
gebeurtenis vast.
U kunt impassegrafieken met XEvents verzamelen met behulp van het ringbufferdoel of een doel voor een gebeurtenisbestand. Overwegingen voor het selecteren van het juiste doeltype worden samengevat in de volgende tabel:
Methode | Vergoedingen | Overwegingen | Gebruiksscenario's |
---|---|---|---|
Ringbufferdoel |
|
|
|
Doel van gebeurtenisbestand |
|
|
|
Selecteer het doeltype dat u wilt gebruiken:
Het ringbufferdoel is handig en eenvoudig in te stellen, maar heeft een beperkte capaciteit, waardoor oudere gebeurtenissen verloren kunnen gaan. De ringbuffer bewaart geen gebeurtenissen voor opslag en het ringbufferdoel wordt gewist wanneer de XEvents-sessie wordt gestopt. Dit betekent dat alle verzamelde XEvents niet beschikbaar zijn wanneer de database-engine om welke reden dan ook opnieuw wordt opgestart, zoals een failover. Het ringbufferdoel is het meest geschikt voor leer- en kortetermijnbehoeften als u niet de mogelijkheid hebt om een XEvents-sessie onmiddellijk in te stellen op een gebeurtenisbestandsdoel.
Met deze voorbeeldcode maakt u een XEvents-sessie die impassegrafieken in het geheugen vastlegt met behulp van het ringbufferdoel. Het maximale geheugen dat is toegestaan voor het ringbufferdoel is 4 MB en de sessie wordt automatisch uitgevoerd wanneer de database online komt, bijvoorbeeld na een failover.
Als u een XEvents-sessie wilt maken en starten voor de sqlserver.database_xml_deadlock_report
gebeurtenis die naar het ringbufferdoel schrijft, maakt u verbinding met uw database en voert u de volgende Transact-SQL uit:
CREATE EVENT SESSION [deadlocks] ON DATABASE
ADD EVENT sqlserver.database_xml_deadlock_report
ADD TARGET package0.ring_buffer
WITH (STARTUP_STATE=ON, MAX_MEMORY=4 MB)
GO
ALTER EVENT SESSION [deadlocks] ON DATABASE
STATE = START;
GO
Een impasse veroorzaken in AdventureWorksLT
Notitie
Dit voorbeeld werkt in de AdventureWorksLT
database met het standaardschema en de standaardgegevens wanneer RCSI is ingeschakeld. Zie De AdventureWorksLT-database maken voor instructies voor het maken van de database.
Als u een impasse wilt veroorzaken, moet u twee sessies verbinden met de AdventureWorksLT
database. We verwijzen naar deze sessies als sessie A en sessie B.
Voer in sessie A de volgende Transact-SQL uit. Deze code begint met een expliciete transactie en voert één instructie uit waarmee de SalesLT.Product
tabel wordt bijgewerkt. Hiervoor verkrijgt de transactie een updatevergrendeling (U) op één rij in een tabel SalesLT.Product
die wordt geconverteerd naar een exclusieve (X)-vergrendeling. We laten de transactie open.
BEGIN TRAN
UPDATE SalesLT.Product SET SellEndDate = SellEndDate + 1
WHERE Color = 'Red';
Voer nu in sessie B de volgende Transact-SQL uit. Deze code begint niet expliciet met een transactie. In plaats daarvan werkt het in de automatische transactiemodus. Met deze instructie wordt de SalesLT.ProductDescription
tabel bijgewerkt. Met de update wordt een updatevergrendeling (U) uitgeschakeld op 72 rijen in de SalesLT.ProductDescription
tabel. De query wordt samengevoegd met andere tabellen, inclusief de SalesLT.Product
tabel.
UPDATE SalesLT.ProductDescription SET Description = Description
FROM SalesLT.ProductDescription as pd
JOIN SalesLT.ProductModelProductDescription as pmpd on
pd.ProductDescriptionID = pmpd.ProductDescriptionID
JOIN SalesLT.ProductModel as pm on
pmpd.ProductModelID = pm.ProductModelID
JOIN SalesLT.Product as p on
pm.ProductModelID=p.ProductModelID
WHERE p.Color = 'Silver';
Om deze update te voltooien, heeft Sessie B een gedeelde vergrendeling (S) nodig voor rijen in de tabel SalesLT.Product
, inclusief de rij die is vergrendeld door Sessie A. Sessie B wordt geblokkeerd op SalesLT.Product
.
Ga terug naar sessie A. Voer de volgende Transact-SQL-instructie uit. Hiermee wordt een tweede UPDATE-instructie uitgevoerd als onderdeel van de open transactie.
UPDATE SalesLT.ProductDescription SET Description = Description
FROM SalesLT.ProductDescription as pd
JOIN SalesLT.ProductModelProductDescription as pmpd on
pd.ProductDescriptionID = pmpd.ProductDescriptionID
JOIN SalesLT.ProductModel as pm on
pmpd.ProductModelID = pm.ProductModelID
JOIN SalesLT.Product as p on
pm.ProductModelID=p.ProductModelID
WHERE p.Color = 'Red';
De tweede update-instructie in sessie A wordt geblokkeerd door sessie B op de SalesLT.ProductDescription
.
Sessie A en sessie B blokkeren elkaar nu wederzijds. Geen van beide transacties kan doorgaan, omdat ze elk een resource nodig hebben die door de andere is vergrendeld.
Na een paar seconden identificeert de impassemonitor dat de transacties in sessie A en sessie B elkaar wederzijds blokkeren en dat geen van beide vooruitgang kan boeken. U ziet dat er een impasse optreedt, met Sessie A gekozen als het impasseslachtoffer. Er wordt een foutbericht weergegeven in sessie A met tekst die er ongeveer als volgt uitziet:
Msg 1205, Niveau 13, State 51, Regel 7 Transactie (Proces-id 91) was impasse op vergrendelingsbronnen met een ander proces en is gekozen als het impasseslachtoffer. Voer de transactie opnieuw uit.
Sessie B wordt voltooid.
Als u impassewaarschuwingen instelt in Azure Portal, ontvangt u kort nadat de impasse is opgetreden een melding.
Impassegrafieken van een XEvents-sessie weergeven
Als u een XEvents-sessie hebt ingesteld om impasses te verzamelen en er een impasse is opgetreden nadat de sessie is gestart, kunt u een interactieve grafische weergave van de impassegrafiek en de XML voor de impassegrafiek bekijken.
Er zijn verschillende methoden beschikbaar om impassegegevens te verkrijgen voor het ringbufferdoel en gebeurtenisbestandsdoelen. Selecteer het doel dat u hebt gebruikt voor uw XEvents-sessie:
Als u een XEvents-sessie instelt die naar de ringbuffer wordt geschreven, kunt u impassegegevens opvragen met de volgende Transact-SQL. Voordat u de query uitvoert, vervangt u de waarde door @tracename
de naam van uw xEvents-sessie.
DECLARE @tracename sysname = N'deadlocks';
WITH ring_buffer AS (
SELECT CAST(target_data AS XML) as rb
FROM sys.dm_xe_database_sessions AS s
JOIN sys.dm_xe_database_session_targets AS t
ON CAST(t.event_session_address AS BINARY(8)) = CAST(s.address AS BINARY(8))
WHERE s.name = @tracename and
t.target_name = N'ring_buffer'
), dx AS (
SELECT
dxdr.evtdata.query('.') as deadlock_xml_deadlock_report
FROM ring_buffer
CROSS APPLY rb.nodes('/RingBufferTarget/event[@name=''database_xml_deadlock_report'']') AS dxdr(evtdata)
)
SELECT
d.query('/event/data[@name=''deadlock_cycle_id'']/value').value('(/value)[1]', 'int') AS [deadlock_cycle_id],
d.value('(/event/@timestamp)[1]', 'DateTime2') AS [deadlock_timestamp],
d.query('/event/data[@name=''database_name'']/value').value('(/value)[1]', 'nvarchar(256)') AS [database_name],
d.query('/event/data[@name=''xml_report'']/value/deadlock') AS deadlock_xml,
LTRIM(RTRIM(REPLACE(REPLACE(d.value('.', 'nvarchar(2000)'),CHAR(10),' '),CHAR(13),' '))) as query_text
FROM dx
CROSS APPLY deadlock_xml_deadlock_report.nodes('(/event/data/value/deadlock/process-list/process/inputbuf)') AS ib(d)
ORDER BY [deadlock_timestamp] DESC;
GO
Een impassegrafiek weergeven en opslaan in XML
Door een impassegrafiek in XML-indeling te bekijken, kunt u de inputbuffer
Transact-SQL-instructies kopiëren die betrokken zijn bij de impasse. U kunt ook liever impasses analyseren in een op tekst gebaseerde indeling.
Als u een Transact-SQL-query hebt gebruikt om impassegrafiekgegevens te retourneren, selecteert u de waarde in de kolom in een deadlock_xml
willekeurige rij om de XML van de impassegrafiek in een nieuw venster in SSMS te openen.
De XML voor deze voorbeeld impassegrafiek is:
<deadlock>
<victim-list>
<victimProcess id="process24756e75088" />
</victim-list>
<process-list>
<process id="process24756e75088" taskpriority="0" logused="6528" waitresource="KEY: 8:72057594045202432 (98ec012aa510)" waittime="192" ownerId="1011123" transactionname="user_transaction" lasttranstarted="2022-03-08T15:44:43.490" XDES="0x2475c980428" lockMode="U" schedulerid="3" kpid="30192" status="suspended" spid="89" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2022-03-08T15:44:49.250" lastbatchcompleted="2022-03-08T15:44:49.210" lastattention="1900-01-01T00:00:00.210" clientapp="Microsoft SQL Server Management Studio - Query" hostname="LAPTOP-CHRISQ" hostpid="16716" loginname="chrisqpublic" isolationlevel="read committed (2)" xactid="1011123" currentdb="8" currentdbname="AdventureWorksLT" lockTimeout="4294967295" clientoption1="671096864" clientoption2="128056">
<executionStack>
<frame procname="unknown" queryhash="0xef52b103e8b9b8ca" queryplanhash="0x02b0f58d7730f798" line="1" stmtstart="2" stmtend="792" sqlhandle="0x02000000c58b8f1e24e8f104a930776e21254b1771f92a520000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
UPDATE SalesLT.ProductDescription SET Description = Description
FROM SalesLT.ProductDescription as pd
JOIN SalesLT.ProductModelProductDescription as pmpd on
pd.ProductDescriptionID = pmpd.ProductDescriptionID
JOIN SalesLT.ProductModel as pm on
pmpd.ProductModelID = pm.ProductModelID
JOIN SalesLT.Product as p on
pm.ProductModelID=p.ProductModelID
WHERE p.Color = 'Red' </inputbuf>
</process>
<process id="process2476d07d088" taskpriority="0" logused="11360" waitresource="KEY: 8:72057594045267968 (39e18040972e)" waittime="2641" ownerId="1013536" transactionname="UPDATE" lasttranstarted="2022-03-08T15:44:46.807" XDES="0x2475ca80428" lockMode="S" schedulerid="2" kpid="94040" status="suspended" spid="95" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2022-03-08T15:44:46.807" lastbatchcompleted="2022-03-08T15:44:46.760" lastattention="1900-01-01T00:00:00.760" clientapp="Microsoft SQL Server Management Studio - Query" hostname="LAPTOP-CHRISQ" hostpid="16716" loginname="chrisqpublic" isolationlevel="read committed (2)" xactid="1013536" currentdb="8" currentdbname="AdventureWorksLT" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="unknown" queryhash="0xef52b103e8b9b8ca" queryplanhash="0x02b0f58d7730f798" line="1" stmtstart="2" stmtend="798" sqlhandle="0x020000002c85bb06327c0852c0be840fc1e30efce2b7c8090000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
UPDATE SalesLT.ProductDescription SET Description = Description
FROM SalesLT.ProductDescription as pd
JOIN SalesLT.ProductModelProductDescription as pmpd on
pd.ProductDescriptionID = pmpd.ProductDescriptionID
JOIN SalesLT.ProductModel as pm on
pmpd.ProductModelID = pm.ProductModelID
JOIN SalesLT.Product as p on
pm.ProductModelID=p.ProductModelID
WHERE p.Color = 'Silver'; </inputbuf>
</process>
</process-list>
<resource-list>
<keylock hobtid="72057594045202432" dbid="8" objectname="9e011567-2446-4213-9617-bad2624ccc30.SalesLT.ProductDescription" indexname="PK_ProductDescription_ProductDescriptionID" id="lock2474df12080" mode="U" associatedObjectId="72057594045202432">
<owner-list>
<owner id="process2476d07d088" mode="U" />
</owner-list>
<waiter-list>
<waiter id="process24756e75088" mode="U" requestType="wait" />
</waiter-list>
</keylock>
<keylock hobtid="72057594045267968" dbid="8" objectname="9e011567-2446-4213-9617-bad2624ccc30.SalesLT.Product" indexname="PK_Product_ProductID" id="lock2474b588580" mode="X" associatedObjectId="72057594045267968">
<owner-list>
<owner id="process24756e75088" mode="X" />
</owner-list>
<waiter-list>
<waiter id="process2476d07d088" mode="S" requestType="wait" />
</waiter-list>
</keylock>
</resource-list>
</deadlock>
De impassegrafiek opslaan als een XML-bestand:
- Selecteer Bestand en Opslaan als....
- Laat de waarde Opslaan als de standaard-XML-bestanden (*.xml) staan
- Stel de bestandsnaam in op de naam van uw keuze.
- Selecteer Opslaan.
Een impassegrafiek opslaan als een XDL-bestand dat interactief kan worden weergegeven in SSMS
Het weergeven van een interactieve weergave van een impassegrafiek kan handig zijn om een kort overzicht te krijgen van de processen en resources die betrokken zijn bij een impasse en het snel identificeren van het impasseslachtoffer.
Een impassegrafiek opslaan als een bestand dat grafisch kan worden weergegeven door SSMS:
Selecteer de waarde in de
deadlock_xml
kolom in een willekeurige rij om de XML van de impassegrafiek te openen in een nieuw venster in SSMS.Selecteer Bestand en Opslaan als....
Stel Opslaan als type in op Alle bestanden.
Stel de bestandsnaam in op de naam van uw keuze, waarbij de extensie is ingesteld op .xdl.
Selecteer Opslaan.
Sluit het bestand door de X te selecteren op het tabblad boven aan het venster of door Bestand te selecteren en vervolgens Sluiten.
Open het bestand opnieuw in SSMS door Bestand te selecteren en vervolgens Bestand te openen en vervolgens Bestand. Selecteer het bestand dat u hebt opgeslagen met de
.xdl
extensie.De impassegrafiek wordt nu weergegeven in SSMS met een visuele weergave van de processen en resources die betrokken zijn bij de impasse.
Een impasse analyseren voor Azure SQL Database
Een impassegrafiek heeft doorgaans drie knooppunten:
- Slachtofferslijst. De proces-id van het slachtoffer van de impasse.
- Proceslijst. Informatie over alle processen die betrokken zijn bij de impasse. Impassegrafieken gebruiken de term 'proces' om een sessie weer te geven waarop een transactie wordt uitgevoerd.
- Resourcelijst. Informatie over de resources die betrokken zijn bij de impasse.
Bij het analyseren van een impasse is het handig om deze knooppunten te doorlopen.
Lijst met slachtoffers van impasses
De lijst met impasseslachtoffers toont het proces dat is gekozen als het impasseslachtoffer. In de visuele weergave van een impassegrafiek worden processen weergegeven door ovalen. Het impasseslachtofferproces heeft een "X" getekend over het ovaal.
In de XML-weergave van een impassegrafiek geeft het victim-list
knooppunt een id voor het proces dat het slachtoffer was van de impasse.
In ons voorbeeld is de proces-id van het slachtoffer proces24756e75088. We kunnen deze id gebruiken bij het onderzoeken van de proceslijst- en resourcelijstknooppunten voor meer informatie over het slachtofferproces en de resources die het vergrendelen of aanvragen om te vergrendelen.
Lijst met impasseprocessen
De lijst met impasseprocessen is een uitgebreide bron van informatie over de transacties die betrokken zijn bij de impasse.
In de grafische weergave van de impassegrafiek ziet u slechts een subset met informatie in de XML van de impassegrafiek. De ovalen in de impassegrafiek vertegenwoordigen het proces en geven informatie weer, waaronder:
Serverproces-id, ook wel de sessie-id of SPID genoemd.
Impasseprioriteit van de sessie. Als twee sessies verschillende impasseprioriteiten hebben, wordt de sessie met de lagere prioriteit gekozen als het impasseslachtoffer. In dit voorbeeld hebben beide sessies dezelfde impasseprioriteit.
De hoeveelheid transactielogboek die door de sessie in bytes wordt gebruikt. Als beide sessies dezelfde impasseprioriteit hebben, kiest de impassemonitor de sessie die minder duur is om terug te draaien als het impasseslachtoffer. De kosten worden bepaald door het aantal logboekbytes te vergelijken dat naar dat punt in elke transactie is geschreven.
In ons voorbeeld was session_id 89 een lager transactielogboek gebruikt en werd geselecteerd als het impasseslachtoffer.
Daarnaast kunt u de invoerbuffer voor de laatste instructie bekijken die in elke sessie vóór de impasse wordt uitgevoerd door de muisaanwijzer over elk proces te bewegen. De invoerbuffer wordt weergegeven in knopinfo.
Aanvullende informatie is beschikbaar voor processen in de XML-weergave van de impassegrafiek, waaronder:
- Identificatiegegevens voor de sessie, zoals de clientnaam, hostnaam en aanmeldingsnaam.
- De hash van het queryplan voor de laatste instructie die wordt uitgevoerd door elke sessie voorafgaand aan de impasse. De hash van het queryplan is handig voor het ophalen van meer informatie over de query uit Query Store.
In ons voorbeeld impasse:
- We kunnen zien dat beide sessies werden uitgevoerd met behulp van de SSMS-client onder de chrisqpublic-aanmelding .
- De hash van het queryplan van de laatste instructie wordt uitgevoerd vóór de impasse door ons impasseslachtoffer is 0x02b0f58d7730f798. De tekst van deze instructie wordt weergegeven in de invoerbuffer.
- De hash van het queryplan van de laatste instructie die door de andere sessie in onze impasse wordt uitgevoerd, is ook 0x02b0f58d7730f798. De tekst van deze instructie wordt weergegeven in de invoerbuffer. In dit geval hebben beide query's dezelfde hash van het queryplan, omdat de query's identiek zijn, met uitzondering van een letterlijke waarde die wordt gebruikt als gelijkheidspredicaat.
Verderop in dit artikel gebruiken we deze waarden om aanvullende informatie te vinden in Query Store.
Beperkingen van de invoerbuffer in de lijst met impasseprocessen
Er zijn enkele beperkingen om rekening mee te houden met betrekking tot informatie over invoerbuffers in de lijst met impasseprocessen.
Querytekst kan worden afgekapt in de invoerbuffer. De invoerbuffer is beperkt tot de eerste 4000 tekens van de instructie die wordt uitgevoerd.
Daarnaast zijn sommige verklaringen die betrokken zijn bij de impasse mogelijk niet opgenomen in de impassegrafiek. In ons voorbeeld heeft Sessie A twee update-instructies uitgevoerd binnen één transactie. Alleen de tweede update-instructie, de update die de impasse heeft veroorzaakt, wordt opgenomen in de impassegrafiek. De eerste update-instructie die door Sessie A wordt uitgevoerd, speelde een rol in de impasse door sessie B te blokkeren. De invoerbuffer query_hash
en gerelateerde informatie voor de eerste instructie die wordt uitgevoerd door Session A, is niet opgenomen in de impassegrafiek.
Als u de volledige Transact-SQL-uitvoering wilt identificeren in een transactie met meerdere instructies die betrokken zijn bij een impasse, moet u de relevante informatie vinden in de opgeslagen procedure of toepassingscode die de query heeft uitgevoerd, of een tracering uitvoeren met behulp van uitgebreide gebeurtenissen om volledige instructies vast te leggen die worden uitgevoerd door sessies die betrokken zijn bij een impasse terwijl deze zich voordoet. Als een instructie die is betrokken bij de impasse is afgekapt en alleen gedeeltelijk Transact-SQL wordt weergegeven in de invoerbuffer, kunt u de Transact-SQL vinden voor de instructie in Query Store met het uitvoeringsplan.
Lijst met impasseresources
In de lijst met impasseresources ziet u welke vergrendelingsresources eigendom zijn van en worden gewacht door de processen in de impasse.
Resources worden weergegeven door rechthoeken in de visuele weergave van de impasse:
Notitie
U ziet mogelijk dat databasenamen worden weergegeven als uniekedientifers in impassegrafieken voor databases in Azure SQL Database. Dit is de physical_database_name
database die wordt vermeld in de sys.databases en sys.dm_user_db_resource_governance dynamische beheerweergaven.
In dit voorbeeld impasse:
Het impasseslachtoffer, dat we sessie A noemen:
- Bezit een exclusieve (X)-vergrendeling op een sleutel in de
PK_Product_ProductID
index in deSalesLT.Product
tabel. - Vraagt een updatevergrendeling (U) aan op een sleutel in de
PK_ProductDescription_ProductDescriptionID
index in deSalesLT.ProductDescription
tabel.
- Bezit een exclusieve (X)-vergrendeling op een sleutel in de
Het andere proces, dat we sessie B noemen:
- De eigenaar is van een updatevergrendeling (U) op een sleutel in de
PK_ProductDescription_ProductDescriptionID
index in deSalesLT.ProductDescription
tabel. - Vraagt een gedeelde (S)-vergrendeling aan op een sleutel in de
PK_ProductDescription_ProductDescriptionID
index in deSalesLT.ProductDescription
tabel.
- De eigenaar is van een updatevergrendeling (U) op een sleutel in de
We kunnen dezelfde informatie zien in de XML van de impassegrafiek in het knooppunt van de resourcelijst .
Queryuitvoeringsplannen zoeken in Query Store
Het is vaak handig om de uitvoeringsplannen voor query's te onderzoeken voor instructies die betrokken zijn bij de impasse. Deze uitvoeringsplannen zijn vaak te vinden in Query Store met behulp van de hash van het queryplan vanuit de XML-weergave van de proceslijst van de impassegrafiek.
Deze Transact-SQL-query zoekt naar queryplannen die overeenkomen met de hash van het queryplan die we hebben gevonden voor onze voorbeeld-impasse. Verbinding maken naar de gebruikersdatabase in Azure SQL Database om de query uit te voeren.
DECLARE @query_plan_hash binary(8) = 0x02b0f58d7730f798
SELECT
qrsi.end_time as interval_end_time,
qs.query_id,
qp.plan_id,
qt.query_sql_text,
TRY_CAST(qp.query_plan as XML) as query_plan,
qrs.count_executions
FROM sys.query_store_query as qs
JOIN sys.query_store_query_text as qt on qs.query_text_id=qt.query_text_id
JOIN sys.query_store_plan as qp on qs.query_id=qp.query_id
JOIN sys.query_store_runtime_stats qrs on qp.plan_id = qrs.plan_id
JOIN sys.query_store_runtime_stats_interval qrsi on qrs.runtime_stats_interval_id=qrsi.runtime_stats_interval_id
WHERE query_plan_hash = @query_plan_hash
ORDER BY interval_end_time, query_id;
GO
Mogelijk kunt u geen queryuitvoeringsplan ophalen uit Query Store, afhankelijk van uw Query Store-CLEANUP_POLICY of QUERY_CAPTURE_MODE-instellingen. In dit geval kunt u vaak de benodigde informatie ophalen door het geschatte uitvoeringsplan voor de query weer te geven.
Zoeken naar patronen die de blokkering vergroten
Wanneer u queryuitvoeringsplannen bekijkt die betrokken zijn bij impasses, kijkt u naar patronen die kunnen bijdragen aan blokkeringen en impasses.
Tabel- of indexscans. Wanneer query's die gegevens wijzigen, worden uitgevoerd onder RCSI, wordt de selectie van rijen die moeten worden bijgewerkt uitgevoerd met behulp van een blokkerende scan waarbij een updatevergrendeling (U) wordt uitgevoerd op de gegevensrij wanneer gegevenswaarden worden gelezen. Als de gegevensrij niet voldoet aan de updatecriteria, wordt de updatevergrendeling vrijgegeven en wordt de volgende rij vergrendeld en gescand.
Door indexen af te stemmen om query's te wijzigen, wordt het aantal uitgegeven updatevergrendelingen efficiënter verminderd. Dit vermindert de kans op blokkeren en impasses.
Geïndexeerde weergaven die verwijzen naar meer dan één tabel. Wanneer u een tabel wijzigt waarnaar wordt verwezen in een geïndexeerde weergave, moet de database-engine ook de geïndexeerde weergave behouden. Hiervoor moeten er meer vergrendelingen worden afgesloten en kan dit leiden tot meer blokkeringen en impasses. Geïndexeerde weergaven kunnen er ook toe leiden dat updatebewerkingen intern worden uitgevoerd onder het isolatieniveau voor lezen.
Wijzigingen in kolommen waarnaar wordt verwezen in beperkingen voor refererende sleutels. Wanneer u kolommen in een tabel wijzigt waarnaar wordt verwezen in een beperking REFERERENDE SLEUTEL, moet de database-engine zoeken naar gerelateerde rijen in de verwijzende tabel. Rijversies kunnen niet worden gebruikt voor deze leesbewerkingen. In gevallen waarin trapsgewijze updates of verwijderingen zijn ingeschakeld, kan het isolatieniveau worden geëscaleerd naar serializeerbaar gedurende de duur van de instructie om bescherming te bieden tegen fantoominvoegingen.
Hints vergrendelen. Zoek naar tabelhints die isolatieniveaus opgeven waarvoor meer vergrendelingen nodig zijn. Deze hints omvatten
HOLDLOCK
(wat gelijk is aan serialiseerbare),SERIALIZABLE
(READCOMMITTEDLOCK
waarmee RCSI wordt uitgeschakeld) enREPEATABLEREAD
. Daarnaast kunnen hints zoalsPAGLOCK
,TABLOCK
enUPDLOCK
XLOCK
kunnen de risico's van blokkeringen en impasses toenemen.Als deze hints aanwezig zijn, onderzoekt u waarom de hints zijn geïmplementeerd. Deze hints kunnen racevoorwaarden voorkomen en ervoor zorgen dat gegevens geldig zijn. Het is mogelijk om deze hints op de plaats te laten en toekomstige impasses te voorkomen met behulp van een alternatieve methode in de sectie Voorkomen dat een impasse indien nodig opnieuw optreedt in dit artikel.
Notitie
Meer informatie over gedrag bij het wijzigen van gegevens met behulp van rijversiebeheer in de handleiding voor transactievergrendeling en rijversiebeheer.
Wanneer u de volledige code voor een transactie bekijkt, hetzij in een uitvoeringsplan of in toepassingsquerycode, zoekt u naar aanvullende problematische patronen:
Gebruikersinteractie in transacties. Gebruikersinteractie binnen een expliciete transactie met meerdere instructies verhoogt de duur van transacties aanzienlijk. Dit maakt het waarschijnlijker dat deze transacties elkaar overlappen en dat er blokkeringen en impasses optreden.
Op dezelfde manier verhoogt het houden van een open transactie en het uitvoeren van query's op een niet-gerelateerde database of systeem mid-transaction de kans op blokkeringen en impasses aanzienlijk.
Transacties die toegang hebben tot objecten in verschillende orders. Impasses zijn minder waarschijnlijk wanneer gelijktijdige expliciete transacties met meerdere instructies dezelfde patronen volgen en objecten in dezelfde volgorde openen.
Voorkomen dat een impasse opnieuw optreedt
Er zijn meerdere technieken beschikbaar om te voorkomen dat impasses opnieuw worden uitgevoerd, waaronder het afstemmen van indexen, het afdwingen van plannen met Query Store en het wijzigen van Transact-SQL-query's.
Controleer de geclusterde index van de tabel. De meeste tabellen profiteren van geclusterde indexen, maar vaak worden tabellen per ongeluk geïmplementeerd als hoop .
Een manier om te controleren op een geclusterde index is met behulp van de sp_helpindex systeem opgeslagen procedure. We kunnen bijvoorbeeld een samenvatting van de indexen in de
SalesLT.Product
tabel bekijken door de volgende instructie uit te voeren:exec sp_helpindex 'SalesLT.Product'; GO
Controleer de kolom index_description. Een tabel kan slechts één geclusterde index hebben. Als een geclusterde index is geïmplementeerd voor de tabel, bevat de index_description het woord 'geclusterd'.
Als er geen geclusterde index aanwezig is, is de tabel een heap. Controleer in dit geval of de tabel opzettelijk is gemaakt als een heap om een specifiek prestatieprobleem op te lossen. Overweeg om een geclusterde index te implementeren op basis van de richtlijnen voor het ontwerpen van geclusterde indexen.
In sommige gevallen kan het maken of afstemmen van een geclusterde index blokkeren in impasses verminderen of elimineren. In andere gevallen moet u mogelijk een extra techniek gebruiken, zoals de andere in deze lijst.
Niet-geclusterde indexen maken of wijzigen. Het afstemmen van niet-geclusterde indexen kan uw wijzigingsquery's helpen om de gegevens sneller te vinden, waardoor het aantal vereiste updatevergrendelingen wordt verminderd.
In het voorbeeld van een impasse bevat het queryuitvoeringsplan in Query Store een geclusterde indexscan voor de
PK_Product_ProductID
index. De impassegrafiek geeft aan dat een gedeelde (S)-vergrendeling wacht op deze index een onderdeel is in de impasse.Deze indexscan wordt uitgevoerd omdat onze updatequery een geïndexeerde weergave met de naam
vProductAndDescription
moet wijzigen. Zoals vermeld in de zoekfunctie voor patronen die het blokkerende gedeelte van dit artikel vergroten, kunnen geïndexeerde weergaven die verwijzen naar meerdere tabellen, de blokkering vergroten en de kans op impasses.Als we de volgende niet-geclusterde index maken in de
AdventureWorksLT
database die de kolommen 'dekt' vanSalesLT.Product
waarnaar wordt verwezen door de geïndexeerde weergave, helpt dit de query veel efficiënter rijen te vinden:CREATE INDEX ix_Product_ProductID_Name_ProductModelID on SalesLT.Product (ProductID, Name, ProductModelID); GO
Nadat u deze index hebt gemaakt, treedt de impasse niet meer op.
Wanneer impasses betrekking hebben op wijzigingen in kolommen waarnaar wordt verwezen in beperkingen voor refererende sleutels, moet u ervoor zorgen dat indexen in de verwijzende tabel van de REFERERENDE SLEUTEL efficiënt gerelateerde rijen vinden.
Hoewel indexen in sommige gevallen de queryprestaties aanzienlijk kunnen verbeteren, hebben indexen ook overhead- en beheerkosten. Bekijk algemene richtlijnen voor indexontwerp om het voordeel van indexen te beoordelen voordat u indexen maakt, met name brede indexen en indexen op grote tabellen.
Beoordeel de waarde van geïndexeerde weergaven. Een andere optie om te voorkomen dat het voorbeeld van een impasse opnieuw optreedt, is door de
SalesLT.vProductAndDescription
geïndexeerde weergave te verwijderen. Als deze geïndexeerde weergave niet wordt gebruikt, vermindert dit de overhead van het onderhouden van de geïndexeerde weergave in de loop van de tijd.Gebruik isolatie van momentopnamen. In sommige gevallen kan het instellen van het niveau van transactieisolatie op momentopname voor een of meer van de transacties in een impasse verhinderen dat blokkeringen en impasses opnieuw optreden.
Deze techniek is waarschijnlijk geslaagd wanneer deze wordt gebruikt in SELECT-instructies wanneer de vastgelegde momentopname wordt gelezen, is uitgeschakeld in een database. Wanneer de vastgelegde momentopname voor lezen is uitgeschakeld, vereisen SELECT-query's die gebruikmaken van het isolatieniveau met leesbewerkingen gedeelde (S)-vergrendelingen. Als u isolatie van momentopnamen voor deze transacties gebruikt, hoeft u geen gedeelde vergrendelingen te gebruiken, waardoor blokkeren en impasses kunnen worden voorkomen.
In databases waarvoor isolatie van vastgelegde momentopnamen is ingeschakeld, vereisen SELECT-query's geen gedeelde (S)-vergrendelingen, zodat impasses waarschijnlijker optreden tussen transacties die gegevens wijzigen. In gevallen waarin impasses optreden tussen meerdere transacties die gegevens wijzigen, kan isolatie van momentopnamen leiden tot een updateconflict in plaats van een impasse. Hiervoor is een van de transacties vereist om de bewerking opnieuw uit te voeren.
Dwing een plan af met Query Store. U kunt merken dat een van de query's in de impasse meerdere uitvoeringsplannen heeft en dat de impasse alleen optreedt wanneer een specifiek plan wordt gebruikt. U kunt voorkomen dat de impasse opnieuw optreedt door een plan af te dwingen in Query Store.
Wijzig de Transact-SQL. Mogelijk moet u Transact-SQL wijzigen om te voorkomen dat de impasse opnieuw optreedt. Het wijzigen van Transact-SQL moet zorgvuldig worden uitgevoerd en wijzigingen moeten grondig worden getest om ervoor te zorgen dat gegevens correct zijn wanneer wijzigingen gelijktijdig worden uitgevoerd. Overweeg het volgende bij het herschrijven van Transact-SQL:
- Orderinstructies in transacties, zodat ze toegang hebben tot objecten in dezelfde volgorde.
- Als u transacties in kleinere transacties opsplitst, indien mogelijk.
- Gebruik zo nodig queryhints om de prestaties te optimaliseren. U kunt hints toepassen zonder toepassingscode te wijzigen met Behulp van Query Store.
Zoek meer manieren om impasses in de handleiding Impasses te minimaliseren.
Notitie
In sommige gevallen kunt u de impasseprioriteit van een of meer sessies die betrokken zijn bij een impasse aanpassen als het belangrijk is dat een van de sessies succesvol kan worden voltooid zonder opnieuw te proberen, of wanneer een van de query's die betrokken zijn bij de impasse niet kritiek is en altijd als slachtoffer moet worden gekozen. Hoewel dit niet verhindert dat de impasse opnieuw optreedt, kan dit de impact van toekomstige impasses verminderen.
Een XEvents-sessie verwijderen
U kunt een XEvents-sessie verlaten die impassegegevens verzamelt die gedurende lange perioden worden uitgevoerd op kritieke databases. Houd er rekening mee dat als u een doel voor een gebeurtenisbestand gebruikt, dit kan leiden tot grote bestanden als er meerdere impasses optreden. U kunt blobbestanden verwijderen uit Azure Storage voor een actieve trace, met uitzondering van het bestand waarnaar momenteel wordt geschreven.
Wanneer u een XEvents-sessie wilt verwijderen, is de Transact-SQL-sessie hetzelfde, ongeacht het geselecteerde doeltype.
Als u een XEvents-sessie wilt verwijderen, voert u de volgende Transact-SQL uit. Voordat u de code uitvoert, vervangt u de naam van de sessie door de juiste waarde.
ALTER EVENT SESSION [deadlocks] ON DATABASE
STATE = STOP;
GO
DROP EVENT SESSION [deadlocks] ON DATABASE;
GO
Azure Storage Explorer gebruiken
Azure Storage Explorer is een zelfstandige toepassing die het werken met gebeurtenisbestandsdoelen vereenvoudigt die zijn opgeslagen in blobs in Azure Storage. U kunt Storage Explorer gebruiken voor het volgende:
- Maak een blobcontainer voor het opslaan van XEvent-sessiegegevens.
- Haal de Shared Access Signature (SAS) op voor een blobcontainer.
- Zoals vermeld in impassegrafieken verzamelen in Azure SQL Database met uitgebreide gebeurtenissen, zijn de lees-, schrijf- en lijstmachtigingen vereist.
- Verwijder een voorloopteken
?
uit deQuery string
lijst om de waarde als geheim te gebruiken bij het maken van een databasereferentiebereik.
- Uitgebreide gebeurtenisbestanden van een blobcontainer weergeven en downloaden .
Download Azure Storage Explorer..
Volgende stappen
Meer informatie over prestaties in Azure SQL Database:
- Problemen met blokkerende Azure SQL Database begrijpen en oplossen
- Handleiding voor transactievergrendeling en versiebeheer van rijen
- Gids voor impasses
- NIVEAU VOOR TRANSACTIEISOLATIE INSTELLEN
- Azure SQL Database: prestaties verbeteren met automatisch afstemmen
- Consistente prestaties leveren met Azure SQL
- Logica voor nieuwe pogingen voor tijdelijke fouten.