SQL Server ne conserve pas d'historique des commandes qui ont été exécutées 1,2 . Vous pouvez déterminer quels objets ont des verrous, mais vous ne pouvez pas nécessairement voir quelle instruction a provoqué ces verrous.
Par exemple, si vous exécutez cette instruction:
BEGIN TRANSACTION
INSERT INTO dbo.TestLock DEFAULT VALUES
Et regardez le texte SQL via le plus récent descripteur SQL, vous verrez que cette déclaration apparaît. Cependant, si la session a fait ceci:
BEGIN TRANSACTION
INSERT INTO dbo.TestLock DEFAULT VALUES
GO
SELECT *
FROM dbo.TestLock;
GO
Vous ne verriez que l' SELECT * FROM dbo.TestLock;
instruction, même si la transaction n'a pas été validée, et l' INSERT
instruction bloque les lecteurs sur la dbo.TestLock
table.
J'utilise ceci pour rechercher des transactions non validées qui bloquent d'autres sessions:
/*
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;
Si nous configurons un banc d'essai simple dans SSMS avec quelques fenêtres de requête, nous pouvons voir que nous ne pouvons voir que la dernière instruction active.
Dans la première fenêtre de requête, exécutez ceci:
CREATE TABLE dbo.TestLock
(
id int NOT NULL IDENTITY(1,1)
);
BEGIN TRANSACTION
INSERT INTO dbo.TestLock DEFAULT VALUES
Dans la deuxième fenêtre, exécutez ceci:
SELECT *
FROM dbo.TestLock
Maintenant, si nous exécutons la requête de transactions de blocage non validée par le dessus, nous voyons la sortie suivante:
╔═══════════╦═══════════════════════════════╦═════ ═══════════════╦══════════════════════════════════ ═══════╗
║ SessionID ║ ItemType ║ BlockedBySessionID ║ QueryText ║
╠═══════════╬═══════════════════════════════╬═════ ═══════════════╬══════════════════════════════════ ═══════╣
║ 67 ║ Transaction ║ 0 ║ COMMENCER LA TRANSACTION ║
║ ║ ║ ║ INSÉRER DANS LES VALEURS PAR DÉFAUT dbo.TestLock ║
║ 68 ║ Demande de session, tâche en attente ║ 67 ║ SELECT * ║
║ ║ ║ ║ DE dbo.TestLock ║
╚═══════════╩═══════════════════════════════╩═════ ═══════════════╩══════════════════════════════════ ═══════╝
(J'ai supprimé certaines colonnes non pertinentes de la fin des résultats).
Maintenant, si nous changeons la première fenêtre de requête en ceci:
BEGIN TRANSACTION
INSERT INTO dbo.TestLock DEFAULT VALUES
GO
SELECT *
FROM dbo.TestLock;
GO
et réexécutez la deuxième fenêtre de requête:
SELECT *
FROM dbo.TestLock
Nous verrons cette sortie de la requête de blocage des transactions:
╔═══════════╦═══════════════════════════════╦═════ ═══════════════╦════════════════════╗
║ SessionID ║ ItemType ║ BlockedBySessionID ║ QueryText ║
╠═══════════╬═══════════════════════════════╬═════ ═══════════════╬════════════════════╣
║ 67 ║ Transaction ║ 0 ║ SELECT * ║
║ ║ ║ ║ DE dbo.TestLock; ║
║ 68 ║ Demande de session, tâche en attente ║ 67 ║ SELECT * ║
║ ║ ║ ║ DE dbo.TestLock ║
╚═══════════╩═══════════════════════════════╩═════ ═══════════════╩════════════════════╝
1 - pas entièrement vrai. Il y a le cache de procédure, qui peut contenir l'instruction responsable du verrou. Cependant, il peut ne pas être facile de déterminer quelle instruction est la cause réelle du verrouillage car il peut y avoir de nombreuses requêtes dans le cache qui touchent la ressource en question.
La requête ci-dessous montre le plan de requête pour les requêtes de test ci-dessus car mon cache de procédure n'est pas très occupé.
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;
Les résultats de cette requête peuvent vous permettre de trouver le coupable, mais sachez que l'inspection du cache de procédure comme celui-ci peut être assez exigeante sur un système occupé.
2 SQL Server 2016 et offre au- dessus du magasin de requête , qui ne conserve l' historique complet des requêtes exécutées.
Blocked Process Reports
fonctionnalité, pour trouver la cause première des scénarios de blocage en production. Chaque transaction exécute plusieurs requêtes, et la plupart du temps la dernière (qui est affichée sur le tampon d'entrée de BPR) est rarement celle qui détient le verrou. Il semble que ma dernière ressource pour résoudre ce problème soit de définir une session xEvents légère pour me dire quelles requêtes ont été exécutées sous chaque session. Si vous connaissez un article en montrant un exemple, je vous en serai reconnaissant.