事务隔离级别

     当不同的事务,对同一资源进行一些相关的操作的时候,彼此对公共数据的更改,会影响到其他事务对数据的操作,这样就导致了数据的不一致性。前两篇博客,介绍了一些事务的锁,这里侧重总结一下事务的隔离级别。

     一、准备工作:

    1、创建银行账号表:

CREATE TABLE dbo.BankAccount
(
    BankAccountId CHAR(16)        NOT NULL, -- 银行账号
    UserName       NVARCHAR(32)    NOT NULL, -- 用户
    Balance        DECIMAL(19, 2) NOT NULL, -- 余额
    LastUpdate     SMALLDATETIME  NOT NULL
)
GO


     2、准备数据:

INSERT INTO dbo.BankAccount
      VALUES ('9555500100071120', N'张三', 10000.00, GETDATE()) -- 北京分行账号
INSERT INTO dbo.BankAccount
      VALUES ('9555507551227787', N'张三', 20000.00, GETDATE()) -- 深圳分行账号
GO
    二、应用场景

    假设张三在招商银行开设了两个账号,一个是招商银行北京分行,一个是招商银行深圳分行,两个账号的余额分别是10,000和20,000。

    1、脏读: 一个事务读到另外一个事务还没有提交的数据,我们称之为脏读。

     情景:张三在网上做了一笔交易,交易额100,买方小王通过银行汇款100到张三的北京分行的账号(见下面左图),柜台操作人员向张三账号存入100(事务一),然后系统些操作日志(假设需要10秒,WAITFOR DELAY '00:00:10')正在此时张三在ATM查了一下账号上余额(事务二),发现已经是10100,于是回去准备发货,但是事务一在写操作日志时超时,这是事务回滚,存款交易被取消,钱退给了小王,这样张三查到的账号余额事实上是事务一还没有提交的数据,导致张三错误的认为已经收到交易款项。

begin tran 

	update BankAccount
	set Balance=Balance+100
	where BankAccountId='9555500100071120'
	
	select Balance from BankAccount
	where BankAccountId='9555500100071120'
	
	waitfor delay '00:00:10'			      --系统此时可能在做写日志等事情,导致延时。

		
rollback						      --事务回滚,导致其他线程对Balance的脏读()			

	select Balance 
	from BankAccount
	where BankAccountId='9555500100071120'


 另一个事务代码:

Begin tran
	set tran isolation level read uncommitted		--降低隔离级别后,容易导致脏读	
	select Balance 
	from BankAccount
	where BankAccountId='9555500100071120'


commit
   

    如才采用read uncommitted   隔离级别,会导致读数据的事务读到还没有执行完的事务中的数据,如果正在执行的其他事务是修改操作,那么Read cuncommitted级别的事务不会等待事务执行完,就读取数据。所以导致脏读。

解决方法:

   设置读数据的事务的隔离级别为set tran isolation level read committed 。

Begin tran	
	set tran isolation level read committed		      --默认的事务隔离级别,避免脏读
	select Balance 
	from BankAccount
	where BankAccountId='9555500100071120'


commit
read committed隔离级别,指定要读数据的事务必须等待事务一执行完后,才可以执行


    2、不可重复读:一个事务先后读取同一条记录,但两次读取的数据不同。

    情景:张三先后两次查询某一账号的余额,在两次查询期间,小王完成了银行转账,导致两次的查询结果不同。

事务一:

begin tran 
		
	select Balance 
	from BankAccount
	where BankAccountId='9555500100071120'
	
	waitfor delay '00:00:10'			   --系统延时,模拟不可重复读
	
	select Balance 
	from BankAccount
	where BankAccountId='9555500100071120'
	
commit



begin tran 

事务二:

begin tran 

	update BankAccount				---修改数据,导致另一个事务在一个事务中不可重复读
	set Balance=Balance+100
	where BankAccountId='9555500100071120'
	
commit

	select Balance 
	from BankAccount
	where BankAccountId='9555500100071120'

解决方法:

   把事务一隔离级别调整到repeatable read。

begin tran 
	
	set tran isolation level repeatable read	 --设置事务的隔离级别为可重复读,其他事务需要等到这个事务执行完为止。解决读到的数据不一致的问题。
	
	select Balance 
	from BankAccount
	where BankAccountId='9555500100071120'
	
	waitfor delay '00:00:10'		         --系统延时,模拟不可重复读
	
	select Balance 
	from BankAccount
	where BankAccountId='9555500100071120'
	
commit

    事务二会一直等到事务一执行完毕再返回结果


3、幻象读:一个事务先后读取一个范围的记录,但两次读取的纪录数不同。

     情景:张三妻子先后两次查询张三招商银行所有账号的总余额,而在此期间张三在广州招商银行分行成功开设了一个账号,并存入5000,导致张三妻子两次查询的总余额不同,在此期间张三原有两个账号的余额均未发生改变

事务一:

 begin tran 
	
	insert into BankAccount
	values( '9555500100071000','N张三',5000.00, GETDATE())
	
	select balance from BankAccount
	where BankAccountId= '9555500100071000'
 commit

事务二:

 begin tran 
 
	set tran isloation level repeatable read 											
	
	select SUM(balance) as Balance
	from BankAccount
	where UserName='N张三'
	
	waitfor delay '00:00:05'	                --此处的延时期间,其他事务进行了操作,导致本事务两次读出的数据库不一致,称为幻读。
	
	select SUM(balance) as Balance
	from BankAccount
	where UserName='balance'
	
 commit
解决方法:

   把事务隔离级别调整到serializable。set tran isloation level  serializable

 begin tran  
	
	set tran isloation level  serializable		 --解决事务幻读的问题,其他事务会等待该事务执行完后才执行。
													
	
	select SUM(balance) as Balance
	from BankAccount
	where UserName='N张三'
	
	waitfor delay '00:00:05'		         --此处的延时,导致其他事务进行了操作,导致本事务两次读出的数据库不一致,称为幻读。
	
	select SUM(balance) as Balance
	from BankAccount
	where UserName='balance'
	
 commit

  重复上面的动作会发现事务二会一直等到事务一执行完毕再返回结果。


三、总结:

  在并发性的系统中,要控制好事务隔离性事务的隔离级别是通过数据库的锁控制的

 SQL默认的隔离级别是READ COMMITTED。

  set tran isloation level  serializable语句可以手动修改事务隔离级别。



参考链接:http://hi.baidu.com/liupras/item/ecf9003525f1644b3175a1e5

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值