Problemen met het blokkeren van SQL Server begrijpen en oplossen

Van toepassing op: SQL Server (alle ondersteunde versies), Azure SQL Managed Instance

Origineel KB-nummer: 224453

Doelstelling

In het artikel worden blokkeringen in SQL Server beschreven en wordt uitgelegd hoe u blokkeringen kunt oplossen.

In dit artikel verwijst de term verbinding naar een enkele aangemelde sessie van de database. Elke verbinding wordt weergegeven als een sessie-id (SPID) of session_id in veel DMV's. Elk van deze SPID's wordt vaak een proces genoemd, hoewel het geen aparte procescontext is in de gebruikelijke zin. In plaats daarvan bestaat elke SPID uit de serverbronnen en datastructuren die nodig zijn om de aanvragen van een enkele verbinding van een bepaalde client te verwerken. Eén clienttoepassing kan een of meer verbindingen hebben. Vanuit het oogpunt van SQL Server is er geen verschil tussen meerdere verbindingen van één clienttoepassing op één clientcomputer en meerdere verbindingen van meerdere clienttoepassingen of meerdere clientcomputers. Ze zijn atomisch. Eén verbinding kan een andere verbinding blokkeren, ongeacht de bronclient.

Opmerking

Dit artikel is gericht op SQL Server-instanties, inclusief door Azure SQL beheerde instanties. Voor informatie over het oplossen van problemen met blokkering in Azure SQL Database raadpleegt u Problemen met het blokkeren van Azure SQL Database begrijpen en oplossen.

Wat blokkeert

Blokkeren is een onvermijdelijk en inherent kenmerk van elk relationeel databasebeheersysteem (RDBMS) met gelijktijdigheid op basis van vergrendelingen. Zoals eerder vermeld, vindt blokkering in SQL Server plaats wanneer een sessie een vergrendeling op een specifieke bron vasthoudt en een tweede SPID probeert een conflicterend vergrendelingstype op dezelfde bron te verkrijgen. Normaal gesproken is het tijdsbestek waarvoor de eerste SPID de bron vergrendelt klein. Wanneer de eigenaarssessie de vergrendeling opheft, is de tweede verbinding dan vrij om zijn eigen vergrendeling op de bron te verkrijgen en door te gaan met verwerken. Blokkeren zoals hier beschreven is normaal gedrag en kan in de loop van een dag vele malen gebeuren zonder merkbaar effect op de systeemprestaties.

De duur en transactiecontext van een query bepalen hoe lang de vergrendelingen pworden bewaard en daarmee het effect ervan op andere query's. Als de query niet wordt uitgevoerd binnen een transactie (en er worden geen vergrendelingshints gebruikt), worden de vergrendelingen voor SELECT-instructies alleen vastgehouden op een bron op het moment dat deze daadwerkelijk wordt gelezen en niet tijdens de query. Voor INSERT-, UPDATE- en DELETE-instructies worden de vergrendelingen vastgehouden tijdens de query, zowel voor gegevensconsistentie als om de query indien nodig terug te draaien.

Voor query's die binnen een transactie worden uitgevoerd, wordt de duur van de blokkering bepaald door het type query, het transactie-isolatieniveau en of er vergrendelingshints in de query worden gebruikt. Raadpleeg de volgende artikelen voor een beschrijving van vergrendeling, vergrendelingstips en transactie-isolatieniveaus:

Wanneer het vergrendelen en blokkeren aanhoudt tot het punt waarop er een nadelig effect is op de systeemprestaties, is dit te wijten aan een van de volgende redenen:

  • Een SPID houdt een reeks bronnen voor een langere periode vast voordat ze worden vrijgegeven. Dit type blokkering lost zichzelf na verloop van tijd op, maar kan prestatievermindering veroorzaken.

  • Een SPID houdt een reeks bronnen vast en geeft deze nooit vrij. Dit type blokkering lost zichzelf niet op en verhindert voor onbepaalde tijd toegang tot de getroffen bronnen.

In het eerste scenario kan de situatie zeer vloeiend zijn omdat verschillende SPID's in de loop van de tijd blokkeringen veroorzaken voor verschillende bronnen, waardoor een bewegend doel wordt gemaakt. Deze situaties zijn moeilijk op te lossen met behulp van SQL Server Management Studio om het probleem te beperken tot afzonderlijke query's. De tweede situatie resulteert daarentegen in een consistente status die gemakkelijker te diagnosticeren is.

Toepassingen en blokkeren

Er kan een neiging zijn om zich te concentreren op afstemmen aan de kant van de server en platformproblemen bij een blokkeringsprobleem. Aandacht alleen voor de database leidt mogelijk niet tot een oplossing en kan tijd en energie kosten die beter kan worden besteed aan het onderzoeken van de clienttoepassing en de query's die worden ingediend. Ongeacht het niveau van zichtbaarheid dat de toepassing biedt met betrekking tot de database-aanroepen die worden gedaan, een blokkeerprobleem vereist niettemin vaak zowel de inspectie van de exacte SQL-instructies die door de toepassing zijn ingediend als het exacte gedrag van de toepassing met betrekking tot het annuleren van query's, verbindingsbeheer, het ophalen van alle resultaatrijen, enzovoort. Als het ontwikkelingshulpprogramma geen expliciete controle toestaat over verbindingsbeheer, het annuleren van query's, time-out van query's, het ophalen van resultaten, enzovoort, kunnen blokkeringsproblemen mogelijk niet worden opgelost. Dit potentieel moet nauwkeurig worden onderzocht voordat een hulpprogramma voor toepassingsontwikkeling voor SQL Server wordt gekozen, met name voor prestatiegevoelige OLTP-omgevingen.

Let op de prestaties van de database tijdens de ontwerp- en bouwfase van de database en de toepassing. Met name het bronverbruik, het isolatieniveau en de lengte van het transactiepad moeten voor elke query worden geëvalueerd. Elke query en transactie moeten zo licht mogelijk zijn. Er moet een goede discipline voor verbindingsbeheer worden uitgeoefend. Zonder die discipline kan de toepassing aanvaardbare prestaties lijken te leveren bij lage aantallen gebruikers, maar kunnen de prestaties aanzienlijk verslechteren naarmate het aantal gebruikers toeneemt.

Met het juiste toepassings- en queryontwerp kan SQL Server vele duizenden gelijktijdige gebruikers op één server ondersteunen, met weinig blokkering.

Problemen met blokkering oplossen

Ongeacht in welke blokkeringssituatie we ons bevinden, de methode voor het oplossen van problemen met vergrendelen is hetzelfde. Deze logische scheidingen zullen de rest van de samenstelling van dit artikel bepalen. Het concept is om de hoofdblokkering te vinden en te identificeren wat die query doet en waarom deze blokkeert. Zodra de problematische query is geïdentificeerd (dat wil zeggen, wat de vergrendelingen voor een langere periode vasthoudt), is de volgende stap het analyseren en bepalen waarom de blokkering plaatsvindt. Nadat we hebben begrepen waarom, kunnen we vervolgens wijzigingen aanbrengen door de query en de transactie opnieuw te ontwerpen.

