丢失更新 A,B两个用户读同一数据并进行修改,其中一个用户的修改结果破坏了另一个修改的结果,比如订票系统 脏读 A用户修改了数据,随后B用户又读出该数据,但A用户因为某些原因取消了对数据的修改,数据恢复原值,此时B得到的数据就与数据库内的数据产生了不一致 不可重复读 A用户读取数据,随后B用户读出该数据并修改,此时A用户再读取数据时发现前后两次的值不一致
隔离等级 未提交读(uncommitted Read) 一个用户进程可以读取另一个用户进程修改却未提交的数据 未提交读那肯定是不适合于股票、金融系统的,但是在一些趋势分析的系统里,要求的只是一种走向,准确性可以不是那么严格时,这个级别因并发性能超强成为首选。
查询一:
SELECT * FROM TA WHERE TCID = 1
BEGIN TRAN
UPDATE TA
SET TCNAME = 'TA'
WHERE TCID = 1
--COMMIT TRAN --Don't commit
SELECT * FROM TA WHERE TCID = 1
SELECT @@SPID
/*
tcid Tcname
----------- --------------------
1 AA
(1 行受影响)
(1 行受影响)
tcid Tcname
----------- --------------------
1 TA
(1 行受影响)
SPID
------
54
(1 行受影响)
*/
查询二:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
SELECT * FROM TA WHERE TCID = 1
/*
tcid Tcname
----------- --------------------
1 TA
(1 行受影响)
*/
--显然未提交读模式我们读到SPID=54未提交的数据。
查询三:
SELECT * FROM TA WHERE TCID = 1
--查询一直进行中…… 无结果
--因为缺省下已提交读级别,所以修改数据设置了排它锁定必须等到SPID=54的事务结束
查询四:
--查看当前的锁定信息
exec sp_us_lockinfo
/*
*/
这个时候如果我们回头到查询一里执行commit tran ,你会发现查询三会得到结果,并且是查询一修改后的结果,如果你改用rollback ,那么结果就是原来的值不变,这个你们自己再测试。
已提交读(Read Committed) 它和未提交读相反,已提交读级别保证一个进程不可能读到另一个进程修改但未提交的数据。这个级别是引擎默认的级别 已提交读可是乐观的也可以是悲观的 行版本控制是乐观并发模式,锁定是悲观并发模式设置已提交读隔离使用行版本控制
ALTER DATABASE testcsdn SET READ_COMMITTED_SNAPSHOT ON 查看当前已提交读隔离并发模型 select name,database_id,is_read_committed_snapshot_on from sys.databases悲观并发下的已提交读,当进程要修改数据时会在数据行上申请排它锁,其它进程(无论是读还是写)必须等到排它锁释放才可以使用这些数据。如果进程仅是读取数据时会使用共享锁,其它进程虽然可以读取数据但是无法更新数据
乐观并发的已提交读,也确保不会读到未提交的数据,不是通过锁定的方式来实现,而是通过行版本控制器生成行的提前交的数据版本,被修改的数据虽然仍然锁定,但是其它进程可以可以读取更新前版本数据。
查询一:
BEGIN TRAN
--用锁定提示模拟共享锁定,并强制共享锁定持续到事务结束
SELECT * FROM TA with(holdlock) WHERE TCID = 1
--COMMIT TRAN --Don't commit
SELECT @@SPID
/*
tcid Tcname
----------- --------------------
1 CA
(1 行受影响)
------
54
(1 行受影响)
*/
查询二:悲观模型下已提交读级别
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
UPDATE TA
SET TCNAME = 'TA'
WHERE TCID = 1
--查询一直没有结果,显然我们验证了共享锁定阻止了排它锁定。
查询三:
exec sp_us_lockinfo
--结果大家自己运行看结果。
II、修改数据测试
查询一:
SELECT * FROM TA WHERE TCID = 1
BEGIN TRAN
UPDATE TA
SET TCNAME = 'READ COMMITTED LOCK'
WHERE TCID = 1
--COMMIT TRAN --Don't commit
SELECT @@SPID
/*
tcid Tcname
----------- --------------------
1 TA
(1 行受影响)
------
54
(1 行受影响)
*/
查询二:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
SELECT * FROM TA WHERE TCID = 1
/*
--查询一直进行中……被锁定无结果
--修改数据设置了排它锁定必须等到SPID=54的事务结束
*/
查询三:
exec sp_us_lockinfo
/*
*/
A、 READ_COMMITTED_SNAPSHOT为ON的情况
先修改当前当前库的READ_COMMITTED_SNAPSHOT为ON
ALTER DATABASE TEST
SET READ_COMMITTED_SNAPSHOT ON
GO
查询一:
SELECT * FROM TA WHERE TCID = 1
BEGIN TRAN
UPDATE TA
SET TCNAME = 'READ COMMITTED SNAP'
WHERE TCID = 1
--COMMIT TRAN --Don't commit
SELECT @@SPID
/*
TCID TCNAME
----------- --------------------
1 AA
(1 行受影响)
(1 行受影响)
------
56
(1 行受影响)
*/
查询二:因为启用行版本控制器来锁定数据,保证其它进程读取到虽然被排它锁定但在事务开始前已经提交的保证一致性的数据。
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
SELECT * FROM TA WHERE TCID = 1
/*
TCID TCNAME
----------- --------------------
1 AA
(1 行受影响)
*/
查询三:
exec sp_us_lockinfo
可重复读(Repeatable Read): 可重复读等级比已提交读多了一个约定:所有的共享锁定持续到事务结束,不是在读取完数据就释放。数据被设置了共享锁定后其它进程只能进行查询与增加不能更改,显然这个级别的隔离对程序有了更高的要求,因为可能因长时间的共享锁定影响系统的并发性能,增加死锁发生的机率。很显然是解决了不可重复读的意外行为。 快照(SnapShot): 这是SQL SERVER2005的新功能,启用快照后所有的读操作不再受其它锁定影响,读取的数据是通过行版本管制器读取事务开始前逻辑确定并符合一致性的数据行版本。 这个级别隔离与已提交读的行版管理器的差别仅是行版本管理器里历史版本数据多久。 可串行化: 这是交易里最健壮最严谨最高级别的隔离。通过索引键范围完全隔离其它交易的干扰,此隔离和select与锁定提示HOLDLOCK效果一样。这个级别基本解决所有的意外行为,显而易见的是并发性能下降或系统资源的损耗上升。 隔离等级设置命令: SET TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SNAPSHOT | SERIALIZABLE}[;]
附录:隔离等级与意外数据行为
意外行为
隔离等级
丢失更新
脏读
不可
重复读
幻影
并发模型
未提交读
否
是
是
是
悲观
已提交读(锁 定)
否
否
是
是
悲观
已提交读(行版本)
否
否
是
是
乐观
可重复读
否
否
否
是
悲观
快照
否
否
否
否
乐观
可串行化
否
否
否
否
悲观
转载于:https://www.cnblogs.com/renjuwht/archive/2011/06/08/2075253.html
