oracle学习笔记 事务ACID及隔离级别

Posted 新站

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了oracle学习笔记 事务ACID及隔离级别相关的知识,希望对你有一定的参考价值。

oracle学习笔记 事务ACID及隔离级别

隔离级别:isolation level


一)事务的含义

我们继续看事务,看事务的含义

事务的含义有一个说法叫ACID
A就是原子性
C就是一致性
I是隔离性
D是持久性
叫ACID

下面内容摘自老师的讲义

事务的含义

1、原子性(Atomicity)

事务的原子性是指事务中包含的所有操作要么都做,要么都不做,保证数据库是一致的。
例如:A帐户向B帐户划账1000,则先将A减少1000,再将B增加1000,
这两个动作要么都提交,要么都回退,不可能发生一个有效、一个无效的情况。

2、一致性(Consistency)

一致性是指数据库在事务操作前和事务处理后,其中的数据必须都满足业务规则约束。
例如:A、B帐户的总金额在转账前和转帐后必须一致,其中的不一致必须是短暂的,在事务提交前才会出现的。
再如:约定B帐户不能多于1000元,则A转出1000成功,B转入1000失败,最终由原子性得到-整个事务回滚

3、隔离性(Isolation)

隔离性是数据库允许多个并发事务同时对数据进行读写和修改的能力,
隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。
例如:在A、B之间转帐时,C同时向A转帐,若同时进行则A、B之间的一致性不能得到满足。
所以在A、B事务执行过程中,其他事务不能访问(修改)当前相关的数值。

4、持久性(Durability)

持久性表示为:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
在提交之前如果系统故障,则所有信息全部丢失。提交之后数据存放在磁盘中,是永久性的。

分别来解释一下

1)原子性

事务的原子性

也就是我们讲的事务由一条或多条DML语句组成
这些语句要么全成功要么全失败

为什么我们将这一组DML语句合成一个事务
为什么它需要要么全成功要么全失败呢

这和实际的业务需求有些关联

举例讲我们要做一个业务

很经典的一个例子

将A账户的钱划到B账户上
比如A账户要给B划一千块钱

将A账户减去一千做了update
将B账户加上一千也做了update

从业务的角度来讲
要么就是全成功,要么就是全失败

全成功了,表示A减去一千了,B加上一千了,转账成功

全失败就是1000没有减但是它的一千也没有加

所以说这两条DML语句
我们的业务上有一个需求,要么全成功,要么全失败
不允许减的成功了,加的失败了;也不允许减的失败了,加的成功了

寄予这种业务的需求
我们就提出事务的概念

因为实际业务
我们有这么个需求
一组操作要么全成功要么全失败
产生了事务的概念

这是为什么事务需要原子性

2)一致性

还有一个一致性

实际和原子性是有关联关系的

对银行来讲它的帐要平衡,要平帐

一个账户减了一千一个账户加了一千了
晚上结账的时候,就会成功
因为账上没有出现问题

如果说假设
A减一千成功了,B加一千没有成功
这时我们的帐就平不起来
数据就会出现不一致

所以说事务是用来保证数据一致性的其中一个手段

就是因为原子性的特性
所以说我们就利用它原子性的特性来实现数据的一致性

3)隔离性

另外一个隔离性

我们讲锁的时候还会讲到隔离性,会讲隔离性是如何实现的

有两个会话A会话和B会话

A会话登上来以后
修改了一行数据和另一行数据,修改了两行数据
还没有提交

这个时候
对B用户来讲,B用户上来以后

他就不能再修改A用户没有提交的这两行

它不能修改了,这是隔离性

举例
A用户将这一行修改了,比如把这个修改成X
把另一行的一个数据修改成Y

如果B用户也能修改的话
会出现问题

这时B用户上来以后
又把X修改了X1,把Y修改了Y1

假设能修改的话
就出现
这时B还没有提交,A提交了

A提交完了以后一查以后发现
我是把数据修改成X,另一个数据修改成Y
怎么提交了以后一查是X1和Y1啊
就有问题了

所以说隔离性是
A会话正在进行的事务
事务1它所修改的所有的数据它所涉及到的数据
它所修改的数据在没有提交以前
B事务就是第二个事务
第一看不见,第二不能修改

隔离性是一个事务正在修改的数据,在没有提交以前另外一个事务不能修改

