• 遇数据库锁问题


     

    一开始根本没有想到锁的概念,数据库用的还是太少了,找到一些文章.

    其实所有的死锁最深层的原因就是一个:资源竞争
    表现一:
        一个用户A 访问表A(锁住了表A),然后又访问表B
        另一个用户B 访问表B(锁住了表B),然后企图访问表A
        这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B,才能继续,好了他老人家就只好老老实实在这等了
        同样用户B要等用户A释放表A才能继续这就死锁了
    解决方法:
        这种死锁是由于你的程序的BUG产生的,除了调整你的程序的逻辑别无他法
        仔细分析你程序的逻辑,
        1:尽量避免同时锁定两个资源
        2: 必须同时锁定两个资源时,要保证在任何时刻都应该按照相同的顺序来锁定资源.
        
    表现二:
        用户A读一条纪录,然后修改该条纪录
        这是用户B修改该条纪录
        这里用户A的事务里锁的性质由共享锁企图上升到独占锁(for update),而用户B里的独占锁由于A有共享锁存在所以必须等A释
    放掉共享锁,而A由于B的独占锁而无法上升的独占锁也就不可能释放共享锁,于是出现了死锁。
        这种死锁比较隐蔽,但其实在稍大点的项目中经常发生。
    解决方法:
        让用户A的事务(即先读后写类型的操作),在select 时就是用Update lock
        语法如下:
        select * from table1 with(updlock) where ....

       相关引自  分析死锁的方法     选择自 caiyunxia 的 Blog

    常看到死锁的问题,一般都是KILL进程,但如果不查出引起死锁的原因,死锁会时常发生
    可以通过查找引起死锁的的操作,就可以方便的解决死锁,现将日常解决问题的方法总结,也许对大家有帮助

    1\死锁发生时,通过如下语法,查询出引起死锁的操作

    use master
    go
    declare @spid int,@bl int
    DECLARE s_cur CURSOR FOR
    select  0 ,blocked
    from (select * from sysprocesses where  blocked>0 ) a
    where not exists(select * from (select * from sysprocesses where  blocked>0 ) b
    where a.blocked=spid)
    union select spid,blocked from sysprocesses where  blocked>0
    OPEN s_cur
    FETCH NEXT FROM s_cur INTO @spid,@bl
    WHILE @@FETCH_STATUS = 0
    begin
     if @spid =0
                select '引起数据库死锁的是: '+ CAST(@bl AS VARCHAR(10)) + '进程号,其执行的SQL语法如下'
     else
                select '进程号SPID:'+ CAST(@spid AS VARCHAR(10))+ '被' + '进程号SPID:'+ CAST(@bl AS VARCHAR(10)) +'阻塞,其当前进程执行的SQL语法如下'
     DBCC INPUTBUFFER (@bl )
     FETCH NEXT FROM s_cur INTO @spid,@bl
    end
    CLOSE s_cur
    DEALLOCATE s_cur

    exec sp_who2
    2\查找程序/数据库,此t_sql语法在什么地方使用
    3\分析找到的,并解决问题

    EG:

    /*
    -------------------------------------------------------
    引起数据库死锁的是: 71进程号,其执行的SQL语法如下

    EventType      Parameters EventInfo                                       
    -------------- ---------- ------------------------------------------------
    Language Event 0         
    select * from test
    insert test values(1,2)


    (所影响的行数为 1 行)

    DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。
                                                                                  
    ------------------------------------------------------------------------------
    进程号SPID:64被进程号SPID:71阻塞,其当前进程执行的SQL语法如下

    EventType      Parameters EventInfo                                       
    -------------- ---------- ------------------------------------------------
    Language Event 0         
    select * from test
    insert test values(1,2)


    (所影响的行数为 1 行)

    DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。
                                                                                  
    ------------------------------------------------------------------------------
    进程号SPID:65被进程号SPID:64阻塞,其当前进程执行的SQL语法如下

    EventType      Parameters EventInfo                                                                                         
    -------------- ---------- --------------------------------------------------------------------------------------------------
    Language Event 0          begin tran
    select * from test with (holdlock)
    waitfor time '12:00'
    select * from test
    commit

    (所影响的行数为 1 行)

    DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。
                                                                                  
    ------------------------------------------------------------------------------
    进程号SPID:73被进程号SPID:64阻塞,其当前进程执行的SQL语法如下

    EventType      Parameters EventInfo                                                                                         
    -------------- ---------- --------------------------------------------------------------------------------------------------
    Language Event 0          begin tran
    select * from test with (holdlock)
    waitfor time '12:00'
    select * from test
    commit

    (所影响的行数为 1 行)

    DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。
    */




                                                                      减少SQL Server数据库死锁的技巧

    如果两个用户进程分别锁定了不同的资源,接着又试图锁定对方所锁定的资源,就会产生死锁。此时,SQL Server将自动地选择并中止其中一个进程以解除死锁,使得另外一个进程能够继续处理。系统将回退被中止的
    事务,并向被回退事务的用户发送错误信息。

    大多数设计良好的应用都会在接收到这个错误信息之后重新提交该事务,此时提交成功的可能性是很大的。但是,如果服务器上经常出现这种情况,就会显著地降低服务器性能。为避免死锁,设计应用应当遵循一定的原则,包括:

    ▲ 让应用每次都以相同的次序访问服务器资源。

    ▲ 在事务期间禁止任何用户输入。应当在事务开始之前收集用户输入。

    ▲ 尽量保持事务的短小和简单。

    ▲ 如合适的话,为运行事务的用户连接指定尽可能低的隔离级别。[适用于6.5,7.0,2000]

    此外,对于SQL Server的死锁问题,下面是几则实践中很有用的小技巧。

    ■ 使用SQL Server Profiler的Create Trace Wizard运行“Identify The Cause of a Deadlock”跟踪来辅助识别死锁问题,它将提供帮助查找数据库产生死锁原因的原始数据。[适用于7.0,2000]

    ■ 如果无法消除应用中的所有死锁,请确保提供了这样一种程序逻辑:它能够在死锁出现并中止用户事务之后,以随机的时间间隔自动重新提交事务。这里等待时间的随机性非常重要,这是因为另一个竞争的事务也可能在等待,我们不应该让两个竞争的事务等待同样的时间,然后再在同一时间执行它们,这样的话将导致新的死锁。[适用于6.5,7.0,2000]

    ■ 尽可能地简化所有T-SQL事务。此举将减少各种类型的锁的数量,有助于提高SQL Server应用的整体性能。如果可能的话,应将较复杂的事务分割成多个较简单的事务。[适用于6.5,7.0,2000]

    ■ 所有条件逻辑、变量赋值以及其他相关的预备设置操作应当在事务之外完成,而不应该放到事务之内。永远不要为了接受用户输入而暂停某个事务,用户输入应当总是在事务之外完成。[适用于6.5,7.0,2000]

    ■ 在存储过程内封装所有事务,包括BEGIN TRANSACTION和COMMIT TRANSACTION语句。此举从两个方面帮助减少阻塞的锁。首先,它限制了事务运行时客户程序和SQL Server之间的通信,从而使得两者之间的任何消息只能出现于非事务运行时间(减少了事务运行的时间)。其次,由于存储过程强制它所启动的事务或者完成、或者中止,从而防止了用户留下未完成的事务(留下未撤销的锁)。[适用于6.5,7.0,2000]

    ■ 如果客户程序需要先用一定的时间检查数据,然后可能更新数据,也可能不更新数据,那么最好不要在整个记录检查期间都锁定记录。假设大部分时间都是检查数据而不是更新数据,那么处理这种特殊情况的一种方法就是:先选择出记录(不加UPDATE子句。UPDATE子句将在记录上加上共享锁),然后把它发送给客户。

    如果用户只查看记录但从来不更新它,程序可以什么也不做;反过来,如果用户决定更新某个记录,那么他可以通过一个WHERE子句检查当前的数据是否和以前提取的数据相同,然后执行UPDATE。

    类似地,我们还可以检查记录中的时间标识列(如果它存在的话)。如果数据相同,则执行UPDATE操作;如果记录已经改变,则应用应该提示用户以便用户决定如何处理。虽然这种方法需要编写更多的代码,但它能够减少加锁时间和次数,提高应用的整体性能。[适用于6.5,7.0,2000]

    ■ 尽可能地为用户连接指定具有最少限制的事务隔离级别,而不是总是使用默认的READ COMMITTED。为了避免由此产生任何其他问题,应当参考不同隔离级别将产生的效果,仔细地分析事务的特性。[适用于6.5,7.0,2000]

    ■ 使用游标会降低并发性。为避免这一点,如果可以使用只读的游标则应该使用READ_ONLY游标选项,否则如果需要进行更新,尝试使用OPTIMISTIC游标选项以减少加锁。设法避免使用SCROLL_LOCKS游标选项,该选项会增加由于记录锁定引起的问题。[适用于6.5,7.0,2000]

    ■ 如果用户抱怨说他们不得不等待系统完成事务,则应当检查服务器上的资源锁定是否是导致该问题的原因。进行此类检查时可以使用SQL Server Locks Object: Average Wait Time (ms),用该计数器来度量各种锁的平均等待时间。

    如果可以确定一种或几种类型的锁导致了事务延迟,就可以进一步探究是否可以确定具体是哪个事务产生了这种锁。Profiler是进行这类具体分析的最好工具。[适用于7.0,2000]

    ■ 使用sp_who和sp_who2(SQL Server Books Online没有关于sp_who2的说明,但sp_who2提供了比sp_who更详细的信息)来确定可能是哪些用户阻塞了其他用户。[适用于6.5,7.0,2000]

    ■ 试试下面的一个或多个有助于避免阻塞锁的建议:1)对于频繁使用的表使用集簇化的索引;2)设法避免一次性影响大量记录的T-SQL语句,特别是INSERT和UPDATE语句;3)设法让UPDATE和DELETE语句使用索引;4)使用嵌套事务时,避免提交和回退冲突。[适用于6.5,7.0,2000]


    我做的档案软件想实现如03年的数据已经整理完,不想再被更改,
    想锁定它,但是表中04年的数据还可以更改。
    判断03 04年通过一个years 字段


    分表,放到另外一个表中


    对大表分区,把这个年份的数据单独保存到一张表并单独存放在一个文件组,然后将该文件组设置为只读文件组


    你可以做个触发器,当年份=03是不允许修改


    squareffx兄,可否说的详细点 
    我对触发器 不太懂


    USE 数据库名
    IF EXISTS (SELECT name FROM sysobjects
          WHERE name = '触发器' AND type = 'TR')
       DROP TRIGGER 触发器
    GO
    CREATE TRIGGER 触发器
    ON 表名
    FOR INSERT, UPDATE, DELETE
    AS RAISERROR ('XXXXXXXXX', 16, 1)
    GO
    ???另外,问一下只要求不能更改已经有的数据(修改或删除),是不是也不能添加..???


    CREATE TRIGGER 触发器
    ON 表名
    FOR  UPDATE
    AS RAISERROR ('XXXXXXXXX', 16, 1)
    GO
    这样就只限制修改操作了


    楼上的两位朋友可否告知
    如何实现当表中字段years=2003时不能修改 而其它年份可以照常修改呢?


    CREATE TRIGGER 触发器
    ON 表名
    FOR  UPDATE
    AS RAISERROR ('XXXXXXXXX', 16, 1)
    GO
    这样就只限制修改操作了

  • 相关阅读:
    通过IDEA创建SpringMVC项目记录
    开发问题总结
    idea控制台乱码解决
    java获取文件是否文件夹
    oralce优化文章
    乱码踩坑记录
    oracle关于左连接失效的一些问题
    volatile关键字
    postman body formdata和xwwwformurlencoded区别
    [转][C#]ScottPlot
  • 原文地址:https://www.cnblogs.com/runfeng/p/207656.html
Copyright © 2020-2023  润新知