数据库事务与隔离示例_第1页
数据库事务与隔离示例_第2页
数据库事务与隔离示例_第3页
数据库事务与隔离示例_第4页
数据库事务与隔离示例_第5页
已阅读5页,还剩5页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、大家都知道,数据库事务的四大特性ACID(Atomic, Consistency, Isolation, Durability),这里主要考虑一致性和隔离性。为了提高事务的处理效率,通常并发的执行多个事务,这就是数据库中非常重要的并发控制。简单 说,并发的执行事务,会有以下问题:· 写丢失(Write Lost):比如事务A将x的值更新为10,然后事务A将y的值更新为20,这时A重新读取x发现自己更新过的数据似乎不见了。 · 脏读(Dirty Read):比如事务A的未提交(还依然缓存)的数据被事务B读走,如果事务A失败回滚,会导致事务B所读取的的数据是错误的;·

2、 不可重复读(Non-repeatable Read):比如事务A中两处读取数据total的值。在第一读的时候,total是100,然后事务B就把total的数据改成200,事务A再读一次,结果就发现,total竟然就变成200了,造成事务A数据混乱。· 幻象(Phantom Read):和Non-Repeatable Read相似,也是同一个事务中多次读不一致的问题。但是Non-Repeatable Read的不一致是因为他所要取的数据集被改变了(比如total的数据),但是Phantom Read所要读的数据的不一致却不是他所要读的数据集改变,而是他的条件数据集改变。比如Sele

3、ct account.id where ="ppgogo*",第一次读去了6个符合条件的id,第二次读取的时候,由于事务b把一个帐号的名字由"dd"改 成"ppgogo1",结果取出来了7个数据。这四种问题呢,有些是可以忍的,有些解决后会大大滴降低并发性。所以,为了适应不同的需求,SQL'92,规定了4种隔离级别,隔离级别越高,数据的一致性越好,数据库的并发性越低。· Serializable(全序列化,级别3)提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执

4、行。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。· Repeatable Read( 可重复读取,级别2)禁止不可重复读取和脏读取,但是有时可能出现幻影数据。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。· Read Committed(授权读取,级别1):允许不可重复读取,但不允许脏读取。这可以通过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。· Re

5、ad Uncommitted(未授权读取,级别0):允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个数据则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。l 本示例文档演示SQL SERVER,ORACLE下不同事务隔离级别的区别,以及两种数据库本身的特点l 为了模拟并发环境,SQL SERVER在SMO程序中打开两个查询窗口即可。oracle可以用两个sql *plus程序连接到相同数据库来模拟l SQL SERVER、ORACLE中两个并发用户用事务1,事务2简称。l 所有测试例子,都以最初测试表脚本运行后状态为基准。l 在下列例子中

