SQL Server 的事务和锁(举例)——在其他博客上做些笔误修改



SQL Server 的事务和锁(举例)

原料:创建Test

CREATE TABLE [dbo].[Test](

    [id] [int] NULL,

    [name] [nchar](10) COLLATEChinese_PRC_CI_AS NULL,

    [info] [nchar](10) COLLATEChinese_PRC_CI_AS NULL

) ON [PRIMARY]

插入一条记录

INSERT INTO Test VALUES(2,'kk',null)

查询显示有此记录

SELECT * FROM Test  Where name='kk'

id  name   info

1   kk      NULL

 

【测试一:丢失更新】

派生到我的代码片--事务1  

begin tran 

select * from dbo.Test(nolock) where name = 'kk' 

waitfor delay '00:00:05' 

update T set info = 'A更改' from Test T(nolock) where name = 'kk'

commit tran 

--事务2  

begin tran 

    select *from dbo.Test(nolock) where name = 'kk' 

    waitfor delay '00:00:05' 

    update T setinfo = 'B更改' from Test T(nolock) where name = 'kk'  

commit tran  --事务1

begintran

  select * from dbo.Test(nolock) where name ='kk'

  waitfor delay '00:00:05'

  update T set info = 'A更改' from Test T(nolock)where name = 'kk'

committran

 

--事务2

begintran

  select * from dbo.Test(nolock) where name ='kk'

  waitfor delay '00:00:05'

  update T set info = 'B更改' from Test T(nolock)where name = 'kk'

committran

 

事务1和事务2的运行结果:

id  name   info

1   kk      NULL

 

事务结束最后结果:

SELECT * FROM Test  Wherename='kk'

id  name   info

1   kk      B更改

 

说明:【事务1】更改了数据,但是没有被读到。最终【事务2】的更改覆盖了【事务1】的更改值

 

解决:允许对事务操作加锁,即取消(nolock)nolock级别最低。

 

当前使用:SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

READ UNCOMMITTED:即未提交的数据只能读取,不能更改)

 

 

【测试二:脏读】

派生到我的代码片--事务1  

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

begin tran 

update T set info = 'A更改' from Test T where name = 'kk'

waitfor delay '00:00:05' 

rollback tran 

--事务2  

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

begin tran 

    select *from dbo.Test where name = 'kk' 

commit tran  --事务1

begintran

  select * from dbo.Test(nolock) where name ='kk'

  waitfor delay '00:00:05'

  update T set info = 'A更改' from Test T(nolock)where name = 'kk'

committran

 

--事务2

begintran

  select * from dbo.Test(nolock) where name ='kk'

  waitfor delay '00:00:05'

  update T set info = 'B更改' from Test T(nolock)where name = 'kk'

committran

 

 

--事务1

SETTRANSACTION ISOLATION LEVEL READ UNCOMMITTED

begintran

  update T set info = 'A更改' from Test T where name= 'kk'

  waitfor delay '00:00:05'

rollbacktran --回滚

 

--事务2

SETTRANSACTION ISOLATION LEVEL READ UNCOMMITTED

begintran

  select * from dbo.Test where name = 'kk'

committran

 

事务2的运行结果:

id  name   info

1   kk      A更改

 

事务结束最后结果:

SELECT * FROM Test  Wherename='kk'

id  name   info

1   kk      NULL

 

说明:【事务1】更改了数据,【事务2】读取了表中更改的值,【事务1】回滚。最终的表是没有被更改的。

 

解决一:提高隔离级别

SET TRANSACTION ISOLATIONLEVEL READCOMMITTED

ALTER DATABASEDBNameSETREAD_COMMITTED_SNAPSHOTOFF(默认OFF

此时【事务1】等待事务【事务2】完成后才读取表中的值。

READ COMMITTED:更改的数据只能是提交后才能读取)

 

解决二:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED

ALTER DATABASEDBNameSETREAD_COMMITTED_SNAPSHOTON(默认OFF

此时【事务2】读到的是表中之前的快照值(不必等待【事务1】完成)。

此时相当于:

SET TRANSACTION ISOLATIONLEVEL READCOMMITTED

select * from dbo.Test WITH(READPAST) where name='kk'

 

解决三:

事务2用表提示READ COMMITTED LOCK把语句级别改为READ COMMITTED

--事务2

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

begin tran

    select *from dbo.Test WITH(READCOMMITTEDLOCK)where name='kk'

commit tran

 

如果将 READ_COMMITTED_SNAPSHOT设置为OFF默认设置),设置时关闭所有对此数据库的连接

数据库引擎会使用共享锁防止其他事务在当前事务执行读取操作期间修改行

