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 Server 优化----SQL语句的执行方式与锁以及阻塞的关系