日期:2014-05-16 浏览次数:20502 次
关于锁,有很多可以学习的,比如,锁的粒度,锁的模式,锁的兼容性,以及隔离级别等等。
但是,具体到运行一个语句时,是怎么去获取到相应的锁的呢,比如,一个select语句,那么需要加S锁,可能是对记录,也可能是对页,也可能是对表,可能通过索引去找到记录,找到记录后,是一次获取所有的锁,还是一部分一部分的获取呢?
比如下面实验中,正好是一条记录占用1个数据页,一共查询32768条数据,那么到底是对32768个页都加上S锁,还是先对一些页加锁,查询出一部分数据,然后再对剩下的页加锁,再查询出一部分数据。
又比如,现在有2个会话,会话1运行select 语句来查询数据,假设查询数据需要大概耗时18秒,而会话2是一个update语句来更新同样的数据,大概需要耗时15秒,会话1先运行,会话2在一秒后运行,那么哪个语句会先运行完呢?
一般的想法,应该是会话1先返回,因为会话1的select语句,会对数据页加上共享锁的,既然加上了共享锁,那么会话2是后运行的,要对页加独占的X锁,肯定是等待的,但实际情况却完全不是这样。如果是还有10个select语句的会话,又会是那个先运行完呢?从实际执行的情况来看,是update语句先执行完的。
言归正传,实验代码
1、建表,插入数据,建索引,这段代码中批处理的次数是18次,也就是go 18,你可以根据实际情况修改,只要保证足够的数据量,使select语句运行需要20秒左右就可以
if OBJECT_ID('test') is not null drop table test go create table test(id int,v varchar(8000)) insert into test select 1,replicate('a',8000) union all select 2,replicate('o',8000) union all select 3,replicate('c',8000) union all select 4,replicate('d',8000) union all select 5,replicate('e',8000) union all select 6,replicate('f',8000) union all select 7,replicate('g',8000) union all select 8,replicate('h',8000) union all select 9,replicate('i',8000) union all select 10,replicate('j',8000) union all select 11,replicate('k',8000) union all select 12,replicate('l',8000) go insert into test select * from test go 18 create index idx_test_id on test(id)
if OBJECT_ID('temp_lock') is not null drop table temp_lock go select 0 id,* into temp_lock from sys.dm_tran_locks where 1 = 2
3、会话1是监控代码,注意这个代码是死循环,所以需要在会话2和会话3的代码,执行完后,取消执行查询,否则会一直运行下去
declare @i int set @i = 1 while 1=1 begin insert into temp_lock select @i,* from sys.dm_tran_locks set @i = @i +1 end
select GETDATE() begin tran select id, %%lockres%%, --文件id:页id:记录id v from test where id =2 commit tran select GETDATE()
select GETDATE() update test set v =replicate('x',8000) where id = 2 select GETDATE()