加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > MsSql教程 > 正文

SQLServer 2005死锁终极大法

发布时间:2020-12-12 14:08:08 所属栏目:MsSql教程 来源:网络整理
导读:项目背景: 一个重要的项目,一个复杂的背景,每天大量人员频繁的操作,在过去的几个月内偶尔发生一两次死锁导致系统不能使用 为缓解查询压力,实现了读写分离,已经增加了3台查询数据库与主数据库同步 死锁解决办法: 1.首先创建表: /*********************
项目背景:

一个重要的项目,一个复杂的背景,每天大量人员频繁的操作,在过去的几个月内偶尔发生一两次死锁导致系统不能使用

为缓解查询压力,实现了读写分离,已经增加了3台查询数据库与主数据库同步

死锁解决办法:

1.首先创建表:
/************************************************************
 * Code formatted by SoftTree SQL Assistant ?v5.0.97
 * Time: 2013-3-1 23:17:23
 ************************************************************/

CREATE TABLE [dbo].[DeadLock]
(
	[ID]             [int],[标志]           [varchar](10),[进程ID]         [smallint],[线程ID]         [smallint],[块进程ID]       [smallint],[数据库ID]       [smallint],[数据库名]       [varchar](50),[用户ID]         [varchar](128),[用户名]         [varchar](100),[累计CPU时间]    [int],[登陆时间]       [datetime],[打开事务数]     [varchar](30),[进程状态]       [varchar](128),[工作站名]       [varchar](128),[应用程序名]     [varchar](120),[工作站进程ID]   [varchar](228),[域名  ]         [nchar](12),[网卡地址]       [varchar](120),[进程的sql语句]  [varchar](3000),[发生时间]       [datetime]
)

这个表是根据下面的存储过程结果创建的,不要问我创建过程,反正我调了好几次才成功

2.创建下面的存储过程:
/************************************************************
 * Code formatted by SoftTree SQL Assistant ?v5.0.97
 * Time: 2013-3-1 23:18:34
 ************************************************************/

CREATE PROC p_lockinfo
@kill_lock_spid BIT = 1,--是否杀掉死锁的进程,1 杀掉,0 仅显示
@show_spid_if_nolock BIT = 0 --如果没有死锁的进程,是否显示正常进程信息,1 显示,0 不显示
AS
DECLARE @count  INT,@s      NVARCHAR(1000),@i      INT

SELECT id = IDENTITY(INT,1,1),标志,进程ID = spid,线程ID = kpid,块进程ID = blocked,数据库ID = dbid,数据库名 = DB_NAME(dbid),用户ID = uid,用户名 = loginame,累计CPU时间 = cpu,登陆时间 = login_time,打开事务数 = open_tran,进程状态 = STATUS,工作站名 = hostname,应用程序名 = program_name,工作站进程ID = hostprocess,域名 = nt_domain,网卡地址 = net_address
       INTO #t
FROM   (
           SELECT 标志 = '死锁的进程',spid,kpid,a.blocked,dbid,uid,loginame,cpu,login_time,open_tran,STATUS,hostname,program_name,hostprocess,nt_domain,net_address,s1 = a.spid,s2 = 0
           FROM   MASTER..sysprocesses a
                  JOIN (
                           SELECT blocked
                           FROM   MASTER..sysprocesses
                           GROUP BY
                                  blocked
                       )b
                       ON  a.spid = b.blocked
           WHERE  a.blocked = 0
                  AND program_name LIKE '%jtds%'
           UNION ALL
           SELECT '|_牺牲品_>',blocked,s1 = blocked,s2 = 1
           FROM   MASTER..sysprocesses a
           WHERE  blocked <> 0
                  AND program_name LIKE '%jtds%'
       )a
ORDER BY
       s1,s2


 


 




SELECT @count = @@rowcount,@i = 1

IF @count = 0
   AND @show_spid_if_nolock = 1
BEGIN
    INSERT #t
    SELECT 标志 = '正常的进程',DB_NAME(dbid),net_address
    FROM   MASTER..sysprocesses
    
    SET @count = @@rowcount
END


