前面几篇已经陆续从服务器的几个大块讲述了SQL SERVER数据库的诊断和调优方式。加上本篇可以说已经可以完成常规的问题诊断及优化,本篇就是SQL SERVER中的锁。为了方便阅读给出系列文章的导读链接:
SQL SERVER全面优化-------Expert for SQL Server 诊断系列
首先阅读本文之前,大家都应该知道锁是影响你性能的一个重大因素,那么SQL SERVER为什么要引入锁呢?那就是要解决多个用户同时对数据库的并发操作时会带来以下数据不一致的问题。我想为了保证数据一致性,哪怕牺牲再多也是值得的!本文主要介绍怎么找到这个牺牲的点,及如何让你的牺牲降到最低!
还记得等待篇中的那个北京三环么?
等待很多时候都是在等待获取对象上的锁!当数据库中出现很多很多锁时,系统瞬间就无法提供正常服务。此时观察系统资源的使用情况,会发现CPU使用率不高,内存占用量也不高,还有很多未使用的内存,网络带宽也充足,硬盘也不繁忙,通过数据库管理工具查询的话,SQL SERVER中的数据也正常无误,但是使用系统的用户访问此数据库时却要需要等很多久很久,更多的就出现连接超时,数据库无响应。
这就好比本来就是早高峰,前面还撞了!十一车连撞很壮观,对于数据库十一条连锁,也很给力!
--------------博客地址---------------------------------------------------------------------------------------
Expert 诊断优化系列 http://www.cnblogs.com/double-K/
废话不多说,直接开整-----------------------------------------------------------------------------------------
锁造成的等待主要有两种:和 LCK_ 和 PAGELATCH_
PAGELATCH_:轻量级数据库内部使用的闩锁,这里不介绍
LCK_ : 八斤半的大锁这里就说它!
注 : 锁相关的基础知识请自行百度学习!
诊断锁常用的性能计数器
- Lock Requests/sec 每秒锁请求数
- Lock Waits/sec 每秒锁等待数
- Lock Wait Time (ms) 锁等待时间
- Average Wait Time (ms) 平均等待时间
- Number of Deadlocks/sec 每秒死锁数
- Latch Waits/sec 闩锁等待数
- Average Latch Wait Time (ms) 闩锁平均等待时间
计数器不过多介绍,不会用的朋友请自行百度。直接上例子:
这个例子中客户反映特定时间点系统特别慢严重影响业务,那么我们按常规顺序进行一次全面分析。
CPU来看在10点左右和晚上6点左右出现90%以上的高峰。
页生命周期和惰性写入器可以看出内存并无明显的压力
以10点为例(为什么不看六点?我默默地分析过是一样的情况)磁盘队列并不高,但10点15分的时候出现磁盘高压力。那么这是一个问题导致的还是两个呢?我们接着看。
事务活动数在10点的时候达到一个很高的值。
用户连接数在10点也彪高,那么问题清楚了,就是10点时候是用户连接太多了压力大了导致系统慢的!别天真了这篇主题是锁,主角还没出场怎么能结束? 反复强调不要轻易下结论!
连接数量多,还一个原因就是连接执行语句的时间长很长时间才能释放,那么其他的应用只能打开新连接,所以连接数会彪高,
log刷新数量彪高这时间点在insert、delete或update?(后文证实是update)
-----------------------------------------下面进入正题了--------------------
我大量update系统会很慢?会跑不动?
我们看下锁相关的计数器
锁请求数! 这个时间点大量的锁请求产生!
锁等待,大量锁等待
再看等待时间,高峰点已经达到了70秒!! 要等待70秒是啥概念? 简直是高考学校门口,还是个早高峰!!
天啊,还好没有死锁....
------------------------------语句及等待诊断--------------------
我通过计数器可以发现2个主要问题:1. 十点的时候大量update更新,导致系统大面积阻塞,语句运行时间过长。2.十点15分以后有大量磁盘读操作,导致磁盘队列暴增。
下面我们看一下语句和等待的情况:
语句和等待总体反应情况很正常,长时间语句少,而且等待并不严重。那么说明,这么系统问题点就是在特定时间点(这也是用户反应的系统慢的原因,开篇就已经提过)
那下面我们就深入10点,看看那时候到底怎么了!
首先 我们先看看语句情况!
上面图中我们只是展示了问题时点的一部分语句,主要可以看出如下结论:
- 问题时间点确实有大量的更新操作
- 更新操作被严重阻塞(锁)
- 且是一个程序循环调用的更新
- 语句运行时间长
- CPU高是因为这个时间点除了update以外还有大量的查询导致CPU高(一般情况下,系统大面积锁等待的时候CPU 资源不能有效利用,CPU会低)
接着我们看一下等待的情况,看看到底是怎么搞得,竟然锁的这么厉害!
语句总体等待来看全天都有但十点大量,并且造成系统卡死(默认30秒超时,很多都应该超时了,所以用户体验非常差!),语句的CPU和读写都不多,也说明就是相互锁的很严重!
大量的语句都是被195锁住的,而195其实本身也是同样的一个update,客户的程序中有频繁的这条update,并且在10点的时候会有另一个程序的一次大批量的循环更新,这也是造成这个大面积锁阻塞的原因!
这个问题的一个最终解决办法就是修改这个大批的循环更新,首先把每个单次处理和计算的结果进行统一计算,在做一次批量更新。这样既缩短事务的时间,降低锁定的时间。同时也降低了整个批语句的执行时间!
类似以SQL 中的游标,和程序中的for 循环单条操作,都可以修改成批量处理,不仅可以减少事务长度,减少锁时间,更是一种很有效的优化手段 !
第二个问题,磁盘10点15为什么那么高?和更新有关系?
这里可以看出第二个问题10点15的时候确实有很多大逻辑读的查询,还跟新没什么关系,但和业务有无关联就不得而知了。导致系统磁盘压力变大,和主题关系不大这里不说了..
关于锁的一个小误区
select 会阻塞 update 么? (这里指的是默认隔离级别,read committed)
上段简单小代码
create table a (a int) insert into a
select OBJECT_ID from sys.objects where object_id between 1 and 1000 begin tran
select * from a with(holdlock)
where a = 3 --------------新开一个session 执行
update a set a = 30
where a = 3
这里的with(holdlock) 是让查询保持S锁,模拟你的查询还没结束。
sp_who2 或 select session_id,status,command,blocking_session_id,wait_type from sys.dm_exec_requests where session_id = 58 查看一下
高能预警: 查询也会阻塞更新的哦~~
--------------博客地址---------------------------------------------------------------------------------------
Expert 诊断优化系列 http://www.cnblogs.com/double-K/
-----------------------------------------------------------------------------------------------------
总结:语句运行时间长,很可能的一个原因就是阻塞导致的,而阻塞大部分情况都是因为资源之间的所等待。
语句调优的时候有必要对系统做一个全面的分析。(如本文所讲)
锁的优化可以说是比较深入问题分析,减少锁的相互影响,主要也可以从语句优化入手,降低消耗缩短时间。另外也可以从业务设计方面入手,降低热点资源的争用。
锁主要是为了维护数据一致性而不得不做的牺牲。我们只能尽一切办法降低他的影响。
PS:限于篇幅本文没有讲述一些基本知识,请自行学习,如隔离级别、锁矩阵兼容性等等.....
补上使用工具链接 : Expert for sqlserver
----------------------------------------------------------------------------------------------------
注:此文章为原创,欢迎转载,请在文章页面明显位置给出此文链接!
若您觉得这篇文章还不错请点击下右下角的推荐,非常感谢!
为了方便阅读给出系列文章的导读链接: