การตรวจจับตารางหรือแถวที่ถูกล็อคใน SQL Server


20

ฉันกำลังพยายามทำความเข้าใจ / เรียนรู้วิธีติดตามรายละเอียดของเซสชันที่ถูกบล็อก

ดังนั้นฉันจึงสร้างการตั้งค่าต่อไปนี้:

create table foo (id integer not null primary key, some_data varchar(20));
insert into foo values (1, 'foo');
commit;

ตอนนี้ฉันเชื่อมต่อกับฐานข้อมูลสองครั้งจากลูกค้าที่แตกต่างกันสองคน

ปัญหาเซสชั่นแรก:

begin transaction
update foo set some_data = 'update'
  where id = 1;

ฉันไม่ได้กระทำอย่างชัดเจนเพื่อรักษาล็อค

ในเซสชั่นที่สองฉันออกคำสั่งเดียวกันและแน่นอนว่าหนึ่งรอเนื่องจากการล็อค ตอนนี้ฉันกำลังพยายามใช้คิวรี่ที่ต่างกันเพื่อดูว่าเซสชัน 2 กำลังรอfooตารางอยู่

sp_who2 แสดงต่อไปนี้ (ฉันลบบางคอลัมน์เพื่อแสดงเฉพาะข้อมูลสำคัญ):

SPID | สถานะ | BlkBy | DBName | คำสั่ง | SPID | requestID
----- + -------------- ------- + + + ---------- ---------- -------- + + ------ ----------
52 | กำลังนอนหลับ . | อาหาร คำสั่งที่รอรับ 52 | 0        
53 | กำลังนอนหลับ . | อาหาร คำสั่งที่รอรับ 53 | 0        
54 | ระงับแล้ว 52 | อาหาร อัพเดท | 54 | 0        
56 | RUNNABLE | . | อาหาร เลือกเป็น | 56 | 0        

คาดว่าจะเป็นเช่นนี้ช่วงที่ 54 จะถูกบล็อกโดยการเปลี่ยนแปลงที่ไม่ได้กระทำจากเซสชัน 52

ข้อความค้นหาsys.dm_os_waiting_tasksยังแสดงสิ่งนี้ คำสั่ง:

select session_id, wait_type, resource_address, resource_description
from sys.dm_os_waiting_tasks
where blocking_session_id is not null;

ผลตอบแทน:

session_id | wait_type | resource_address | resource_description                                                            
----------- ----------- + + + -------------------- ----- -------------------------------------------------- --------------------------
        54 | LCK_M_X | 0x000000002a35cd40 | keylock hobtid = 72057594046054400 dbid = 6 id = โหมด lock4ed1dd780 = X ที่เกี่ยวข้อง ObbjectId = 72057594046054400

คาดว่าจะเป็นเช่นนี้อีกครั้ง

ปัญหาของฉันคือฉันไม่สามารถหาวิธีค้นหาชื่อวัตถุจริงที่เซสชัน 54 กำลังรออยู่

ฉันพบข้อสงสัยหลายข้อที่เข้าร่วมsys.dm_tran_locksและเป็นsys.dm_os_waiting_tasksเช่นนี้:

SELECT ....
FROM sys.dm_tran_locks AS l
  JOIN sys.dm_os_waiting_tasks AS wt ON wt.resource_address = l.lock_owner_address

แต่ในสถานการณ์จำลองการทดสอบของฉันการเข้าร่วมครั้งนี้ไม่ได้ส่งคืนสิ่งใด ดังนั้นการเข้าร่วมนั้นผิดหรือdm_tran_locksไม่มีข้อมูลที่ฉันต้องการ

ดังนั้นสิ่งที่ฉันกำลังมองหาคือแบบสอบถามที่ส่งกลับสิ่งที่ต้องการ:
" เซสชั่น 54 กำลังรอการล็อกในตารางfoo "


ข้อมูลพื้นหลังบางส่วน:

