在之前的一片随笔中,简单的说了一下SQL Server中的隔离级别。而SQL Server的隔离级别是通过锁的机制来实现的。现在深入一下,谈谈SQL Server中的锁。
开始之前,先要定义一下前提:
1.隔离级别的实现其实就是在不同的资源上加锁。
2.对数据库的每一次访问(CRUD)我们称其为一个事务(在begin tran,commit tran|rollback tran中的语句块,或者即席查询--单条SQL)。不同的事务如果同时访问同一个数据库资源,会产生一系列的问题(见
Sql Server隔离级别(1)
)。
3.大多数的事务在访问资源之前都会尝试对资源加上对应的锁(acquire lock,请求一个锁)。只有在Read Uncommitted隔离级别以及在表上加了WITH(No Lock) hint的Select语句不会请求锁。
3.读锁(共享锁S)不会阻塞读锁,读锁会阻塞写锁(独占锁X),写锁会阻塞读锁。
4.在一个事务中,只有在Repeatable Read或更高的隔离级别下,会将select语句的读锁一直保持到事务结束(提交或回滚),Read Committed隔离级别会造成不可重复读的原因就是在事务中的select语句执行完成后(事务没有执行完)读锁就直接释放了。
5.当前事务中的锁可以通过SELECT * FROM sys.dm_tran_locks 这个查询查看
====================================开始的分割线=========================================================
Server中存在不同粒度的锁:行锁,键锁,页锁,表锁等等,之所以要存在这些锁,是为了在并发性与系统开销间做一个平衡。粒度更小的锁将带了更好的并发性和更大的系统开销例如行锁的并发性好于表锁,不过系统的开销却大于表锁。
a.获取表锁时:整张表包括未被查询使用的行将会被锁住,其他试图请求表上不兼容锁的查询都将等待。此时系统中只需要维护一个表锁即可。
b.获取行锁时:只有查询使用到的行会被锁住,请求其他行的查询可以顺利的进行。此时并发性好于a,但是对于每一个被使用的行系统都要维护一个锁,系统开销增加。
一下是一些锁的资源,这里先大概了解下就好
资源 |
说明 |
---|---|
RID |
行标识,用于锁住整个表的其中一行 |
KEY |
通过在索引来锁住行,用于在序列化的隔离级别中锁住一个行的范围。 |
PAGE |
8kb大小的数据页或者索引页 |
EXTENT |
一组连续的八页,例如数据页或索引页。 |
TABLE |
包括所有数据和索引的整个表。 |
DATABASE |
数据库。 |
在一个SQL执行的时候,或多或少都会请求上述的资源,而对这些资源的请求可以看成是对这些资源上的锁的请求。只有成功的请求到这些资源上的锁或者说成功的在当前的事务中对这些资源加锁,才能对这些资源进行CRUD的操作。
对于不同的资源请求,SQL Server会应用不同的锁模式来试图在资源上加锁。经常接触到的锁模式如下:
共享锁(S):
请求到共享锁(读锁)允许事务可以读取某一个资源,此时其他的事务将请求不到该资源的独占锁(写锁),但是可以请求到共享锁。在资源读取完成后,共享锁将会释放(即使此时事务还没有结束)。除非事务在read repeatable或者更高的隔离级别下执行,或者通过使用sql hint在事务执行时一直持有锁(例如WITH(HOLDLOCK)),共享锁将持续到事务结束。
更新锁(U):
更新锁的存在是为了避免在并发环境下不同事务的Update语句造成死锁。
一个典型的Update操作将经过以下两步:
步骤1.获取将要更新的资源的共享锁将数据读出
步骤2.将共享锁转换成独占锁并将更新的数据写入到资源中
考虑到并发环境,两个事务同时到达步骤1,获取到同一资源的共享锁(共享锁之间不会阻塞)。然后两个事务都开始将试图共享锁转换成独占锁,发现无法转换,因为此时两个事务同时还保留了对资源的共享锁,而共享锁是会阻塞独占锁的。两个事务都等待对方的共享锁释放,于是就产生了死锁。
上述的Update模式中,一个Update操作将不再是原子操作,因为在步骤1到步骤2之间会被其他的事务影响。所以需要引入更新锁来替换步骤1中获取的共享锁,更新锁是互斥的,所以将不会出现两个Update操作同时获取到更新锁的情况。只可能是一个事务获取到了更新锁,而另一个事务只好等待更新锁的释放。
在Update用更新锁来替代共享锁来查找资源将防止并发更新产生死锁,并且由于更新锁可以和共享锁兼容,所以其他的事务依然可以在资源上放置共享锁。
独占锁(X)
独占锁阻塞其他的所有锁,无法读取或者修改加了独占锁的资源。
意向锁(IS,IX,SIX)
一个意向锁表明Sql Server希望获取当前资源的层次更低的共享锁或者独占锁。例如,表上的IS锁(意向共享锁)表明事务将要表中的页或者行(粒度比表更小的资源)上放置共享锁,而放置意向锁将避免随后的其他事务在表上获取到独占锁。意向锁能够提高性能,因为当一个事务要获取一个表的锁的时候不用再去检查表的资资源(页或行)是否被锁住了, 而只需要检查表上是否有意向锁就行了。