SqlServer 并发事务(一):事务隔离级别

时间:2021-11-09 17:59:43

概念理解:

可参考博客:God Is Coder事务属性小结


--查了当前数据库是事务隔离级别
DBCC USEROPTIONS

表初始内容:

SELECT * FROM Test  Wherename='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 set info = 'B更改' from Test T(nolock) where name = 'kk'
commit tran

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

id  name   info

1   kk      NULL

 

事务结束最后结果:

SELECT * FROM Test  Wherename='kk'

id  name   info

1   kk      B更改

 

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

 

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

当前使用:SETTRANSACTIONISOLATIONLEVELREADUNCOMMITTED

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

事务2的运行结果:

id  name   info

1   kk      A更改

 

事务结束最后结果:

SELECT * FROM Test  Wherename='kk'

id  name   info

1   kk      NULL

 

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

 

解决一:提高隔离级别

SET TRANSACTIONISOLATIONLEVELREADCOMMITTED

ALTER DATABASEDBNameSETREAD_COMMITTED_SNAPSHOTOFF(默认OFF

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

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

 

解决二:

SET TRANSACTIONISOLATIONLEVELREADCOMMITTED

ALTER DATABASEDBNameSETREAD_COMMITTED_SNAPSHOTON(默认OFF

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

此时相当于:

SET TRANSACTIONISOLATIONLEVELREADCOMMITTED

select * from dbo.TestWITH(READPAST)wherename='kk'

 

解决三:

事务2用表提示READCOMMITTEDLOCK把语句级别改为READCOMMITTED

--事务2

SET TRANSACTIONISOLATIONLEVELREADUNCOMMITTED

begin tran

    select *from dbo.TestWITH(READCOMMITTEDLOCK)wherename='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 set info = 'B更改' from Test T where name = 'kk'
commit tran


事务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 TRANSACTIONISOLATIONLEVELREPEATABLEREAD

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

 

解决二:更改为快照级别

ALTER DATABASEDBNameSETALLOW_SNAPSHOT_ISOLATIONON

SET TRANSACTIONISOLATIONLEVELSNAPSHOT

 

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

id  name   info

1   kk      NULL

 

 

 

【测试四:幻读】

--事务1 
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
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 SERIALIZABLE
begin tran
INSERT INTO Test VALUES(2,'kk',null)
commit tran


事务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 TRANSACTIONISOLATIONLEVELSERIALIZABLE

 

此时【事务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




参考:SET TRANSACTION ISOLATION LEVEL (Transact-SQL)

数据库引擎中的隔离级别