首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 数据库 > SQL Server >

关于事务锁机制的疑问.解决方法

2012-03-22 
关于事务锁机制的疑问.测试过程如下,环境: WIN7 + SQL2008R2SQL code-- 建测试表ap,yx字段没索引.create t

关于事务锁机制的疑问.
测试过程如下,环境: WIN7 + SQL2008R2

SQL code
-- 建测试表ap,yx字段没索引.create table ap( Id int not null,  yx char(8000) not null  constraint pk_ap primary key clustered (Id))-- 插入5笔记录: 40,41,42,43,44.insert into ap select 40,'ap0405140_0'insert into ap select 41,'ap0405140_1'insert into ap select 42,'ap0405140_2'insert into ap select 43,'ap0405140_3'insert into ap select 44,'ap0405140_4'select * from apId          yx----------- ---------------40          ap0405140_0 41          ap0405140_1 42          ap0405140_2 43          ap0405140_3 44          ap0405140_4-- 用dbcc ind()及dbcc page()获取记录与页的对应关系./*  [Id]      PagePID------------------------   40         148   41         174   42         175   43         192   44         193    */-- 1.测试read uncommitted时的锁set transaction isolation level read uncommittedbegin tran  update ap set yx='ap0405140_16' where yx like '%3'--> 执行计划: Clustered Index Scan, 锁范围: PagePID 192 IX锁rollback tran-- 2.测试read committed时的锁set transaction isolation level read committedbegin tran  update ap set yx='ap0405140_16' where yx like '%3'--> 执行计划: Clustered Index Scan, 锁范围: PagePID 192 IX锁rollback tran-- 3.测试repeatable read时的锁set transaction isolation level repeatable readbegin tran  update ap set yx='ap0405140_16' where yx like '%3'--> 执行计划: Clustered Index Scan, 锁范围: PagePID 148 IU锁,175 IU锁,174 IU锁,193 IU锁,192 IX锁.rollback tran-- 4.测试serializable时的锁set transaction isolation level serializablebegin tran  update ap set yx='ap0405140_16' where yx like '%3'--> 执行计划: Clustered Index Scan, 锁范围: PagePID 148 IU锁,175 IU锁,174 IU锁,193 IU锁,192 IX锁.rollback tran

问题: 此处故意用Clustered Index Scan的方式,为何repeatable read和serializable时,
需要对非192的页面(192是最后需更新的页面)加IU锁?这样的机制未免也太严格了点,与之前的理解有些出入.

[解决办法]
探讨
测试过程如下,环境: WIN7 + SQL2008R2
SQL code

-- 建测试表ap,yx字段没索引.
create table ap
( Id int not null,
yx char(8000) not null
constraint pk_ap primary key clustered (Id)
)

-- 插入5笔记录: 40,41,42,43,44.
inser……

[解决办法]
由于走的是scan
1和2并不是没有锁,是加完锁,但释放了
3和4则保持锁到commit或roll
如果多加点数据行,3,4可能就不是页了,是表级的x或u


[解决办法]
1,2也锁的,但读完资料就释放锁了

3会将碰到资料都锁起来直到交易结束,但无法防止新增资料

4如果不设索引键锁定范围的话,一旦被它碰到的资料,就完全锁了,插都插不进
[解决办法]
可重复读--正在进行中的事务数据被上锁,即便是select语句执行完成.别的事务也不能修改这些数据,只能读取这些数据.从而在一个事务中两次读取结果一样...并发低于默认隔离级别.
SQL code
--可重复读begin transelect * from student where sno=2006001 --读完了这一条共享锁不释放掉..我不释放掉共享..你两一个事务的排他就不能修改..排他共享不能共存--他只有等到 这个事务commit掉后才会释放掉共享锁...所以事务2 进行修改就必须等待..select * from student where sno=2006001   --这里和上一条的结果是完全一样的..因为上一条为该行上了共享不释放,其他修改不了.commit tran   --这个时候才释放掉了行的共享锁..begin tran2update student set sname='大力水手'  --修改不掉..必须等待 上一个事务释放掉共享锁才能修改.end tran2但是,这个可重复读得事务隔离级别是保证了..读锁一只保持..重复读可以了..解决不了幻读--可串行没有锁现象. 在数据集上防止一个范围锁, 以防止其他用户在事务完成之前更新数据集或插入数据集内.这个级别限制最大.该级别作用与在事务内所有select 语句中所有表上设置holdlock相同. 也就是上了一个范围锁.最高级别,并发最低.如果你查询的数据量比较大..跟表锁没有什么区别了!其工作原理: 事务中的锁被报纸在一个更高的级别上,利用索引产生 key range 锁,从而阻止对数据集插入.为了防止向数据集插入行,数据集需要锁定,如果没有合适的索引,那么便又可能产生更高级别的锁,如 表锁,而非范围锁. --避免幻读,隔离级别可串行化..上范围锁begin transelect a from t where country='ch'--读出a=5select a from t where country='ch'--读出a=5commit tran事务2 insert into t values(6,'ch')  --由于支队'ch'加了范围锁..所以insert语句将等到事务1 提交后才执行--如果事务1 查询的数据量比较大..就相当于..锁了整个表
[解决办法]
有时候,系统会隐式地加一些锁,我们也可以利用一下的。
------解决方案--------------------


