【问题标题】:How to find out what is locking my tables?如何找出是什么锁定了我的表?
【发布时间】:2012-02-03 16:28:00
【问题描述】:

我有一个 SQL 表,它突然无法返回数据,除非我在末尾包含 with (nolock),这表明我的表上留下了某种锁。

我已经对sys.dm_tran_locks 进行了一些实验,以确定实际上桌子上有许多锁,但是我如何识别 什么 正在锁定它们(即请求元素sys.dm_tran_locks)?

编辑:我知道sp_lock 用于 SQL 2005 之前的版本,但现在该 sp 已被弃用,AFAIK 正确的方法是使用sys.dm_tran_locks。我正在使用 SQL Server 2008 R2。

【问题讨论】:

    标签: sql sql-server sql-server-2008-r2 locking query-optimization


    【解决方案1】:

    为了直接了解“谁被阻止/阻止”,我将 sp_who 和 sp_lock 组合/缩写成一个查询,它很好地概述了谁将什么对象锁定到什么级别。

    --Create Procedure WhoLock
    --AS
    set nocount on
    if object_id('tempdb..#locksummary') is not null Drop table #locksummary
    if object_id('tempdb..#lock') is not null Drop table #lock
    create table #lock (    spid int,    dbid int,    objId int,    indId int,    Type char(4),    resource nchar(32),    Mode char(8),    status char(6))
    Insert into #lock exec sp_lock
    if object_id('tempdb..#who') is not null Drop table #who
    create table #who (     spid int, ecid int, status char(30),
                loginame char(128), hostname char(128),
                blk char(5), dbname char(128), cmd char(16)
                --
                , request_id INT --Needed for SQL 2008 onwards
                --
             )
    Insert into #who exec sp_who
    Print '-----------------------------------------'
    Print 'Lock Summary for ' + @@servername  + ' (excluding tempdb):'
    Print '-----------------------------------------' + Char(10)
    Select     left(loginame, 28) as loginame, 
        left(db_name(dbid),128) as DB,
        left(object_name(objID),30) as object,
        max(mode) as [ToLevel],
        Count(*) as [How Many],
        Max(Case When mode= 'X' Then cmd Else null End) as [Xclusive lock for command],
        l.spid, hostname
    into #LockSummary
    from #lock l join #who w on l.spid= w.spid
    where dbID != db_id('tempdb') and l.status='GRANT'
    group by dbID, objID, l.spid, hostname, loginame
    
    Select * from #LockSummary order by [ToLevel] Desc, [How Many] Desc, loginame, DB, object
    
    Print '--------'
    Print 'Who is blocking:'
    Print '--------' + char(10)
    SELECT p.spid
    ,convert(char(12), d.name) db_name
    , program_name
    , p.loginame
    , convert(char(12), hostname) hostname
    , cmd
    , p.status
    , p.blocked
    , login_time
    , last_batch
    , p.spid
    FROM      master..sysprocesses p
    JOIN      master..sysdatabases d ON p.dbid =  d.dbid
    WHERE     EXISTS (  SELECT 1
              FROM      master..sysprocesses p2
              WHERE     p2.blocked = p.spid )
    
    Print '--------'
    Print 'Details:'
    Print '--------' + char(10)
    Select     left(loginame, 30) as loginame,  l.spid,
        left(db_name(dbid),15) as DB,
        left(object_name(objID),40) as object,
        mode ,
        blk,
        l.status
    from #lock l join #who w on l.spid= w.spid
    where dbID != db_id('tempdb') and blk <>0
    Order by mode desc, blk, loginame, dbID, objID, l.status
    

    (有关锁定级别缩写的含义,请参见例如https://technet.microsoft.com/en-us/library/ms175519%28v=sql.105%29.aspx

    复制自:sp_WhoLock – a T-SQL stored proc combining sp_who and sp_lock...

    注意 [Xclusive lock for command] 列可能会产生误导——它显示了该 spid 的当前命令;但 X 锁可能是由事务中的较早命令触发的。

    【讨论】:

    • 对于 Sql Server 2012,您需要将 ,request_id INT 作为附加参数(在末尾)添加到 create table #who(...
    • 2008 R2 相同,需要添加@gordon613 评论
    • 注意 [Xclusive lock for command] 列可能会产生误导——它显示了该 spid 的当前命令;但是 X 锁可能是由事务中的较早命令触发的
    • 建议在顶部添加set nocount on,这样受影响消息的行就不会分散输出消息的注意力
    • @ChrisFCarroll 这非常有用。非常感谢。
    【解决方案2】:

    看看以下可以在 SQLServer Management Studio (SSMS) 中运行的系统存储过程:

    • sp_who
    • sp_lock

    另外,在 SSMS 中,您可以通过不同的方式查看锁和进程:

    不同版本的 SSMS 将活动监视器放在不同的位置。例如,当您右键单击服务器节点时,SSMS 2008 和 2012 会将其显示在上下文菜单中。

    【讨论】:

    • 如何在“管理”文件夹下获得活动监视器?我有点想念它。
    • 我相信您需要获得正确的权限。如果您看不到它(或无法执行 sp_who/sp_lock),您需要给您的 DBA 打个招呼,让他看看这个问题。
    • 本地执行 sp_lock、sp_who 没有问题。只是想知道您是如何在对象资源管理器中将活动监视器作为一个单独的项目获得的——我必须右键单击数据库——显然那里没有“按对象查看锁定”项目。
    • 不同版本在某处具有基本相同的功能。我相信旧版本将其停在 IIRC 的“管理”下。微软在随意移动东西方面有着悠久的历史,只是为了移动东西。
    • SSMS 2008 和 2012 活动监视器可以通过右键单击树中的服务器节点来访问
    【解决方案3】:
    exec sp_lock
    

    这个查询应该给你现有的锁。

    exec sp_who SPID -- will give you some info
    

    拥有 spid,您可以检查活动监视器(进程选项卡)以找出哪些进程正在锁定表(“详细信息”以获取更多信息,“终止进程”以终止它)。

    【讨论】:

    • “exec sp_lock”是什么意思?你在哪里运行这个命令?
    • 您在 SQL 中运行该命令就像运行查询一样。将其粘贴(不带引号)并执行它。
    【解决方案4】:

    我有一个我整理的存储过程,它不仅处理锁和阻塞,还查看服务器中运行的内容。 我已经把它放在主人那里了。 分享给大家,代码如下:

    USE [master]
    go
    
    
    CREATE PROCEDURE [dbo].[sp_radhe] 
    
    AS
    BEGIN
    
    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
    
    
    -- the current_processes
    -- marcelo miorelli 
    -- CCHQ 
    -- 04 MAR 2013 Wednesday
    
    SELECT es.session_id AS session_id
    ,COALESCE(es.original_login_name, '') AS login_name
    ,COALESCE(es.host_name,'') AS hostname
    ,COALESCE(es.last_request_end_time,es.last_request_start_time) AS last_batch
    ,es.status
    ,COALESCE(er.blocking_session_id,0) AS blocked_by
    ,COALESCE(er.wait_type,'MISCELLANEOUS') AS waittype
    ,COALESCE(er.wait_time,0) AS waittime
    ,COALESCE(er.last_wait_type,'MISCELLANEOUS') AS lastwaittype
    ,COALESCE(er.wait_resource,'') AS waitresource
    ,coalesce(db_name(er.database_id),'No Info') as dbid
    ,COALESCE(er.command,'AWAITING COMMAND') AS cmd
    ,sql_text=st.text
    ,transaction_isolation =
    CASE es.transaction_isolation_level
        WHEN 0 THEN 'Unspecified'
        WHEN 1 THEN 'Read Uncommitted'
        WHEN 2 THEN 'Read Committed'
        WHEN 3 THEN 'Repeatable'
        WHEN 4 THEN 'Serializable'
        WHEN 5 THEN 'Snapshot'
    END
    ,COALESCE(es.cpu_time,0) 
        + COALESCE(er.cpu_time,0) AS cpu
    ,COALESCE(es.reads,0) 
        + COALESCE(es.writes,0) 
        + COALESCE(er.reads,0) 
    + COALESCE(er.writes,0) AS physical_io
    ,COALESCE(er.open_transaction_count,-1) AS open_tran
    ,COALESCE(es.program_name,'') AS program_name
    ,es.login_time
    FROM sys.dm_exec_sessions es
    LEFT OUTER JOIN sys.dm_exec_connections ec ON es.session_id = ec.session_id
    LEFT OUTER JOIN sys.dm_exec_requests er ON es.session_id = er.session_id
    LEFT OUTER JOIN sys.server_principals sp ON es.security_id = sp.sid
    LEFT OUTER JOIN sys.dm_os_tasks ota ON es.session_id = ota.session_id
    LEFT OUTER JOIN sys.dm_os_threads oth ON ota.worker_address = oth.worker_address
    CROSS APPLY sys.dm_exec_sql_text(er.sql_handle) AS st
    where es.is_user_process = 1 
      and es.session_id <> @@spid
      and es.status = 'running'
    ORDER BY es.session_id
    
    end 
    
    GO
    

    在过去的几年里,这个程序对我来说非常好。 运行它只需键入 sp_radhe

    关于将 sp_radhe 放入主数据库

    我使用下面的代码并把它做成一个系统存储过程

    exec sys.sp_MS_marksystemobject 'sp_radhe'
    

    您可以在下面的链接中看到

    Creating Your Own SQL Server System Stored Procedures

    关于事务隔离级别

    Questions About T-SQL Transaction Isolation Levels You Were Too Shy to Ask

    Jonathan Kehayias

    一旦您更改了事务隔离级别,它只会在以下情况下更改 范围在过程结束或返回调用结束时退出,或者如果 您使用 SET TRANSACTION ISOLATION LEVEL 再次显式更改它。

    此外,TRANSACTION ISOLATION LEVEL 仅适用于 存储过程,因此您可以拥有多个嵌套存储过程 以自己特定的隔离级别执行。

    【讨论】:

    • 只是指出 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED 出现了两次!另外我知道这是对主数据库而不是一个实际的数据库进行的,但是是否有任何后果,即隔离级别不会在之后恢复?谢谢
    【解决方案5】:

    这应该会为您提供现有锁的所有详细信息。

    DECLARE @tblVariable TABLE(SPID INT, Status VARCHAR(200), [Login] VARCHAR(200), HostName VARCHAR(200), 
        BlkBy VARCHAR(200), DBName VARCHAR(200), Command VARCHAR(200), CPUTime INT, 
        DiskIO INT, LastBatch VARCHAR(200), ProgramName VARCHAR(200), _SPID INT, 
        RequestID INT)
    
    INSERT INTO @tblVariable
    EXEC Master.dbo.sp_who2
    
    SELECT v.*, t.TEXT 
    FROM @tblVariable v
    INNER JOIN sys.sysprocesses sp ON sp.spid = v.SPID
    CROSS APPLY sys.dm_exec_sql_text(sp.sql_handle) AS t
    ORDER BY BlkBy DESC, CPUTime DESC
    

    然后,您可以谨慎地终止阻止您的表的 SPID。

    kill 104 -- Your SPID
    

    【讨论】:

    • 我向您鞠躬,先生。你刚刚让我从可怜的 9 小时尝试中解脱出来。
    【解决方案6】:

    您也可以使用sp_who2 提供更多信息

    这里有一些信息http://dbadiaries.com/using-sp_who2-to-help-with-sql-server-troubleshooting

    【讨论】:

      【解决方案7】:

      根据官方文档,sp_lock 被标记为已弃用:

      此功能处于维护模式,将来可能会被删除 Microsoft SQL Server 的版本。避免在新版本中使用此功能 开发工作,并计划修改当前使用的应用程序 这个功能。

      建议改用sys.dm_tran_locks。此动态管理对象返回有关当前活动的锁管理器资源的信息。每行代表当前对锁管理器的活动请求,请求已授予或等待授予的锁。

      它通常以比sp_lock 更友好的语法返回更多细节。

      Adam Machanic 编写的whoisactive 例程非常适合检查您环境中的当前活动,并查看哪些类型的等待/锁定会减慢您的查询速度。您可以非常轻松地找到阻碍您查询的原因以及大量其他方便的信息。


      例如,假设我们在默认 SQL Server 隔离级别 - 已提交读中运行以下查询。每个查询都在单独的查询窗口中执行:

      -- creating sample data
      CREATE TABLE [dbo].[DataSource]
      (
          [RowID] INT PRIMARY KEY
         ,[RowValue] VARCHAR(12)
      );
      
      INSERT INTO [dbo].[DataSource]([RowID], [RowValue])
      VALUES (1,  'samle data');
      
      -- query window 1
      BEGIN TRANSACTION;
      
          UPDATE [dbo].[DataSource]
          SET [RowValue] = 'new data'
          WHERE [RowID] = 1;
      
      --COMMIT TRANSACTION;
      
      -- query window 2
      SELECT *
      FROM [dbo].[DataSource];
      

      然后执行sp_whoisactive(只显示部分列):

      您可以很容易地看到阻塞SELECT 语句的会话,甚至它的T-SQL 代码。该例程有很多参数,可以查看docs了解更多详情。

      如果我们查询sys.dm_tran_locks 视图,我们可以看到其中一个会话正在等待一个资源的共享锁,该资源已被其他会话排他锁:

      【讨论】:

        【解决方案8】:

        情节转折!

        您可以拥有持有独占锁的孤立分布式事务,如果您的脚本假定存在与事务关联的会话(没有!),您将看不到它们。运行以下脚本来识别这些交易:

        ;WITH ORPHANED_TRAN AS (
        SELECT
            dat.name,
            dat.transaction_uow,
            ddt.database_transaction_begin_time,
            ddt.database_transaction_log_bytes_reserved,
            ddt.database_transaction_log_bytes_used
        FROM
            sys.dm_tran_database_transactions ddt,
            sys.dm_tran_active_transactions dat,
            sys.dm_tran_locks dtl
        WHERE
            ddt.transaction_id = dat.transaction_id AND
            dat.transaction_id = dtl.request_owner_id AND
            dtl.request_session_id = -2 AND
            dtl.request_mode = 'X'
        )
        SELECT DISTINCT * FROM ORPHANED_TRAN
        

        确定事务后,使用 transaction_uow 列在 MSDTC 中找到它并决定是中止还是提交它。如果事务被标记为 In Doubt(旁边带有问号),您可能想要中止它。

        您还可以通过在 KILL 命令中指定 transaction_uow 来终止工作单元 (UOW):

        KILL '<transaction_uow>'
        

        参考资料:

        https://docs.microsoft.com/en-us/sql/t-sql/language-elements/kill-transact-sql?view=sql-server-2017#arguments

        https://www.mssqltips.com/sqlservertip/4142/how-to-kill-a-blocking-negative-spid-in-sql-server/

        【讨论】:

          【解决方案9】:

          我和一位同事为此创建了一个工具。 它是您的会话产生的所有锁的直观表示。 试试看 (http://www.sqllockfinder.com),它是开源的 (https://github.com/LucBos/SqlLockFinder)

          【讨论】:

            猜你喜欢
            • 2018-07-12
            • 1970-01-01
            • 2011-05-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-03-13
            相关资源
            最近更新 更多