隔离性保证了
第一个事务能够完完整整的把这个数据修改完
而且以它修改完的这个结果保存起来
它防止了一事务修改了所有的数据
因为没有隔离性,被二事务中间又修改了
一事务它保存的时候保存的不是自己所修改的结果
数据就不一致了

4)持久性

还有持久性

持久性非常简单
一个事务提交以后,oracle保证这个事务不会丢失

这个是oracle在commit的时候通过日志实现的

这是事务的ACID这几个特性
总之1原子性,2一致性,3隔离性都是保证数据的一致性,4是持久性

二)事务的默认隔离级别

A会话登上来开始事务1
B会话登上来开始事务2

默认的隔离级别是
1事务修改的数据在没有提交以前
2事务看不到1事务修改后的结果

也就是一个事务没有提交以前
别的会话是看不见它未提交的数据的

1事务提交以后
事务2就可以看到1事务已提交的数据了,而且看到修改后的结果

默认的就是
SET TRANSACTION ISOLATION LEVEL READ COMMITED

我们来演示一下

使用sqlplus用hr用户建立一个会话1
[oracle@redhat4 ~]$ sqlplus hr/hr
再使用sqlplus用hr用户建立一个会话2

t1表中原始的数据

会话一中

SQL> select * from t1;

        ID NAME
---------- --------------------
         1 xkj


会话二中

SQL> select * from t1;

        ID NAME
---------- --------------------
         1 xkj


默认的隔离级别下

会话一中删除数据

SQL> delete from t1;

1 row deleted.


会话一中自己修改完了自己是可以看到的

SQL> select * from t1;

no rows selected


表中没有数据了

因为这时它没有提交

我们在另外一个会话会话2里面

SQL> select * from t1;

        ID NAME
---------- --------------------
         1 xkj


其实事务1里面数据被删了
但因为它没有提交,事务2里面看不到事务1所做的修改
我们只能看到,事务1事务开始前的数据

在事务1中对修改进行提交

SQL> commit;

Commit complete.


提交以后事务2中马上就看到了

SQL> select * from t1;

no rows selected


事务1提交了事务2中可以看到事务一做的修改了
事务2中的查询结果没有数据了

这就是默认的一种隔离级别
其它事务没有提交本事务看不到,提交后马上看到了

这是原来的一种隔离级别是默认的隔离级别

三)SERIALIZABLE隔离级别

1)串行化作用

再看另外一种隔离级别

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

需要设置一下
在事务2中设置执行

SQL> SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

Transaction set.


这个命令只能用普通用户不能用sys用户

如果用系统管理员用户

SQL> SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
*
ERROR at line 1:
ORA-08178: illegal SERIALIZABLE clause specified for user INTERNAL


意思是指定的用户使用SERIALIZABLE是违法的

但是系统管理员可以使用下面这条命令

SQL> SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

Transaction set.


接着在事务2中执行

SQL> select * from t1;

no rows selected


没有数据

在事务1中执行

SQL> insert into t1 values(1,'xkj');

1 row created.


事务1中的这个事务没有提交

在serializable的事务2中查询

SQL> select * from t1;

no rows selected


没有看到结果
因为事务1没有提交,在事务2中查的时候结果肯定没有

然后事务1提交

SQL> commit;

Commit complete.


按照以前的说法事务2为默认隔离级别的话,事务2中应该有结果了

SQL> select * from t1;

no rows selected


在事务2中查询仍然没有结果

这是第二种隔离级别

2)串行化的解释

有两个会话
把第二个会话的隔离级别改成串行化SERIALIZABLE

第一个会话开始一个事务1
在第二个会话
执行了SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;事务开始以后
在A时刻看了一下t1

一直到B时刻
这个事务2一直没有提交

也就是在事务2的生命周期内
A时刻看到t1是什么状况,到B时刻看到t1还是什么状况

即使这个过程中
事务1中的事务对t1增加修改删除提交
例子中对事务1提交了

按正规来讲
事务2在A时刻我看到的t1是零行
中间事务1对t1表插入了一行提交了
在B时刻应该是一行,这是普通的隔离级别

但是对当前的串行化事务来讲
把这个事务2设成串行化了
也就是这个事务在A时刻和B时刻
看到的t1
不管其他事务对t1做了多少次提交修改
我总是在B时刻看到的t1
都是没有改变前的t1

也就是事务1中你这个t1中间有多少事务
对事务2没有影响
总是看到的A时刻的

3)串行化意义