SQLSERVER加锁本来就是把所有可能更新到的页都加,有时候是很搞。
[解决办法]
可重复读和可串行化 这两个级别对于真实环境应该是很少用的..
隔离级别太高,可串行化没有合适的索引升级到表锁,并发就很差了!
2005以上就分为两种并发控制了..
悲观并发控制
乐观并发控制

--行版本控制技术 产生 快照隔离级别.
行版本控制: 可以理解为."已经被更改的这些行的原始版本放到tempdb进行维护"这应该是也2005比2000
更重视tempDB原因吧?

支持行版本控制的事务隔离界别:
1.新实现的已提交读隔离级别,使用行版本控制提供语句级的读取一致性.
2.新快照隔离界别,提供事务级别的读取一致性

当行版本控制隔离级别下运行事务读取数据时,读取操作不会获取正被读取的数据行上的
共享锁, 因此不会阻塞正在死u该数据的事务,另外,锁定资源的开销随着所获取的锁的
数量的减少而降至最低. 使用行版本控制的已提交读隔离级别和快照隔离都是在
提供副本数据的语句级别或事务读取一致性.

行版本控制新实现的已提交读隔离级别
begin tran
select a from t where country='ch' --这里读取数据并没有给读到的行上(共享锁)s,而是上了sch-s表级别模式锁,不需要叶锁或行锁.
select a from t where country='ch' --这里和上面读到的数据有可能不同, 他读到的别的事务已提交的.
commit tran


begin tran2
update t set a=0 where country='ch'
 --事务1 并不影响修改.因为他没有上共享锁,这里能顺利修改并提交.
commit tran2
--这样读和写就能并行操作!

快照隔离,采用乐观方法:直到数据被修改时才获取数据上的锁,当数据行符合更新标准时,快照事务将验证数据行未被并发事务(在快照开始后提交)修改.如果数据行已在快照事务以为修改,则出现更新冲突,同事快照事务也将终止.更新冲突是由数据库引擎处理, 无法禁用更新冲突检查.
--快照隔离级别
begin tran
q1=select count(1) from dbo.tname where country='usa' 
--这个时刻有别的事务修改了数据并做了提交..q1 还是等于q2
q2=select count(1) from dbo.tname where country='usa' --q1=q2
commit tran

--technet 上的例子
use adventureworks;
go
alter database adventureworks set allow_snapshot_isolation on; --打开快照隔离级别
go
begin tran
select employeeid,vacationhours from humanresources.employee where employeeid=4 --4,48 这条事务没有结束,不提交..
select employeeid,vacationhours from humanresources.employee where employeeid=4 --第二个事务修改后并没有体现出来 这里还是 4,48, 可重复读.

update humanresources.employee set vacationhours =vacationhours -8 where employeeid=4; --这里会报出更新冲突而终止,这里直接回滚掉了.所以快照隔离也将在这里终止..
commit tran --这里没有事务了..已经回滚了..这一句也会报错..

新开一个查询--修改 begin tran update humanresources.employee set vacationhours =vacationhours -8 where employeeid=4; commit tran --修改并提交
select employeeid,vacationhours from humanresources.employee where employeeid=4 --4,40 --修改完毕

--快照隔离级别,在同一个事务里面读到的数据时一致的..但是有肯能会产生更新冲突!

[解决办法]
什么呀?看不懂。。。。
[解决办法]
那个是这样的 默认的隔离级别 是 读提交
没问题吧
读提交就是读完释放锁
那么 u锁 是怎么一个状况呢
u锁 可以理解为 s+x 锁
但是 也不完全是 因为 u锁和u锁之间是不兼容的

可以怎么理解
所以 你在 读提交的状况下,读完就是释放了
第二个解释清楚了吧

第一个 比第二个 更简单 因为它不加读锁啊
现在说第3个
第三个 隔离级别是可重复读
可重复读的话,那么就会 在事务完成前 是不会释放锁的。

所以 前面2个虽然是被扫描过 ,但是还是又 u锁残留在那边
没问题吧
第3 和 第4 在你这边是一样的
为啥呢
第三个 和第四个 从 联机文档的 隔离级别 看 差别 就是 幻读
你这个例子 是不会产生幻读的,所以也没问题了
[解决办法]
是个重要问题
[解决办法]
学习学习,不错的资料
[解决办法]
这块儿很让人头疼

热点排行