共享锁还会阻止语句在其他事务完成之前读取由这些事务修改的行

 

 

 

【测试三:不可重读】

 

--事务1  

SET TRANSACTION ISOLATION LEVEL READ COMMITTED 

    begin tran 

    select *from dbo.Test where name = 'kk' 

    waitfor delay '00:00:05' 

    select *from dbo.Test where name = 'kk' 

commit tran  

--事务2  

SET TRANSACTION ISOLATION LEVEL READ COMMITTED 

    begin tran 

    update T setinfo = 'B更改' from Test T where name = 'kk' 

commit tran 

--事务1

SETTRANSACTION ISOLATION LEVEL READ COMMITTED

begintran

  select * from dbo.Test where name = 'kk'

  waitfor delay '00:00:05'

  select * from dbo.Test where name = 'kk'

committran

 

--事务2

SETTRANSACTION ISOLATION LEVEL READ COMMITTED

begintran

  update T set info = 'B更改' from Test T where name= 'kk'

committran

 

事务2的运行结果:

id  name   info

1   kk      NULL

 

id  name   info

1   kk      B更改

 

事务结束最后结果:

SELECT * FROM Test  Wherename='kk'

id  name   info

1   kk      B更改

 

说明:【事务1】读取了数据,【事务2】更改数据,【事务1】再读取数据。【事务1】两次读取都不一样!

 

解决一:提高隔离级别

SET TRANSACTION ISOLATIONLEVEL REPEATABLE READ

REPEATABLE READ:允许重复读取数据,但读取的都是第一次读取的副本)

 

解决二:更改为快照级别

ALTER DATABASE DBName SETALLOW_SNAPSHOT_ISOLATIONON

SET TRANSACTION ISOLATION LEVEL SNAPSHOT

 

此时【事务1】的运行结果2次都一样,读到的都是未更改前的值:

id  name   info

1   kk      NULL

 

 

 

【测试四:幻读】

--事务  

SET TRANSACTION ISOLATION LEVEL REPEATABLEREAD

begin tran 

    select *from dbo.Test where name = 'kk' 

    waitfor delay '00:00:05'  

    select *from dbo.Test where name = 'kk' 

commit tran 

     

--事务2  

SET TRANSACTION ISOLATION LEVELREPEATABLE READ

begin tran 

    INSERT INTOTest VALUES(2,'kk',null)  

commit tran  --事务1

SETTRANSACTION ISOLATION LEVEL SERIALIZABLE

begintran

  select * from dbo.Test where name = 'kk'

  waitfor delay '00:00:05'

  select * from dbo.Test where name = 'kk'

committran

 

--事务2

SETTRANSACTION ISOLATION LEVEL SERIALIZABLE

begintran

  INSERT INTO Test VALUES(2,'kk',null)

committran

 

事务2的运行结果:

id  name   info

1   kk      NULL

 

id  name   info

1   kk      NULL

2   kk      NULL

 

事务结束最后结果:

SELECT * FROM Test  Wherename='kk'

id  name   info

1   kk      NULL

2   kk      NULL

 

说明:【事务1】读取了数据,【事务2】插入了数据,【事务1】再读取数据。【事务1】两次读取都不一样!幻觉?

 

解决:提高隔离级别

SET TRANSACTION ISOLATIONLEVEL SERIALIZABLE 

此时【事务2】等待【事务1】完成。

事务1的运行结果2次都一样:

id  name   info

1   kk      NULL

 

范围锁:范围锁处于与事务中执行的每个语句的搜索条件相匹配的键值范围之内这样可以阻止其他事务更新或插入任何行从而限定当前事务所执行的任何语句这意味着如果再次执行事务中的任何语句则这些语句便会读取同一组行在事务完成之前将一直保持范围锁这是限制最多的隔离级别因为它锁定了键的整个范围并在事务完成之前一直保持范围锁因为并发级别较低所以应只在必要时才使用该选项该选项的作用与在事务内所有SELECT语句中的所有表上设置HOLDLOCK相同

 

 

如果在批处理中设置 REPEATABLE READ并且该批处理调用一个将隔离级别设置为SERIALIZABLE的存储过程则当该存储过程将控制返回给该批处理时隔离级别就会恢复为REPEATABLEREAD

 

并发事务引起的问题

      问题          

                    描述                      

              结果             

                          解决                     

丢失更新

A—B—A—B

A更改丢失

READ UNCOMMITTED

脏读

A—B—A回滚

B读无效值

READ COMMITTED

不可重读

A—B—A

A读不一致

REPEATABLE READ

不可重读

A—B—A

A读不一致

SNAPSHOT

幻读

A—B增删—A

A读或多或少

SERIALIZABLE

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值