以NA_TrafficMemberOrderRelation表为例,表的索引如下: index_id name cols 1 PK__NA_Traff__FB6751642649A903 TrafficMemberOrderRelationId 26 IX_NA_TrafficMemberOrderRelation_usermobile UserMobile 下面的查询语句,正常情况下会先使用IX_NA_TrafficM
以NA_TrafficMemberOrderRelation表为例,表的索引如下:
index_id | name | cols |
1 | PK__NA_Traff__FB6751642649A903 | TrafficMemberOrderRelationId |
26 | IX_NA_TrafficMemberOrderRelation_usermobile | UserMobile |
下面的查询语句,正常情况下会先使用IX_NA_TrafficMemberOrderRelation_usermobile索引进行查找,再返回聚集索引查找剩下的列
select * from NA_TrafficMemberOrderRelation where usermobile='13719805324' and ecprdcode='50615801625'
因为在read committed隔离级别上,查询语句申请的锁,会马上释放,所以把隔离级别设置为可重复读(repeatable read),再开启事务后运行查询语句,但不提交事务
set transaction isolation level repeatable read;
begin tran select * from NA_TrafficMemberOrderRelation with(index=PK__NA_Traff__FB6751642649A903) where usermobile='13719805324' and ecprdcode='50615801625'
然后运行 exec sp_lock来查看申请的锁:
spid | dbid | ObjId | IndId | Type | Resource | Mode | Status |
58 | 17 | 610361489 | 1 | PAG | 1:8005696 | IS | GRANT |
58 | 17 | 610361489 | 26 | KEY | (525b6b18cbd1) | S | GRANT |
58 | 17 | 610361489 | 26 | PAG | 4:53295 | IS | GRANT |
58 | 17 | 610361489 | 1 | KEY | (b1b78d542d80) | S | GRANT |
58 | 17 | 610361489 | 26 | KEY | (6e79de43f52b) | S | GRANT |
58 | 17 | 610361489 | 0 | TAB | IS | GRANT | |
58 | 17 | 610361489 | 1 | KEY | (8d95380f137a) | S | GRANT |
图中看出,查询语句申请的锁如下:
1、对象上的意向共享锁(IS)
58 | 17 | 610361489 | 0 | TAB | IS | GRANT |
2、UserMobile索引页面上的意向共享锁(IS)
58 | 17 | 610361489 | 26 | PAG | 4:53295 | IS | GRANT |
3、符合条件的键值上的共享锁(S),因为手机号码13719805324有两条记录,所以申请了两个共享锁
58 | 17 | 610361489 | 26 | KEY | (525b6b18cbd1) | S | GRANT |
58 | 17 | 610361489 | 26 | KEY | (6e79de43f52b) | S | GRANT |
58 | 17 | 610361489 | 1 | PAG | 1:8005696 | IS | GRANT |
58 | 17 | 610361489 | 1 | KEY | (b1b78d542d80) | S | GRANT |
58 | 17 | 610361489 | 1 | KEY | (8d95380f137a) | S | GRANT |
所以,通过索引查找来获取数据的查询,大致总结如下:
1、对使用到的PAGE加IS锁
2、对使用到的每一个索引键或聚集索引键加共享锁
下面看下在没有索引的情况,通过表扫描,需要申请哪些锁(先通过alter table NA_TrafficMemberOrderRelation set(lock_escalation=disable)禁止表上的索升级,防止直接升级为表上的S锁):
<span style="font-family: Arial, Helvetica, sans-serif;">select * from NA_TrafficMemberOrderRelation where CreateTime='2015-10-25 05:29:47.000'</span>
spid | dbid | ObjId | IndId | Type | Resource | Mode | Status |
58 | 17 | 610361489 | 0 | TAB | IS | GRANT |
58 | 17 | 610361489 | 1 | PAG | 1:8086440 | S | GRANT |
58 | 17 | 610361489 | 1 | PAG | 4:459840 | S | GRANT |
58 | 17 | 610361489 | 1 | PAG | 4:167476 | S | GRANT |
58 | 17 | 610361489 | 1 | PAG | 1:8317404 | S | GRANT |
58 | 17 | 610361489 | 1 | PAG | 1:8432309 | S | GRANT |
58 | 17 | 610361489 | 1 | PAG | 1:8154231 | S | GRANT |
58 | 17 | 610361489 | 1 | PAG | 1:8267887 | S | GRANT |
58 | 17 | 610361489 | 1 | PAG | 1:8052942 | S | GRANT |
58 | 17 | 610361489 | 1 | PAG | 1:8086441 | S | GRANT |
58 | 17 | 610361489 | 1 | PAG | 4:459841 | S | GRANT |
58 | 17 | 610361489 | 1 | PAG | 4:167477 | S | GRANT |
58 | 17 | 610361489 | 1 | PAG | 1:8317405 | S | GRANT |
58 | 17 | 610361489 | 1 | PAG | 1:8432308 | S | GRANT |
58 | 17 | 610361489 | 1 | PAG | 1:8154230 | S | GRANT |
58 | 17 | 610361489 | 1 | PAG | 1:8267886 | S | GRANT |
........ |
因为使用的是聚集索引扫描,表中的每一行都会读取到,如果还是按上面的规则的话,对每一行都加锁,那会产生大量的锁,所以SQL SERVER默认对锁进行了升级,直接在PAGE上加了S锁,如果页中的数据被修改(插入或修改页中的一行数据),会在页上加IX锁,而S和IX是不兼容的,所以在PAGE上的S锁就能达到隔离级别的要求
所以,通过索引扫描来获取数据的查询,大致总结如下:
1、对扫描到的每一个PAGE加上S锁。
S锁和IS锁的释放时间是:
1、当事务隔离级别为read committed时,当读取完这一行或页时(此时查询还没有结束),直接释放。
2、当事务隔离级别为repeatable read时,如果读取的这一行不满足查询条件,会直接释放掉,如果满足条件,会等到事务结束再释放,而通过扫描对页加的S锁,会等到事务结束才释放
锁的申请和释放可以通过Sql server profile中跟踪:Lock:Acquired,Lock:Released来获得: