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

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

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

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

(四)未检测到的分布式死锁

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

在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 lock或table 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语句的效率高,因为前者不允许查询参数化。

三、基本原则:

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

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

3. 正确建立索引。可以参考本人前面的相关文章。

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

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

4. 数据表最好有聚集索引,而且聚集索引的键值不要太大,因为所有的非聚集索引存储的都是聚集索引的键值。不要使用经常需要进行更新的字段做为聚集索引的键值,因为聚集索引一旦进行了变更,则所有的非聚集索引也要跟着进行变更,导致大量的锁定。索引建少了,影响查询效率,建多了,浪费维护的资源与降低新增、修改、删除的效率,所以建好索引之后,要小心观察SQL SERVER 使用索引的情况,将多余的索引删除,对于数据密度大,或者查询条件鉴别率太低的字段不要建立索引。

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

6. 尽量降低事务隔离级别

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

  四、 防止与处理死锁

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

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

3.让不同的连接使用相同的锁定。或两条连接因为修改相同的资源而互相锁定,如果你的系统对于更新数据的正确性不做强制性要求,可以考虑使用sp_getbindtoken和sp_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语句,因为抢相同索引而导致死锁,可以考虑为不同的访问语句建立不同的索引,通过索引提示强制让两条连接访问各自的索引。或者是两条不同的连接访问相同的数据表,如果引用不同的索引,但各自的访问顺序正彼此交错,形成死锁,则可强制两条连接使用相同的索引,以维护访问先后秩序。

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

五、发生死锁后的处理

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

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

SQL SERVER 查询性能优化——分析事务与锁(五)的更多相关文章

  1. SQL Server 查询性能优化 相关文章

    来自: SQL Server 查询性能优化——堆表.碎片与索引(一) SQL Server 查询性能优化——堆表.碎片与索引(二) SQL Server 查询性能优化——覆盖索引(一) SQL Ser ...

  2. Sql Server查询性能优化之走出索引的误区

    据了解绝大多数开发人员对于索引的理解都是一知半解,局限于大多数日常工作没有机会.也什么没有必要去关心.了解索引,实在哪天某个查询太慢了找到查询条件建个索引就ok,哪天又有个查询慢了,再建立个索引就是, ...

  3. SQL Server查询性能优化——堆表、碎片与索引(二)

    本文是对 SQL Server查询性能优化——堆表.碎片与索引(一)的一些总结.  第一:先对 SQL Server查询性能优化——堆表.碎片与索引(一)中的例一的SET STATISTICS IO之 ...

  4. SQL Server查询性能优化——覆盖索引(二)

    在SQL Server 查询性能优化——覆盖索引(一)中讲了覆盖索引的一些理论. 本文将具体讲一下使用不同索引对查询性能的影响. 下面通过实例,来查看不同的索引结构,如聚集索引.非聚集索引.组合索引等 ...

  5. SET STATISTICS IO和SET STATISTICS TIME 在SQL Server查询性能优化中的作用

    近段时间以来,一直在探究SQL Server查询性能的问题,当然也漫无目的的查找了很多资料,也从网上的大神们的文章中学到了很多,在这里,向各位大神致敬.正是受大神们无私奉献精神的影响,所以小弟也作为回 ...

  6. Sql Server查询性能优化之不可小觑的书签查找

    小小程序猿SQL Server认知的成长 1.没毕业或工作没多久,只知道有数据库.SQL这么个东东,浑然分不清SQL和Sql Server Oracle.MySql的关系,通常认为SQL就是SQL S ...

  7. SQL Server查询性能优化——堆表、碎片与索引(一)

    SQL Server在堆表中查询数据时,是不知道到底有多少数据行符合你所指定的查找条件,它将根据指定的查询条件把数据表的全部数据都查找 一遍.如果有可采用的索引,SQL Server只需要在索引层级查 ...

  8. SQL Server 查询性能优化——创建索引原则(一)(转载)

    索引是什么?索引是提高查询性能的一个重要工具,索引就是把查询语句所需要的少量数据添加到索引分页中,这样访问数据时只要访问少数索引的分页就可以.但是索引对于提高查询性能也不是万能的,也不是建立越多的索引 ...

  9. SQL Server 查询性能优化——覆盖索引

    覆盖索引又可以称为索引覆盖. 解释一: 就是select的数据列只用从索引中就能够取得,不必从数据表中读取,换句话说查询列要被所使用的索引覆盖. 解释二: 索引是高效找到行的一个方法,当能通过检索索引 ...

随机推荐

  1. python 版本升级(CentOS) 从2.6.6升级到2.7.6

    安装必须的包 yum install zlib-devel bzip2-devel pcre-devel openssl-devel ncurses-devel sqlite-devel readli ...

  2. Winpcap安装,Cannot open include file 'pcap.h'

    VC报错 fatal error C1083: Cannot open include file: 'pcap.h': No such file or directory Winpcap是window ...

  3. [LeetCode] 452 Minimum Number of Arrows to Burst Balloons

    There are a number of spherical balloons spread in two-dimensional space. For each balloon, provided ...

  4. C#中调用python方法

    最近因为项目设计,有部分使用Python脚本,因此代码中需要调用python方法. 1.首先,在c#中调用python必须安装IronPython,在 http://ironpython.codepl ...

  5. hdu 5104 素数打表水题

    http://acm.hdu.edu.cn/showproblem.php?pid=5104 找元组数量,满足p1<=p2<=p3且p1+p2+p3=n且都是素数 不用素数打表都能过,数据 ...

  6. 练习1-21:编写程序entab,将空格串替换为最少数量的制表符和空格。。。(C程序设计语言 第2版)

    #include <stdio.h> #define N 5 main() { int i, j, c, lastc; lastc = 'a'; i = j = ; while ((c=g ...

  7. wlan-mcs来自百度百科

    工作原理 802.11n射频速率的配置通过MCS(Modulation and Coding Scheme,调制与编码策略)索引值实现.MCS调制编码表是802.11n为表征WLAN的通讯速率而提出的 ...

  8. 使用Code::blocks在windows下写网络程序

    使用Code::blocks在windows下写网络程序 作者 He YiJun – storysnail<at>gmail.com 团队 ls 版权 转载请保留本声明! 本文档包含的原创 ...

  9. 在ABP中通过EF直接执行原生Sql的解决方案

    一般情况下,使用EF中的查询语法和方法语法可以帮助我们完成绝大部分业务,但是也有特殊的情况需要直接执行Sql语句.比如,我们的业务过于复杂繁琐,或是有些业务使用EF操作时比较复杂,但是使用Sql时会很 ...

  10. 【C语言学习】《C Primer Plus》第2章 C语言的概述

    学习总结 1.#include是C预处理命令之一,#include指向的是头文件,如#include <stdio.h>,这个stdio.h就是程序需要引用的C标准库之一.里面包含了pri ...