zoukankan      html  css  js  c++  java
  • SqlServer 锁详解(转)

    1 处理死锁和设置死锁优先级
    死锁就是多个用户申请不同封锁,由于申请者均拥有一部分封锁权而又等待其他用户拥有的部分封锁而引起的无休止的等待
    可以使用SET DEADLOCK_PRIORITY控制在发生死锁情况时会话的反应方式。
    Syntax:
    SET DEADLOCK_PRIORITY { LOW | NORMAL}
    其中LOW说明该进程会话的优先级较低,在出现死锁时,可以首先中断该进程的事务。
    2 处理超时和设置锁超时持续时间。
    @@LOCK_TIMEOUT 返回当前会话的当前锁超时设置,单位为毫秒
    SET LOCK_TIMEOUT 设置允许应用程序设置语句等待阻塞资源的最长时间。当语句等待的时间大于 LOCK_TIMEOUT 设置时,系统将自动取消阻塞的语句,并给应用程序返回"已超过了锁请求超时时段"的 1222 号错误信息
    示例
    1)将锁超时期限设置为 1,800 毫秒。
    SET LOCK_TIMEOUT 1800
    2) 配置索引的锁定粒度
    可以使用 sp_indexoption 系统存储过程来设置用于索引的锁定粒度
    3)设置事务隔离级别
    SET   TRANSACTION   ISOLATION   LEVEL

    五 查看锁的信息
    1 执行 EXEC SP_LOCK 报告有关锁的信息
    2 查询分析器中按Ctrl+2可以看到锁的信息

    六、奇怪的sql语句
    Java代码
    begin tran   
    update titles set title_idid=title_id  where 1=2  
    if (selectavg(price)fromtitles)>$15  
    begin   
    update titles set price=price*1.10  
    where price<(select avg(price)from titles)   
    end   
    commit tran  

    begin tran
    update titles set title_idid=title_id  where 1=2
    if (selectavg(price)fromtitles)>$15
    begin
    update titles set price=price*1.10
    where price<(select avg(price)from titles)
    end
    commit tran

    update titles set title_idid=title_id  where 1=2,这个条件是永远也不会成立的,如此写的含义是什么呢?
    这里的where子句看起来很奇怪,尽管计算出的结果总是false。当优化器处理此查询时,因为它找不到任何有效的SARG,它的查询规划就会强制使用一个独占锁定来进行表扫描。此事务执行时,where子句立即得到一个false值,于是不会执行实际上的扫描,但此进程仍得到了一个独占的表锁定。
    因为此进程现在已有一个独占的表锁,所以可以保证没有其他事务会修改任何数据行,能进行重复读,且避免了由于holdlock所引起的潜在性死锁。
    但是,在使用表锁定来尽可能地减少死锁的同时,也增加了对表锁定的争用。因此,在实现这种方法之前,你需要权衡一下:避免死锁是否比允许并发地对表进行访问更重要。
    所以,在这个事务中,没有其他进程修改表中任何行的price。

    七 如何避免死锁
    1 使用事务时,尽量缩短事务的逻辑处理过程,及早提交或回滚事务;
    2 设置死锁超时参数为合理范围,如:3分钟-10分种;超过时间,自动放弃本次操作,避免进程悬挂;
    3 所有的SP都要有错误处理(通过@error)
    4 一般不要修改SQL SERVER事务的默认级别。不推荐强行加锁
    5 优化程序,检查并避免死锁现象出现;
    1)合理安排表访问顺序
    2)在事务中尽量避免用户干预,尽量使一个事务处理的任务少些。
    3)采用脏读技术。脏读由于不对被访问的表加锁,而避免了锁冲突。在客户机/服务器应用环境中,有些事务往往不允许读脏数据,但在特定的条件下,我们可以用脏读。
    4)数据访问时域离散法。数据访问时域离散法是指在客户机/服务器结构中,采取各种控制手段控制对数据库或数据库中的对象访问时间段。主要通过以下方式实现: 合理安排后台事务的执行时间,采用工作流对后台事务进行统一管理。工作流在管理任务时,一方面限制同一类任务的线程数(往往限制为1个),防止资源过多占用; 另一方面合理安排不同任务执行时序、时间,尽量避免多个后台任务同时执行,另外,避免在前台交易高峰时间运行后台任务
    5)数据存储空间离散法。数据存储空间离散法是指采取各种手段,将逻辑上在一个表中的数据分散到若干离散的空间上去,以便改善对表的访问性能。主要通过以下方法实现: 第一,将大表按行或列分解为若干小表; 第二,按不同的用户群分解。
    6)使用尽可能低的隔离性级别。隔离性级别是指为保证数据库数据的完整性和一致性而使多用户事务隔离的程度,SQL92定义了4种隔离性级别:未提交读、提交读、可重复读和可串行。如果选择过高的隔离性级别,如可串行,虽然系统可以因实现更好隔离性而更大程度上保证数据的完整性和一致性,但各事务间冲突而死锁的机会大大增加,大大影响了系统性能。
    7)使用Bound Connections。Bound connections 允许两个或多个事务连接共享事务和锁,而且任何一个事务连接要申请锁如同另外一个事务要申请锁一样,因此可以允许这些事务共享数据而不会有加锁的冲突。
    8)考虑使用乐观锁定或使事务首先获得一个独占锁定。  

    八如何对行、 表、数据库加锁
    1 如何锁一个表的某一行
    Java代码
    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED   
    SELECT * FROM table1 ROWLOCK WHERE A = 'a1'  

    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
    SELECT * FROM table1 ROWLOCK WHERE A = 'a1'
    2 锁定数据库的一个表
    select col1 from 表 (tablockx) where 1=1 ;
    加锁后其它人不可操作,直到加锁用户解锁,用commit或rollback解锁
    3.实例
    建表
    Java代码
    create table table1(A varchar(50)  not  null, B varchar(50) ,C varchar(50));   
    create table table2(D varchar(50),E varchar(50))   
    insert table1 (A,B,C) values(‘a1’,’b1’,’c1’);   
    insert table1 (A,B,C) values(‘a2’,’b2’,’c2’);   
    insert table1 (A,B,C) values(‘a3’,’b3’,’c3’);   
    insert table2 (D,E) values(‘d1’,’e1’);   
    insert table2 (D,E) values(‘d2’,’e2’);  

    create table table1(A varchar(50)  not  null, B varchar(50) ,C varchar(50));
    create table table2(D varchar(50),E varchar(50))
    insert table1 (A,B,C) values(‘a1’,’b1’,’c1’);
    insert table1 (A,B,C) values(‘a2’,’b2’,’c2’);
    insert table1 (A,B,C) values(‘a3’,’b3’,’c3’);
    insert table2 (D,E) values(‘d1’,’e1’);
    insert table2 (D,E) values(‘d2’,’e2’);
    1)排它锁
    Java代码
    -- A事务先更新table1表,在更新时,对其他事务进行排他   
    begin tran   
    update table1 set A='aa' where B='b2';   
    waitfor delay '00:00:30'; --等待30秒   
    commit tran   
    -- A事务先更新table2表   
    begin tran   
    select * from table1 where B='b2';   
    commit tran  

    -- A事务先更新table1表,在更新时,对其他事务进行排他
    begin tran
    update table1 set A='aa' where B='b2';
    waitfor delay '00:00:30'; --等待30秒
    commit tran
    -- A事务先更新table2表
    begin tran
    select * from table1 where B='b2';
    commit tran若同时执行上述两个事务,则select查询必须等待update执行完毕才能执行即要等待30秒
    2)共享锁
    Java代码
    -- A事务先查询table1表,在查询时,加共享锁,防止其他事务对该表进行修改操作   
    begin tran   
    select * from table1 holdlock where B='b2' ;   
    -holdlock人为加锁   
    waitfor delay '00:00:30';--等待30秒   
    commit tran   
    -- A事务先查询table1表,后更改table1表   
    begin tran   
    select A,C from table1 where B='b2';   
    update table1 set A='aa' where B='b2';   
    commit tran  

    -- A事务先查询table1表,在查询时,加共享锁,防止其他事务对该表进行修改操作
    begin tran
    select * from table1 holdlock where B='b2' ;
    -holdlock人为加锁
    waitfor delay '00:00:30';--等待30秒
    commit tran
    -- A事务先查询table1表,后更改table1表
    begin tran
    select A,C from table1 where B='b2';
    update table1 set A='aa' where B='b2';
    commit tran若并发执行上述两个事务,则B事务中的select查询可以执行,而update必须等待第一个事务释放共享锁转为排它锁后才能执行即要等待30秒
    3)死锁
    Java代码
    -- A事务先更新table1表,然后延时30秒,再更新table2表;   
    begin tran   
    update table1 set A='aa' where B='b2';   
    --这将在 Table1 中生成排他行锁,直到事务完成后才会释放该锁。   
    waitfor delay '00:00:30';   
    --进入延时   
    update table2 set D='d5' where E='e1' ;   
    commit tran   
    -- B事务先更新table2表,然后延时10秒,再更新table1表;   
    begin tran   
    update table2 set D='d5' where E='e1';   
    --这将在 Table2 中生成排他行锁,直到事务完成后才会释放该锁   
    waitfor delay '00:00:10'  
    --进入延时   
    update table1 set A='aa' where B='b2' ;   
    commit tran  

    -- A事务先更新table1表,然后延时30秒,再更新table2表;
    begin tran
    update table1 set A='aa' where B='b2';
    --这将在 Table1 中生成排他行锁,直到事务完成后才会释放该锁。
    waitfor delay '00:00:30';
    --进入延时
    update table2 set D='d5' where E='e1' ;
    commit tran
    -- B事务先更新table2表,然后延时10秒,再更新table1表;
    begin tran
    update table2 set D='d5' where E='e1';
    --这将在 Table2 中生成排他行锁,直到事务完成后才会释放该锁
    waitfor delay '00:00:10'
    --进入延时
    update table1 set A='aa' where B='b2' ;
    commit tran若并发执行上述两个事务,A,B两事务都要等待对方释放排他锁,这样便形成了死锁。

    九、sqlserver提供的表级锁
    sqlserver所指定的表级锁定提示有如下几种
    1. HOLDLOCK: 在该表上保持共享锁,直到整个事务结束,而不是在语句执行完立即释放所添加的锁。  
    2. NOLOCK:不添加共享锁和排它锁,当这个选项生效后,可能读到未提交读的数据或“脏数据”,这个选项仅仅应用于SELECT语句。   
    3. PAGLOCK:指定添加页锁(否则通常可能添加表锁)
    4. READCOMMITTED用与运行在提交读隔离级别的事务相同的锁语义执行扫描。默认情况下,SQL Server 2000 在此隔离级别上操作。
    5. READPAST: 跳过已经加锁的数据行,这个选项将使事务读取数据时跳过那些已经被其他事务锁定的数据行,而不是阻塞直到其他事务释放锁,READPAST仅仅应用于READ COMMITTED隔离性级别下事务操作中的SELECT语句操作
    6. READUNCOMMITTED:等同于NOLOCK。   
    7. REPEATABLEREAD:设置事务为可重复读隔离性级别。  
    8. ROWLOCK:使用行级锁,而不使用粒度更粗的页级锁和表级锁。
    9. SERIALIZABLE:用与运行在可串行读隔离级别的事务相同的锁语义执行扫描。等同于 HOLDLOCK。
      10. TABLOCK:指定使用表级锁,而不是使用行级或页面级的锁,SQL Server在该语句执行完后释放这个锁,而如果同时指定了HOLDLOCK,该锁一直保持到这个事务结束。
    11. TABLOCKX:指定在表上使用排它锁,这个锁可以阻止其他事务读或更新这个表的数据,直到这个语句或整个事务结束。
    12. UPDLOCK :指定在读表中数据时设置更新锁(update lock)而不是设置共享锁,该锁一直保持到这个语句或整个事务结束,使用UPDLOCK的作用是允许用户先读取数据(而且不阻塞其他用户读数据),并且保证在后来再更新数据时,这一段时间内这些数据没有被其他用户修改
    SELECT * FROM table WITH (HOLDLOCK) 其他事务可以读取表,但不能更新删除
    SELECT * FROM table WITH (TABLOCKX) 其他事务不能读取表,更新和删除

    十、应用程序锁

    应用程序锁就是客户端代码生成的锁,而不是sql server本身生成的锁处理应用程序锁的两个系统存储过程
    sp_getapplock: 锁定应用程序资源
    sp_releaseapplock: 为应用程序资源解锁
    )使用尽可能低的隔离性级别。隔离性级别是指为保证数据库数据的完整性和一致性而使多用户事务隔离的程度,SQL92定义了4种隔离性级别:未提交读、提交读、可重复读和可串行。如果选择过高的隔离性级别,如可串行,虽然系统可以因实现更好隔离性而更大程度上保证数据的完整性和一致性,但各事务间冲突而死锁的机会大大增加,大大影响了系统性能。
    7)使用Bound Connections。Bound connections 允许两个或多个事务连接共享事务和锁,而且任何一个事务连接要申请锁如同另外一个事务要申请锁一样,因此可以允许这些事务共享数据而不会有加锁的冲突。
    8)考虑使用乐观锁定或使事务首先获得一个独占锁定。

    八如何对行、 表、数据库加锁
    1 如何锁一个表的某一行
    Java代码
    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED   
    SELECT * FROM table1 ROWLOCK WHERE A = 'a1'  



    如果能有更多的 隔离级别对应锁的详细对比说明,就更好
  • 相关阅读:
    02-print的用法
    01-Hello World
    01-查看系统整体性能情况:sar
    03-购物车
    Python之路,Day2
    02-三级菜单
    Python之路,Day1
    loadrunner中配置java脚本环境
    算法
    实现testNg的retry机制
  • 原文地址:https://www.cnblogs.com/BlueWoods/p/1871807.html
Copyright © 2011-2022 走看看