什么时候这个串行化有意义呢

比如我希望在整个的事务的生命周期内
比如开始一个事务了

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
开始一直向下
都是这个事务的生命周期

在这个生命周期内
我看到的t1始终是
在串行化事务开始时刻
开始以后
在这个事务后面的时刻看到的t1
永远是没有改变

有时我们有这个需求

比如我需要t1的十行数据
在事务中查十行数据
我希望我这十行数据在我的事务内一直使用这十行数据
可以设置SERIALIZABLE

当然一般的我们不把这个事务这么搞
但是也可以这么搞

现在把事务2这个串行化事务提交

SQL> commit;

Commit complete.


提交以后这个事务
刚才开始的串行化事务结束了

结束以后在
会话二中再去查

SQL> select * from t1;

        ID NAME
---------- --------------------
         1 xkj


有了

记住有这么个操作
开始一个事务
在整个的事务的生命周期内
他所查询的数据的结果是一致的没有改变
即使其它事务的改变提交了它也是看以前的数据

一般我们不做SERIALIZABLE
READ COMMITTED就够了
默认的隔离级别是READ COMMITTED

四)事务读写属性设置的read only

还有SET TRANSACTION READ ONLY
这个其实没有多大意义

会话1中执行

SQL> SET TRANSACTION READ ONLY;

Transaction set.


read only以后我们就不能做DML语句了

如执行:

SQL> delete from t1;
delete from t1
            *
ERROR at line 1:
ORA-01456: may not perform insert/delete/update operation inside a READ ONLY
transaction


它提示
接下来只能做select不能做任何DML
这时我把我的事务设成了只读

SQL> rollback;

Rollback complete.


事务结束

然后执行

SQL> delete from t1;

1 row deleted.


命令执行成功
可以执行DML语句了

这里结束这个事务

SQL>  rollback;

Rollback complete.


事务存取方式默认的是
SET TRANSACTION READ WRITE

SET TRANSACTION READ ONLY
是一组设置

这是给大家展示了事务相关的一些信息

五)查询事务信息

我们可以查相关的事务的信息
可以使用下面的语句
select xidusn,ubablk,ubafil from v$transaction;
可以查出当前数据库中活跃的一些事务的信息
如:

SQL> select xidusn,ubablk,ubafil from v$transaction;

no rows selected


当前数据库没有事务

这是事务的相关的操作,以及该注意的地方

六)详解事务隔离级别

老师前面把事务的隔离级别的主旨说了一下
其它还有很多的内容,本人查了一些资料这里多说几句

事务隔离级别:一个事务对数据库的修改与并行的另一个事务的隔离程度。
我们讨论隔离级别的场景,主要是在多个事务并发的情况下
不同隔离级别对应不同的干扰程度, 隔离级别越高, 数据一致性就越好, 但并发性越弱

标准组织ANSI的ANSI/ISO SQL标准(SQL92)定义了四种事务隔离级别(transaction isolation level)
SQL92标准的隔离级别由低到高分别为Read uncommitted 、Read committed 、Repeatable read 、Serializable 。
但各个数据库厂商实现的方式不尽相同

这些事务隔离级别是针对三种现象定义的

三种需要阻止的现象(preventable phenomena)是:

1、脏读取(dirty read):
一个事务读取了被其他事务写入但还未提交的数据。

2、不可重复读取(nonrepeatable read):
一个事务再次读取其之前曾经读取过的数据时,发现数据已被其他已提交的事务修改或删除。
一般由其它事务的update语句引起

3、不存在读取(phantom read):
事务按照之前的条件重新查询时,返回的结果集中包含其它已提交事务插入的满足条件的新数据。
一般由其它事务的insert语句引起

在并发事务执行时,需要阻止这三种现象中的一种或多种发生

隔离级别 \\ 现象脏读取不可重复读取不存在读取
未提交读取(read uncommitted)允许允许允许
已提交读取(read committed)不允许允许允许
可重复读取(repeatable read)不允许不允许允许
串行化(rerializable)不允许不允许不允许

 
还有一种现象
更新丢失(lost update):当系统允许两个事务同时更新同一数据时,发生更新丢失
这个所有的数据库都不允许,所以未列在隔离级别要阻止的现象中