ปัญหาในชีวิตจริงที่ฉันพยายามแก้ไขมีความซับซ้อนขึ้นเล็กน้อย แต่ก็เดือดร้อนไปกับคำถามที่ว่า“ ตารางใดเป็นเซสชั่น 54 ที่รออยู่” ปัญหาดังกล่าวเกี่ยวข้องกับกระบวนการจัดเก็บที่มีขนาดใหญ่มากซึ่งจะอัปเดตหลายตารางและเลือกจากมุมมองที่เข้าถึงตารางเหล่านั้นบางส่วน selectคำสั่งจะถูกปิดกั้นแม้ว่าเราจะมีการแยกภาพรวมและอ่านภาพรวมความมุ่งมั่นที่เปิดใช้งาน การหาสาเหตุที่การเลือกถูกบล็อก (ซึ่งฉันคิดว่าคงเป็นไปไม่ได้หากเปิดใช้งานการแยกสแน็ปช็อต) เป็นขั้นตอนต่อไป

เป็นขั้นตอนแรกที่ผมต้องการที่จะหาในสิ่งที่เซสชั่นที่เป็นที่รอคอย


msdn.microsoft.com/en-us/library/ms190345.aspxบอกว่าการเข้าร่วมของคุณถูกต้อง
Max Vernon

@ MaxVernon: ขอบคุณสำหรับการยืนยันว่า แต่แล้วฉันก็สับสนมากขึ้น เหตุใดจึงไม่ส่งคืนสิ่งใดแม้ว่าฉันรู้ว่ามีการล็อกและเซสชันที่ถูกบล็อก
a_horse_with_no_name

ฉันไม่สามารถสร้างปัญหาที่คุณเห็นใน SQL Server 2012 อีกครั้งฉันสร้างฐานข้อมูลทดสอบเปิดใช้งาน RCSI สร้างตารางของคุณและรันทั้งคำสั่งการอัปเดตและฉันเห็นแถวที่ส่งคืนโดยแบบสอบถามล่าสุดของคุณ
Max Vernon

หากคุณต้องการเครื่องช่วยด้านภาพในการตรวจจับการล็อกของคุณมีเครื่องมือโอเพนซอร์สที่เรียกว่า SQL lock finder คุณสามารถค้นหาแหล่งที่มาได้ที่: github.com/LucBos/SqlLockFinderหรือดาวน์โหลดไฟล์ปฏิบัติการได้ที่: sqllockfinder.comนอกจากนี้เรายังรักสิ่งที่คุณทำกับรหัสเพื่อให้เราสามารถทำให้ดีขึ้น
Luc Bos

คำตอบ:


23

ฉันคิดว่านี่เป็นสิ่งที่คุณต้องการ

USE 'yourDB'
GO
SELECT  
    OBJECT_NAME(p.[object_id]) BlockedObject
FROM    sys.dm_exec_connections AS blocking
    INNER JOIN sys.dm_exec_requests blocked
        ON blocking.session_id = blocked.blocking_session_id
    INNER JOIN sys.dm_os_waiting_tasks waitstats
        ON waitstats.session_id = blocked.session_id
    INNER JOIN sys.partitions p ON SUBSTRING(resource_description, 
        PATINDEX('%associatedObjectId%', resource_description) + 19, 
        LEN(resource_description)) = p.partition_id

3

คุณสามารถลอง:

SELECT 
db_name(rsc_dbid) AS 'DATABASE_NAME',
case rsc_type when 1 then 'null'
              when 2 then 'DATABASE' 
              WHEN 3 THEN 'FILE'
              WHEN 4 THEN 'INDEX'
              WHEN 5 THEN 'TABLE'
              WHEN 6 THEN 'PAGE'
              WHEN 7 THEN 'KEY'
              WHEN 8 THEN 'EXTEND'
              WHEN 9 THEN 'RID ( ROW ID)'
              WHEN 10 THEN 'APPLICATION' end  AS 'REQUEST_TYPE',

CASE req_ownertype WHEN 1 THEN 'TRANSACTION'
                   WHEN 2 THEN 'CURSOR'
                   WHEN 3 THEN 'SESSION'
                   WHEN 4 THEN 'ExSESSION' END AS 'REQUEST_OWNERTYPE',

OBJECT_NAME(rsc_objid ,rsc_dbid) AS 'OBJECT_NAME', 
PROCESS.HOSTNAME , 
PROCESS.program_name , 
PROCESS.nt_domain , 
PROCESS.nt_username , 
PROCESS.program_name ,
SQLTEXT.text 
FROM sys.syslockinfo LOCK JOIN 
     sys.sysprocesses PROCESS
  ON LOCK.req_spid = PROCESS.spid
CROSS APPLY sys.dm_exec_sql_text(PROCESS.SQL_HANDLE) SQLTEXT
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.