MS SQL Server 排查阻塞和查找被锁语句

Posted 天海沙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MS SQL Server 排查阻塞和查找被锁语句相关的知识,希望对你有一定的参考价值。

-- 方法1
SELECT
\'资源类型\' = t1.resource_type,
\'来源数据库\' = CONVERT(CHAR(25), DB_NAME(resource_database_id)),
\'数据库中与资源相关联的实体的 ID\' = t1.resource_associated_entity_id,
\'锁模式\' = t1.request_mode,  --锁的模式:S-共享锁,U-更新锁,X-排他锁,IS/IU/IX-意向共享/更新/排他锁
\'被堵塞的语句\' = t1.request_session_id,  
\'造成左边语句堵塞的语句\' = t2.blocking_session_id
FROM sys.dm_tran_locks as t1
INNER JOIN sys.dm_os_waiting_tasks as t2
ON t1.lock_owner_address = t2.resource_address;
--KILL blocking_session_id


--方法2
--1.查找blocked大于0的被锁进程, 观察并找到可疑的spid
SELECT \'进程ID\' = spid
, \'Windows线程ID\' = kpid
, \'当前状态\' = CONVERT(CHAR(25), status)
, \'正在阻塞请求的会话的ID\' = blocked
, \'当前等待时间(ms)\' = waittime
, \'进程的累计CPU时间\' = cpu
, \'进程的累计磁盘IO次数\' = physical_io
, \'当前正由进程使用的数据库\' = CONVERT(CHAR(25), DB_NAME(dbid))
, \'正在执行的命令\' = CONVERT(CHAR(16), cmd)
, \'工作站名称\' = CONVERT(CHAR(25), hostname)
, \'执行命令的用户\' = CONVERT(CHAR(15), SUSER_NAME(uid))
FROM sys.sysprocesses WHERE blocked >0 ORDER BY waittime desc;
--2.依据spid查询分析此进程阻塞的源头
SELECT * FROM sys.sysprocesses WHERE spid =1204
--3.查看此spid进程执行的SQL语句
DBCC inputbuffer(1204);
--4.直接干掉死锁进程
KILL 259;

Sql Server 优化----SQL语句的执行方式与锁以及阻塞的关系

  阻塞原因之一是不同的Session在访问同一张表的时候因为不兼容锁的原因造成的,   当前执行的SQL语句是否被阻塞(或者死锁),不仅跟当前表上的已有的锁有关,也会跟当前执行的SQL语句的执行方式有关   简单来说,对于表的访问方式,SQL语句的执行无非是表扫描,索引扫描,(聚集索引或者非聚集索引)索引查找等等   如果SQL语句的执行方式不当或者没有合理的索引,会造成没必要的阻塞,如果逻辑控制不当,甚至造成更严重的问题,造成数据逻辑的错误

 

建个测试表,下面测试演示一下

复制代码
create table testIndexAndLock
(
    id int identity(1,1),
    col2 varchar(50)
)
GO

insert into testIndexAndLock values (NEWID())
GO 100000
复制代码

 

  很常见的业务就是,当前Session中开启了事物,执行更新或者删除某一行数据,然后再进行一系列其他的操作,当前事物提交之前,该排它锁一致保持,直到事物提交

  比如下面截图:

  第一个Session中利用事物和排他性操作锁定一行数据,,进行业务逻辑处理,按道理来说:目的仅仅是锁定当前行的数据(id = 6666),进行排他性操作,并没有锁定其的数据行

  

  然后再第二个Session中,查询其他的无关的Id,按道理,上面一个Session只是锁定了id = 6666的行,怎么当前Session查询Id = 7777的行的时候会被阻塞呢?

  

  这里就是上面说的对当前表的访问方式了,因为当前查询的表上没有任何索引,在查询id = 7777的时候,虽然id = 7777的行上没有任何锁,为什么查询还是被阻塞?   这里在执行查询id = 7777的时候,用的是全表扫描的方式执行的,此时遇到Id = 6666的这行数据的时候,因为这行数据上有排它锁,当前Session被阻塞   可以简单理解为当前Session会逐行扫描表中的数据行,在扫描的过程中,不管是表也好,数据行也好,如果遇到兼容的锁,可以正常访问,   如果遇到不兼容的锁,比如这里的查询是共享锁(s锁),遇到第一个Session中的id = 6666的这行数据的时候,发现上面是排它锁,   此时,当前Session要对该行加的锁(共享锁)与该行上的排它锁(x锁)不兼容,就只能等待,知道id = 6666这行数据上的排它锁释放或者变化与当前请求的共享锁兼容的锁,才能执行   如果id = 6666上面的排它锁一直没有释放或者变化为兼容共享锁的锁类型,当前Session就一直等待   表现为当前Session被阻塞   这种问题解决办法也很简单,类似现象如果要不被阻塞,要么等到第一个Session的排它锁释放,要么就换一种查询的方式   尝试在表的Id列上建立一个索引,当然聚集索引也可以,目的是防止查询的时候以全表扫描的方式进行

  

  在第二个Session中执行上面的查询,查询Id = 7777的数据行,可以发现这个查询可以顺利完成

  

  这个就是通过索引改变查询的执行计划,避免全表扫描的时候遇到任何一行数据上有排它锁造成阻塞的情况。   这里说的索引,并不仅仅是为了提高效率,更重要的是改变了查询的执行方式,避免遇到表上有排他性锁的锁在成阻塞的情况。

 

索引的作用不仅仅是提高查询效率,更重要的是避免全表扫描的过程中遇到排它锁被阻塞的情况,对减少阻塞有一定的帮助作用。

以上是关于MS SQL Server 排查阻塞和查找被锁语句的主要内容,如果未能解决你的问题,请参考以下文章

SQL:查找被锁的表,以及锁表的SQL语句(重点推荐)

Sql Server 优化----SQL语句的执行方式与锁以及阻塞的关系

Sql Server 优化----SQL语句的执行方式与锁以及阻塞的关系

查看SQL Server被锁的表以及如何解锁

在MS SQL Server中怎么查看正在执行的SQL

sql server 阻塞查询