图1
我们做个简单的测试,就清楚了
• 新建会话(90)执行的SQL,按1,2,3的方式逐条执行,不提交事务
图2
• 然后新建会话(128)执行的SQL,按1,2的方式逐条执行,然后我们会发现128会话显示“…正在执行”
图3
• 此时使用获取阻塞的SQL,将得到图1的结果。
SELECT t1.resource_type AS [锁类型], DB_NAME(resource_database_id) AS [数据库名],
t1.resource_associated_entity_id AS [阻塞资源对象],
t1.resource_description as [资源描述信息], t1.request_mode AS [请求的锁],
t1.request_session_id AS [等待会话], t2.wait_duration_ms AS [等待时间],
(SELECT [text] FROM sys.dm_exec_requests AS r WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle])
WHERE r.session_id = t1.request_session_id
) AS [等待会话执行的批SQL],
(SELECT SUBSTRING(qt.[text],r.statement_start_offset/2,
(CASE WHEN r.statement_end_offset = -1
THEN LEN(CONVERT(nvarchar(max), qt.[text])) * 2
ELSE r.statement_end_offset END )/2)
FROM sys.dm_exec_requests AS r WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) AS qt
WHERE r.session_id = t1.request_session_id
) AS [等待会话执行的SQL],
t2.blocking_session_id AS [阻塞会话],
(SELECT [text] FROM sys.sysprocesses AS p
CROSS APPLY sys.dm_exec_sql_text(p.[sql_handle])
WHERE p.spid = t2.blocking_session_id
) AS [阻塞会话执行的批SQL]
FROM sys.dm_tran_locks AS t1 WITH (NOLOCK)
INNER JOIN sys.dm_os_waiting_tasks AS t2 WITH (NOLOCK)
ON t1.lock_owner_address = t2.resource_address OPTION (RECOMPILE);
到这,问题就很明白了,虽然会话90当前正在执行select * from test1操作,但其之前执行了对test的update操作,由于事务没有结束,所以锁没有释放,这样当会话128执行update时,由于需要的锁没有释放从而导致阻塞。
所以,当碰到这类场景的时候,如果阻塞源是select的语句时,需要检查代码,找到阻塞会话之前执行的等待会话中更新、修改或者删除表的操作来确认原因。
如果阻塞的时间长,需要检查update操作后,是否有长时间执行的SQL,或者代码中有太多的的逻辑,需要耗费的时间过长,从而导致整个事务太长。