Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
van toepassing op: SQL Server, Azure SQL Managed Instance
Oorspronkelijk KB-nummer: 224453
Overzicht
Blokkeren is normaal in elke relationele database op basis van vergrendeling, maar permanente of langlopende blokkeren verslechtert de doorvoer en veroorzaakt time-outs van toepassingen. In dit artikel wordt beschreven wat blokkeren is in SQL Server, hoe u blokkeringsgegevens van dynamische beheerweergaven (DMV's) en uitgebreide gebeurtenissen kunt vastleggen en hoe u de hoofdblokkering kunt identificeren. Vervolgens worden de meest voorkomende blokkeringsscenario's en hun oplossingen beschreven.
In dit artikel verwijst de term verbinding naar één aangemelde sessie van de database. Elke verbinding wordt weergegeven als een sessie-id (SPID) of session_id in veel DMV's. Elke SPID wordt vaak een 'proces' genoemd, maar het is geen afzonderlijk besturingssysteemproces. Dit zijn de resources en gegevensstructuren aan de serverzijde die één clientverbinding gebruiken. Eén clienttoepassing kan een of meer verbindingen hebben. Vanuit SQL Server perspectief worden verbindingen van veel clients en veel verbindingen van één client hetzelfde behandeld. Eén verbinding kan een andere verbinding blokkeren, ongeacht de bronclient.
Notitie
Dit artikel is gericht op SQL Server exemplaren, waaronder Azure SQL Managed Instance. 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 is blokkering
Blokkering is een onvermijdbaar en zo ontworpen kenmerk van elk relationeel databasebeheersysteem (RDBMS) met gelijktijdigheid op basis van vergrendeling. 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 sessie die eigenaar is, de vergrendeling vrijgeeft, is de tweede verbinding vervolgens vrij om een eigen vergrendeling op de resource te verkrijgen en kan de verwerking worden voortgezet. 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 worden uitgevoerd binnen een transactie, het type query, het niveau van transactieisolatie en of vergrendelingshints worden gebruikt in de query, bepaalt u de duur waarvoor de vergrendelingen worden bewaard. Raadpleeg de volgende artikelen voor een beschrijving van vergrendeling, vergrendelingstips en transactie-isolatieniveaus:
- Vergrendeling in de database-engine
- Vergrendelen en rijversies aanpassen
- Vergrendelingsmodi
- Compatibiliteit vergrendelen
- Op rijversies gebaseerde isolatieniveaus in de database-engine
- Transacties
Wanneer vergrendeling en blokkering behouden blijven tot het punt waar ze een schadelijk effect op de systeemprestaties veroorzaken, hebben ze 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
Wanneer u een blokkeringsprobleem ondervindt, kunt u zich richten op problemen met het afstemmen aan de serverzijde en het platform. Het is echter mogelijk dat de database alleen aandacht besteedt aan een oplossing. Het kan tijd en energie opnemen die u beter kunt gebruiken bij het onderzoeken van de clienttoepassing en de query's die het verzendt. Ongeacht het zichtbaarheidsniveau van de toepassing met betrekking tot de databaseaanroepen die de toepassing maakt, vereist een blokkerend probleem vaak zowel de inspectie van de exacte SQL-instructies die de toepassing indient als het exacte gedrag van de toepassing met betrekking tot het annuleren van query's, verbindingsbeheer, het ophalen van alle resultaatrijen, enzovoort. Als het ontwikkelprogramma geen expliciete controle toestaat over verbindingsbeheer, annulering van query's, time-out van query's, ophalen van resultaten, enzovoort, kunt u mogelijk geen blokkeringsproblemen oplossen. Bekijk dit potentieel nauwkeurig voordat u een hulpprogramma voor het ontwikkelen van toepassingen selecteert voor SQL Server, met name voor prestatiegevoelige OLTP-omgevingen.
Let op de prestaties van de database tijdens de ontwerp- en bouwfase van de database en de toepassing. Evalueer met name het resourceverbruik, het isolatieniveau en de lengte van het transactiepad voor elke query. Elke query en transactie moeten zo licht mogelijk zijn. Oefen een goede discipline voor verbindingsbeheer. Zonder dit is het mogelijk dat de toepassing acceptabele prestaties heeft bij een laag aantal gebruikers, maar de prestaties kunnen aanzienlijk afnemen naarmate het aantal gebruikers omhoog wordt geschaald.
Met het juiste toepassings- en queryontwerp kan SQL Server vele duizenden gelijktijdige gebruikers op één server ondersteunen, met weinig blokkeren.
Problemen met blokkeren oplossen
Ongeacht de blokkerende situatie waarin u zich bevindt, is de methodologie voor het oplossen van vergrendelingen hetzelfde. Deze logische scheidingen bepalen de rest van de samenstelling van dit artikel. Het concept is om de hoofdblokkering te vinden en te identificeren wat die query doet en waarom deze blokkeert. Zodra u de problematische query hebt geïdentificeerd (wat vergrendelingen voor de langere periode vasthoudt), moet u de volgende stap analyseren en bepalen waarom de blokkering plaatsvindt. Nadat u weet waarom, kunt u wijzigingen aanbrengen door de query en de transactie opnieuw te ontwerpen.
Stappen voor het oplossen van problemen:
Identificeer de hoofdblokkeringssessie (hoofdblokkering).
Zoek de query en transactie die de blokkering veroorzaken (wat vergrendelingen voor een langere periode bevat).
Analyseren en begrijpen waarom de langdurige blokkering plaatsvindt.
Los het blokkeringsprobleem op 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 methode is het uitvoeren van query's op dynamische beheerobjecten (DMO's) en het opslaan van de resultaten 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 methode is het gebruik van Extended Events (XEvents) of SQL Profiler Traces om vast te leggen wat er wordt uitgevoerd. Omdat SQL Trace en SQL Server Profiler afgeschaft zijn, is deze gids voor probleemoplossing gericht op XEvents.
Informatie verzamelen van DMV's
Als u problemen met blokkeren wilt oplossen, gebruikt u DMV's om de SPID (sessie-id) aan het hoofd van de blokkerende keten en de SQL-instructie te identificeren. Zoek naar slachtoffer SPID's die zijn geblokkeerd. Als een SPID wordt geblokkeerd door een andere SPID, onderzoekt u de SPID die eigenaar is van de resource (de blokkerende SPID). Is die eigenaar SPID ook geblokkeerd? U kunt de ketting volgen om de hoofdblokkering te vinden en vervolgens te onderzoeken waarom deze de vergrendeling onderhoudt.
Gebruik hiervoor een van de volgende methoden:
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 Activiteitsmonitor in SSMS en raadpleeg de kolom Geblokkeerd door . Zie Activiteitsmonitor voor meer informatie.
Meer gedetailleerde op query's gebaseerde methoden zijn ook beschikbaar met behulp van DMV's:
De
sp_whoopdrachten zijnsp_who2oudere opdrachten die alle huidige sessies weergeven. De DMVsys.dm_exec_sessionsretourneert meer gegevens in een resultatenset die gemakkelijker te doorzoeken en te filteren is. U vindtsys.dm_exec_sessionsin de kern van andere query's.Als u al een bepaalde sessie hebt geïdentificeerd, gebruikt
DBCC INPUTBUFFER(<session_id>)u de laatste instructie die een sessie heeft ingediend. Vergelijkbare resultaten worden geretourneerd door desys.dm_exec_input_bufferfunctie dynamisch beheer (DMF), in een resultatenset die gemakkelijker te doorzoeken en te filteren is, waardoor de session_id en de request_id worden verstrekt. 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_requestskolom en verwijs naar deblocking_session_idkolom. Wanneerblocking_session_id= 0, wordt een sessie niet geblokkeerd. Terwijlsys.dm_exec_requestsalleen aanvragen worden weergegeven die momenteel worden uitgevoerd,sys.dm_exec_sessionsworden alle verbindingen (actief of niet) weergegeven. Bouw voort op deze gemeenschappelijke join tussensys.dm_exec_requestsensys.dm_exec_sessionsin de volgende query. Houd er rekening mee dat de query actief moet worden uitgevoerdsys.dm_exec_requestsmet 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
textvansys.dm_exec_sql_textNULL zijn, wordt de query momenteel niet uitgevoerd. In dat geval bevat de kolomevent_infovansys.dm_exec_input_bufferde 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;
- Om langlopende of niet-vastgelegde transacties op te vangen, gebruikt u een andere set DMV's om huidige openstaande transacties te bekijken, waaronder sys.dm_tran_database_transactions, sys.dm_tran_session_transactions, sys.dm_exec_connections en
sys.dm_exec_sql_text. Zie DMV's voor transacties voor meer DMV's die zijn gekoppeld aan traceringstransacties.
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];
- Verwijs sys.dm_os_waiting_tasks die zich in de thread-/taaklaag van SQL Server bevindt. Deze DMV retourneert informatie over wat SQL wait_type de aanvraag momenteel ondervindt. Net als
sys.dm_exec_requests, worden alleen actieve aanvragen geretourneerd doorsys.dm_os_waiting_tasks.
Notitie
Zie de DMV-sys.dm_db_wait_stats voor meer informatie over wachttypen, waaronder 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 de voorgaande informatie moet u vaak een tracering van de activiteiten op de server vastleggen om een blokkerend probleem in SQL Server grondig te onderzoeken. Als een sessie bijvoorbeeld meerdere instructies binnen een transactie uitvoert, wordt alleen de laatste instructie weergegeven die de sessie indient. Een van de eerdere instructies kan echter de reden zijn dat vergrendelingen nog steeds worden bewaard. Met een tracering kunt u alle opdrachten zien die een sessie binnen de huidige transactie uitvoert.
U kunt traceringen vastleggen in SQL Server met behulp van twee methoden: Extended Events (XEvents) en Profiler Traces. SQL-traceringen die gebruikmaken van de SQL Server Profiler, worden echter afgeschaft. XEvents is het nieuwere, superieure traceringsplatform dat meer veelzijdigheid en minder impact biedt op het waargenomen systeem. De interface is geïntegreerd in SSMS.
In Objectverkenner vindt u in het menu voor XEvent Profiler vooraf gemaakte uitgebreide gebeurtenissessies die klaar zijn om te starten in SSMS. Raadpleeg XEvent Profiler voor meer informatie. U kunt ook uw eigen aangepaste uitgebreide gebeurtenissessies maken in SSMS. Zie de wizard Uitgebreide gebeurtenissen voor nieuwe sessie voor meer informatie. Voor het oplossen van blokkeringsproblemen kunt u meestal het volgende vastleggen:
- Categoriefouten:
- Opmerking
- 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 voorgaande informatie te bekijken, kunt u de oorzaak van de meeste blokkeringsproblemen bepalen. In de rest van dit artikel wordt beschreven 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 wordt verwezen) gebruikt om informatie vast te leggen over de blokkerende SPID's en toepassingsactiviteit vast te leggen met behulp van een XEvent-sessie.
Blokkerende gegevens analyseren
Bekijk de uitvoer van de DMV's
sys.dm_exec_requestsensys.dm_exec_sessionsom het begin van de blokkeerketens te bepalen, met behulp vanblocking_theseensession_id. Deze uitvoer geeft duidelijk aan welke aanvragen worden geblokkeerd en welke blokkeren. 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_requestsensys.dm_exec_sessionsvoor informatie over de SPID's aan het begin van de blokkeringsketen. Zoek de volgende kolommen:sys.dm_exec_requests.statusIn deze kolom ziet u de status van een bepaalde aanvraag. Normaal gesproken geeft een slaapstatus aan dat de uitvoering van SPID is voltooid en wacht totdat de toepassing een andere query of batch verzendt. 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. Slaapstatus De SPID wordt momenteel niet uitgevoerd. Dit geeft meestal aan dat de SPID wacht op een opdracht van de toepassing. Wordt uitgevoerd De SPID wordt momenteel uitgevoerd op een scheduler. Kan worden uitgevoerd De SPID bevindt zich in de uitvoerbare wachtrij van een scheduler en wacht tot de tijd van de scheduler wordt opgehaald. Onderbroken De SPID wacht op een resource, zoals een vergrendeling. sys.dm_exec_sessions.open_transaction_countIn deze kolom ziet u het aantal geopende transacties in deze sessie. Als deze waarde groter is dan 0, bevindt de SPID zich binnen een geopende transactie en kan deze sloten bevatten die zijn verkregen door een instructie binnen de transactie. De open transactie kan worden gemaakt door een momenteel actieve instructie of door een instructieaanvraag die in het verleden is uitgevoerd en niet langer actief is.
sys.dm_exec_requests.open_transaction_countOp dezelfde manier toont deze kolom het aantal geopende transacties in deze aanvraag. Als deze waarde groter is dan 0, bevindt de SPID zich binnen een geopende transactie en kan deze vergrendelingen bevatten die zijn verkregen door een actieve instructie binnen de transactie. In tegenstelling tot
sys.dm_exec_sessions.open_transaction_count, als er geen actieve aanvraag is, wordt in deze kolom 0 weergegeven.sys.dm_exec_requests.wait_type,wait_timeenlast_wait_typeAls de
sys.dm_exec_requests.wait_typewaarde NULL is, wacht de aanvraag momenteel niet op iets. Delast_wait_typewaarde geeft het laatstewait_typeaan dat de aanvraag is aangetroffen. Raadpleegsys.dm_os_wait_statsvoor meer informatie over en een beschrijving van de meest voorkomende wachttypen. De waardewait_timekan worden gebruikt om te bepalen of de aanvraag voortgang maakt. Wanneer een query voor desys.dm_exec_requeststabel een waarde retourneert in dewait_timekolom die kleiner is dan dewait_timewaarde van een vorige querysys.dm_exec_requests, geeft deze voorwaarde aan dat de eerdere vergrendeling is verkregen en vrijgegeven en nu wacht op een nieuwe vergrendeling (uitgaande van niet-nulwait_time). U kunt deze voorwaarde controleren door dewait_resourcesys.dm_exec_requestsuitvoer te vergelijken, waarin de resource wordt weergegeven waarvoor de aanvraag wacht.sys.dm_exec_requests.wait_resourceDeze kolom geeft de resource aan waarop een geblokkeerde aanvraag wacht. De volgende tabel geeft een overzicht van veelvoorkomende
wait_resourceindelingen en hun betekenis:Bron Format Voorbeeld Uitleg Tabel DatabaseID:ObjectID:IndexID TABBLAD: 5:261575970:1 In dit geval is database-ID 5 de pubs-voorbeelddatabase en is object_id261575970 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_resourcedoorgeeft.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.partitionscatalogusweergave om dehobt_idte koppelen aan een bepaaldeindex_idenobject_idEr 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. sys.dm_tran_active_transactionsDe 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;Overige 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 ingediendlast_request_start_timewas insys.dm_exec_sessions, hoe lang een verzoek actief was metstart_timeinsys.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. De kolom 'Lost?' geeft aan of de blokkering zelfstandig wordt omgezet of dat de sessie via de KILL opdracht moet worden gedood. 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:
U kunt dit blokkeringsprobleem oplossen door de query te optimaliseren. Dit type blokkerende probleem kan een prestatieprobleem zijn. Behandel het als zodanig. 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 aanbevolen en waardevol hulpprogramma voor het identificeren van de meest kostbare query's en suboptimale uitvoeringsplannen.
Als u een langlopende query hebt die andere gebruikers blokkeert en u deze niet kunt optimaliseren, kunt u overwegen om deze te verplaatsen van een OLTP-omgeving naar een toegewezen rapportagesysteem. U kunt Always On-beschikbaarheidsgroepen ook gebruiken om een alleen-lezen replica van de database te synchroniseren.
Notitie
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
U kunt dit type blokkering vaak identificeren door een SPID die slaapt of wacht op een opdracht met een transactie genest niveau (@@TRANCOUNT, open_transaction_count van sys.dm_exec_requests) groter 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 voor een query of een annulering ontvangt, wordt de huidige query en batch beëindigd, maar wordt de transactie niet automatisch teruggedraaid of doorgevoerd. De toepassing is verantwoordelijk voor deze actie, omdat SQL Server niet kan aannemen dat een volledige transactie moet worden teruggedraaid omdat één 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:
Deze klasse van blokkerende problemen kan ook een prestatieprobleem zijn. Als u de uitvoeringstijd van de query kunt verminderen, kan de time-out van de query of annuleren niet optreden. Het is belangrijk dat de toepassing de time-out- of annuleringsscenario's kan verwerken als deze zich voordoen, maar u kunt ook profiteren van het onderzoeken van de prestaties van de query.
Toepassingen moeten de nestniveaus van transacties op de juiste manier beheren, of ze kunnen een blokkerend probleem veroorzaken na de annulering van de query op deze manier. Overweeg de volgende:
Voer in de foutafhandler van de clienttoepassing
IF @@TRANCOUNT > 0 ROLLBACK TRANuit 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 omIF @@ERROR <> 0te controleren en de transactie af te breken, wordt deze terugdraaicode in dergelijke gevallen niet uitgevoerd.Als u groepsgewijze verbindingen gebruikt in een toepassing waarmee de verbinding wordt geopend en een paar query's worden uitgevoerd voordat de verbinding met de groep wordt vrijgegeven, zoals een webtoepassing, kan het tijdelijk uitschakelen van verbindingspooling helpen het probleem te verhelpen totdat de clienttoepassing is gewijzigd om de fouten op de juiste manier af te handelen. Door groepsgewijze verbindingen uit te schakelen, veroorzaakt het vrijgeven van de verbinding een fysieke verbroken verbinding van de SQL Server verbinding, wat resulteert in het terugdraaien van geopende transacties op de server.
Gebruik
SET XACT_ABORT ONvoor de verbinding of in alle opgeslagen procedures die transacties starten en niet worden opgeschoond na een fout. In het geval van een runtimefout wordt met deze instelling alle geopende transacties afgebroken en wordt het besturingselement naar de client geretourneerd. Raadpleeg SET XACT_ABORT (Transact-SQL) voor meer informatie.
Notitie
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.
Let op
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: Blokkeren veroorzaakt door een SPID waarvan de bijbehorende clienttoepassing niet alle resultaatrijen tot voltooiing 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, kan deze vergrendelingen achterlaten in de tabellen die andere gebruikers blokkeren. Als u een toepassing gebruikt die op transparante wijze SQL-instructies naar de server verzendt, moet de toepassing alle resultaatrijen ophalen. Als dit niet zo is (en als dit niet kan worden geconfigureerd), kunt u het blokkeringsprobleem mogelijk niet oplossen. Om het probleem te voorkomen, beperkt u slecht gedragen toepassingen tot een rapportage- of beslissingsondersteuningsdatabase, gescheiden van de primaire OLTP-database.
Oplossing:
Herschrijf de toepassing om alle rijen van het resultaat op te halen tot 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 geopende verbindingen en één thread met uitvoering heeft, 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 voorgaande geval heeft één clienttoepassingsthread twee geopende 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 de gegevens terugkomen (afhankelijk van wat dbproc het eerst reageert; stel dat dit dbproc1 is), worden alle gegevens die op die dbproc worden geretourneerd, voltooid. 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. Deze situatie resulteert in een impasse die SQL Server niet kan detecteren of oplossen omdat slechts een van de betrokken resources een SQL Server resource is.
Voorbeeld B: Client/server gedistribueerde impasse met een thread per verbinding
Zelfs als er een afzonderlijke thread bestaat voor elke verbinding op de client, kan er nog steeds een variant van deze gedistribueerde impasse optreden, zoals wordt weergegeven in het volgende voorbeeld.
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 u een time-out voor query's opgeeft, breekt de time-out de impasse als de gedistribueerde impasse zich voordoet. Zie 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
Wanneer u een gegevenswijzigingsquery buiten een door de gebruiker gedefinieerde transactie beëindigt of annuleert, wordt de query teruggedraaid. Deze terugdraaiactie kan ook optreden als een neveneffect van de verbinding met de clientnetwerksessie, het opnieuw opstarten van de clientcomputer of het verzoek dat wordt geselecteerd als een impasseslachtoffer. U kunt een gegevenswijzigingsquery vaak niet sneller terugdraaien dan de wijzigingen in eerste instantie zijn toegepast. Als een , INSERTof UPDATE instructie DELETEbijvoorbeeld een uur is uitgevoerd, kan het minstens een uur duren om terug te draaien. Dit gedrag wordt verwacht, omdat als de wijzigingen niet worden teruggedraaid, transactionele en fysieke integriteit in de database worden aangetast. Omdat deze terugdraaiactie moet worden voltooid, SQL Server de sessie in de status KILL/ROLLBACK markeert en u kunt de sessie niet opnieuw beëindigen of deze als een impasseslachtoffer selecteren. U kunt deze status vaak identificeren door de uitvoer van sys.dm_exec_requests, waar de command kolomrapporten KILLED/ROLLBACK en de kolom de percent_complete voortgang tonen.
Notitie
Lange terugdraaiacties zijn zeldzaam wanneer de functie Accelerated Database Recovery is ingeschakeld. Deze functie is toegevoegd in SQL Server 2019.
Oplossing:
Wacht totdat de sessie klaar is met het terugdraaien van de wijzigingen.
Als u het exemplaar in het midden van deze bewerking afsluit, bevindt de database zich in de herstelmodus bij het opnieuw opstarten en is deze niet toegankelijk totdat alle geopende transacties worden 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 te herstellen, is dus vaak contraproductief. In SQL Server 2019 waarvoor versneld databaseherstel is ingeschakeld, mag deze situatie niet optreden.
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 voorwaarde te voorkomen, is door toepassingsfouten en uitzonderingsafhandeling 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 ONte gebruiken voor de sessie of in alle opgeslagen procedures die transacties starten en niet worden opgeschoond na een fout. In het geval van een runtimefout die de batch afbreekt, wordt met deze instelling automatisch alle geopende transacties teruggedraaid en wordt het besturingselement teruggezet naar de client. Zie 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
Notitie
Het voltooien van de opdracht KILL kan tot 30 seconden duren, vanwege het interval tussen de controles voor de opdracht KILL.