这四种隔离级别从名字看出都是从本事务读数据出发的
在写数据时还是考虑其它事务的锁情况
第四种仅仅通过“行级锁”是无法实现的
必须通过其它机制保证:
1在执行查询操作时其它事务新插入的数据不会被本事务访问到;
2得知本事务的数据修改的行在本事务执行期间是否有其它事务的修改提交。
这样才能保证这个事务的串行化
序列化是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用

这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题

SQL标准所定义的默认事务隔离级别是SERIALIZABLE

数据库mysql,支持这4种事务隔离级别。
Mysql 默认的事务隔离级别为: REPEATABLE READ

Oracle支持SQL92标准中的2种事务隔离级别:READ COMMITED, SERIALIZABLE,
Oracle 默认的事务隔离级别为: READ COMMITED

其它数据库如:SQLServer、DB2、PostgreSQL
默认事务隔离级别都是:Read Committed

oracle有一个会话存取方式的设置,包括READ WRITE默认和READ ONLY
因为READ ONLY也有隔离机制
所以oracle同时提供了非SQL92标准的read-only隔离方式

官方文档有对oracle的三个隔离级别的解释:

英文原文:
Oracle provides these transaction isolation levels.

Isolation LevelDescription
Read committedThis is the default transaction isolation level. Each query executed by a transaction sees only data that was committed before the query (not the transaction) began. An Oracle query never reads dirty (uncommitted) data.
Because Oracle does not prevent other transactions from modifying the data read by a query, that data can be changed by other transactions between two executions of the query. Thus, a transaction that runs a given query twice can experience both nonrepeatable read and phantoms.
SerializableSerializable transactions see only those changes that were committed at the time the transaction began, plus those changes made by the transaction itself through INSERT, UPDATE, and DELETE statements. Serializable transactions do not experience nonrepeatable reads or phantoms.
Read-onlyRead-only transactions see only those changes that were committed at the time the transaction began and do not allow INSERT, UPDATE, and DELETE statements.

翻译成中文:

隔离级别(Isolation Level)描述
已提交读取Oracle 默认使用的事务隔离级别。事务内执行的查询只能看到查询执行前(而非事务开始前)就已经提交的数据。Oracle 的查询永远不会读取脏数据(未提交的数据)。
Oracle 不会阻止一个事务修改另一事务中的查询正在访问的数据,因此在一个事务内的两个查询的执行间歇期间,数据有可能被其他事务修改。举例来说,如果一个事务内同一查询执行两次,可能会遇到不可重复读取或不存在读取的现象。
串行化串行化隔离的事务只能看到事务执行前就已经提交的数据,以及事务内 INSERT , UPDATE ,及 DELETE 语句对数据的修改。串行化隔离的事务不会出现不可重复读取或不存在读取的现象。
只读模式只读事务只能看到事务执行前就已经提交的数据,且事务中不能执行 INSERT , UPDATE ,及 DELETE 语句。

应用程序的设计开发者及数据库管理员可以依据应用程序的需求及系统负载(workload)而为不同的事务选择不同的隔离级别。

用户可以在事务开始时使用以下语句设定事务的隔离级别:

已提交读模式:SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

串行模式:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

只读模式:SET TRANSACTION READ ONLY;

The SET TRANSACTION statement must be the first statement of a new transaction
SET TRANSACTION语句只能在整个事务的开始第一句执行
在其它位置执行会有报错
ORA-01453: SET TRANSACTION must be first statement of transaction
只有在此事务结束后才可以重新设置隔离级别并且是设置另一个事务的隔离级别

经实验以上命令在非sysdba用户下可以成功执行
sysdba下SERIALIZABLE不能执行,READ ONLY虽然命令可以执行成功但是READ ONLY状态无效

如果在每个事务开始时都使用 SET TRANSACTION 语句,将加重网络及处理器的负担。

用户可以使用 ALTER SESSION 语句改变一个会话内所有事务的默认隔离级别:

1、ALTER SESSION SET ISOLATION_LEVEL=SERIALIZABLE;

2、ALTER SESSION SET ISOLATION_LEVEL=READ COMMITTED;

无法设置会话级别的只读隔离模式

在ALTER SESSION改变会话的隔离级别后
其后此会话中的事务的隔离级别默认都是它设置的值了
但是还是可以在为其后的单个事务设置自己的隔离级别,
这个事务结束后,其后的事务仍然默认采用设置的会话的隔离级别
上面的SET TRANSACTION和alter session都不能在一个事务已经开始后执行
执行都会有ORA-01453的报错
因为它们要改变的都是整个事务的属性

