SQL Server speichert keinen Verlauf der ausgeführten Befehle 1,2 . Sie können bestimmen, welche Objekte Sperren haben, aber Sie können nicht unbedingt sehen, welche Anweisung diese Sperren verursacht hat.
Wenn Sie beispielsweise diese Anweisung ausführen:
BEGIN TRANSACTION
INSERT INTO dbo.TestLock DEFAULT VALUES
Wenn Sie sich den SQL-Text über das neueste SQL-Handle ansehen, sehen Sie, dass die Anweisung angezeigt wird. Wenn die Sitzung dies jedoch tat:
BEGIN TRANSACTION
INSERT INTO dbo.TestLock DEFAULT VALUES
GO
SELECT *
FROM dbo.TestLock;
GO
Sie sehen nur die SELECT * FROM dbo.TestLock;
Anweisung, obwohl die Transaktion noch nicht festgeschrieben wurde, und die INSERT
Anweisung blockiert Leser für die dbo.TestLock
Tabelle.
Ich verwende dies, um nach nicht festgeschriebenen Transaktionen zu suchen, die andere Sitzungen blockieren:
/*
This query shows sessions that are blocking other sessions, including sessions that are
not currently processing requests (for instance, they have an open, uncommitted transaction).
By: Max Vernon, 2017-03-20
*/
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; --reduce possible blocking by this query.
USE tempdb;
IF OBJECT_ID('tempdb..#dm_tran_session_transactions') IS NOT NULL
DROP TABLE #dm_tran_session_transactions;
SELECT *
INTO #dm_tran_session_transactions
FROM sys.dm_tran_session_transactions;
IF OBJECT_ID('tempdb..#dm_exec_connections') IS NOT NULL
DROP TABLE #dm_exec_connections;
SELECT *
INTO #dm_exec_connections
FROM sys.dm_exec_connections;
IF OBJECT_ID('tempdb..#dm_os_waiting_tasks') IS NOT NULL
DROP TABLE #dm_os_waiting_tasks;
SELECT *
INTO #dm_os_waiting_tasks
FROM sys.dm_os_waiting_tasks;
IF OBJECT_ID('tempdb..#dm_exec_sessions') IS NOT NULL
DROP TABLE #dm_exec_sessions;
SELECT *
INTO #dm_exec_sessions
FROM sys.dm_exec_sessions;
IF OBJECT_ID('tempdb..#dm_exec_requests') IS NOT NULL
DROP TABLE #dm_exec_requests;
SELECT *
INTO #dm_exec_requests
FROM sys.dm_exec_requests;
;WITH IsolationLevels AS
(
SELECT v.*
FROM (VALUES
(0, 'Unspecified')
, (1, 'Read Uncomitted')
, (2, 'Read Committed')
, (3, 'Repeatable')
, (4, 'Serializable')
, (5, 'Snapshot')
) v(Level, Description)
)
, trans AS
(
SELECT dtst.session_id
, blocking_sesion_id = 0
, Type = 'Transaction'
, QueryText = dest.text
FROM #dm_tran_session_transactions dtst
LEFT JOIN #dm_exec_connections dec ON dtst.session_id = dec.session_id
OUTER APPLY sys.dm_exec_sql_text(dec.most_recent_sql_handle) dest
)
, tasks AS
(
SELECT dowt.session_id
, dowt.blocking_session_id
, Type = 'Waiting Task'
, QueryText = dest.text
FROM #dm_os_waiting_tasks dowt
LEFT JOIN #dm_exec_connections dec ON dowt.session_id = dec.session_id
OUTER APPLY sys.dm_exec_sql_text(dec.most_recent_sql_handle) dest
WHERE dowt.blocking_session_id IS NOT NULL
)
, requests AS
(
SELECT des.session_id
, der.blocking_session_id
, Type = 'Session Request'
, QueryText = dest.text
FROM #dm_exec_sessions des
INNER JOIN #dm_exec_requests der ON des.session_id = der.session_id
OUTER APPLY sys.dm_exec_sql_text(der.sql_handle) dest
WHERE der.blocking_session_id IS NOT NULL
AND der.blocking_session_id > 0
)
, Agg AS (
SELECT SessionID = tr.session_id
, ItemType = tr.Type
, CountOfBlockedSessions = (SELECT COUNT(*) FROM requests r WHERE r.blocking_session_id = tr.session_id)
, BlockedBySessionID = tr.blocking_sesion_id
, QueryText = tr.QueryText
FROM trans tr
WHERE EXISTS (
SELECT 1
FROM requests r
WHERE r.blocking_session_id = tr.session_id
)
UNION ALL
SELECT ta.session_id
, ta.Type
, CountOfBlockedSessions = (SELECT COUNT(*) FROM requests r WHERE r.blocking_session_id = ta.session_id)
, BlockedBySessionID = ta.blocking_session_id
, ta.QueryText
FROM tasks ta
UNION ALL
SELECT rq.session_id
, rq.Type
, CountOfBlockedSessions = (SELECT COUNT(*) FROM requests r WHERE r.blocking_session_id = rq.session_id)
, BlockedBySessionID = rq.blocking_session_id
, rq.QueryText
FROM requests rq
)
SELECT agg.SessionID
, ItemType = STUFF((SELECT ', ' + COALESCE(a.ItemType, '') FROM agg a WHERE a.SessionID = agg.SessionID ORDER BY a.ItemType FOR XML PATH ('')), 1, 2, '')
, agg.BlockedBySessionID
, agg.QueryText
, agg.CountOfBlockedSessions
, des.host_name
, des.login_name
, des.is_user_process
, des.program_name
, des.status
, TransactionIsolationLevel = il.Description
FROM agg
LEFT JOIN #dm_exec_sessions des ON agg.SessionID = des.session_id
LEFT JOIN IsolationLevels il ON des.transaction_isolation_level = il.Level
GROUP BY agg.SessionID
, agg.BlockedBySessionID
, agg.CountOfBlockedSessions
, agg.QueryText
, des.host_name
, des.login_name
, des.is_user_process
, des.program_name
, des.status
, il.Description
ORDER BY
agg.BlockedBySessionID
, agg.CountOfBlockedSessions
, agg.SessionID;
Wenn wir in SSMS eine einfache Testumgebung mit einigen Abfragefenstern einrichten, sehen wir, dass nur die zuletzt aktive Anweisung angezeigt wird.
Führen Sie im ersten Abfragefenster Folgendes aus:
CREATE TABLE dbo.TestLock
(
id int NOT NULL IDENTITY(1,1)
);
BEGIN TRANSACTION
INSERT INTO dbo.TestLock DEFAULT VALUES
Führen Sie im zweiten Fenster Folgendes aus:
SELECT *
FROM dbo.TestLock
Wenn wir nun die obige Abfrage für nicht festgeschriebene Blockierungstransaktionen ausführen, sehen wir die folgende Ausgabe:
╔═══════════╦═══════════════════════════════╦═════ ═══════════════╦══════════════════════════════════ ═══════╗
║ SessionID ║ ItemType ║ BlockedBySessionID ║ QueryText ║
╠═══════════╬═══════════════════════════════╬═════ ═══════════════╬══════════════════════════════════ ═══════╣
║ 67 ║ Transaktion ║ 0 ║ TRANSAKTION BEGINNEN ║
║ ║ ║ ║ INSERT IN dbo.TestLock DEFAULT VALUES ║
~ 68 ~ Sitzungsanforderung, wartende Aufgabe ~ 67 ~ AUSWÄHLEN * ~
║ ║ ║ ║ FROM dbo.TestLock ║
╚═══════════╩═══════════════════════════════╩═════ ═══════════════╩══════════════════════════════════ ═══════╝
(Ich habe einige irrelevante Spalten am Ende der Ergebnisse entfernt).
Wenn wir nun das erste Abfragefenster folgendermaßen ändern:
BEGIN TRANSACTION
INSERT INTO dbo.TestLock DEFAULT VALUES
GO
SELECT *
FROM dbo.TestLock;
GO
und führen Sie das 2. Abfragefenster erneut aus:
SELECT *
FROM dbo.TestLock
Diese Ausgabe wird in der Abfrage zum Sperren von Transaktionen angezeigt:
╔═══════════╦═══════════════════════════════╦═════ ═══════════════╦════════════════════╗
║ SessionID ║ ItemType ║ BlockedBySessionID ║ QueryText ║
╠═══════════╬═══════════════════════════════╬═════ ═══════════════╬════════════════════╣
~ 67 ~ Transaktion ~ 0 ~ AUSWÄHLEN * ~
║ ║ ║ ║ FROM dbo.TestLock; ║
~ 68 ~ Sitzungsanforderung, wartende Aufgabe ~ 67 ~ AUSWÄHLEN * ~
║ ║ ║ ║ FROM dbo.TestLock ║
╚═══════════╩═══════════════════════════════╩═════ ═══════════════╩════════════════════╝
1 - nicht ganz richtig. Es gibt den Prozedur-Cache, der möglicherweise die Anweisung enthält, die für die Sperre verantwortlich ist. Es ist jedoch möglicherweise nicht einfach zu bestimmen, welche Anweisung die eigentliche Ursache für die Sperre ist, da sich möglicherweise viele Abfragen im Cache befinden, die die betreffende Ressource berühren.
Die folgende Abfrage zeigt den Abfrageplan für die obigen Testabfragen, da mein Prozedurcache nicht sehr ausgelastet ist.
SELECT TOP(30) t.text
, p.query_plan
, deqs.execution_count
, deqs.total_elapsed_time
, deqs.total_logical_reads
, deqs.total_logical_writes
, deqs.total_logical_writes
, deqs.total_rows
, deqs.total_worker_time
, deqs.*
FROM sys.dm_exec_query_stats deqs
OUTER APPLY sys.dm_exec_sql_text(deqs.sql_handle) t
OUTER APPLY sys.dm_exec_query_plan(deqs.plan_handle) p
WHERE t.text LIKE '%dbo.TestLock%' --change this to suit your needs
AND t.text NOT LIKE '/\/\/\/\/EXCLUDE ME/\/\/\/\/\'
ORDER BY
deqs.total_worker_time DESC;
Mit den Ergebnissen dieser Abfrage können Sie möglicherweise den Täter finden. Beachten Sie jedoch, dass das Überprüfen des Prozedur-Cache auf einem ausgelasteten System sehr anstrengend sein kann.
2 SQL Server 2016 und höher bieten den Abfragespeicher , in dem der vollständige Verlauf der ausgeführten Abfragen gespeichert ist.
Blocked Process Reports
Features aufgeworfen, um die Hauptursache für Blockierungsszenarien in der Produktion zu finden. Jede Transaktion führt mehrere Abfragen aus. In den meisten Fällen ist die letzte Abfrage (die im Eingabepuffer von BPR angezeigt wird) selten die gesperrte. Es scheint, dass meine letzte Ressource zur Behebung dieses Problems darin besteht, eine einfache xEvents-Sitzung einzurichten, um mir mitzuteilen, welche Abfragen in jeder Sitzung ausgeführt wurden. Wenn Sie einen Artikel kennen, der ein Beispiel dafür zeigt, bin ich Ihnen dankbar.