SQL SERVER 查询性能优化——分析事务与锁(五)

      SQL SERVER 查询性能优化——分析事务与锁(一)html

      SQL SERVER 查询性能优化——分析事务与锁(二)sql

      SQL SERVER 查询性能优化——分析事务与锁(三)数据库

 

上接SQL SERVER 查询性能优化——分析事务与锁(四)缓存

 

(四)未检测到的分布式死锁性能优化

        某应用程序持有数据库资源,开启事务以后又与用户交互,而在与用户的交互过程当中出现了错误,致使数据库资源迟迟不能释放。SQL SERVER 2005/2008 动态管理视图sys.dm_exec_requests提供相关信息,该SESSION_IDstatus字段值为“sleeping”,wait_type为“NULL”值。若是是SQL 2005则能够经过Microsoft SQL Server Management Studio管理工具中的“活动监视器--》进程信息”视图,该进程的“开启事务字段”显示非“0”值。以下图。session

 

 

      在SQL 2005(2008)执行代码,即SQL SERVER 查询性能优化——分析事务与锁(二)中的“例一”,也就是下面的代码,获得以下图。并发

select spid 进程,STATUS 状态, 登陆账号=SUBSTRING(SUSER_SNAME(sid),1,30)

,用户机器名称=SUBSTRING(hostname,1,12)

,是否被锁住=convert(char(3),blocked)

,数据库名称=SUBSTRING(db_name(dbid),1,20),cmd 命令,waittype as 等待类型

,last_batch 最后批处理时间,open_tran 未提交事务的数量

from master.sys.sysprocesses

--列出锁住别人(在别的进程中blocked字段中出现的值)但本身未被锁住(blocked=0)

Where spid in (select blocked from master.sys.sysprocesses) and blocked=0

 

 

 

      因为应用程序持有事务,并且应用程序出错以后,没对事务的相应处理,也没有须要等待的资源,但持有事务,与前一种(三)状况相似,但经过SQL PROFILER工具进行跟踪,却没法发现任何错误事件。分布式

建议解决方式高并发

     应用程序所形成的分布式死锁,很难加以跟踪分析,须要程序开发人员自行记录该应用程序的行为,比较多用户状况下,在进行哪些工做以后,系统就迟滞没法正常执行下去。这须要程序开发人员保持良好的开发习惯:事务越晚开启越好,使用资源越少越好,一旦开启了事务早晚关闭,事务执行过程当中不要与用户有任何交互,要输入的参数或内容应该在开启事务以前就应该输入完毕,对相关数据的各类校验也要在开启事务以前进行校验,事务应该只是在往数据库中插入更新数据时开启,插入更新完毕以后,就当即关闭。工具

 

 

(五)锁定数据粒度过低或过高

        用户设置不当的锁定粒度时,若是设置事务一概使用Row locktable lock都可能产生问题,或是当系统资源使用过分,也很容易产生被锁定的情形。

建议解决方式

       能够经过SQL PROFILER 观察“TextData”字段所呈现的SQL语句,观察该应用程序是否设置了锁定提示,若想要暂时中止锁定提示形成的影响,能够经过如下语句

       Dbcc traceon(8755)

或以SQL SERVER 激活参数-T 8755 来中止锁定提示功能,如有改善,能够从新考虑从应用程序重新移除锁定提示的可能性。

 

(六)Compile Blocking

         此现象是因为编译存储过程致使被锁定,在master.sys.sysprocesses视图中或sp_lock存储过程当中观察到的等待资源字段中的内容是“COMPILE”,或者使用SQL PROFILER 录制过程当中出现大量的“SP:REComplie”事件。因为从新编译须要耗费CPU资源,因此,此种锁定是在一长串的被锁定链接中,单一锁定者锁定时间不长,但整个连接各点都有一点耗时,因此在连接尾端的被锁定者须要等待较长时间。同时会出现CPU的使用率比较高。

        当存储过程当中使用了缓存数据表,而该缓存数据表还须要设置结构,如须要要设置主键或者利用缓存数据表开打开游标,则每次调用该存储过程进,都会要求从新编译。或这个存储过程是当应用程序执行时,经常会被调用的热门存储过程,就会出现Compile Blocking的情况出现。

       但存储过程第一次使用时,也会须要编译,因此不要一看到是在等待编译,就识觉得是COMPILE Blocking现象。

建议解决方式

      使用sp_executesql执行语句,即便用sp_executesql执行SQL语句,SQL语句不会编译为存储过程执行计划的一部分,所以在执行该类语句时,SQL SERVER 会自由的使用高速缓存中的现有语句计划,或者在执行阶段创建新的执行计划,无论任何一种状况,调用存储过程的计划都不会受影响,也没必要进行从新编译。

      EXECUTE语句也有相同的效果,但不建议你使用。由于使用EXECUTE没有使用SP_EXECUTESQL语句的效率高,由于前者不容许查询参数化。

 