如果一个serializable transaction在执行期间,
有其它事务对本事务DML操作的数据行有DML操作并且在本事务这个串行化期间其它事务把这个修改提交了
这时在这个串行化事务修改这个表的被其它事务修改并提交的行时会有报错
ORA-08177: can’t serialize access for this transaction
就是这个事务无法完成串行化访问
整个事务这次的串行化执行就无法完成,就是事务失败了,只有想法弥补!

串行化的目的是想达到各个会话执行起来像是一个接一个的执行,相互之间没有任何的影响,不会有数据操作的冲突。
但这只是一个理想,比如ORA-08177错误的出现。
事务串行设置只作用于本事务,对串行化的读一般可以实现,
如果出现DML语句,会有一定的几率发生错误,使整个事务失败。

七)查询事务隔离级别

既然事务有隔离级别那么在会话中就应该查询到
其它数据库基本都有一个简单的命令
oracle我找到了几个有些复杂的命令

1、
SELECT s.sid, s.serial#,
  CASE BITAND(t.flag, POWER(2, 28))
    WHEN 0 THEN 'READ COMMITTED'
    ELSE 'SERIALIZABLE'
  END AS isolation_level
FROM v$transaction t
JOIN v$session s ON t.addr = s.taddr AND s.sid = sys_context('USERENV', 'SID');

2、
select sid,serial#,flag,
  CASE WHEN BITAND(FLAG,268435456) = 0 THEN 'READ COMMITTED'
                                       ELSE 'SERIALIZABLE'
                                       END AS ISOLATIONLEVEL
  from V$transaction t,v$session s
  where t.addr=s.taddr
  AND   audsid = USERENV('SESSIONID');

3、
select decode(Bitand(Flag,268435456),268435456,'Serializable','READ COMMITTED') 
 From V$Transaction, V$Session 
 Where Taddr=Addr and 
 Sid=(select dbms_support.mysid from dual);


实验运行了上面的几个语句,可以得到正确的隔离级别结果。
当transaction设置为read only时
因为这时无法执行DML语句,导致无法开始一个事务,所以查询事务的隔离级别与它无关

它们可以查出会话中事务当前所处的隔离级别是READ COMMITTED还是SERIALIZABLE状态
都需要管理员账号运行,管理员直接运行可以马上得到事务的隔离级别
如果是普通用户需要先找到它会话的sid或audsid替换上面语句条件中的sid值或audsid值
然后用管理员身份运行这些命令,可以得到普通用户事务的隔离级别。

语句中的一些内容:

sid用来确定用户会话
audsid(audit session id) 也可以唯一确定一个普通用户会话,
USERENV(‘SESSIONID’)返回当前会话的audsid

flag为V$transaction的一个字段,用二进制位形式标志着事务的类型和状态

DBMS_SUPPORT是Oracle提供的一个软件包
默认情况下,系统不安装这个包。如果需要使用的话,需进行单独设置。
在$ORACLE_HOME/rdbms/admin/目录下有dbmssupp.sql,prvtsupp.plb两个文件
我的安装过程

[oracle@redhat4 ~]$ sqlplus / as sysdba

SQL> @?/rdbms/admin/dbmssupp.sql

Package created.


Package body created.

SQL>

SQL> @?/rdbms/admin/prvtsupp.plb

Package body created.


这时系统管理员可以使用这个包了,要使普通用户也能使用
还需要授权并创建同义词

SQL> GRANT EXECUTE ON dbms_support TO PUBLIC;

Grant succeeded.

SQL> CREATE PUBLIC SYNONYM dbms_support FOR dbms_support;

Synonym created.


这样普通用户也可以使用这个dbms_support包了

如:

[oracle@redhat4 ~]$ sqlplus hr/hr

SQL> SELECT dbms_support.mysid FROM DUAL;

     MYSID
----------
       138


跑题越来越远了,不多说了
需要的话也可以找一些资料看看

2017年6月7日
文字:韵筝

以上是关于oracle学习笔记 事务ACID及隔离级别的主要内容,如果未能解决你的问题,请参考以下文章

Mysql事务隔离级别及ACID实现原理

学习数据库四大特性及事务隔离级别

MySQL事务篇:ACID原则事务隔离级别及事务机制原理剖析

Mysql事务并发问题与隔离级别深入解析

事务及事务隔离级别

事务及事务隔离级别