Stappen voor het oplossen van problemen:

  1. De belangrijkste blokkeringssessie identificeren (hoofdblokkering)

  2. Zoek de query en transactie die de blokkering veroorzaken (wat vergrendelingen voor een lange periode vasthoudt)

  3. Analyseren/begrijpen waarom langdurige blokkeringen optreden

  4. Blokkeringsprobleem oplossen door de query en transactie opnieuw te ontwerpen

Laten we nu eens bespreken hoe we de belangrijkste blokkeringssessie kunnen lokaliseren met een passende gegevensvastlegging.

Blokkeringsinformatie verzamelen

Om de moeilijkheid van het oplossen van blokkeringsproblemen op te lossen, kan een databasebeheerder SQL-scripts gebruiken die constant de status van vergrendeling en blokkering op SQL Server controleren. Er zijn er twee complementaire methoden om deze gegevens te verzamelen.

De eerste is om dynamische beheerobjecten (DMO's) op te vragen en de resultaten op te slaan voor vergelijking in de loop van de tijd. Sommige objecten waarnaar in dit artikel wordt verwezen, zijn dynamische beheerweergaven (DMV's) en andere dynamische beheerfuncties (DMF's).

De tweede is om Uitgebreide gebeurtenissen (XEvents) of SQL Profiler-traceringen te gebruiken om vast te leggen wat wordt uitgevoerd. Aangezien SQL Trace en SQL Server Profiler zijn verouderd, is deze handleiding voor probleemoplossing gericht op XEvents.

Informatie verzamelen van DMV's

Het verwijzen naar DMV's om problemen met blokkering op te lossen, heeft tot doel de SPID (sessie-ID) aan het begin van de blokkeringsketen en de SQL-instructie te identificeren. Zoek naar slachtoffer-SPID's die worden geblokkeerd. Als een SPID wordt geblokkeerd door een andere SPID, onderzoekt u de SPID die eigenaar is van de bron (de blokkerende SPID). Wordt de SPID van die eigenaar ook geblokkeerd? U kunt de keten doorlopen om de hoofdblokkering te vinden en vervolgens onderzoeken waarom deze de vergrendeling vasthoudt.

Gebruik een van de volgende manieren om dit te doen:

  • Klik in de objectverkenner van SQL Server Management Studio (SSMS) met de rechtermuisknop op het serverobject op het hoogste niveau, vouw Rapporten uit, vouw Standaardrapporten uit en selecteer vervolgens Activiteit - Alle blokkerende transacties. Dit rapport toont actuele transacties aan het hoofd van een blokkeringsketen. Als u de transactie uitvouwt, worden in het rapport de transacties weergegeven die door de hoofdtransactie worden geblokkeerd. In dit rapport worden ook de Blokkerende SQL-instructie en de Geblokkeerde SQL-instructie weergegeven.

  • Open de activiteitbewaking in SSMS en raadpleeg de kolom Geblokkeerd door. Meer informatie over Activiteitbewaking vindt u hier.

Meer gedetailleerde op query's gebaseerde methoden zijn ook beschikbaar met behulp van DMV's:

  • De opdrachten sp_who en sp_who2 zijn oudere opdrachten om alle huidige sessies weer te geven. De DMV sys.dm_exec_sessions retourneert meer gegevens in een resultatenset die gemakkelijker te doorzoeken en te filteren is. U vindt sys.dm_exec_sessions in de kern van andere query's.

  • Als u al een bepaalde sessie hebt geïdentificeerd, kunt u DBCC INPUTBUFFER(<session_id>) gebruiken om de laatste instructie te vinden die door een sessie is ingediend. Vergelijkbare resultaten kunnen worden geretourneerd met de sys.dm_exec_input_buffer dynamische beheerfunctie (DMF), in een resultatenset die gemakkelijker te doorzoeken en te filteren is, met de session_id en de request_id. Als u bijvoorbeeld de meest recente query wilt retourneren die is ingediend door session_id 66 en request_id 0:

SELECT * FROM sys.dm_exec_input_buffer (66,0);
  • Raadpleeg de sys.dm_exec_requests kolom en verwijs naar de blocking_session_id kolom. Wanneer blocking_session_id = 0, wordt een sessie niet geblokkeerd. Hoewel sys.dm_exec_requests alleen verzoeken vermeldt die momenteel worden uitgevoerd, wordt elke verbinding (actief of niet) weergegeven in sys.dm_exec_sessions. Bouw voort op deze gemeenschappelijke join tussen sys.dm_exec_requests en sys.dm_exec_sessions in de volgende query. Houd er rekening mee dat om te worden geretourneerd door sys.dm_exec_requests, de query actief moet worden uitgevoerd met SQL Server.

  • Voer deze voorbeeldquery uit om de actief uitgevoerde query's en hun huidige SQL-batchtekst of invoerbuffertekst te vinden, met behulp van de DMV's sys.dm_exec_sql_text of sys.dm_exec_input_buffer. Als de gegevens die worden geretourneerd door de kolom text van sys.dm_exec_sql_text NULL zijn, wordt de query momenteel niet uitgevoerd. In dat geval bevat de kolom event_info van sys.dm_exec_input_buffer de laatste opdrachtreeks die aan de SQL-engine is doorgegeven. Deze query kan ook worden gebruikt om sessies te identificeren die andere sessies blokkeren, waaronder een lijst met session_ids geblokkeerd per session_id.

WITH cteBL (session_id, blocking_these) AS 
(SELECT s.session_id, blocking_these = x.blocking_these FROM sys.dm_exec_sessions s 
CROSS APPLY    (SELECT isnull(convert(varchar(6), er.session_id),'') + ', '  
                FROM sys.dm_exec_requests as er
                WHERE er.blocking_session_id = isnull(s.session_id ,0)
                AND er.blocking_session_id <> 0
                FOR XML PATH('') ) AS x (blocking_these)
)
SELECT s.session_id, blocked_by = r.blocking_session_id, bl.blocking_these
, batch_text = t.text, input_buffer = ib.event_info, * 
FROM sys.dm_exec_sessions s 
LEFT OUTER JOIN sys.dm_exec_requests r on r.session_id = s.session_id
INNER JOIN cteBL as bl on s.session_id = bl.session_id
OUTER APPLY sys.dm_exec_sql_text (r.sql_handle) t
OUTER APPLY sys.dm_exec_input_buffer(s.session_id, NULL) AS ib
WHERE blocking_these is not null or r.blocking_session_id > 0
ORDER BY len(bl.blocking_these) desc, r.blocking_session_id desc, r.session_id;
  • Voer deze uitgebreidere voorbeeldquery uit, geleverd door de ondersteuning van Microsoft, om het begin van een blokkeringsketen voor meerdere sessies te identificeren, inclusief de querytekst van de sessies die betrokken zijn bij een blokkeringsketen.
WITH cteHead ( session_id,request_id,wait_type,wait_resource,last_wait_type,is_user_process,request_cpu_time
,request_logical_reads,request_reads,request_writes,wait_time,blocking_session_id,memory_usage
,session_cpu_time,session_reads,session_writes,session_logical_reads
,percent_complete,est_completion_time,request_start_time,request_status,command
,plan_handle,sql_handle,statement_start_offset,statement_end_offset,most_recent_sql_handle
,session_status,group_id,query_hash,query_plan_hash) 
AS ( SELECT sess.session_id, req.request_id, LEFT (ISNULL (req.wait_type, ''), 50) AS 'wait_type'
    , LEFT (ISNULL (req.wait_resource, ''), 40) AS 'wait_resource', LEFT (req.last_wait_type, 50) AS 'last_wait_type'
    , sess.is_user_process, req.cpu_time AS 'request_cpu_time', req.logical_reads AS 'request_logical_reads'
    , req.reads AS 'request_reads', req.writes AS 'request_writes', req.wait_time, req.blocking_session_id,sess.memory_usage
    , sess.cpu_time AS 'session_cpu_time', sess.reads AS 'session_reads', sess.writes AS 'session_writes', sess.logical_reads AS 'session_logical_reads'
    , CONVERT (decimal(5,2), req.percent_complete) AS 'percent_complete', req.estimated_completion_time AS 'est_completion_time'
    , req.start_time AS 'request_start_time', LEFT (req.status, 15) AS 'request_status', req.command
    , req.plan_handle, req.[sql_handle], req.statement_start_offset, req.statement_end_offset, conn.most_recent_sql_handle
    , LEFT (sess.status, 15) AS 'session_status', sess.group_id, req.query_hash, req.query_plan_hash
    FROM sys.dm_exec_sessions AS sess
    LEFT OUTER JOIN sys.dm_exec_requests AS req ON sess.session_id = req.session_id
    LEFT OUTER JOIN sys.dm_exec_connections AS conn on conn.session_id = sess.session_id 
    )
, cteBlockingHierarchy (head_blocker_session_id, session_id, blocking_session_id, wait_type, wait_duration_ms,
wait_resource, statement_start_offset, statement_end_offset, plan_handle, sql_handle, most_recent_sql_handle, [Level])
AS ( SELECT head.session_id AS head_blocker_session_id, head.session_id AS session_id, head.blocking_session_id
    , head.wait_type, head.wait_time, head.wait_resource, head.statement_start_offset, head.statement_end_offset
    , head.plan_handle, head.sql_handle, head.most_recent_sql_handle, 0 AS [Level]
    FROM cteHead AS head
    WHERE (head.blocking_session_id IS NULL OR head.blocking_session_id = 0)
    AND head.session_id IN (SELECT DISTINCT blocking_session_id FROM cteHead WHERE blocking_session_id != 0)
    UNION ALL
    SELECT h.head_blocker_session_id, blocked.session_id, blocked.blocking_session_id, blocked.wait_type,
    blocked.wait_time, blocked.wait_resource, h.statement_start_offset, h.statement_end_offset,
    h.plan_handle, h.sql_handle, h.most_recent_sql_handle, [Level] + 1
    FROM cteHead AS blocked
    INNER JOIN cteBlockingHierarchy AS h ON h.session_id = blocked.blocking_session_id and h.session_id!=blocked.session_id --avoid infinite recursion for latch type of blocking
    WHERE h.wait_type COLLATE Latin1_General_BIN NOT IN ('EXCHANGE', 'CXPACKET') or h.wait_type is null
    )
SELECT bh.*, txt.text AS blocker_query_or_most_recent_query 
FROM cteBlockingHierarchy AS bh 
OUTER APPLY sys.dm_exec_sql_text (ISNULL ([sql_handle], most_recent_sql_handle)) AS txt;
SELECT [s_tst].[session_id],
[database_name] = DB_NAME (s_tdt.database_id),
[s_tdt].[database_transaction_begin_time], 
[sql_text] = [s_est].[text] 
FROM sys.dm_tran_database_transactions [s_tdt]
INNER JOIN sys.dm_tran_session_transactions [s_tst] ON [s_tst].[transaction_id] = [s_tdt].[transaction_id]
INNER JOIN sys.dm_exec_connections [s_ec] ON [s_ec].[session_id] = [s_tst].[session_id]
CROSS APPLY sys.dm_exec_sql_text ([s_ec].[most_recent_sql_handle]) AS [s_est];
  • Verwijzing sys.dm_os_waiting_tasks die zich in de thread/taaklaag van SQL Server bevindt. Hiermee wordt informatie geretourneerd over wat SQL wait_type de aanvraag momenteel ondervindt. Net als sys.dm_exec_requests, worden alleen actieve aanvragen geretourneerd door sys.dm_os_waiting_tasks.

Opmerking

Raadpleeg de DMV sys.dm_db_wait_stats voor veel meer over wachttypen, inclusief geaggregeerde wachtstatistieken in de loop van de tijd.

  • Gebruik de DMV sys.dm_tran_locks voor meer gedetailleerde informatie over welke vergrendelingen door query's zijn geplaatst. Deze DMV kan grote hoeveelheden gegevens retourneren op een productie-SQL Server-exemplaar en is handig om te diagnosticeren welke vergrendelingen momenteel worden vastgehouden.

Vanwege de INNER JOIN op sys.dm_os_waiting_tasks, beperkt de volgende query de uitvoer van sys.dm_tran_locks alleen tot momenteel geblokkeerde aanvragen, hun wachtstatus en hun vergrendelingen:

SELECT table_name = schema_name(o.schema_id) + '.' + o.name
, wt.wait_duration_ms, wt.wait_type, wt.blocking_session_id, wt.resource_description
, tm.resource_type, tm.request_status, tm.request_mode, tm.request_session_id
FROM sys.dm_tran_locks AS tm
INNER JOIN sys.dm_os_waiting_tasks as wt ON tm.lock_owner_address = wt.resource_address
LEFT OUTER JOIN sys.partitions AS p on p.hobt_id = tm.resource_associated_entity_id
LEFT OUTER JOIN sys.objects o on o.object_id = p.object_id or tm.resource_associated_entity_id = o.object_id
WHERE resource_database_id = DB_ID()
AND object_name(p.object_id) = '<table_name>';

Met DMV's biedt het opslaan van de queryresultaten in de loop van de tijd gegevenspunten waarmee u blokkeringen gedurende een opgegeven tijdsinterval kunt controleren om persistente blokkeringen of trends te identificeren. Het hulpprogramma voor CSS om dergelijke problemen op te lossen, is door de PSSDiag-gegevensverzamelaar te gebruiken. Dit hulpprogramma gebruikt de 'SQL Server Perf Stats' om in de loop van de tijd resultatensets te verzamelen van DMV's waarnaar hierboven wordt verwezen. Als dit hulpprogramma voortdurend in ontwikkeling is, bekijkt u de nieuwste openbare versie van DiagManager op GitHub.

Informatie verzamelen van uitgebreide gebeurtenissen

Naast bovenstaande informatie is het vaak nodig om een tracering van de activiteiten op de server vast te leggen om een blokkeringsprobleem in SQL Server grondig te onderzoeken. Als een sessie bijvoorbeeld meerdere instructies binnen een transactie uitvoert, wordt alleen de laatste instructie weergegeven die is verzonden. Een van de eerdere instructies kan echter de reden zijn dat er nog steeds vergrendelingen worden vastgehouden. Een tracering stelt u in staat om alle opdrachten te zien die door een sessie binnen de huidige transactie zijn uitgevoerd.

Er zijn twee manieren om traceringen vast te leggen in SQL Server: Uitgebreide gebeurtenissen (XEvents) en Profiler-traceringen. SQL-traceringen die de SQL Server Profiler gebruiken, zijn echter afgeschaft. XEvents is het nieuwere, superieure traceringsplatform dat meer veelzijdigheid en minder impact op het waargenomen systeem mogelijk maakt en de interface is geïntegreerd in SSMS.

Er zijn vooraf gemaakte uitgebreide gebeurtenissessies die kunnen worden gestart in SSMS en deze staan vermeld in de objectverkenner onder het menu voor XEvent Profiler. Raadpleeg XEvent Profiler voor meer informatie. U kunt ook uw eigen aangepaste uitgebreide gebeurtenissessies maken in SSMS. Raadpleeg de wizard Nieuwe sessie voor uitgebreide gebeurtenissen. Voor het oplossen van blokkeringsproblemen wordt doorgaans het volgende vastgelegd:

  • Categoriefouten:
    • Let op
    • Blocked_process_report**
    • Error_reported (kanaalbeheerder)
    • Exchange_spill
    • Execution_warning

**Om de drempel en frequentie te configureren waarmee rapporten over geblokkeerde processen worden gegenereerd, gebruikt u de opdracht sp_configure om de optie voor de drempelwaarde voor geblokkeerde processen te configureren, die in seconden kan worden ingesteld. Standaard worden er geen geblokkeerde procesrapporten geproduceerd.

  • Categoriewaarschuwingen:

    • Hash_warning
    • Missing_column_statistics
    • Missing_join_predicate
    • Sort_warning
  • Categorie-uitvoering:

    • Rpc_completed
    • Rpc_starting
    • Sql_batch_completed
    • Sql_batch_starting
  • Categorievergrendeling

    • Lock_deadlock
  • Categoriesessie

    • Existing_connection
    • Aanmelden
    • Afmelden

Veelvoorkomende blokkeringsscenario's identificeren en oplossen

Door de bovenstaande informatie te onderzoeken, kunt u de oorzaak van de meeste blokkeringsproblemen bepalen. In de rest van dit artikel wordt besproken hoe u deze informatie kunt gebruiken om enkele veelvoorkomende blokkeringsscenario's te identificeren en op te lossen. In deze discussie wordt ervan uitgegaan dat u de blokkerende scripts (waarnaar eerder is verwezen) hebt gebruikt om informatie over de blokkerende SPID's vast te leggen en dat u toepassingsactiviteit hebt vastgelegd met behulp van een XEvent-sessie.

Blokkerende gegevens analyseren

  • Bekijk de uitvoer van de DMV's sys.dm_exec_requests en sys.dm_exec_sessions om het begin van de blokkeerketens te bepalen, met behulp van blocking_these en session_id. Hiermee wordt het duidelijkst aangegeven welke verzoeken zijn geblokkeerd en welke worden geblokkeerd. Onderzoek de sessies die geblokkeerd zijn en blokkeren. Is er een algemene of hoofdmap voor de blokkeringsketen? Ze delen waarschijnlijk een gemeenschappelijke tabel en een of meer van de sessies die betrokken zijn bij een blokkeringsketen voeren een schrijfbewerking uit.

  • Bekijk de uitvoer van de DMV's sys.dm_exec_requests en sys.dm_exec_sessions voor informatie over de SPID's aan het begin van de blokkeringsketen. Zoek de volgende kolommen:

    • sys.dm_exec_requests.status
      In deze kolom ziet u de status van een bepaalde aanvraag. Meestal geeft een slaapstandstatus aan dat de SPID de uitvoering heeft voltooid en wacht tot de toepassing een andere query of batch indient. Een uitvoerbare of actieve status geeft aan dat de SPID momenteel een query verwerkt. De volgende tabel bevat een korte uitleg van de verschillende statuswaarden.

      Status Betekenis
      Achtergrond De SPID voert een achtergrondtaak uit, zoals impassedetectie, logboekschrijver of controlepunt.
      Slaapstand De SPID wordt momenteel niet uitgevoerd. Dit geeft meestal aan dat de SPID wacht op een opdracht van de toepassing.
      Uitvoeren De SPID wordt momenteel uitgevoerd op een planner.
      Uitvoerbaar De SPID staat in de uitvoerbare wachtrij van een planner en wacht op tijd van de planner.
      Opgeschort De SPID wacht op een hulpbron, zoals een vergrendeling of een latch.
    • sys.dm_exec_sessions.open_transaction_count
      Deze kolom geeft het aantal openstaande transacties in deze sessie weer. Als deze waarde groter is dan 0, bevindt de SPID zich binnen een open transactie en kan deze vergrendelingen bevatten die zijn verkregen door een instructie binnen de transactie.

    • sys.dm_exec_requests.open_transaction_count
      Op dezelfde manier geeft deze kolom het aantal openstaande transacties in deze aanvraag weer. Als deze waarde groter is dan 0, bevindt de SPID zich binnen een open transactie en kan deze vergrendelingen bevatten die zijn verkregen door een instructie binnen de transactie.

    • sys.dm_exec_requests.wait_type, wait_time en last_wait_type
      Als de sys.dm_exec_requests.wait_type NULL is, wacht de aanvraag momenteel nergens op en geeft de waarde last_wait_type de laatste wait_type aan die de aanvraag is tegengekomen. Raadpleeg sys.dm_os_wait_stats voor meer informatie over sys.dm_os_wait_stats en een beschrijving van de meest voorkomende wachttypen. De waarde wait_time kan worden gebruikt om te bepalen of de aanvraag voortgang maakt. Wanneer een query op de sys.dm_exec_requests tabel een waarde in de wait_time kolom retourneert die kleiner is dan de wait_time waarde van een eerdere query van sys.dm_exec_requests, geeft dit aan dat de eerdere vergrendeling is verkregen en vrijgegeven en nu wacht op een nieuwe vergrendeling (ervan uitgaande dat het niet nul is wait_time). Dit kan worden geverifieerd door de uitvoer wait_resource tussen sys.dm_exec_requests te vergelijken, die de bron weergeeft waarop de aanvraag wacht.

    • sys.dm_exec_requests.wait_resource Deze kolom geeft de bron aan waarop een geblokkeerde aanvraag wacht. De volgende tabel geeft een overzicht van veelvoorkomende wait_resource indelingen en hun betekenis:

      Resource Formaat Voorbeeld Uitleg
      Tabel DatabaseID:ObjectID:IndexID TAB: 5:261575970:1 In dit geval is database-ID 5 de pubs-voorbeelddatabase en is object_id 261575970 de titeltabel en is 1 de geclusterde index.
      Pagina DatabaseID:FileID:PageID PAGINA: 5:1:104 In dit geval is database-ID 5 pubs, is bestands-ID 1 het primaire gegevensbestand en is pagina 104 een pagina die bij de titeltabel hoort. Om de object_id te identificeren waartoe de pagina behoort, gebruikt u de dynamische beheerfunctie sys.dm_db_page_info, waarbij u de DatabaseID, FileId, PageId van de wait_resource doorgeeft.
      Sleutel DatabaseID:Hobt_id (Hash-waarde voor indexsleutel) SLEUTEL: 5:72057594044284928 (3300a4f361aa) In dit geval is database-ID 5 Pubs, Hobt_ID 72057594044284928 komt overeen met index_id 2 voor object_id 261575970 (tabel met titels). Gebruik de sys.partitions catalogusweergave om de hobt_id te koppelen aan een bepaalde index_id en object_id Er is geen manier om de hash van de indexsleutel los te maken van een specifieke sleutelwaarde.
      Rij DatabaseID:FileID:PageID:Slot(row) RID: 5:1:104:3 In dit geval is database-ID 5 pubs, is bestands-ID 1 het primaire gegevensbestand, is pagina 104 een pagina die behoort tot de titeltabel en geeft slot 3 de positie van de rij op de pagina aan.
      Compileren DatabaseID:FileID:PageID:Slot(row) RID: 5:1:104:3 In dit geval is database-ID 5 pubs, is bestands-ID 1 het primaire gegevensbestand, is pagina 104 een pagina die behoort tot de titeltabel en geeft slot 3 de positie van de rij op de pagina aan.
    • sys.dm_tran_active_transactions De sys.dm_tran_active_transactions DMV bevat gegevens over open transacties die kunnen worden samengevoegd met andere DMV's voor een compleet beeld van transacties die wachten op vastlegging of terugdraaien. Gebruik de volgende query om informatie over openstaande transacties te retourneren, gekoppeld aan andere DMV's, waaronder sys.dm_tran_session_transactions. Houd rekening met de huidige status van een transactie, transaction_begin_time, en andere situationele gegevens om te evalueren of deze een bron van blokkering kan zijn.

      SELECT tst.session_id, [database_name] = db_name(s.database_id)
      , tat.transaction_begin_time
      , transaction_duration_s = datediff(s, tat.transaction_begin_time, sysdatetime()) 
      , transaction_type = CASE tat.transaction_type  WHEN 1 THEN 'Read/write transaction'
                                                      WHEN 2 THEN 'Read-only transaction'
                                                      WHEN 3 THEN 'System transaction'
                                                      WHEN 4 THEN 'Distributed transaction' END
      , input_buffer = ib.event_info, tat.transaction_uow     
      , transaction_state  = CASE tat.transaction_state    
                  WHEN 0 THEN 'The transaction has not been completely initialized yet.'
                  WHEN 1 THEN 'The transaction has been initialized but has not started.'
                  WHEN 2 THEN 'The transaction is active - has not been committed or rolled back.'
                  WHEN 3 THEN 'The transaction has ended. This is used for read-only transactions.'
                  WHEN 4 THEN 'The commit process has been initiated on the distributed transaction.'
                  WHEN 5 THEN 'The transaction is in a prepared state and waiting resolution.'
                  WHEN 6 THEN 'The transaction has been committed.'
                  WHEN 7 THEN 'The transaction is being rolled back.'
                  WHEN 8 THEN 'The transaction has been rolled back.' END 
      , transaction_name = tat.name, request_status = r.status
      , tst.is_user_transaction, tst.is_local
      , session_open_transaction_count = tst.open_transaction_count  
      , s.host_name, s.program_name, s.client_interface_name, s.login_name, s.is_user_process
      FROM sys.dm_tran_active_transactions tat 
      INNER JOIN sys.dm_tran_session_transactions tst  on tat.transaction_id = tst.transaction_id
      INNER JOIN Sys.dm_exec_sessions s on s.session_id = tst.session_id 
      LEFT OUTER JOIN sys.dm_exec_requests r on r.session_id = s.session_id
      CROSS APPLY sys.dm_exec_input_buffer(s.session_id, null) AS ib;
      
    • Andere kolommen

      De overige kolommen in sys.dm_exec_sessions en sys.dm_exec_request kunnen ook inzicht geven in de oorzaak van een probleem. Hun nut varieert afhankelijk van de omstandigheden van het probleem. U kunt bijvoorbeeld bepalen of het probleem zich alleen voordoet bij bepaalde clients (hostname), bij bepaalde netwerkbibliotheken (client_interface_name), wanneer de laatste batch die door een SPID is ingediend last_request_start_time was in sys.dm_exec_sessions, hoe lang een verzoek actief was met start_time in sys.dm_exec_requests, enzovoort.

Veelvoorkomende blokkeringsscenario's

De onderstaande tabel brengt veelvoorkomende symptomen in kaart met hun waarschijnlijke oorzaken.

De kolommen wait_type, open_transaction_count en status verwijzen naar informatie die wordt geretourneerd door sys.dm_exec_request, andere kolommen kunnen worden geretourneerd door sys.dm_exec_sessions. In de kolom 'Wordt opgelost?' wordt aangegeven of de blokkering vanzelf wordt opgelost of dat de sessie moet worden beëindigd via de KILL opdracht. Raadpleeg KILL (Transact-SQL) voor meer informatie.

Scenario Wait_type Open_Tran Status Opgelost? Andere symptomen
1 NIET NULL >= 0 uitvoerbaar Ja, wanneer de query is voltooid. In sys.dm_exec_sessions, reads, cpu_time en/of memory_usage zullen de kolommen in de loop van de tijd toenemen. De duur van de query zal hoog zijn wanneer deze is voltooid.
2 NULL >0 slaapstand Nee, maar SPID kan worden geannuleerd Er kan een aandachtssignaal worden weergegeven in de uitgebreide gebeurtenissessie voor deze SPID, wat aangeeft dat er een time-out voor query's of annulering is opgetreden.
3 NULL >= 0 uitvoerbaar Nee. Wordt pas opgelost als de client alle rijen ophaalt of de verbinding verbreekt. SPID kan worden geannuleerd, maar het kan tot 30 seconden duren. Als open_transaction_count = 0 en de SPID houdt vergrendelingen vast terwijl het transactie-isolatieniveau standaard is (READ COMMITTED), is dit waarschijnlijk een oorzaak.
4 Varieert >= 0 uitvoerbaar Nee. Wordt pas opgelost als de client query's annuleert of verbindingen sluit. SPID's kunnen worden geannuleerd, maar het kan tot 30 seconden duren. De kolom hostname in sys.dm_exec_sessions voor de SPID aan het begin van een blokkerende keten is dezelfde als een van de SPID die wordt geblokkeerd.
5 NULL >0 terugdraaien Ja. Er kan een aandachtssignaal worden weergegeven in de uitgebreide gebeurtenissensessie voor deze SPID, wat aangeeft dat er een time-out voor de query of een annulering is opgetreden, of dat er gewoon een instructie voor terugdraaien is uitgegeven.
6 NULL >0 slaapstand Uiteindelijk. Wanneer Windows NT vaststelt dat de sessie niet meer actief is, wordt de verbinding verbroken. De waarde last_request_start_time in sys.dm_exec_sessions is veel vroeger dan de huidige tijd.

Gedetailleerde blokkeringsscenario's

Scenario 1: blokkering veroorzaakt door een normaal lopende query met een lange uitvoeringstijd

In dit scenario heeft een actief uitgevoerde query vergrendelingen verkregen en worden de vergrendelingen niet vrijgegeven (dit wordt beïnvloed door het transactie-isolatieniveau). Andere sessies wachten dus op de vergrendelingen totdat ze worden vrijgegeven.

Oplossing:

De oplossing voor dit blokkeringsprobleem is om te zoeken naar manieren om de query te optimaliseren. Deze klasse van blokkeringsproblemen kan een prestatieprobleem zijn en vereist dat u het als zodanig behandelt. Voor informatie over het oplossen van problemen met een specifieke trage query, raadpleeg Problemen oplossen met trage query's op SQL Server. Raadpleeg Bewaken en afstemmen op prestaties voor meer informatie.

Rapporten die zijn ingebouwd in SSMS vanuit de Query Store (geïntroduceerd in SQL Server 2016) zijn ook een zeer aan te bevelen en waardevol hulpmiddel voor het identificeren van de meest kostbare query's en suboptimale uitvoeringsplannen.

Als u een langlopende query heeft die andere gebruikers blokkeert en deze niet kan worden geoptimaliseerd, kunt u overwegen deze van een OLTP-omgeving naar een speciaal rapportagesysteem te verplaatsen. U kunt Always On-beschikbaarheidsgroepen ook gebruiken om een alleen-lezen replica van de database te synchroniseren.

Opmerking

Blokkering tijdens het uitvoeren van query's kan worden veroorzaakt door query-escalatie, een scenario waarbij rij- of paginavergrendelingen escaleerden naar tabelvergrendelingen. Microsoft SQL Server bepaalt dynamisch wanneer vergrendelingsescalatie moet worden uitgevoerd. De eenvoudigste en veiligste manier om escalatie van vergrendelingen te voorkomen, is door transacties kort te houden en de voetafdruk van dure query's te verkleinen, zodat de drempels voor vergrendelingsescalatie niet worden overschreden. Voor meer informatie over het detecteren en voorkomen van buitensporige vergrendelingsescalatie, raadpleeg Oplossen van blokkeringsproblemen veroorzaakt door vergrendelingsescalaties.

Scenario 2: blokkering veroorzaakt door een slapende SPID met een niet-vastgelegde transactie

Dit type blokkering kan vaak worden geïdentificeerd door een SPID in slaapstand of die wacht op een opdracht, maar waarvan het nestingniveau van de transactie (@@TRANCOUNT, open_transaction_count van sys.dm_exec_requests) groter is dan nul. Deze situatie kan optreden als de toepassing een querytime-out ervaart of een annulering uitvoert zonder het vereiste aantal ROLLBACK- en/of COMMIT-instructies. Wanneer een SPID een time-out van de query of een annulering ontvangt, worden de huidige query en batch beëindigd, maar wordt de transactie niet automatisch teruggedraaid of doorgevoerd. De toepassing is hiervoor verantwoordelijk, omdat SQL Server er niet vanuit kan gaan dat een hele transactie moet worden teruggedraaid omdat een enkele query wordt geannuleerd. De time-out of annulering van de query wordt weergegeven als een ATTENTION-signaalgebeurtenis voor de SPID in de uitgebreide gebeurtenissessie.

Voer de volgende query uit om een niet-doorgevoerde expliciete transactie aan te tonen:

CREATE TABLE #test (col1 INT);
INSERT INTO #test SELECT 1;
GO
BEGIN TRAN
UPDATE #test SET col1 = 2 where col1 = 1;

Voer vervolgens deze query uit in hetzelfde venster:

SELECT @@TRANCOUNT;
ROLLBACK TRAN
DROP TABLE #test;

De uitvoer van de tweede query geeft aan dat het aantal transacties één is. Alle vergrendelingen die in de transactie zijn verkregen, worden nog steeds vastgehouden totdat de transactie is doorgevoerd of teruggedraaid. Als toepassingen expliciet transacties openen en vastleggen, kan een communicatiefout of een andere fout de sessie en de transactie in een open status laten.

Gebruik het script eerder in dit artikel op basis van sys.dm_tran_active_transactions om momenteel niet-doorgevoerde transacties in de instantie te identificeren.

Oplossingen:

  • Bovendien kan deze klasse van blokkeringsproblemen ook een prestatieprobleem zijn en moet u dit als zodanig behandelen. Als de uitvoeringstijd van de query kan worden verkort, vindt de time-out of annulering van de query mogelijk niet plaats. Het is belangrijk dat de toepassing de time-out- of annuleringsscenario's aankan als deze zich voordoen, maar het kan ook nuttig zijn om de prestaties van de query te onderzoeken.

  • Toepassingen moeten de nestingniveaus van transacties goed beheren, anders kunnen ze een blokkeringsprobleem veroorzaken na het op deze manier annuleren van de query. Overweeg het volgende:

    • Voer in de foutafhandler van de clienttoepassing IF @@TRANCOUNT > 0 ROLLBACK TRAN uit na een fout, zelfs als de clienttoepassing niet denkt dat er een transactie is geopend. Controleren op openstaande transacties is vereist, omdat een opgeslagen procedure die tijdens de batch wordt aangeroepen, een transactie kan hebben gestart zonder medeweten van de clienttoepassing. Bepaalde voorwaarden, zoals het annuleren van de query, voorkomen dat de procedure wordt uitgevoerd voorbij de huidige instructie, dus zelfs als de procedure logica heeft om IF @@ERROR <> 0 te controleren en de transactie af te breken, wordt deze terugdraaicode in dergelijke gevallen niet uitgevoerd.

    • Als groepsgewijze verbinding wordt gebruikt in een toepassing die de verbinding opent en een paar query's uitvoert voordat de verbinding weer wordt vrijgegeven aan de groep, zoals een webtoepassing, kan het tijdelijk uitschakelen van de groepsgewijze verbinding het probleem helpen verlichten totdat de clienttoepassing is gewijzigd om de fouten op de juiste manier af te handelen. Door groepsgewijze verbinding uit te schakelen, zal het vrijgeven van de verbinding leiden tot een fysieke verbreking van de SQL Server-verbinding, waardoor de server alle openstaande transacties terugdraait.

    • Gebruik SET XACT_ABORT ON voor de verbinding of in alle opgeslagen procedures die transacties starten en niet worden opgeschoond na een fout. In het geval van een runtimefout worden met deze instelling alle geopende transacties afgebroken en wordt het besturingselement geretourneerd naar de client. Raadpleeg SET XACT_ABORT (Transact-SQL) voor meer informatie.

Opmerking

De verbinding wordt pas opnieuw ingesteld als deze opnieuw wordt gebruikt vanuit de verbindingsgroep. Het is dus mogelijk dat een gebruiker een transactie kan openen en vervolgens de verbinding met de verbindingsgroep kan vrijgeven, maar deze mogelijk enkele seconden niet opnieuw kan worden gebruikt, gedurende welke tijd de transactie open blijft. Als de verbinding niet opnieuw wordt gebruikt, wordt de transactie afgebroken wanneer er een time-out voor de verbinding optreedt en wordt deze uit de verbindingsgroep verwijderd. Het is dus optimaal voor de clienttoepassing om transacties af te breken in hun handler voor fouten of SET XACT_ABORT ON te gebruiken om deze mogelijke vertraging te vermijden.

Voorzichtigheid

Na SET XACT_ABORT ON worden T-SQL-instructies die volgen op een instructie die een fout veroorzaakt, niet uitgevoerd. Dit kan van invloed zijn op de beoogde stroom van bestaande code.

Scenario 3: blokkering veroorzaakt door een SPID waarvan de bijbehorende clienttoepassing niet alle resultaatrijen heeft opgehaald

Nadat een query naar de server is verzonden, moeten alle toepassingen onmiddellijk alle resultaatrijen ophalen om te voltooien. Als een toepassing niet alle resultaatrijen ophaalt, kunnen er vergrendelingen op de tabellen blijven staan, waardoor andere gebruikers worden geblokkeerd. Als u een toepassing gebruikt die op transparante wijze SQL-instructies naar de server verzendt, moet de toepassing alle resultaatrijen ophalen. Als dit niet het geval is (en als dit niet kan worden geconfigureerd), kunt u het blokkeringsprobleem mogelijk niet oplossen. Om het probleem te voorkomen, kunt u slecht functionerende toepassingen beperken tot een rapportage- of een beslissingsondersteunende database, los van de hoofd-OLTP-database.

Oplossing:

De toepassing moet worden herschreven om alle rijen van het resultaat volledig op te halen voor voltooiing. Dit sluit het gebruik van OFFSET en FETCH in de ORDER BY-component van een query niet uit om paging aan de kant van de server uit te voeren.

Scenario 4: blokkering veroorzaakt door een gedistribueerde client/server-impasse

In tegenstelling tot een conventionele impasse, kan een gedistribueerde impasse niet worden gedetecteerd met behulp van de RDBMS-vergrendelingsmanager. Dit komt omdat slechts een van de bronnen die bij de impasse betrokken zijn, een SQL Server-vergrendeling is. De andere kant van de impasse bevindt zich op het niveau van de clienttoepassing, waarover SQL Server geen controle heeft. De volgende twee secties laten voorbeelden zien van hoe dit kan gebeuren en mogelijke manieren waarop de toepassing dit kan voorkomen.

Voorbeeld A: Client/server gedistribueerde impasse met een enkele clientthread

Als de client meerdere open verbindingen heeft en één thread voor uitvoering, kan de volgende gedistribueerde impasse optreden. Let op, de term dbproc die hier wordt gebruikt, verwijst naar de clientverbindingsstructuur.

 SPID1------blocked on lock------->SPID2
   /\ (waiting to write results back to client)
   | 
   | |
   | | Server side
   | ================================|==================================
   | <-- single thread --> | Client side
   | \/
   dbproc1 <------------------- dbproc2
   (waiting to fetch (effectively blocked on dbproc1, awaiting
   next row) single thread of execution to run)

In het hierboven getoonde geval heeft een enkele clienttoepassingsthread twee open verbindingen. Er wordt asynchroon een SQL-bewerking op dbproc1 verzonden. Dit betekent dat niet wordt gewacht op de oproep tot retourneren alvorens verder te gaan. De toepassing verzendt vervolgens nog een SQL-bewerking op dbproc2 en wacht op de resultaten om te beginnen met het verwerken van de geretourneerde gegevens. Wanneer gegevens terug beginnen te komen (welke dbproc het eerst reageert - neem aan dat dit dbproc1 is), verwerkt het alle gegevens die op die dbproc zijn geretourneerd tot voltooiing. Het haalt resultaten op van dbproc1 totdat SPID1 wordt geblokkeerd op een vergrendeling dat wordt vastgehouden door SPID2 (omdat de twee query's asynchroon op de server worden uitgevoerd). Op dit moment wacht dbproc1 voor onbepaalde tijd op meer gegevens. SPID2 wordt niet geblokkeerd op een vergrendeling, maar probeert gegevens te verzenden naar de client, dbproc2. Dbproc2 wordt echter effectief geblokkeerd op dbproc1 op de toepassingslaag, omdat de enige thread voor uitvoering voor de toepassing wordt gebruikt door dbproc1. Dit resulteert in een impasse die SQL Server niet kan detecteren of oplossen omdat slechts een van de betrokken bronnen een SQL Server-bron is.

Voorbeeld B: Client/server gedistribueerde impasse met een thread per verbinding

Zelfs als er een aparte thread bestaat voor elke verbinding op de client, kan er nog steeds een variatie op deze gedistribueerde impasse optreden, zoals hieronder wordt weergegeven.

SPID1------blocked on lock-------->SPID2
  /\ (waiting on net write) Server side
  | |
  | |
  | INSERT |SELECT
  | ================================|==================================
  | <-- thread per dbproc --> | Client side
  | \/
  dbproc1 <-----data row------- dbproc2
  (waiting on (blocked on dbproc1, waiting for it
  insert) to read the row from its buffer)

Dit geval is vergelijkbaar met voorbeeld A, behalve dat dbproc2 en SPID2 een SELECT instructie uitvoeren met de bedoeling om een rij per keer te verwerken en uit te voeren en elke rij door een buffer te geven aan dbproc1 voor een INSERT, UPDATEof DELETE instructie op dezelfde tabel. Uiteindelijk wordt SPID1 (het uitvoeren van de INSERT, UPDATEof DELETE) geblokkeerd op een vergrendeling die wordt vastgehouden door SPID2 (het uitvoeren van de SELECT). SPID2 schrijft een resultaatrij naar de client-dbproc2. Dbproc2 probeert vervolgens de rij in een buffer door te geven aan dbproc1, maar merkt dat dbproc1 bezet is (het wacht op SPID1 om de huidige INSERT te voltooien, die wordt geblokkeerd op SPID2). Op dit moment wordt dbproc2 op de toepassingslaag geblokkeerd door dbproc1 waarvan de SPID (SPID1) op databaseniveau wordt geblokkeerd door SPID2. Dit resulteert opnieuw in een impasse die SQL Server niet kan detecteren of oplossen omdat slechts een van de betrokken bronnen een SQL Server-bron is.

Beide voorbeelden A en B zijn fundamentele problemen waarvan toepassingsontwikkelaars op de hoogte moeten zijn. Ze moeten toepassingen coderen om deze zaken op de juiste manier af te handelen.

Oplossing:

Wanneer er een time-out voor een query is opgegeven en de gedistribueerde impasse optreedt, wordt deze verbroken wanneer er een time-out optreedt. Raadpleeg de documentatie van uw verbindingsprovider voor meer informatie over het gebruik van een time-out voor query's.

Scenario 5: blokkering veroorzaakt door een sessie met de status terugdraaien

Een gegevenswijzigingsquery die is beëindigd of geannuleerd buiten een door de gebruiker gedefinieerde transactie, wordt teruggedraaid. Dit kan ook optreden als een neveneffect van het verbreken van de verbinding met de netwerksessie van de client of wanneer een verzoek wordt geselecteerd als het impasseslachtoffer. Dit kan vaak worden geïdentificeerd door de uitvoer van sys.dm_exec_requests te observeren, wat het terugdraaien command kan aangeven en de kolom percent_complete kan de voortgang aangeven.

Een gegevenswijzigingsquery die is beëindigd of geannuleerd buiten een door de gebruiker gedefinieerde transactie, wordt teruggedraaid. Dit kan ook optreden als bijwerking van het opnieuw opstarten van de clientcomputer en het verbreken van de verbinding met de netwerksessie. Op dezelfde manier wordt een query die is geselecteerd als het impasseslachtoffer teruggedraaid. Een query voor gegevenswijziging kan vaak niet sneller worden teruggedraaid dan de wijzigingen die in eerste instantie waren toegepast. Als een instructie DELETE, INSERT of UPDATE bijvoorbeeld al een uur actief is, kan het minstens een uur duren om terug te draaien. Dit is verwacht gedrag, omdat de aangebrachte wijzigingen moeten worden teruggedraaid, anders worden transactionele en fysieke integriteit in de database aangetast. Omdat dit moet gebeuren, markeert SQL Server de SPID in een gouden of terugdraaistatus (wat betekent dat deze niet kan worden geannuleerd of geselecteerd als impasseslachtoffer). Dit kan vaak worden geïdentificeerd door de uitvoer van sp_who te observeren, wat kan duiden op de opdracht TERUGDRAAIEN. De kolom status van sys.dm_exec_sessions geeft status TERUGDRAAIEN aan.

Opmerking

Lange terugdraaiacties zijn zeldzaam wanneer de functie Accelerated Database Recovery is ingeschakeld. Deze functie is toegevoegd in SQL Server 2019.

Oplossing:

U moet wachten tot de sessie is voltooid om de aangebrachte wijzigingen terug te draaien.

Als de instantie halverwege deze bewerking wordt afgesloten, bevindt de database zich bij het opnieuw opstarten in de herstelmodus en is deze niet toegankelijk totdat alle openstaande transacties zijn verwerkt. Opstartherstel kost in wezen dezelfde hoeveelheid tijd per transactie als runtimeherstel en de database is gedurende deze periode niet toegankelijk. Het afdwingen van de server om een SPID in een terugdraaistatus op te lossen, werkt dus vaak contraproductief. In SQL Server 2019 met Accelerated Database Recovery ingeschakeld, mag dit niet gebeuren.

Om deze situatie te voorkomen, mag u tijdens drukke uren op OLTP-systemen geen grote batch-schrijfbewerkingen of indexcreatie- of onderhoudsbewerkingen uitvoeren. Voer dergelijke operaties indien mogelijk uit tijdens perioden van geringe activiteit.

Scenario 6: Blokkering veroorzaakt door een zwevende transactie

Dit is een veelvoorkomend probleemscenario en overlapt gedeeltelijk met scenario 2. Als de clienttoepassing stopt, het clientwerkstation opnieuw wordt opgestart of als er een batch-afbrekende fout is, kunnen deze allemaal een transactie open laten. Deze situatie kan optreden als de toepassing de transactie in de CATCH- of FINALLY-blokken van de toepassing niet terugdraait of als deze situatie op een andere manier niet wordt afgehandeld.

In dit scenario, terwijl de uitvoering van een SQL-batch is geannuleerd, laat de toepassing de SQL-transactie open. Vanuit het perspectief van de SQL Server-instantie lijkt de client nog steeds aanwezig te zijn en worden alle verkregen vergrendelingen behouden.

Om een zwevende transactie te demonstreren, voert u de volgende query uit, die een batchafbrekende fout simuleert door gegevens in een niet-bestaande tabel in te voegen:

CREATE TABLE #test2 (col1 INT);
INSERT INTO #test2 SELECT 1;
go
BEGIN TRAN
UPDATE #test2 SET col1 = 2 where col1 = 1;
INSERT INTO #NonExistentTable values (10)

Voer vervolgens deze query uit in hetzelfde venster:

SELECT @@TRANCOUNT;

De uitvoer van de tweede query geeft aan dat het aantal transacties één is. Alle vergrendelingen die in de transactie zijn verkregen, worden nog steeds vastgehouden totdat de transactie is doorgevoerd of teruggedraaid. Aangezien de batch al door de query is afgebroken, kan de toepassing die deze uitvoert doorgaan met het uitvoeren van andere query's in dezelfde sessie zonder de transactie die nog open staat op te schonen. De vergrendeling wordt bewaard totdat de sessie wordt beëindigd of de SQL Server-instantie opnieuw wordt gestart.

Oplossingen:

  • De beste manier om deze toestand te voorkomen, is door de verwerking van toepassingsfouten/uitzonderingen te verbeteren, met name voor onverwachte beëindigingen. Zorg ervoor dat u een Try-Catch-Finally-blok in de toepassingscode gebruikt en de transactie terugdraait in het geval van een uitzondering.
  • Overweeg om SET XACT_ABORT ON te gebruiken voor de sessie of in alle opgeslagen procedures die transacties starten en niet worden opgeschoond na een fout. In het geval van een runtime-fout die de batch afbreekt, zal deze instelling automatisch alle openstaande transacties terugdraaien en de controle teruggeven aan de client. Raadpleeg SET XACT_ABORT (Transact-SQL) voor meer informatie.
  • Om een zwevende verbinding op te lossen van een clienttoepassing die de verbinding heeft verbroken zonder de bronnen op de juiste manier op te schonen, kunt u de SPID beëindigen met de opdracht KILL. Raadpleeg KILL (Transact-SQL) voor meer informatie.

De opdracht KILL neemt de SPID-waarde als invoer. Als u SPID 9 bijvoorbeeld wilt beëindigen, voert u de volgende opdracht uit:

KILL 99

Opmerking

Het voltooien van de opdracht KILL kan tot 30 seconden duren, vanwege het interval tussen de controles voor de opdracht KILL.

Zie ook