SQL Server ไม่เก็บประวัติของคำสั่งที่ได้รับการดำเนินการ1,2 คุณสามารถตรวจสอบสิ่งที่วัตถุมีล็อค แต่คุณไม่สามารถจำเป็นต้องเห็นสิ่งที่เกิดจากคำสั่งล็อคเหล่านั้น
ตัวอย่างเช่นหากคุณดำเนินการคำสั่งนี้:
BEGIN TRANSACTION
INSERT INTO dbo.TestLock DEFAULT VALUES
และดูข้อความ SQL ผ่านจุดจับ sql ล่าสุดคุณจะเห็นข้อความแจ้งว่าปรากฏขึ้น อย่างไรก็ตามหากเซสชันทำสิ่งนี้:
BEGIN TRANSACTION
INSERT INTO dbo.TestLock DEFAULT VALUES
GO
SELECT *
FROM dbo.TestLock;
GO
คุณจะเห็นSELECT * FROM dbo.TestLock;
คำสั่งเท่านั้นแม้ว่าธุรกรรมนั้นยังไม่ได้รับการยอมรับและINSERT
คำสั่งนั้นกำลังบล็อกผู้อ่านจากdbo.TestLock
ตาราง
ฉันใช้สิ่งนี้เพื่อค้นหาธุรกรรมที่ไม่มีข้อผูกมัดที่กำลังบล็อกเซสชันอื่น:
/*
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;
หากเราตั้งค่า test-bed แบบง่าย ๆ ใน SSMS ด้วยหน้าต่างการสืบค้นสองชุดเราจะเห็นว่าเราสามารถเห็นคำสั่งที่ใช้งานล่าสุดเท่านั้น
ในหน้าต่างแบบสอบถามแรกให้เรียกใช้สิ่งนี้:
CREATE TABLE dbo.TestLock
(
id int NOT NULL IDENTITY(1,1)
);
BEGIN TRANSACTION
INSERT INTO dbo.TestLock DEFAULT VALUES
ในหน้าต่างที่สองให้เรียกใช้สิ่งนี้:
SELECT *
FROM dbo.TestLock
ตอนนี้ถ้าเราเรียกใช้แบบสอบถามการปิดกั้นการทำธุรกรรมจากด้านบนเราจะเห็นผลลัพธ์ต่อไปนี้:
╔═══════════╦═══════════════════════════════╦═════ ═══════════════╦══════════════════════════════════ ═══════╗
║ SessionID ║ประเภทรายการ║ถูกบล็อคโดย BessSessionID ║ QueryText ║
╠═══════════╬═══════════════════════════════╬═════ ═══════════════╬══════════════════════════════════ ═══════╣
║ 67 ║ธุรกรรม║ 0 ║การทำธุรกรรมเริ่มต้น║
║║║║ INSERT INTO dbo.TestLock ค่าเริ่มต้น║
║ 68 ║คำขอเซสชันกำลังรองาน║ 67 ║เลือก * ║
║║║║จาก dbo.TestLock ║
╚═══════════╩═══════════════════════════════╩═════ ═══════════════╩══════════════════════════════════ ═══════╝
(ฉันได้ลบคอลัมน์ที่ไม่เกี่ยวข้องออกจากส่วนท้ายของผลลัพธ์)
ตอนนี้ถ้าเราเปลี่ยนหน้าต่างคิวรีแรกเป็น:
BEGIN TRANSACTION
INSERT INTO dbo.TestLock DEFAULT VALUES
GO
SELECT *
FROM dbo.TestLock;
GO
และเรียกใช้หน้าต่างแบบสอบถามที่ 2 อีกครั้ง:
SELECT *
FROM dbo.TestLock
เราจะเห็นผลลัพธ์นี้จากข้อความค้นหาการบล็อกธุรกรรม:
╔═══════════╦═══════════════════════════════╦═════ ═══════════════╦════════════════════╗
║ SessionID ║ประเภทรายการ║ถูกบล็อคโดย BessSessionID ║ QueryText ║
╠═══════════╬═══════════════════════════════╬═════ ═══════════════╬════════════════════╣
║ 67 ║ธุรกรรม║ 0 ║ SELECT * ║
ROM ║║║จาก dbo.TestLock; ║
║ 68 ║คำขอเซสชันกำลังรองาน║ 67 ║เลือก * ║
║║║║จาก dbo.TestLock ║
╚═══════════╩═══════════════════════════════╩═════ ═══════════════╩════════════════════╝
1 - ไม่จริงทั้งหมด มีโพรซีเดอร์แคชซึ่งอาจมีคำสั่งที่รับผิดชอบในการล็อค อย่างไรก็ตามอาจไม่ใช่เรื่องง่ายที่จะระบุว่าคำสั่งใดเป็นสาเหตุที่แท้จริงของการล็อกเนื่องจากอาจมีการสืบค้นจำนวนมากในแคชที่สัมผัสกับทรัพยากรที่เป็นปัญหา
แบบสอบถามด้านล่างแสดงแผนแบบสอบถามสำหรับแบบสอบถามทดสอบด้านบนเนื่องจากโพรซีเดอร์แคชของฉันไม่ยุ่งมาก
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;
ผลลัพธ์ของแบบสอบถามนี้อาจช่วยให้คุณค้นหาผู้กระทำผิด แต่ระวังการตรวจสอบขั้นตอนการแคชเช่นนี้สามารถเรียกร้องมากในระบบไม่ว่าง
2 SQL Server 2016 และสูงกว่าเสนอQuery Storeซึ่งจะเก็บประวัติของการสืบค้นทั้งหมด
Blocked Process Reports
คุณสมบัติเพื่อค้นหาสาเหตุของการบล็อกสถานการณ์ในการผลิต แต่ละธุรกรรมรันหลายเคียวรีและส่วนใหญ่ครั้งล่าสุด (ที่แสดงในอินพุตบัฟเฟอร์ที่ BPR) ไม่ค่อยถือล็อค ดูเหมือนว่าทรัพยากรสุดท้ายของฉันในการแก้ไขปัญหานี้คือการตั้งค่าเซสชัน xEvents ที่มีน้ำหนักเบาเพื่อบอกฉันว่าแบบสอบถามใดทำงานภายใต้แต่ละเซสชัน หากคุณรู้ว่าบทความแสดงตัวอย่างของเรื่องนี้ฉันจะขอบคุณ