6、,set transaction isolation level语句会改变会话的隔离级别,直到会话结束。故测试完毕需要改回默认级别。l 最后,但并不是最不重要。以下的演示和相关解释,都是基于易于理解的原则来的,实际的情况可能更复杂,但对开发人员来说,理解如此程度的简化模型已经足够了。测试表脚本:SQL SERVERCREATE TABLE Customer(CustID int NOT NULL,Fname nvarchar(20),Lname nvarchar(20),Address nvarchar(50),City nvarchar(20),State nchar(2) DEFAULT

7、('CA'),Zip nchar(5) NOT NULL,Phone nchar(10)insert into customer values(1, 'Gary', 'Mckee', '111 Main', 'Palm Springs', 'CA', 94312, 7605551212)insert into customer values(2, 'Tom', 'Smith', '609 Geogia', 'Fresno' 'J

8、P', 33045, 5105551212)insert into customer values(3, 'Jams', 'bond', 'ST Geogie 21', 'Washington', 'NY', 20331, 4405551864)ORACLECREATE TABLE Customer(CustID int NOT NULL,Fname nvarchar2(20),Lname nvarchar2(20),Address nvarchar2(50),City nvarchar2(20),Stat

9、e nchar(2) DEFAULT 'CA',Zip nchar(5) NOT NULL,Phone nchar(10);insert into customer values(1, 'Gary', 'Mckee', '111 Main', 'Palm Springs', 'CA', 94312, 7605551212);insert into customer values(2, 'Tom', 'Smith', '609 Geogia', '

10、;Fresno', 'JP', 33045, 5105551212);insert into customer values(3, 'Jams', 'bond', 'ST Geogie 21', 'Washington', 'NY', 20331, 4405551864);1。Sqlserver与oracle单条语句处理对比SQL SERVER单条语句默认自动提交,即单条语句自动作为一个事务处理;而oracle的原则是尽量延后提交,除非遇到显式提交命令或者DDL语句。SQL SERVER打开

11、事务1:运行:select * from customer可以看到表有3条记录运行:insert into customer values(4, 'Hello', 'world', 'paradise road 01', 'heaven', 'XY', 00001, 1234564321)转到事务2:运行:select * from customer可以看到事务1中刚插入的custid为4的记录。ORACLE打开事务1,运行:select * from customer;可以看到表有3条记录,运行:insert i

12、nto customer values(4, 'Hello', 'world', 'paradise road 01', 'heaven', 'XY', 00001, 1234564321);转到事务2,运行:select * from customer;能看到的还是3条记录,事务1中刚插入的一条记录未自动提交,看不到。转到事务1,运行:commit;转到事务2,运行:select * from customer;现在能看到4条记录了。2. 丢失更新Sqlserver完全兼容ANSI 92标准定义的4个隔离级别。它

13、的默认隔离级别是提交读(read committed),在该级别下,可能会有丢失更新的问题。Oracle的默认情形也一样。故不再重复。SQL SERVER打开事务1运行:set transaction isolation level read committedbegin transelect * from customer-看到3条记录现在切换到事务2,此时事务1还未结束。在事务2中运行:set transaction isolation level read committedbegin transelect * from customer-看到3条记录,和事务1中相同现在假设事务1事务继

14、续运行,修改数据并提交:update customer set state = 'TK' where CustID = 3commit回到事务2,事务2根据先前查询到的结果修改数据:update customer set Zip = 99999 where state = 'NY'commit结果因为事务1已经修改了事务2的where条件数据,事务2未成功修改数据(其实准确的说应该算是幻象读引起的更新失败。不过若满足条件的记录数多的话,事务2的update可能更新比预期的数量少的记录数,也可算“丢失”了部分本应完成的更新。个人认为只要明白实际上发生了什么即可,不

15、必过分追究字眼)。丢失更新还可能有别的情形,比如事务2也是update customer set state = 'KO' where CustID = 3两个事务都结束后,事务2的结果反映到数据库中,但事务1的更新丢失了,事务2也不知道自己覆盖了事务1的更新。3脏读演示sqlserver的默认隔离级别是提交读(read committed),当手工将其改为未提交读时,事务可以读取其它事务没提交的数据;oracle由于自身特殊实现机制,可以理解为自身基础的隔离级别就是可重复读(与ANSI标准还是有区别的,后面例子会说明)。SQL SERVER打开事务1,运行:begin tra

16、nselect * from customerupdate customer set state = 'TN' where CustID = 3转到事务2,运行:set transaction isolation level read uncommittedbegin transelect * from customer此时看到的数据是事务1已经更新但还未提交的(3号记录state值TN)。而如果事务1发觉数据处理有误,转到事务1,进行回滚:Rollback此时事务2如根据刚读取的数据进一步处理,会造成错误。它读取的数据并未更新到数据库,是“脏”的。ORACLEANSI定义未提

17、交读(read uncommitted)级别本意不是为了故意引入错误,而是提供一种可能的最大并发程度级别,即一个事务的数据更新不影响其它事务的读取。Oracle从内核层面实现了更新数据不阻塞读。可以说它提供未提交读级别的兼容,但没有脏读问题。(详情参考对应PPT文档)故oracle没有手工设置read uncommitted级别的语句。4不可重复读Sql server的默认级别没有脏读问题,但存在不可重复读问题。Oracle默认级别也是提交读,不过它因为自身特殊机制,在语句一级不存在不可重复读问题。也就是说当运行时间较长的查询时,查询结果是与查询开始时刻一致的(即使查询过程中其它事务修改了要查

18、询的数据),而SQL SERVER就存在问题(sql server 2005新特性提供了可选的语句一级一致性支持,叫做行版本机制,实际上可以说是照着oracle的多版本来的,大体原理差不多)。由于语句一级的事务一致性难以演示,下面例子是事务一级,提交读隔离级别下发生的不可重复读现象:SQL SERVER打开事务1,运行:set transaction isolation level read committedbegin transelect * from customer where State = 'CA'可以得到1条记录,这个时候事务2中运行:set transactio

19、n isolation level read committedbegin tranupdate Customer set state = 'JP' where state = 'CA'commit事务2插入一条记录并提交。回到事务1,事务1继续运行,此时它再次相同的查询,并借此作进一步修改,却发现读取到的数据发生了变化。select * from customer where State = 'CA'-2次读取不一致,之后的数据处理应该取消。否则不正确update Customer set city = 'garden' whe

20、re state = 'CA'commit读取未能获得记录。也就是说在同一事务中两次相同的查询获得了不同的结果,产生读取不可重复现象。ORACLE尽管oracle在默认隔离级别下提供了语句级的事务读一致性,但在事务级仍然是会出现不可重复读现象。和sql server一样,故不再重复。5幻像读当sqlserver的隔离级别设置为可重复读(repeatable read),可以解决上面例子出现的问题。其内部是通过事务期间保持读锁来实现的。SQL SERVER开始事务1,修改事务级别为可重复读,执行:set transaction isolation level repeatable

21、 readbegin transelect * from customer where State = 'CA'和上例一样得到1条记录,这个时候事务2中运行:set transaction isolation level repeatable readbegin tranupdate Customer set state = 'JP' where state = 'CA'commit会发现事务2一直等待,并不结束。返回事务1,运行:select * from customer where State = 'CA'-2次读取结果一致

22、update Customer set city = 'garden' where state = 'CA'commit事务2成功结束后,再返回事务1,发现事务1也完成了。通过锁机制阻塞其它事务的修改,保持了事务期间读取的一致性。然而,如果是插入数据,则还是会出现问题:开始事务1,修改事务级别为可重复读,执行:set transaction isolation level repeatable readbegin transelect * from customer where State = 'CA'得到1条记录,这个时候事务2中运行:set

23、transaction isolation level repeatable readbegin traninsert into customer values(4, 'hellow', 'world', 'paradise 001', 'garden', 'CA', 00000, 1119995555)commit发现事务2立刻提交并正常结束了。返回事务1,运行:select * from customer where State = 'CA'会发现得到了2条记录。这种现象就叫做幻像读。ORACL

24、E由于自身特殊的机制,oracle没有提供一致读隔离级别的选项,想要获得一致读的效果,实际上需要将事务提升到串行化等级,即serializable。6.串行化级别不同数据库实现在这个级别,可以认为事务中的数据无论何时都是一致的,此级别使它显得好像没有其它用户在修改数据,数据库在事务开始时候被“冻结”(至少,对于本事务涉及的数据如此)。然而在不同数据库中,其实现机制也不同。SQL SERVER开始事务1,运行:set transaction isolation level serializablebegin transelect * from customer where State = 

25、9;CA'会得到1条记录,这时事务2开始运行:set transaction isolation level serializablebegin traninsert into customer values(4, 'hellow', 'world', 'paradise 001', 'garden', 'CA', 00000, 1119995555)commit会发现事务2挂起,它在等待事务1结束。回到事务1,继续:select * from customer where State = 'CA&

26、#39;update Customer set city = 'garden' where state = 'CA'commit在片刻的等待以后,事务1得到类似以以下格式消息:消息1205,级别13,状态56,第1 行事务(进程ID 51)与另一个进程被死锁在锁资源上,并且已被选作死锁牺牲品。请重新运行该事务。而事务2更新了数据并正常结束。这是因为两个事务都设置成了串行化级别,当遇到冲突时候,sql server根据一定的规则选择牺牲掉其中一个事务,来保证事务的串行性。上面的例子,如果将事务2的隔离级别改为提交读,那么事务2会等待事务1完成,之后自己正常完成(因

27、为事务2没有串行需求,不会有死锁)。ORACLE在oracle中,通过多版本,可以在一定程度上避免死锁。开始事务1,运行:set transaction isolation level serializable;select * from customer where State = 'CA'-set tran语句隐式开始事务得到1条记录,然后事务2开始运行:set transaction isolation level serializable;insert into customer values(4, 'hellow', 'world',

28、'paradise 001', 'garden', 'CA', 00000, 1119995555);commit;可以发现事务2立刻完成,没有阻塞。回到事务1继续:select * from customer where State = 'CA'update Customer set city = 'garden' where state = 'CA'commit;事务1中的第二次查询和事务开始时刻一致,就好像事务2已经完成的提交不存在。事务最终正常更新完毕,并保持了“事务开始”时刻的数据一致性。

29、然而,如果事务1,2修改同样的数据行,也会有错误,开始事务1,运行:set transaction isolation level serializable;select * from customer where State = 'CA'-set tran语句隐式开始事务得到1条记录,然后事务2开始运行:set transaction isolation level serializable;update customer set state = 'KO' where state = 'CA'commit;可以发现事务2立刻完成,没有阻塞。回到

30、事务1继续:select * from customer where State = 'CA'update Customer set city = 'garden' where state = 'CA'commit;出现错误信息:第 1 行出现错误:ORA-08177: 无法连续访问此事务处理总的来说,oracle利用多版本的方式实现串行化级别更少造成死锁,除非两个事务修改了相同的数据行,一般也不会造成冲突。7不同隔离级别的相互影响前面的例子基本都是两个相同隔离级别事务的情况。如果不同隔离级别的事务发生冲突,会有什么不同吗?实际上,对于一个事务来

31、说,其它事务的隔离级别对它来说是未知的,更进一步,甚至数据库中有没有其它事务,有多少事务也不知道。影响事务访问数据就两方面因素:该事务自身的隔离级别,该事务要访问的数据上面锁的状态。SQL SERVER开始事务1,运行:set transaction isolation level serializablebegin transelect * from customer where State = 'CA'事务1的查询获得1条记录,转到事务2,运行:set transaction isolation level read uncommittedbegin transelect

32、* from customer事务2查询获得3条记录,回到事务1,运行:update Customer set city = 'garden' where state = 'CA'切换到事务2,运行:select * from customerupdate customer set state = 'KO' where state = 'CA'commit;因为事务2隔离级别为未提交读,因此事务1中刚作的修改可以立刻从查询看到,即使事务1还未结束。进一步的update因为事务1对记录加了独占锁,因此事务2挂起。回到事务1将其提交:

33、Commit事务1正常结束,独占锁释放,从而让事务2得以继续修改数据,并最终完成。ORACLEOracle数据库的隔离级别设置语句只有read committed和serializable(read only暂不讨论),加上其特殊锁机制,不同隔离级别事务间的影响除了上例(例6)中两个都为serializable的情况,其它都可视为互不阻塞。8页锁与行锁(限sql server)Sql server的锁可以到行一级。然而它又存在自动的锁定扩大,锁定转换。因此存在一些意想不到的情况。下面是演示:开始事务1,运行:set transaction isolation level read commit

34、tedbegin transelect * from customer where State = 'CA'update Customer set city = 'garden' where state = 'CA'理论上来说,在提交读级别下,上面的update语句只是在state值为CA的数据行上加了独占锁,表中其它数据应该可以被其它事务更新,然而,如下事务2开始:set transaction isolation level read committedbegin transelect * from customerupdate customer set state = 'KO' where state = 'JP'commit发现事务2陷入阻塞状态。尽管它们更新的不是同一条记录。回到事务1,运行:Commit事务1结束后事务2才继续运行至结束。如果我们在表上加入索引,如下:CREATE NONCLUSTERED INDEX idx_state ON dbo.Customer (State)再重复上面的步骤,会发现阻塞不再存在。PS:这种现象应该和数据库默认加锁参数/机制有关,应该可以调整,但目前手中没有进一步资料。故仅罗列了现象。ORA

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论