IF @count > 0
BEGIN
    CREATE TABLE #t1
    (
    	id         INT IDENTITY(1,a          NVARCHAR(30),b          INT,EventInfo  NVARCHAR(255)
    )
    
    
    
    
    IF @kill_lock_spid = 1
    BEGIN
        DECLARE @spid  VARCHAR(10),@标志  VARCHAR(10)
        
        WHILE @i <= @count
        BEGIN
            SELECT @spid = 进程ID,@标志 = 标志
            FROM   #t
            WHERE  id = @i
            
            INSERT #t1
            EXEC ('dbcc inputbuffer(' + @spid + ')')
            
            IF @标志 = '死锁的进程'
                EXEC ('kill ' + @spid)
            
            SET @i = @i + 1
        END
    END
    ELSE
        WHILE @i <= @count
        BEGIN
            SELECT @s = 'dbcc inputbuffer(' + CAST(进程ID AS VARCHAR) + ')'
            FROM   #t
            WHERE  id = @i
            
            INSERT #t1
            EXEC (@s)
            
            SET @i = @i + 1
        END
    
    SELECT a.*,进程的SQL语句 = b.EventInfo
    FROM   #t a
           JOIN #t1 b
                ON  a.id = b.id
    --hellolongbin添加记录
    
    
    INSERT INTO deadlock
    SELECT a.ID,a.标志,a.进程ID,a.线程ID,a.块进程ID,a.数据库ID,a.数据库名,a.用户ID,a.用户名,a.累计CPU时间,a.登陆时间,a.打开事务数,a.进程状态,a.工作站名,a.应用程序名,a.工作站进程ID,a.域名,a.网卡地址,b.EventInfo,GETDATE()
    FROM   #t a
           JOIN #t1 b
                ON  a.id = b.id
                    
                    
                    
                    
                    ----hellolongbin添加记录结束
END
GO

3.存储过程创建后,可以创建一个死锁测试一下。死锁创建:


运行1:

/************************************************************
 * Code formatted by SoftTree SQL Assistant ?v5.0.97
 * Time: 2013-3-1 23:19:16
 ************************************************************/


BEGIN   TRAN 
UPDATE t_a_sex
SET    sex = '00'
WHERE  sex_name = '请选择'
 
WAITFOR DELAY '00:02:30'
COMMIT   TRAN



打开新的查询窗口运行:


运行2:

/************************************************************
 * Code formatted by SoftTree SQL Assistant ?v5.0.97
 * Time: 2013-3-1 23:19:34
 ************************************************************/


BEGIN   TRAN 
SELECT *
FROM   t_a_sex
WHERE  sex_name = '请选择'


COMMIT   TRAN
? 4.运行exec p_lockinfo查看运行结果 5.打开deadlock表查看记录 6.最后,设置数据库任务,自动定时执行,我设置的是主数据库每隔一分钟执行一次,查询数据库2分钟执行一次 ? 每台数据库上均做如上操作 ? (双保险:程序代码可以在数据库建立连接时设置 "set lock_timeout 30000" 此步骤与上面的措施没有任何关系,可以忽略不做) ? ? 这种方式不是最完美的,因为被杀掉的操作无法恢复,如果用在网站充值或其他方面还需要进一步完善,但对我们的系统来说已经足以应付了 ---------------------- 2010-7-16 补充 上面的存储过程存在风险,会杀掉一些系统后台执行的锁,在这两天的跟踪过程中,发现先后数次杀掉了 EXEC dbo.sp_MShistory_cleanup @history_retention = 48 sys.sp_MSadd_logreader_history;1 等语句,今天早晨更是把数据库同步的一个事务给干掉了,导致查询机无法与正式库同步,所以上面的存储过程要优化为只杀业务程序造成的 死锁,就是上面添加的红色字体部分 and program_name like '%jtds%' ? 这里的名子根据业务程序名自行修改 ? DBCC INPUTBUFFER ,DBCC OUTPUTBUFFER 当数据库发生死锁时,可以使用这些查看发生死锁的语句或指令DBCC INPUTBUFFER 显示从客户端发送到 Microsoft? SQL Server? 的最后一个语句。 语法 DBCC INPUTBUFFER(spid) 参数 spid 是 sp_who 系统存储过程的输出中所显示的用户连接系统进程 ID (SPID)。 DBCC OUTPUTBUFFER 以十六进制或 ASCII 格式返回指定系统进程 ID (SPID) 的当前输出缓冲区。 语法 DBCC OUTPUTBUFFER ( spid ) 参数 spid 是 sp_who 系统存储过程的输出中所显示的用户连接系统进程 ID。 注释 使用 DBCC OUTPUTBUFFER 时,DBCC OUTPUTBUFFER 显示发送到指定客户端 (spid) 的结果。对于不包含输出流的进程,则返回错误信息。 若要显示所执行的、其返回的结果由 DBCC OUTPUTBUFFER 所显示的语句,请执行 DBCC INPUTBUFFER。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读