Dela via


Analysera och förhindra dödlägen i Azure SQL Database och Fabric SQL-databas

gäller för:Azure SQL DatabaseSQL-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:

  1. 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.

  2. Session B kör ett uppdateringsuttryck som ändrar tabellen SalesLT.ProductDescription. Uppdateringsfrasen ansluter till tabellen SalesLT.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.

  3. 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.

diagram som visar två sessioner i ett dödläge. Varje session äger en resurs som den andra processen behöver för att fortsätta.

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.

Diagram över ett dödläge mellan två sessioner. En session väljs som offer för dödläget.

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 BSalesLT.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:

  1. Välj Fil och Spara som....
  2. Låt värdet Spara som filtyp lämnas som standardvärde XML-filer (*.xml)
  3. Ange Filnamn till önskat namn.
  4. 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:

  1. 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.

  2. Välj Fil och Spara som....

  3. Ange Spara som typ till Alla filer.

  4. Ställ in filnamn till ett namn du väljer, med filändelsen inställd på .xdl.

  5. Välj Spara.

    Skärmbild i SSMS för att spara en XML-fil för ett dödlägesdiagram till en fil med xsd-tillägget.

  6. Stäng filen genom att välja X- på fliken överst i fönstret, eller genom att välja Filoch sedan Stäng.

  7. Ö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.

    Skärmbild av en XDL-fil som öppnats i SSMS. Grafen för dödläge visas grafiskt, med processer som indikeras av ovaler och låser resurser som rektanglar.

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.

Skärmbild av den visuella visningen av ett dödläge. Ovalen som representerar den process som valts som offer har ett X ritat över sig.

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.

Skärmbild av ett dödlägesdiagram som visas visuellt i SSMS. Två ovaler representerar processer. Indatabufferten för en process visas.

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:

Skärmbild av ett dödlägesdiagram som visas visuellt i SSMS. Rektanglar visar de resurser som ingår i 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å tabellen SalesLT.Product.

    • Begär ett uppdateringslås (U) på en nyckel i PK_ProductDescription_ProductDescriptionID-indexet i tabellen SalesLT.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å tabellen SalesLT.ProductDescription.

    • Begär ett delat (S) lås på en nyckel på indexet PK_ProductDescription_ProductDescriptionID på tabellen SalesLT.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) och REPEATABLEREAD. Dessutom kan tips som PAGLOCK, TABLOCK, UPDLOCKoch XLOCK ö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åller index_description ordet clustered.

    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.

    Skärmbild av en frågekörningsplan. En klustrad indexsökning utförs mot PK_Product_ProductID-indexet i tabellen Produkt.

    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ån SalesLT.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äver SELECT 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:

Ladda ned Azure Storage Explorer.