T-SQL查询进阶—理解SQL Server中的锁

2022-08-16 00:00:00 死锁 所示 粒度 资源 如图

简介

    在SQL Server中,每一个查询都会找到短路径实现自己的目标。如果数据库只接受一个连接一次只执行一个查询。那么查询当然是要多快好省的完成工作。但对于大多数数据库来说是需要同时处理多个查询的。这些查询并不会像绅士那样排队等待执行,而是会找短的路径执行。因此,就像十字路口需要一个红绿灯那样,SQL Server也需要一个红绿灯来告诉查询:什么时候走,什么时候不可以走。这个红绿灯就是锁。

    


    图1.查询可不会像绅士们那样按照次序进行排队

 

为什么需要锁

    在开始谈锁之前,首先要简单了解一下事务和事务的ACID属性。可以参看我之前的一篇关于ACID的文章。如果你了解了事务之间的影响方式,你就应该知道在数据库中,理论上所有的事务之间应该是完全隔离的。但是实际上,要实现完全隔离的成本实在是太高(必须是序列化的隔离等级才能完全隔离,这个并发性有点….)。所以,SQL Server默认的Read Commited是一个比较不错的在隔离和并发之间取得平衡的选择。

    SQL Server通过锁,就像十字路口的红绿灯那样,告诉所有并发的连接,在同一时刻上,那些资源可以读取,那些资源可以修改。前面说到,查询本身可不是什么绅士,所以需要被监管。当一个事务需要访问的资源加了其所不兼容的锁,SQL Server会阻塞当前的事务来达成所谓的隔离性。直到其所请求资源上的锁被释放,如图2所示。

    


    图2.SQL Server通过阻塞来实现并发

 

如何查看锁

    了解SQL Server在某一时间点上的加锁情况无疑是学习锁和诊断数据库死锁和性能的有效手段。我们常用的查看数据库锁的手段不外乎两种:

    使用sys.dm_tran_locks这个DMV

         SQL Server提供了sys.dm_tran_locks这个DMV来查看当前数据库中的锁,前面的图2就是通过这个DMV来查看的.

         这里值得注意的是sys.dm_tran_locks这个DMV看到的是在查询时间点的数据库锁的情况,并不包含任何历史锁的记录。可以理解为数据库在查询时间点加锁情况的快照。sys.dm_tran_locks所包含的信息分为两类,以resource为开头的描述锁所在的资源的信息,另一类以request开头的信息描述申请的锁本身的信息。如图3所示。更详细的说明可以查看MSDN(http://msdn.microsoft.com/en-us/library/ms190345.aspx)

       


       图3.sys.dm_tran_locks

 

        这个DMV包含的信息比较多,所以通常情况下,我们都会写一些语句来从这个DMV中提取我们所需要的信息。如图4所示。

        


         图4.写语句来提取我们需要的锁信息

 

    使用Profiler来捕捉锁信息

       我们可以通过Profiler来捕捉锁和死锁的相关信息,如图5所示。

       


       图5.在Profiler中捕捉锁信息

 

     但默认如果不过滤的话,Profiler所捕捉的锁信息包含SQL Server内部的锁,这对于我们查看锁信息非常不方便,所以往往需要筛选列,如图6所示。

    


    图6.筛选掉数据库锁的信息

 

    所捕捉到的信息如图7所示。

    


    图7.Profiler所捕捉到的信息

 

锁的粒度

    锁是加在数据库对象上的。而数据库对象是有粒度的,比如同样是1这个单位,1行,1页,1个B树,1张表所含的数据完全不是一个粒度的。因此,所谓锁的粒度,是锁所在资源的粒度。所在资源的信息也就是前面图3中以Resource开头的信息。

    对于查询本身来说,并不关心锁的问题。就像你开车并不关心哪个路口该有红绿灯一样。锁的粒度和锁的类型都是由SQL Server进行控制的(当然你也可以使用锁提示,但不推荐)。锁会给数据库带来阻塞,因此越大粒度的锁造成更多的阻塞,但由于大粒度的锁需要更少的锁,因此会提升性能。而小粒度的锁由于锁定更少资源,会减少阻塞,因此提高了并发,但同时大量的锁也会造成性能的下降。因此锁的粒度对于性能和并发的关系如图8所示。

    


    图8.锁粒度对于性能和并发的影响

 

    SQL Server决定所加锁的粒度取决于很多因素。比如键的分布,请求行的数量,行密度,查询条件等。但具体判断条件是微软没有公布的秘密。开发人员不用担心SQL Server是如何决定使用哪个锁的。因为SQL Server已经做了好的选择。

    在SQL Server中,锁的粒度如表1所示。

 

资源

说明

RID

用于锁定堆中的单个行的行标识符。

KEY

索引中用于保护可序列化事务中的键范围的行锁。

PAGE

数据库中的 8 KB 页,例如数据页或索引页。

EXTENT

一组连续的八页,例如数据页或索引页。

HoBT

堆或 B 树。 用于保护没有聚集索引的表中的 B 树(索引)或堆数据页的锁。

TABLE

包括所有数据和索引的整个表。

FILE

数据库文件。

APPLICATION

应用程序专用的资源。

METADATA

元数据锁。

ALLOCATION_UNIT

分配单元。

DATABASE

整个数据库。

相关文章