SQL事务与锁定学习笔记(一)

[共享锁定]
发生在查询记录时,直观就是我们select啦,但是并不是只有select才有共享锁定。一个查询记录的语句必须在没有与共享锁定互斥锁定存在或等待互斥锁定结束后,才能设置共享锁定并提取数据(互斥不互斥就是锁的兼容性,这在以后再说明)。
[排它锁定]
发生在对数据增加、删除、修改时,事务开始以后语句申请并设置排它锁定(前提是没有其它互斥锁定存在),以明确告知其它进程对当前数据不可以查询或修改,等待事务结束后其它进程才可以查询或修改。
[更新锁定]
是一个介于共享与排它之间的中继锁定,比如我们带where条件的update语句,在查询要更新的记录时是设置共享锁定,当要更新数据时这时锁定必须由共享锁定升级成更新锁定继而升级为排它锁定,当排它锁定设置成功才可以进行数据修改操作。显然也是要要求在锁升级的过程中没有互斥锁定的存在。简单的理解更新锁定是一个中继闸一样,把升级成排它锁定进程“序列化”,以解决死锁。最后重点说明一下,数据更新阶段是要对数据排它锁定不是更新锁定,不要被字面意思训导哦。
并发副作用:丢失更新、脏读、不可重复读、幻影

 

丢失更新
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_SNAPSHOTON的情况

先修改当前当前库的READ_COMMITTED_SNAPSHOTON

ALTER DATABASE TESTCSDN

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}[;]

 

附录:隔离等级与意外数据行为

         意外行为

隔离等级

丢失更新

脏读

不可

重复读

幻影

并发模型

未提交读

悲观

已提交读(  )

悲观

已提交读(行版本)

乐观

可重复读

悲观

快照

乐观

可串行化

悲观


 

posted @ 2011-06-08 15:49  小白.net  阅读(640)  评论(0编辑  收藏  举报