日期:2014-05-16  浏览次数:20452 次

数据库事务转载基础一:oarcle事务
Oracle事务
2010-11-29 14:22

前言
首先思考一个问题:如果对数据库进行了多次修改,如果前面的修改成功,后面修改时发生了意外,怎么办?
案例:以银行转账为例。
有帐户表记录如下:
帐号 姓名 余额
1 张三 2000
2 李四 1000
假设张三给李四转账300块,则对数据库的修改必然有两步:
第一步:减少张三的余额:2000→1700
第二步:增加李四的余额:1000→1300
但是如果第一步完成以后,马上就死机(或者断电、地震……)了。
等数据库重启以后,目前的帐户情况变为:
帐号 姓名 余额
1 张三 1700
2 李四 1000
这表示,张三的余额减少了,但是李四并没有收到转账的钱。
说明:
?在实际的银行项目中,转账不会是只有一个帐户表,直接进行余额修改。在此简化的目的是为了便于说明事务的相关概念。

思考:数据库如何操作才能避免这种情况发生呢?
要解决上面的问题,必须学习一个新的知识点:事务,这是数据库中非常重要的概念,必须掌握!
问题:为什么要事务?
回答:
在对数据进行多次增删改以后,如果要保证所有的操作同时成功,或者同时失败。必须使用事务。
问题:什么是事务?
回答:
事务就是业务上的一个逻辑工作单元,它能够保证其中对数据所有的操作,要么全部成功,要么失败。
例如在转账时,一个帐户要增加余额,一个帐户要减少余额,这两个操作在业务上必须当一个整体,也叫做“一个逻辑工作单元”。
“符合逻辑”就是指满足符合业务要求。

问题:事务的原理是什么?
回答:
主要依赖于日志。如果事务没有完成,则日志中没有结束标记,数据库就会执行前面各步的反向操作,例如:
1、事务开始→张三减300(2000→1700)→死机。
2、数据库重启→读取日志,检查事务没有结束标记,执行“反向”操作→张三加300(1700→2000)→张三数据恢复了。
说明:数据库事务恢复的过程其实是很很复杂的,在此只是讲解最基本的原理。
问题:事务有哪些特征?
回答:
一共有4个:
?原子性:以转账为例。帐户减少和帐户增加是两个DML语句,但是被当一个“原子”,所以外部程序是不知道这个原子中有几个SQL的。更不能只执行其中一个SQL,只能同时成功,或者同时失败。即:无法拆出事务中某一条SQL语句来单独执行。
?一致性:要么都是改变前的改变,要么都是改变后的状态。
例如:转账前分别是1000和2000,总和3000;转账300后分别是1300和1700,总和也是3000。这就叫做一致性。不一致就是,转账后分别1300和2000(不变),总是3300,一个是改变后的状态,另一个还是改变前的状态。
?隔离性:在某个时间段,肯定有很多人都在转账,每个人的转账都是在一个自己的事务中,所以在一个数据库中,有很多事务会同时存在。虽然同时存在很多事务,但是事务之间不会相互影响。
?持久性:如果事务提交成功,则数据修改永远生效;如果是回滚,则数据完全没有被修改,就相当于没有这件事情发生。事务结束以后,必须重新启动一个新的事务才能修改数据。
说明:可以用口诀(一原永隔)来帮助记忆。
问题:如何使用事务?
回答:一共有三步。
1、先开启事务。
?在Oracle中,事务是在上一次事务结束以后,数据“第一次”被修改时自动开启。
?在java中,设置连接为手动提交模式开始。代码:
connection.setAutoCommit(false)。
2、进行(多次)数据操作(增删改)……
3、结束事务: commit、rollback。
问题:如何结束事务?
回答:
有两种方法:
1、确认对数据的修改:提交,commit。
2、撤消对数据的修改:回滚,rollback。
另外,当Oracle系统或者PL/SQL脚本抛出异常,都会造成数据回滚。
问题:能不能做局部提交或者局部回滚?
如执行了三个insert语句,能不能只提交或者回滚前面2个。
回答:
如果说要提交,只能提交所有的sql语句。
如果说要回滚,可以利用“事务保存点”来做局部回滚。注意,此时事务并没有结束。
示例:
执行:
1、 insert into dept values(50,'a',null);
2、 insert into dept values(60,'b',null);
3、 savepoint a;
4、 insert into dept values(70,'c',null);
5、 rollback to savepoint a;

问题:现在插入了几个新部门?
分析:回滚到保存点“a”,表示保存点以后的所有数据操作取消。所以只插入了两个部门。
问题:现在的事务结束了吗?
回答:没有,必须再执行commit或者rollback来结束事务。
说明:在工作中这种“局部回滚”用得比较少。
示例:异常与事务。
问题:假设现在有50和60两个新部门。执行下面代码以后,这两个部门删除了没有?
begin
delete from dept where deptno=50;
delete from dept where deptno=60;
raise_application_error(-20000,'抛个异常玩');
end;
分析:
没有。因为抛出异常以后,数据库会自动回滚。
问题:现在再执行commit,是否删除了?
回答:
还是没有。因为事务在回滚以后,事务结束,这表示刚才执行的sql语句完全不存在了,所以如果还要删除,则必须重新执行sql语句。
即:在事务结束以后,再执行commit和rollback是没有用的。

问题:什么是提交模式,与事务有什么关系?
回答:
提交模式有两种:
自动提交:在每一次数据被修改以后,自动提交。这种模式下是无法支持事务的。
手动提交:所有的数据修改都在内存中,当执行commit时才一起提交。只要有一条sql执行失败,则所有的sql自动回滚。只能在手动提交模式下才能使用事务。
问题:事务有哪几种隔离级别?
回答:
事务隔离级别在理论上有4种,从低到高分别是:
?未提交读:一个事务可以读到其它事务未提交的数据。
?提交读:一个事务只能读到其它事务已提交的数据。
?可重复读:一个事务中,不管数据有没有被其它事务所修改,读到的数据都是不变的。
?串行读:一个事务要操作数据,必须要等到其它事务结束才能访问,包括查询。
Oracle只支持其中两种: 提交读和串行读,默认就是提交读。
问题:数据库为什么要在理论上分这4种隔离级别,通常用哪一种呢?
回答:
事务隔离级别越高,则并发性越低,即能够同时使用数据库的人就越少。
虽然未提交读性能最高,但是会造成数据不一致(这叫:脏读)。而串行读能最大限度的保护数据,但性能又太低。
所以通常要在性能和安全之间做一个选择,在Oracle中,就使用默认的隔离级别:提交读。
原文地址:
http://hi.baidu.com/xydba/blog/item/b64894fe5ec34b076d22eb11.html