Analysera och förhindra dödlägen i Azure SQL Database och Fabric SQL-databas
gäller för:Azure SQL Database
SQL-databas i Fabric
Den här artikeln lär dig hur du identifierar dödlägen, använder deadlock-grafer och Query Store för att identifiera frågorna i dödläget och planera och testa ändringar för att förhindra att dödlägen upprepas. Den här artikeln gäller för Azure SQL Database och Fabric SQL Database, som delar många funktioner i Azure SQL Database.
Den här artikeln fokuserar på att identifiera och analysera dödlägen på grund av låskonflikter. Läs mer om andra typer av dödlägen i resurser som kan blockera.
Så här uppstår dödlägen
Varje ny databas i Azure SQL Database har read committed snapshot-isolering databasinställningen aktiverad som standardinställning. Blockering mellan sessioner som läser och skriver data minimeras under RCSI, som använder radversionering för att öka samtidigheten. Blockering och dödlägen kan dock fortfarande inträffa i databaser i Azure SQL Database eftersom:
Frågor som ändrar data kan blockera varandra.
Frågor kan köras under isoleringsnivåer som ökar blockeringen. Isoleringsnivåer kan anges via klientbiblioteksmetoder, frågetipseller ANGE TRANSAKTIONSISOLERINGSNIVÅ i Transact-SQL.
RCSI kan inaktiveras, vilket gör att databasen använder delade (S) lås för att skydda
SELECT
-instruktioner som körs under den läsincheckade isoleringsnivån. Detta kan öka blockering och dödlägen.
Ett exempel på ett dödläge
Ett dödläge uppstår när två eller flera aktiviteter permanent blockerar varandra eftersom varje aktivitet har ett lås på en resurs som den andra aktiviteten försöker låsa. Ett dödläge kallas också för ett cykliskt beroende: vid ett dödläge med två aktiviteter har transaktion A ett beroende av transaktion B och transaktion B stänger cirkeln genom att ha ett beroende av transaktion A.
Till exempel:
Session A påbörjar en explicit transaktion och kör en uppdateringsinstruktion som hämtar ett uppdateringslås (U) på en rad i tabell
SalesLT.Product
som konverteras till ett exklusivt (X) lås.Session B kör ett uppdateringsuttryck som ändrar tabellen
SalesLT.ProductDescription
. Uppdateringsfrasen ansluter till tabellenSalesLT.Product
för att hitta rätt rader som ska uppdateras.Session B hämtar ett uppdateringslås (U) på 72 rader i tabellen
SalesLT.ProductDescription
.Session B behöver ett delat lås på rader i tabellen
SalesLT.Product
, inklusive raden som är låst av Session A. Session B blockeras påSalesLT.Product
.
Session A fortsätter sin transaktion och kör nu en uppdatering mot tabellen
SalesLT.ProductDescription
. Session A blockeras av Session B påSalesLT.ProductDescription
.
Alla transaktioner i ett dödläge väntar på obestämd tid om inte en av de deltagande transaktionerna återställs, till exempel på grund av att sessionen avslutades.
Övervakaren av databasmotorns dödläge söker regelbundet efter uppgifter som är i ett dödläge. Om dödlägesövervakaren identifierar ett cykliskt beroende väljer den en av uppgifterna som offer och avslutar dess transaktion med fel 1205: Transaction (Process ID <N>) was deadlocked on lock resources with another process and is chosen as the deadlock victim. Rerun the transaction.
Att bryta dödläget på detta sätt möjliggör för den andra uppgiften eller uppgifterna i dödläget att slutföra sina transaktioner.
Not
Läs mer om kriterierna för att välja ett dödlägesoffer i den här artikelns Deadlock-processlista i avsnittet.
Programmet med transaktionen som valts som dödlägesoffer bör försöka köra transaktionen igen, vilken vanligtvis slutförs efter att den andra transaktionen eller transaktionerna som är inblandade i dödläget har slutförts.
Det är bästa praxis att införa en kort, slumpmässig fördröjning innan du försöker igen för att undvika att stöta på samma dödläge igen. Läs mer om hur du utformar omförsökslogik för tillfälliga fel.
Standardisoleringsnivå i Azure SQL Database
Nya databaser i Azure SQL Database aktiverar skrivskyddade ögonblicksbilder (RCSI) som standard. RCSI ändrar beteendet för läsincheckad isoleringsnivå att använda radversionshantering för att tillhandahålla konsekvens på instruktionsnivå utan att använda delade (S) lås för SELECT
-instruktioner.
Med RCSI aktiverat:
- Instruktioner som läser data blockerar inte instruktioner som ändrar data.
- Instruktioner som ändrar data blockerar inte instruktioner som läser data.
isoleringsnivå för ögonblicksbilder är också aktiverad som standard för nya databaser i Azure SQL Database. Isolering av ögonblicksbilder är ytterligare en radbaserad isoleringsnivå som ger konsekvens på transaktionsnivå för data och som använder radversioner för att välja rader som ska uppdateras. Om du vill använda ögonblicksbildisolering måste frågor eller anslutningar uttryckligen ange transaktionsisoleringsnivån till SNAPSHOT
. Detta kan bara göras när ögonblicksbildisolering är aktiverat för databasen.
Du kan identifiera om RCSI och/eller ögonblicksbildisolering är aktiverade med Transact-SQL. Anslut till databasen i Azure SQL Database och kör följande fråga:
SELECT name,
is_read_committed_snapshot_on,
snapshot_isolation_state_desc
FROM sys.databases
WHERE name = DB_NAME();
GO
Om RCSI är aktiverat returnerar kolumnen is_read_committed_snapshot_on
värdet 1
. Om ögonblicksbildisolering är aktiverat returnerar kolumnen snapshot_isolation_state_desc
värdet ON
.
Om RCSI är inaktiverat för en databas i Azure SQL Database undersöker du varför RCSI inaktiverades innan den aktiverades igen. Programkoden kan förvänta sig att frågeställningar som läser data blockeras av frågeställningar som skriver data, vilket resulterar i felaktiga resultat på grund av loppvillkor när RCSI är aktiverat.
Tolka dödlägeshändelser
En dödlägeshändelse genereras efter att deadlockhanteraren i Azure SQL Database identifierar ett dödläge och väljer en transaktion som offer. Om du med andra ord konfigurerar aviseringar för dödlägen utlöses meddelandet efter att ett enskilt dödläge har lösts. Det finns ingen användaråtgärd som behöver vidtas för det här dödläget. Program ska skrivas för att inkludera omförsökslogik så att de automatiskt fortsätter efter att ha fått fel 1205: Transaction (Process ID <N>) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
Det är dock användbart att konfigurera aviseringar eftersom dödlägen kan upprepas. Med deadlockaviseringar kan du undersöka om ett mönster av upprepade dödlägen inträffar i databasen, i vilket fall du kan välja att vidta åtgärder för att förhindra att dödlägen upprepas. Läs mer om aviseringar i avsnittet Övervaka och varna om dödlägen i den här artikeln.
De vanligaste metoderna för att förhindra dödlägen
Metoden med lägst risk för att förhindra att dödlägen upprepas är vanligtvis att justera icke-klustrade index för att optimera frågor som är involverade i dödläget.
Risken är låg för den här metoden eftersom justering av icke-klustrade index inte kräver ändringar i frågans kod, vilket minskar risken för ett användarfel när Transact-SQL skrivs om och orsakar att felaktiga data returneras till användaren.
Effektiv icke-klustrad indextuning hjälper sökfrågor att hitta data för att läsa och ändra mer effektivt. Genom att minska mängden data som en fråga behöver komma åt minskar sannolikheten för blockering och dödlägen kan ofta förhindras.
I vissa fall kan skapande eller justering av ett grupperat index minska blockering och dödlägen. Eftersom det klustrade indexet ingår i alla icke-grupperade indexdefinitioner kan det vara en I/O-intensiv och tidskrävande åtgärd att skapa eller ändra ett klustrade index i större tabeller med befintliga index som inte är grupperade. Läs mer om riktlinjer för design av klustrade index .
När indexjusteringen inte lyckas med att förhindra dödlägen är andra metoder tillgängliga:
Om dödläget bara inträffar när en viss plan väljs för en av frågorna som ingår i dödläget kan tvinga fram en frågeplan med Query Store förhindra att dödlägen upprepas.
Om du skriver om Transact-SQL för en eller flera transaktioner som ingår i dödläget kan du också förhindra dödlägen. Att dela upp explicita transaktioner i mindre transaktioner kräver noggrann kodning och testning för att säkerställa data giltighet när samtidiga ändringar inträffar.
Läs mer om var och en av dessa metoder i avsnittet Förhindra att ett dödläge upprepas i den här artikeln.
Övervaka och varna för dödlägen
I den här artikeln använder vi AdventureWorksLT
exempeldatabas för att konfigurera aviseringar för dödlägen, orsaka ett exempel på ett dödläge, analysera dödlägesdiagrammet för exempellåset och testa ändringar för att förhindra att dödläget upprepas.
Vi använder klienten SQL Server Management Studio (SSMS) i den här artikeln, eftersom den innehåller funktioner för att visa dödlägesdiagram i ett interaktivt visuellt läge. Du kan använda andra klienter som Azure Data Studio- för att följa exemplen, men du kanske bara kan visa dödlägesdiagram som XML.
Skapa AdventureWorksLT-databasen
Om du vill följa exemplen skapar du en ny databas i Azure SQL Database och väljer Exempel data som datakälla.
Detaljerade anvisningar om hur du skapar AdventureWorksLT
med Azure-portalen, Azure CLI eller PowerShell finns i Snabbstart: Skapa en enkel Azure SQL Database-databas.
Konfigurera dödlägesaviseringar i Azure-portalen
Om du vill konfigurera aviseringar för dödlägeshändelser följer du stegen i artikeln Skapa aviseringar för Azure SQL Database och Azure Synapse Analytics med hjälp av Azure-portalen.
Välj Deadlocks som signalnamn för aviseringen. Konfigurera åtgärdsgruppen att meddela dig med valfri metod, till exempel åtgärdstypen e-post/SMS/push/röst.
Samla in dödlägesdiagram i Azure SQL Database med utökade händelser
Deadlockdiagram är en rik informationskälla om de processer och lås som involveras i ett dödläge. Samla in deadlock-grafer med Extended Events (XEvents) i Azure SQL Database genom att samla in händelsen sqlserver.database_xml_deadlock_report
.
Du kan samla in dödlägesdiagram med XEvents genom att använda antingen -ringbuffermålet eller -händelsefilmålet. Överväganden för att välja lämplig måltyp sammanfattas i följande tabell:
Tillvägagångssätt | Fördelar | Överväganden | Användningsscenarier |
---|---|---|---|
mål för ringbuffert | – Enkel installation med endast Transact-SQL. | – Händelsedata rensas när XEvents-sessionen stoppas av någon anledning, till exempel att ta databasen offline eller en databasredundans. – Databasresurser används för att underhålla data i ringbufferten och för att fråga sessionsdata. |
– Samla in exempelspårningsdata för testning och inlärning. – Skapa för kortsiktiga behov om du inte kan konfigurera en session med hjälp av ett händelsefilmål omedelbart. – Använd som en landningsplatta för spårningsdata när du har konfigurerat en automatiserad process för att spara spårningsdata i en tabell. |
Mål för händelsefil | – Bevarar händelsedata till en blob i Azure Storage så att data är tillgängliga även efter att sessionen har stoppats. – Händelsefiler kan laddas ned från Azure-portalen eller Azure Storage Explorer och analyseras lokalt, vilket inte kräver att databasresurser används för att köra frågor mot sessionsdata. |
– Installationsprogrammet är mer komplext och kräver konfiguration av en Azure Storage-container och databasomfångsbegränsade autentiseringsuppgifter. | – Allmän användning när du vill att händelsedata ska sparas även efter att händelsesessionen har avslutats. – Du vill köra en spårning som genererar större mängder händelsedata än du vill spara i minnet. |
Välj den måltyp som du vill använda:
Ringbuffertmålet är bekvämt och enkelt att konfigurera, men har en begränsad kapacitet, vilket kan leda till att äldre händelser går förlorade. Ringbufferten bevarar inte händelser till lagringen och ringbuffertmålet rensas när XEvents-sessionen stoppas. Det innebär att alla XEvents som samlas in inte är tillgängliga när databasmotorn startas om av någon anledning, till exempel en redundansväxling. Ringbuffertmålet passar bäst för inlärning och kortsiktiga behov om du inte har möjlighet att konfigurera en XEvents-session till ett händelsefilmål omedelbart.
Den här exempelkoden skapar en XEvents-session som samlar in dödlägesdiagram i minnet med hjälp av ringbuffertmålet. Det maximala minne som tillåts för ringbuffertmålet är 4 MB och sessionen körs automatiskt när databasen är online, till exempel efter en redundansväxling.
Om du vill skapa och sedan starta en XEvents-session för den sqlserver.database_xml_deadlock_report
händelse som skriver till ringbuffertmålet ansluter du till databasen och kör följande Transact-SQL:
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
Orsaka ett dödläge i AdventureWorksLT
Notera
Det här exemplet fungerar i AdventureWorksLT
-databasen med standardschemat och data när RCSI är aktiverat. Se Skapa AdventureWorksLT-databasen för instruktioner för att skapa databasen.
För att orsaka ett dödläge måste du ansluta två sessioner till AdventureWorksLT
-databasen. Vi refererar till dessa sessioner som Session A och Session B.
Kör följande Transact-SQL i session A. Den här koden startar en explicit transaktion och kör en enda instruktion som uppdaterar SalesLT.Product
-tabellen. För att göra detta förvärvar transaktionen en uppdateringslås (U) på en rad i tabellen SalesLT.Product
, vilket konverteras till ett exklusivt (X) lås. Vi lämnar transaktionen öppen.
BEGIN TRANSACTION;
UPDATE SalesLT.Product
SET SellEndDate = SellEndDate + 1
WHERE Color = 'Red';
Kör nu följande Transact-SQL i session B. Den här koden påbörjar inte uttryckligen en transaktion. I stället fungerar den i autokommit-transaktionsläge. Detta uttalande uppdaterar tabellen SalesLT.ProductDescription
. Uppdateringen tar ut ett uppdateringslås (U) på 72 rader i tabellen SalesLT.ProductDescription
. Frågan kopplas till andra tabeller, inklusive tabellen SalesLT.Product
.
UPDATE SalesLT.ProductDescription
SET Description = Description
FROM SalesLT.ProductDescription AS pd
INNER JOIN SalesLT.ProductModelProductDescription AS pmpd
ON pd.ProductDescriptionID = pmpd.ProductDescriptionID
INNER JOIN SalesLT.ProductModel AS pm
ON pmpd.ProductModelID = pm.ProductModelID
INNER JOIN SalesLT.Product AS p
ON pm.ProductModelID = p.ProductModelID
WHERE p.Color = 'Silver';
För att slutföra uppdateringen behöver Session B ett delat (S) lås på rader i tabellen SalesLT.Product
, inklusive raden som är låst av Session A. session B- blockeras på SalesLT.Product
.
Gå tillbaka till session A. Kör följande Transact-SQL-instruktion. Detta kör en andra UPDATE
-instruktion som en del av den öppna transaktionen.
UPDATE SalesLT.ProductDescription
SET Description = Description
FROM SalesLT.ProductDescription AS pd
INNER JOIN SalesLT.ProductModelProductDescription AS pmpd
ON pd.ProductDescriptionID = pmpd.ProductDescriptionID
INNER JOIN SalesLT.ProductModel AS pm
ON pmpd.ProductModelID = pm.ProductModelID
INNER JOIN SalesLT.Product AS p
ON pm.ProductModelID = p.ProductModelID
WHERE p.Color = 'Red';
Den andra uppdateringssatsen i Session A blockeras av Session B på SalesLT.ProductDescription
.
Session A och Session B blockerar nu varandra ömsesidigt. Ingen av transaktionerna kan fortsätta eftersom var och en behöver en resurs som är låst av den andra.
Efter några sekunder identifierar dödlägesövervakaren att transaktionerna i session A och session B blockerar varandra ömsesidigt och att ingen av dem kan göra framsteg. Du bör se ett dödläge inträffa, där Session A har valts som dödlägesoffer. Ett felmeddelande visas i Session A med text som liknar följande:
Msg 1205, Level 13, State 51, Line 7
Transaction (Process ID 91) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
session B slutförs framgångsrikt.
Om du konfigurerar dödlägesaviseringar i Azure-portalenbör du få ett meddelande kort efter att dödläget har inträffat.
Visa grafer för dödlägen från en XEvents-session
Om du konfigurerar en XEvents-session för att samla in dödlägen och ett dödläge inträffar efter att sessionen har startat, kan du visa en interaktiv grafisk presentation av dödlägesdiagrammet och dess XML.
Olika metoder finns tillgängliga för att hämta information om dödlägen för ringbuffertmål och händelsefilmål. Välj det mål som du använde för XEvents-sessionen:
Om du konfigurerar en XEvents-session som skriver till ringbufferten kan du hämta dödlägesinformation genom följande Transact-SQL. Innan du kör frågan ersätter du värdet för @tracename
med namnet på XEvents-sessionen.
DECLARE @tracename AS sysname = N'deadlocks';
WITH ring_buffer
AS (SELECT CAST (target_data AS XML) AS rb
FROM sys.dm_xe_database_sessions AS s
INNER 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
Visa och spara ett dödlägesdiagram i XML
Om du visar ett dödlägesdiagram i XML-format kan du kopiera inputbuffer
av Transact-SQL-instruktioner som ingår i dödläget. Du kan också analysera dödlägen i ett textbaserat format.
Om du har använt en Transact-SQL fråga för att returnera information om ett dödlägesdiagram väljer du värdet i kolumnen deadlock_xml
från valfri rad för att öppna xml-koden för dödlägesdiagrammet i ett nytt fönster i SSMS.
XML-koden för det här exemplets dödlägesdiagram är:
<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>
Så här sparar du dödlägesdiagrammet som en XML-fil:
- Välj Fil och Spara som....
- Låt värdet Spara som filtyp lämnas som standardvärde XML-filer (*.xml)
- Ange Filnamn till önskat namn.
- Välj Spara.
Spara ett dödlägesdiagram som en XDL-fil som kan visas interaktivt i SSMS
Det kan vara användbart att visa en interaktiv representation av ett dödlägesdiagram för att få en snabb översikt över de processer och resurser som ingår i ett dödläge och snabbt identifiera det låsta offret.
Så här sparar du ett dödlägesdiagram som en fil som kan visas grafiskt av SSMS:
Välj värdet i kolumnen
deadlock_xml
från valfri rad för att öppna xml-koden för dödlägesdiagrammet i ett nytt fönster i SSMS.Välj Fil och Spara som....
Ange Spara som typ till Alla filer.
Ställ in filnamn till ett namn du väljer, med filändelsen inställd på
.xdl
.Välj Spara.
Stäng filen genom att välja X- på fliken överst i fönstret, eller genom att välja Filoch sedan Stäng.
Öppna filen igen i SSMS genom att välja Filoch sedan Öppnaoch sedan Fil. Välj den fil som du sparade med tillägget
.xdl
.Dödlägesdiagrammet visas nu i SSMS med en visuell representation av de processer och resurser som ingår i dödläget.
Analysera ett dödläge för Azure SQL Database
Ett dödlägesdiagram har vanligtvis tre noder:
offerlista. Processidentifierare för dödlägesoffer.
Processlista. Information om alla processer som ingår i dödläget. Deadlock-grafer använder termen "process" för att representera en session som kör en transaktion.
Resurslista. Information om de resurser som är inblandade i dödläget.
När du analyserar ett dödläge är det bra att gå igenom dessa noder.
Lista över offer för dödläge
Listan över offer för dödläge visar den process som valdes som dödlägets offer. I den visuella representationen av ett dödlägesdiagram representeras processerna av ovaler. Dödläget offer processen har en "X" dras över ovalen.
I XML-vyn för ett dödlägesdiagramger noden victim-list
ett ID för processen som utsattes för dödläget.
I vårt exempel dödläge är offerprocess-ID:t process24756e75088
. Vi kan använda det här ID:t när vi undersöker noderna för processlistan och resurslistan för att lära oss mer om offerprocessen och de resurser som den låste eller begärde att låsa.
Lista över dödläge-processer
Listan över processer i dödläge är en omfattande informationskälla om transaktionerna som ingår i dödläget.
Den grafiska representationen av dödlägesdiagrammet visar endast en delmängd av informationen som finns i XML:en för dödlägesdiagrammet. Ovalerna i dödlägesdiagrammet representerar processen och visar information, inklusive:
Sessions-ID, även kallat SPID.
Deadlock-prioritet för sessionen. Om två sessioner har olika prioriteter för dödlägen väljs sessionen med den lägre prioriteten som dödlägesoffer. I det här exemplet har båda sessionerna samma prioritet för dödläge.
Mängden transaktionslogg som används av sessionen i byte. Om båda sessionerna har samma prioritet för dödlås väljer dödlåsövervakaren den session som är billigare att rulla tillbaka som ett dödlåsoffer. Kostnaden bestäms genom att jämföra antalet loggbyte som skrivits till den punkten i varje transaktion.
I vårt exempel på ett dödläge hade
session_id
89 använt en lägre mängd transaktionslogg och valts som dödlägesoffer.
Dessutom kan du visa indatabufferten för den sista instruktionen som kördes i varje session före dödläget genom att hovra musen över varje process. Indatabufferten visas i ett verktygstips.
Ytterligare information finns för processer i XML-vyn för dödlägesdiagrammet, inklusive:
Identifiera information för sessionen, till exempel klientnamn, värdnamn och inloggningsnamn.
Hashen för frågeplanen för den sista instruktionen som kördes av varje session före deadlocken. Frågeplanens hash är användbar för att hämta mer information om frågan från Query Store-.
I vårt exempel dödläge:
Vi kan se att båda sessionerna kördes med hjälp av SSMS-klienten under
chrisqpublic
inloggning.Frågeplanens hashvärde för den sista satsen som kördes av vårt dödlägesoffer vid dödläget är
0x02b0f58d7730f798
. Vi kan se texten i det här uttalandet i indatabufferten.Frågeplanens hash, för den senaste instruktionen som kördes av den andra sessionen i vårt dödläge, är också
0x02b0f58d7730f798
. Vi kan se texten i det här påståendet i indatabufferten. I det här fallet har båda frågorna samma hash för frågeplanen eftersom frågorna är identiska, förutom ett bokstavligt värde som används som likhetspredikat.
Vi använder dessa värden senare i den här artikeln för att hitta ytterligare information i Query Store.
Begränsningar för indatabufferten i listan över dödlägesprocesser
Det finns vissa begränsningar att vara medveten om när det gäller information om indatabuffert i listan över dödlägesprocesser.
Frågetexten kan trunkeras i indatabufferten. Indatabufferten är begränsad till de första 4 000 tecknen i instruktionen som körs.
Dessutom kanske vissa uttalanden som ingår i dödläget inte inkluderas i dödlägesgrafen. I vårt exempel körde Session A två uppdateringsinstruktioner i en enda transaktion. Endast den andra uppdateringssatsen, uppdateringen som orsakade dödläget, ingår i grafen för dödläge. Den första uppdateringskommandot som kördes av Session A spelade en roll i dödläget genom att blockera Session B. Indatabufferten, query_hash
, och relaterad information för den första instruktionen som körs av Session A, ingår inte i dödlåsdiagrammet.
För att identifiera den fullständiga körningen av Transact-SQL i en flerinstruktionstransaktion som är involverad i ett dödläge måste du antingen hitta relevant information i den lagrade proceduren eller programkoden som körde frågan, eller köra en spårning med Extended Events för att samla in fullständiga instruktioner som körs av sessioner som är inblandade när dödläget inträffar. Om en instruktion som ingår i dödläget trunkeras och bara delvis Transact-SQL visas i indatabufferten kan du hitta Transact-SQL för instruktionen i Query Store med körningsplanen.
Lista över låsta resurser
Listan över låsta resurser visar vilka låsresurser som ägs och väntas på av processerna i dödläge.
Resurser representeras av rektanglar i den visuella representationen av dödläget:
Not
Databasnamn representeras som GUID:er (unikidentifierare) i dödlägesdiagram för databaser i Azure SQL Database. Det här är physical_database_name
för databasen som visas i sys.databases och sys.dm_user_db_resource_governance dynamiska hanteringsvyer.
I det här exemplet dödläge:
Dödlägeoffer, som vi har kallat session A:
Innehaver ett exklusivt (X) lås på en nyckel i indexet
PK_Product_ProductID
på tabellenSalesLT.Product
.Begär ett uppdateringslås (U) på en nyckel i
PK_ProductDescription_ProductDescriptionID
-indexet i tabellenSalesLT.ProductDescription
.
Den andra processen, som vi har kallat session B:
Äger ett uppdateringslås (U) för en nyckel i indexet
PK_ProductDescription_ProductDescriptionID
på tabellenSalesLT.ProductDescription
.Begär ett delat (S) lås på en nyckel på indexet
PK_ProductDescription_ProductDescriptionID
på tabellenSalesLT.ProductDescription
.
Vi kan se samma information i XML för dödlägesdiagrammet i resurslistnod.
Hitta frågekörningsplaner i Query Store
Det är ofta användbart att undersöka körningsplaner för frågor i satser som ingår i en deadlock-situation. Dessa körningsplaner kan ofta hittas i Query Store genom att använda frågeplanens hash från XML-vyn av dödlägesgrafens processlista för .
Den här Transact-SQL-frågan letar efter frågeplaner som matchar den frågeplanens hash vi hittade för vårt exempel på dödläge. Anslut till användardatabasen i Azure SQL Database för att köra frågan.
DECLARE @query_plan_hash AS 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
INNER JOIN sys.query_store_query_text AS qt
ON qs.query_text_id = qt.query_text_id
INNER JOIN sys.query_store_plan AS qp
ON qs.query_id = qp.query_id
INNER JOIN sys.query_store_runtime_stats AS qrs
ON qp.plan_id = qrs.plan_id
INNER JOIN sys.query_store_runtime_stats_interval AS 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
Du kanske inte kan hämta en frågeutförandeplan från Query Store, beroende på dina Query Store-CLEANUP_POLICY eller QUERY_CAPTURE_MODE-inställningar. I det här fallet kan du ofta få nödvändig information genom att visa den uppskattade körningsplanen för sökfrågan.
Leta efter mönster som ökar blockeringen
När du undersöker frågekörningsplaner som ingår i dödlägen bör du se upp för mönster som kan bidra till blockering och dödlägen.
Tabell eller index genomsöker. När frågor som ändrar data körs under RCSI görs valet av rader som ska uppdateras med hjälp av en blockerande genomsökning där ett uppdateringslås (U) tas på dataraden när datavärden läses. Om dataraden inte uppfyller uppdateringsvillkoren släpps uppdateringslåset och nästa rad låses och genomsöks.
Att justera index för att hjälpa modifikationsfrågor att hitta rader mer effektivt minskar antalet utfärdade uppdateringslås. Detta minskar risken för blockering och dödlägen.
indexerade vyer som refererar till mer än en tabell. När du ändrar en tabell som refereras till i en indexerad vy måste databasmotorn också underhålla den indexerade vyn. Detta kräver att fler lås tas bort och kan leda till ökad blockering av resurser och dödlägen. Indexerade vyer kan också orsaka att uppdateringsåtgärder körs internt under den lässkyddade isoleringsnivån.
Ändringar av kolumner som refereras i främmandenyckelbegränsningar. När du ändrar kolumner i en tabell som refereras till i ett
FOREIGN KEY
villkor måste databasmotorn leta efter relaterade rader i referenstabellen. Radversioner kan inte användas för dessa läsningar. Om sammanhängande uppdateringar eller borttagningar är aktiverade kan isoleringsnivån eskaleras till serialiserbar under hela instruktionen för att skydda mot fantominfogningar.Låsledtrådar. Leta efter tabelltips som anger isoleringsnivåer som kräver fler lås. Dessa indikationer omfattar
HOLDLOCK
(vilket motsvarar serialiserbar),SERIALIZABLE
,READCOMMITTEDLOCK
(som inaktiverar RCSI) ochREPEATABLEREAD
. Dessutom kan tips somPAGLOCK
,TABLOCK
,UPDLOCK
ochXLOCK
öka risken för blockering och dödlägen.Om dessa tips finns på plats undersöker du varför tipsen implementerades. Dessa tips kan förhindra kapplöpningstillstånd och säkerställa datagiltighet. Det kan vara möjligt att låta dessa indikationer vara kvar och förhindra framtida dödlägen genom att använda en alternativ metod i Förhindra att ett dödläge upprepas-avsnittet i denna artikel om det behövs.
Not
Läs mer om beteende när du ändrar data med hjälp av radversionshantering i -guiden för transaktionslåsning och radversionshantering.
När du undersöker den fullständiga koden för en transaktion, antingen i en körningsplan eller i programfrågekoden, letar du efter ytterligare problematiska mönster:
Användarinteraktion i transaktioner. Användarinteraktion i en explicit transaktion med flera instruktioner ökar transaktionernas varaktighet avsevärt. Detta gör det mer sannolikt att dessa transaktioner överlappar varandra och att blockering och dödlägen inträffar.
Risken för blockering och dödlägen ökar avsevärt när man håller en transaktion öppen och samtidigt kör frågor mot en orelaterad databas eller ett orelaterat system mitt i transaktionen.
Transaktioner som använder objekt i olika ordningar. Dödlägen är mindre benägna att inträffa när samtidiga explicita transaktioner med flera instruktioner följer samma mönster och åtkomstobjekt i samma ordning.
Förhindra att ett dödläge upprepas
Det finns flera tillgängliga tekniker för att förhindra att dödlägen upprepas, till exempel justering av index, att tvinga planer med Query Store och ändra Transact-SQL-frågor.
Granska tabellens klustrade index. De flesta tabeller drar nytta av klustrade index, men ofta implementeras tabeller som heaps av misstag.
Ett sätt att söka efter ett grupperat index är genom att använda den systemlagrade proceduren sp_helpindex. Vi kan till exempel visa en sammanfattning av indexen i tabellen
SalesLT.Product
genom att köra följande instruktion:EXECUTE sp_helpindex 'SalesLT.Product'; GO
Granska kolumnen
index_description
. En tabell kan bara ha ett grupperat index. Om ett klustrat index implementerades för tabellen innehållerindex_description
ordetclustered
.Om det inte finns något klustrat index är tabellen en heap (ostrukturerad datamängd). I det här fallet granskar du om tabellen avsiktligt skapades som en heap för att lösa ett specifikt prestandaproblem. Överväg att implementera ett klustrat index baserat på klustrade index designriktlinjerna .
I vissa fall kan skapande eller justering av ett grupperat index minska eller eliminera blockering i dödlägen. I andra fall kan du använda ytterligare en teknik, till exempel de andra i den här listan.
Skapa eller ändra icke-grupperade index. Om du justerar icke-klustrade index kan du hjälpa dina modifieringsfrågor att hitta data som ska uppdateras snabbare, vilket minskar antalet uppdateringslås som krävs.
I vårt exempel på dödläge innehåller frågeverkställningsplanen i Query Store en klustrad indexgenomsökning mot indexet
PK_Product_ProductID
. Dödlägesdiagrammet visar att väntan på ett delat (S) lås på det här indexet är en del av dödläget.Den här indexgenomsökningen utförs eftersom vår uppdateringsfråga måste ändra en indexerad vy med namnet
vProductAndDescription
. Som vi nämnde i Leta efter mönster som ökar blockeringen avsnittet i den här artikeln kan indexerade vyer som refererar till flera tabeller öka blockeringen och sannolikheten för dödlägen.Om vi skapar följande icke-klustrade index i databasen
AdventureWorksLT
som "täcker" kolumnerna frånSalesLT.Product
som refereras av den indexerade vyn, förbättrar det frågan att hitta rader mer effektivt.CREATE INDEX IX_Product_ProductID_Name_ProductModelID ON SalesLT.Product(ProductID, Name, ProductModelID); GO
När du har skapat det här indexet upprepas inte längre dödläget.
När dödlägen omfattar ändringar av kolumner som refereras till i begränsningar för främmande nyckel, kontrollerar du att indexen på den refererande tabellen i
FOREIGN KEY
kan användas för att effektivt hitta relaterade rader.Index kan avsevärt förbättra frågeprestanda i vissa fall, men index har även kostnader för omkostnader och hantering. Granska allmänna riktlinjer för indexdesign för att utvärdera fördelarna med index innan du skapar index, särskilt breda index och index i stora tabeller.
Utvärdera värdet för indexerade vyer. Ett annat alternativ för att förhindra att vårt exempel på dödläge upprepas är att ta bort den
SalesLT.vProductAndDescription
indexerade vyn. Om den indexerade vyn inte används minskar kostnaderna för att underhålla den indexerade vyn över tid.Använd ögonblicksbildisolering. I vissa fall kan inställning av transaktionsisoleringsnivån ögonblicksbild för en eller flera av transaktionerna som är inblandade i ett dödläge förhindra att blockering och dödlägen upprepas.
Den här tekniken lyckas troligen när den används på
SELECT
-instruktioner när läsincheckad ögonblicksbild inaktiveras i en databas. När lässkyddad ögonblicksbild är inaktiverad kräverSELECT
förfrågningar som använder den lässkyddade isoleringsnivån delad (S) lås. Om du använder ögonblicksbildisolering för dessa transaktioner tar du bort behovet av delade lås, vilket kan förhindra blockering och dödlägen.I databaser där "read committed snapshot isolation" är aktiverat kräver
SELECT
-frågor inte delade (S) lås, vilket gör att dödlägen är mer sannolika att inträffa mellan transaktioner som ändrar data. I fall där dödlägen uppstår mellan flera transaktioner som ändrar data kan ögonblicksbildisolering resultera i en uppdateringskonflikt i stället för ett dödläge. Detta kräver på samma sätt att en av transaktionerna försöker utföra åtgärden igen.Tvinga fram en plan med Query Store. Du kanske upptäcker att en av frågorna i dödläget har flera exekveringsplaner och att dödläget bara inträffar när en specifik plan används. Du kan förhindra att dödläget upprepas genom att framtvinga en plan i Query Store.
Ändra Transact-SQL-. Du kan behöva ändra Transact-SQL för att förhindra att dödläget upprepas. Att ändra Transact-SQL bör göras noggrant och dessa förändringar bör testas noggrant för att säkerställa att data är korrekta när de körs samtidigt. När du skriver om Transact-SQL bör du tänka på:
Ordna satser i transaktioner så att de får åtkomst till objekt i samma ordning.
Dela upp transaktioner i mindre transaktioner när det är möjligt.
Om det behövs använder du frågetips för att optimera prestanda. Du kan använda tips utan att ändra programkod med hjälp av Query Store.
Hitta fler sätt att minimera dödlägen i guiden Dödlägen.
Notera
I vissa fall kan du justera prioriteten för dödläget för en eller flera sessioner som är inblandade i ett dödläge om det är viktigt att en av sessionerna slutförs utan att försöka igen, eller när en av frågorna som ingår i dödläget inte är kritisk och bör alltid väljas som offer. Även om detta inte hindrar dödläget från att upprepas kan det minska effekten av framtida dödlägen.
Avsluta en XEvents-session
Du kan låta en XEvents-session som samlar in information om dödlägen vara igång på kritiska databaser under långa perioder. Om du använder ett händelsefilmål kan det resultera i stora filer om flera dödlägen inträffar. Du kan ta bort blobfiler från Azure Storage vid aktiv spårning, förutom den fil som för närvarande skrivs till.
När du vill ta bort en XEvents-session är kommandot Transact-SQL för att ta bort sessionen detsamma, oavsett vilken måltyp som valts.
Om du vill ta bort en XEvents-session kör du följande Transact-SQL. Innan du kör koden ersätter du namnet på sessionen med lämpligt värde.
ALTER EVENT SESSION [deadlocks] ON DATABASE
STATE = STOP;
GO
DROP EVENT SESSION [deadlocks] ON DATABASE;
GO
Använda Azure Storage Explorer
Azure Storage Explorer är ett fristående program som förenklar arbetet med händelsefilmål som lagras i blobar i Azure Storage. Du kan använda Storage Explorer för att:
Skapa en blobcontainer för att lagra XEvent-sessionsdata.
Hämta signaturen för delad åtkomst (SAS) för en blobcontainer.
Som vi nämnde i Samla in dödlägesdiagram i Azure SQL Database med Extended Events, krävs behörigheterna läsa, skriva och lista.
Ta bort alla inledande
?
tecken frånQuery string
för att använda värdet som hemlighet när att skapa en databasomfattande autentiseringsuppgift.
Visa och ladda ned utökade händelsefiler från en blobcontainer.
Ladda ned Azure Storage Explorer.
Relaterat innehåll
- Förstå och lösa blockeringsproblem
- Transaktionslåsning och versionshantering av rader: En guide
- Guide för dödlägen
- ANGE TRANSAKTIONSISOLERINGSNIVÅ
- Azure SQL Database: Förbättra prestandajusteringen med automatisk justering
- Leverera konsekventa prestanda med Azure SQL
- Omförsökslogik för tillfälliga fel