学习目标:
1、掌握事务的概念和意义
2、掌握Oracle对事务的支持和语法
3、在实际项目中能掌握事务的使用
学习过程:
一、什么是事务
事务(TRANSACTION)是作为单个逻辑工作单元执行的一系列操作,这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行。事务是一个不可分割的工作逻辑单元
1、事务的必要性
基本上讲解事务都会举这个例子讲解。银行转帐问题:
我们假定资金从帐户A转到帐户B,转账是一个单一的业务,但是却需要两部数据库操作才能完成:
(1)帐户A的资金减少
(2)然后帐户B的资金相应增加
我们都知道,一般存款的约束条件都是大于0的,新建这个账户表如下:
1 2 3 4 5 6 |
|
然后插入两条数据
1 2 |
|
现在从一个客户A转账100到另一个客户B,客户B的金额增加100,客户A的金额减少100。转账成功,这没有问题,因为客户A钱还没有出现少于0;
1 2 |
|
但是如果现在要转账1000呢,那么我们看看最后结果。客户A的钱没有少,因为它不符合约束,所以操作失败,但是账号B的钱却多了,如果这样的话,银行岂不亏大了。
1 2 |
|
结果如下:
帐号A还是100没有变化,帐号B却是1300。
所以一个业务逻辑可以是几个数据库的操作,这个几个操作必须作为一个整体,要么都成功执行,要么就都失败,如果一半成功,一半失败的话,那么整个业务执行就会错乱了。
2、事务的四个特性
事务必须具备以下属性:
-
原子性(Atomicity):事务是一个完整的操作。事务的各步操作是不可分的(原子的);要么都执行,要么都不执行。
-
一致性(Consistency):当事务完成时,数据必须处于一致状态。
-
隔离性(Isolation):对数据进行修改的所有并发事务是彼此隔离的,这表明事务必须是独立的,它不应以任何方式依赖于或影响其他事务
-
永久性(Durability):事务完成后,它对数据库的修改被永久保持,事务日志能够保持事务的永久性
3、事务和锁
那么数据库如何实现事务呢,这里就需要用到锁的机制了。当执行事务操作的时候(DML语句)时,Oracle会在被作用表上面加表锁.以防止其他用户改变表结构,同时会在被作用行上加行锁,以防止其他事务在应用行上执行DML操作。在Oracle数据库中,为了确保数据库数据的读一致性,不允许其他用户读取脏数据(未提交事务)。我们在操作事务时,数据库为自动加锁。
在执行一段sql语句时,如果确认没有错误,那么我们可以提交事务,这样所有的sql语句都会提交给数据库,如果有问题,那么我们可以回滚事务,这样所有的操作都会回到初始化时候的状态。
4、事务隔离级别
大部分数据库事务操作都是并发执行的,这就可能遇到下面的几种问题:
丢失更新:两个事务同时更新一行数据,最后一个事务的更新会覆盖掉第一个事务的更新,从而导致第一个事务更新的数据丢失,后果比较严重。一般是由于没加锁的原因造成的。
脏读(Dirty reads):一个事务A读取到了另一个事务B还没有提交的数据,并在此基础上进行操作。如果B事务rollback,那么A事务所读取到的数据就是不正确的,会带来问题。
不可重复读(Non-repeatable reads):在同一事务范围内读取两次相同的数据,所返回的结果不同。比如事务B第一次读数据后,事务A更新数据并commit,那么事务B第二次读取的数据就与第一次是不一样的。
幻读(Phantom reads):一个事务A读取到了另一个事务B新提交的数据。比如,事务A对一个表中所有行的数据按照某规则进行修改(整表操作),同时,事务B向表中插入了一行原始数据,那么后面事务A再对表进行操作时,会发现表中居然还有一行数据没有被修改,就像发生了幻觉,飘飘欲仙一样。
注意:不可重复读和幻读的区别是,不可重复读对应的表的操作是更改(UPDATE),而幻读对应的表的操作是插入(INSERT),两种的应对策略不一样。对于不可重复读,只需要采用行级锁防止该记录被更新即可,而对于幻读必须加个表级锁,防止在表中插入数据。有关锁的问题,下面会讨论。
为了处理这几种问题,SQL定义了下面的4个等级的事务隔离级别:
未提交读(READ UNCOMMITTED ):最低隔离级别,一个事务能读取到别的事务未提交的更新数据,很不安全,可能出现丢失更新、脏读、不可重复读、幻读;
提交读(READ COMMITTED):一个事务能读取到别的事务提交的更新数据,不能看到未提交的更新数据,不会出现丢失更新、脏读,但可能出现不可重复读、幻读;
可重复读(REPEATABLE READ):保证同一事务中先后执行的多次查询将返回同一结果,不受其他事务影响,不可能出现丢失更新、脏读、不可重复读,但可能出现幻读;
序列化(SERIALIZABLE):最高隔离级别,不允许事务并发执行,而必须串行化执行,最安全,不可能出现更新、脏读、不可重复读、幻读,但是效率最低。
隔离级别越高,数据库事务并发执行性能越差,能处理的操作越少。所以一般地,推荐使用REPEATABLE READ级别保证数据的读一致性。对于幻读的问题,可以通过加锁来防止。
MySQL支持这四种事务等级,默认事务隔离级别是REPEATABLE READ。Oracle数据库支持READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别,所以Oracle数据库不支持脏读。Oracle数据库默认的事务隔离级别是READ COMMITTED。
(1)提交事务
使用commit语句可以提交事务。当执行了commit语句之后,会确认事务变化,结束事务,删除保存点,释放锁。这时候其他会话可以看到事务变化后的新数据。
但是有时候我们也不需要显示的commit,因为出现一下情况会自动提交事务:
-
执行DDL语句,如create,alter,drop table
-
执行DCL语句, grant , revoke
-
退出 sqlplus
(2)回退事务
首先我们先认识一下什么是保存点:保存点是事务回退点, 他用于取消部分事务,当结束事务的时候,会自动删除该事务所定义的所有保存点。
当执行rollback命令的时候,通过指定保存点可以取消部分事务。设置保存点命名格式是:
savepoint 保存点名称
取消部分事务
rollback to 保存点
这样保存点之前的操作提交,之后的操作取消。
如果要取消全部事务那么可以直接写
rollback;
当使用rollback取消事务的时候,会取消所有事务变化,结束事务,删除所有保存点并释放锁。当出现系统灾难或应用程序地址例外的时候,会自动回退其事务变化.
二、示例操作
1、先打开sql plus测试。
(1)设置保存点。
1 |
|
(2)删除用户。
1 |
|
这时候你查询用户帐号A已经删除了。但是使用其他的客户端还是可以看到帐号A的,因为你还没有提交数据。
(3)回滚,如果你现在后悔,那么你可以回滚回去。刚才删除的数据有重新回来了。
1 |
|
(4)再次删除数据。
1 |
|
(5)提交。这次我们不回滚了,而是提交数据。使用其他客户端也可以看到这条数据已经删除了。
1 |
|
2、也可以使用PL/SQL写一个语句块。我们使用事务修改上面的转账操作。代码如下:
1 2 3 4 5 6 7 8 9 |
|
你可以测试一下,这个语句块要么转账成功,要么就会转账失败。