3、基本原则:

     1. 事务不能够跨批处理,语句越短越好,事务期间不要与用户进行交互

     2. 当心处理逾时放弃,或者执行错误等状况。

    3. 正确创建索引。能够参考本人前面的相关文章。

    (如SQL Server 查询性能优化——建立索引原则(一)

      SQL Server 查询性能优化——覆盖索引(一)等系列文章

    4. 数据表最好有汇集索引,并且汇集索引的键值不要太大,由于全部的非汇集索引存储的都是汇集索引的键值。不要使用常常须要进行更新的字段作为汇集索引的键值,由于汇集索引一旦进行了变动,则全部的非汇集索引也要跟着进行变动,致使大量的锁定。索引建少了,影响查询效率,建多了,浪费维护的资源与下降新增、修改、删除的效率,因此建好索引以后,要当心观察SQL SERVER 使用索引的状况,将多余的索引删除,对于数据密度大,或者查询条件鉴别率过低的字段不要创建索引。

     5. 尽可能不要激活Implicit Transaction,以避免它长时间的持有事务。

     6. 尽可能下降事务隔离级别

     7. 进行压力测试以了解当大用户量时,交互将形成何种程度的锁定问题。

 

  4、 防止与处理死锁

        1.尽可能避免或尽快处理锁定,当锁定与被锁定过多时,就可能形成死锁

        2.访问资源的顺序要相同。例如链接A先访问资源1,而后访问资源2,而链接B的访问顺序与之相反,则可能发生死锁。不要在开启事务的状况下,调用外部程序,容易形成分布式死锁。

        3.让不一样的链接使用相同的锁定。或两条链接由于修改相同的资源而互相锁定,若是你的系统对于更新数据的正确性不作强制性要求,能够考虑使用sp_getbindtokensp_bindsession两个系统存储过程,让链接共享锁定,则两条链接同时更新数据,也就可能形成数据更新遗失。

例:

use Test

go

create proc sp_upd_OPINION

@OPINIONID varchar(20),

@bindToken varchar(255) output

as

exec sp_getbindtoken @bindToken output

update WBK_OPINION set OPINION_VALUE='true'

where OPINION_ID=@OPINIONID

go

create proc sp_upd_OPINION2

@OPINIONID varchar(20),

@bindSession varchar(255) output

as

exec sp_bindsession @bindSession 

update WBK_OPINION set OPINION_VALUE='False'

where OPINION_ID=@OPINIONID

 

go

----在第一个链接中执行

declare @bindToken varchar(255)

begin tran

exec sp_upd_opinion 'PreEntryIDUse',@bindToken output

select * from WBK_OPINION

select @@trancount  --事务数量为

select @bindToken

 

 

 

----在第二个链接中执行

---其中@binToken是由第一个链接执行完毕以后,而获取的

begin tran

exec sp_upd_opinion2 'PreEntryIDUse',@bindToken 

select * from WBK_OPINION

select @@trancount  --事务数量为

 

 

 

 

---在第三个链接中执行如下语句,因为不在同一个事务以内,因此会被锁定

update WBK_OPINION set OPINION_VALUE='true'

where OPINION_ID='PreEntryIDUse' 

 

 

rollback tran  ---回滚

 

 

         1. 你能够根据以上代码,自行编码相应的测试示例,经过Management studio分别使用三条链接来执行更新示例代码,你会发现享有相同TOKEN的两条链接会一同更新,而其获取的@@TRANCOUNT系统变量也是同样的。而不在同一事务中的其余链接则会被锁住。@@TRANCOUNT也与前述的事务无关。

        2.提交不一样的数据访问路径。若是两条不一样链接的SQL语句,由于抢相同索引而致使死锁,能够考虑为不一样的访问语句创建不一样的索引,经过索引提示强制让两条链接访问各自的索引。或者是两条不一样的链接访问相同的数据表,若是引用不一样的索引,但各自的访问顺序正彼此交错,造成死锁,则可强制两条链接使用相同的索引,以维护访问前后秩序。

      无论如何,采用此类解决方式时,都要考虑额外的性能损耗,由于你经过索引提示强制了索引访问,让查询优化程序不能凭借数据的特性使用最佳的索引。

 

5、发生死锁后的处理

       经过设置SET DEADLOCK_PRIORITY LOW,让不重要的事务自动放弃,并在这些链接执行的业务逻辑中,加上针对死锁的错误处理。

      事实上,在很是复杂的高并发量的系统中,要彻底预防死锁,或者要知道什么样的用户在特殊的访问次序中会发生死锁,是很是困难的。因此应用程序应该对死锁错误“1205”要有相应的处理,以完成原有的业务逻辑的处理或是善后清除处理。

相关文章
相关标签/搜索