数据库事务隔离性和锁,加深理解

1.假设有一个事务A

1
2
3
4
5
6
-----事务A------- 
begin tran 
update [TestDB].[dbo].tb_score set score=score+1 where id=3 
   waitfor delay '00:00:10'   --延迟10秒读取 
SELECT *  FROM [TestDB].[dbo].tb_score 
commit TRANSACTION


它会运行10s左右,主要是更新表tb_score。在这10s期间,有语句对该表做查询。

1
SELECT *  FROM [TestDB].[dbo].tb_score

该语句会被阻塞,因为update会默认加上共享锁,直到10s后,事务A提交后才能查出结果。

以上语句就等介于下面的语句,因为事务隔离级别READ COMMITTED是SQLServer的默认设置。

1
2
3
4
5
SET TRANSACTION ISOLATION LEVEL 
READ committed 
begin tran 
SELECT *  FROM [TestDB].[dbo].tb_score 
commit TRANSACTION


此处,值得注意的事情是,在事务A中当update语句更新的时候,如果要更新的目标值=现在的值,比如,在id=3的记录,score已经等于100了的情况下,语句如果是update [TestDB].[dbo].tb_score set score=100 where id=3。那么执行SELECT *  FROM [TestDB].[dbo].tb_score将不被阻塞。原因猜测是sql不对这种实际不做更改的Update语句加锁

     

2.还是以上事务A

把查询语句改为如下

语句B:

1
2
3
4
5
SET TRANSACTION ISOLATION LEVEL 
READ uncommitted 
begin tran 
SELECT *  FROM [TestDB].[dbo].tb_score 
commit TRANSACTION

此时

SELECT *  FROM [TestDB].[dbo].tb_score语句被允许“脏读”

该查询未等事务A提交,就已经读出,并且读书的值为A以修改但未提交的值。

例如id=3的score=100,运行A事务的时候,score=101了,此时运行语句B,则立刻返回出id=3的score=101,而不管A是否真的已经提交或者rollback。

还有一点要注意,当你设置了READ uncommitted,那么接下来的sql语句都会是 READ uncommitted。msdn上:一次只能设置一个隔离级别选项,而且设置的选项将一直对那个连接始终有效,直到显式更改该选项为止。事务中执行的所有读取操作都会在指定的隔离级别的规则下运行,除非语句的 FROM 子句中的表提示为表指定了其他锁定行为或版本控制行为。所以在查询分析器中做实验的时候,要注意,一旦设置了READ uncommitted,接下来在当前窗口中的语句都是如此。

3

1
2
3
4
5
-----事务A------- 
begin tran 
select *from  [TestDB].[dbo].tb_score with(tablockx) where id=3 
   waitfor delay '00:00:10'   --延迟10秒读取 
commit TRANSACTION


此事务对表加了表级别的排它锁。因此在此期间,

1
2
3
4
5
SET TRANSACTION ISOLATION LEVEL 
READ uncommitted 
begin tran 
SELECT *  FROM [TestDB].[dbo].tb_score where id=1 
commit TRANSACTION

更多内容,可以看

http://msdn.microsoft.com/zh-cn/library/ms187373.aspx

http://msdn.microsoft.com/zh-cn/library/ms173763.aspx    

4

此处有些疑问:

两个事务   

1
2
3
4
5
6
7
8
9
10
11
-----事务A-------
 begin tran 
 select *from  [TestDB].[dbo].tb_score with(updlock,rowlock) where id=3    waitfor delay '00:00:10'   --延迟10秒读取 
 update  [TestDB].[dbo].tb_score set score=score+1  where id=3 commit TRANSACTION
 
-----事务B------ 
SET TRANSACTION ISOLATION LEVEL READ committed 
begin tran 
select *from  [TestDB].[dbo].tb_score with(updlock,rowlock) where
id=3
commit TRANSACTION

我的理解是,事务A运行的时候,对id=3的加了行锁,还有更新锁,更新锁的话一定要等到事务运行完才能再被获取。   
因此事务A运行的时候,事务B也马上运行,但是事务B也要对改行获取更新锁,因此被阻塞在此,所以最后运行的结果是A运行完了B在运行。以上一切正常

但是把事务B改成   

-----事务B------- 

1
2
3
4
SET TRANSACTION ISOLATION LEVEL READ committed 
begin tran 
select *from  [TestDB].[dbo].tb_score with(updlock,rowlock) where id=1
commit TRANSACTION

事务B对id=1的行加锁。   
那么我的理解是,既然事务A加锁的对象是id=3的行,那么和事务B根本不会有什么冲突。但是事实上,运行事务A后马上运行B,发现事务B也会被阻塞。并且事务A还会发出异常错误:说是检测到死锁。    
Transaction (Process ID 52) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

本人愚钝,百思不解,求专家指教。
























本文转自cnn23711151CTO博客,原文链接:http://blog.51cto.com/cnn237111/638055 ,如需转载请自行联系原作者







上一篇:【百度地图API】如何制作公交线路的搜索?如331路


下一篇:git的日常应用