SQL Server không lưu giữ lịch sử của các lệnh đã được thực thi 1,2 . Bạn có thể xác định đối tượng nào có khóa, nhưng bạn không nhất thiết phải xem câu lệnh nào gây ra các khóa đó.
Ví dụ: nếu bạn thực hiện câu lệnh này:
BEGIN TRANSACTION
INSERT INTO dbo.TestLock DEFAULT VALUES
Và nhìn vào Văn bản SQL thông qua trình xử lý sql gần đây nhất, bạn sẽ thấy câu lệnh đó xuất hiện. Tuy nhiên, nếu phiên làm việc này:
BEGIN TRANSACTION
INSERT INTO dbo.TestLock DEFAULT VALUES
GO
SELECT *
FROM dbo.TestLock;
GO
Bạn chỉ nhìn thấy SELECT * FROM dbo.TestLock;
tuyên bố, mặc dù giao dịch chưa được thực hiện và INSERT
tuyên bố đang chặn độc giả đối với dbo.TestLock
bảng.
Tôi sử dụng điều này để tìm kiếm các giao dịch không được cam kết đang chặn các phiên khác:
/*
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;
Nếu chúng ta thiết lập một giường thử nghiệm đơn giản trong SSMS với một vài cửa sổ truy vấn, chúng ta có thể thấy rằng chúng ta chỉ có thể thấy câu lệnh hoạt động gần đây nhất.
Trong cửa sổ truy vấn đầu tiên, hãy chạy này:
CREATE TABLE dbo.TestLock
(
id int NOT NULL IDENTITY(1,1)
);
BEGIN TRANSACTION
INSERT INTO dbo.TestLock DEFAULT VALUES
Trong cửa sổ thứ hai, chạy này:
SELECT *
FROM dbo.TestLock
Bây giờ, nếu chúng tôi chạy truy vấn chặn giao dịch không được cam kết từ phía trên, chúng tôi sẽ thấy đầu ra sau:
╔═══════════╦═══════════════════════════════╦═════ ═══════════════╦══════════════════════════════════ ═══════╗
║ SessionID ║ ItemType BlockedBySessionID ║ QueryText
╠═══════════╬═══════════════════════════════╬═════ ═══════════════╬══════════════════════════════════ ═══════╣
67 ║ Giao dịch 0 ║ GIAO DỊCH BEGIN
║ ║ XÁC NHẬN VÀO dbo.TestLock DEFAULT GIÁ TRỊ
68 Yêu cầu phiên, Nhiệm vụ chờ 67 ║ CHỌN *
║ ║ TỪ dbo.TestLock ║
╚═══════════╩═══════════════════════════════╩═════ ═══════════════╩══════════════════════════════════ ═══════╝
(Tôi đã xóa một số cột không liên quan từ cuối kết quả).
Bây giờ, nếu chúng ta thay đổi cửa sổ truy vấn đầu tiên thành này:
BEGIN TRANSACTION
INSERT INTO dbo.TestLock DEFAULT VALUES
GO
SELECT *
FROM dbo.TestLock;
GO
và chạy lại cửa sổ truy vấn thứ 2:
SELECT *
FROM dbo.TestLock
Chúng ta sẽ thấy đầu ra này từ truy vấn chặn giao dịch:
╔═══════════╦═══════════════════════════════╦═════ ═══════════════╦════════════════════╗
║ SessionID ║ ItemType BlockedBySessionID ║ QueryText
╠═══════════╬═══════════════════════════════╬═════ ═══════════════╬════════════════════╣
67 ║ Giao dịch ║ 0 ║ CHỌN *
║ ║ ║ TỪ dbo.TestLock; ║
68 Yêu cầu phiên, Nhiệm vụ chờ 67 ║ CHỌN *
║ ║ TỪ dbo.TestLock ║
╚═══════════╩═══════════════════════════════╩═════ ═══════════════╩════════════════════╝
1 - không hoàn toàn đúng. Có bộ đệm thủ tục, có thể chứa câu lệnh chịu trách nhiệm cho khóa. Tuy nhiên, có thể không dễ để xác định câu lệnh nào là nguyên nhân thực sự của khóa vì có thể có nhiều truy vấn trong bộ đệm chạm vào tài nguyên được đề cập.
Truy vấn bên dưới hiển thị kế hoạch truy vấn cho các truy vấn thử nghiệm ở trên vì bộ đệm thủ tục của tôi không quá bận rộn.
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;
Kết quả của truy vấn này có thể cho phép bạn tìm ra thủ phạm, nhưng lưu ý, việc kiểm tra bộ đệm thủ tục như thế này có thể khá đòi hỏi trên một hệ thống bận rộn.
2 SQL Server 2016 và phục vụ trên các cửa hàng Query , mà không giữ lại lịch sử hoàn chỉnh các truy vấn thực hiện.
Blocked Process Reports
tính năng, để tìm ra nguyên nhân gốc rễ của các kịch bản chặn trong sản xuất. Mỗi giao dịch chạy một số truy vấn và hầu hết thời gian của truy vấn cuối cùng (được hiển thị trên bộ đệm đầu vào tại BPR) hiếm khi là một truy vấn giữ khóa. Dường như tài nguyên cuối cùng của tôi để giải quyết vấn đề này là đặt phiên xEvents nhẹ để cho tôi biết những truy vấn nào được chạy trong mỗi phiên. Nếu bạn biết một bài viết cho thấy một ví dụ về điều này, tôi sẽ biết ơn.