oraclesql事务
⑴ oracle 数据库隔离级别学习
oracle
事务隔离级别
事务不同引发的状况:
脏读(Dirty
reads)
一个事务读取另一个事务尚未提交的修改时,产生脏读
很多数据库允许脏读以避免排它锁的竞争。
不可重复读(Nonrepeatable
reads)
同一查询在同一事务中多次进行,由于其他提交事务所做的修改或删除,每次返回不同的结果集,此时发生非重复读。
幻读(Phantom
reads)
同一查询在同一事务中多次进行,由于其他提交事务所做的插入操作,每次返回不同的结果集,此时发生幻像读。
数据库操作的隔离级别
未提交读(read
uncommitted)
提交读(read
committed)
重复读(repeatable
read)
序列化(serializable)
oracle默认隔离级别read
committed
(statement
level
serialization)
每一个语句,在语句开始时,会获取一个此刻的数据快照。
一个事务有多条语句,如果语句之间存在其它完成的事务,这可能引起不可持续读和幻读。
新建一个测试表books:
name,code,price三个字段
添加两条测试数据
使用pl/sql和java程序模拟并发
不允许脏读测试:
程序段首先查询code是qqq的书的价格
复制代码
代码如下:
//获取连接
省略
pstat
=
conn.prepareStatement("select
price
from
books
where
code='qqq'");
rs
=
pstat.executeQuery();
while(rs.next()){
System.out.println("price:"+rs.getDouble(1));
}
close();
输出结果:price:15.0
然后pl/sql执行更新
复制代码
代码如下:
update
books
set
price=18
where
code='qqq'
!pl/sql设置成手动更新,不自动更新
在执行上面java查询代码
输出仍是price:15.0,说明读不到pl/sql中未提交的执行结果,即不允许脏读
pl/sql
执行
commit;
在执行java查询:
输出结果:price:18.0
会有不可重复读何幻读的现象发生就不用测试了吧,
这两种现象都是针对提交后事物的读引起的,read
commited隔离级别是允许对提交后
的事物进行读的。
隔离级别:重复读(repeatable
read)
这个不允许脏读,不可重复读,但是会有幻读现象。
这个oracle不支持,不好测试。
理解的话就是如果一条查询语句查询的内容有其它事物正在更新的时候,这
查询处于等待状态,直到先前事物提交更新后,才会执行本条查询。也就是
查询的时候也会有锁,需要等待并发的事物释放锁。然后自己获取到锁,执行
自己事物。这样查询也加锁,并发性更低
select
...
for
update
就是这样可以避免不可重复读的发生
隔离级别:serializable
这个就更严格了,事物执行是一个一个的。一个事务中的语句共享同一个数据快照(在事务开始时存在的数据)。
是事物级别的,脏读,不可重复读,幻读根本就没有机会发生。
前面像read
committed都是语句级别的,以语句为单元。
比如
read
committed一个事物A有a(select),b(select),c(update)三条语句
当A事物执行a,b的时候,若有B事物执行更新操作,是有可能的
因为a,b是不加锁的
例子:
复制代码
代码如下:
//获取连接和关闭连接代码
省略
//不自动提交
conn.setAutoCommit(false);
/**
*
a
查询
*/
pstat
=
conn.prepareStatement("select
price
from
books
where
code='qqq'");
rs
=
pstat.executeQuery();
while(rs.next()){
//输出
price:25.0
System.out.println("price:"+rs.getDouble(1));
}
close();
/**
*
暂停一会,用pl/sql执行B事务
*
update
books
set
price=15
where
code='qqq'
*
commit;
*/
Thread.sleep(10000);
/**
*
如果这里再执行a查询的话,和第一次查询结果就不一样,因为中间有B事务的提交更新
*
修改,这也是不可重复读
*/
//b
更新
pstat
=
conn.prepareStatement("update
books
set
price=price+10
where
code='qqq'");
pstat.executeUpdate();
close();
//c
查询
pstat
=
conn.prepareStatement("select
price
from
books
where
code='qqq'");
rs
=
pstat.executeQuery();
while(rs.next()){
//输出
还是price:25.0
,因为B事务的干预
System.out.println("price:"+rs.getDouble(1));
}
close();
//提交事务
conn.commit();
if(conn
!=
null){
conn.close();
}
上面执行的顺序,事务B是在A的执行过程中执行的。
以上通过实例介绍了oracle数据库隔离级别的相关内容,希望对大家有所帮助。
下面是一些补充:
数据库中的事务基本作用是将数据库从一致状态转换到另一种一致状态,那么事务隔离级别就是定义了一个事务对于另外一个事务做出的修改有多“敏感”。也就是不同的隔离级别定义了事务相互影响的程度,下面分别介绍一下几种不同的隔离级别。
1.
READ
UNCOMMITTED
其实,oracle不支持这种隔离级别。这种隔离级别允许脏读(也就是可以读取到用户未提交的数据),支持这种隔离级别的数据库主要是为了支持非阻塞读,但是oracle默认支持非阻塞读,所以oracle里面不支持这种隔离级别。下面举一个例子:
如上图所示,假设某一家银行要统计所有账号总共有多少金额。事务A负责统计,事务A从第一行开始读取。假设读取到100行的时候,事务B从账号123转了400元到账户987(事务B还未提交),支持脏读的数据库当事务A读取到342023行的时候,就会得到500元,从而多加了400元。
2.
READ
COMMITTED
这种隔离级别指的是,事务只能读取已经提交的数据,(但是支持可重复读与幻想读)是oracle数据库默认的隔离模式。其实这种隔离级别在别的数据库里面可能还是会“退化”得像脏读一样。就看前面那个例子,假设在事务A读取到342023行前,事务B提前锁定了这一行,并将金额由100改成了500。那么事务A读取到这一行的时候,发现已经被其他事务锁定了,于是进行等待,直到事务B提交。但是当事务B提交之后,事务A还是读取到了500这一个错误信息,这样就和脏读一样的了,而且还让用户等待这个错误的答案。
3.
REPEATABLE
READ
这种隔离级别不支持脏读,不支持可重复读,支持幻想读。主要是为了得到一致性的答案与防止丢失更新。
a.
得到一致性答案
在oracle里面这个通过多版本机制得到了实现,但是在其他的数据库需要通过加锁机制进行控制,就以上一个例子为例,怎样才能统计出正确的总金额呢,事务A在读取每一行的时候,给每一行加上共享读锁,这样当事务B执行从账号123转400元到账户987的时候。先是操作第一行将账户123的金额由500修改成100,但是第一行已经被事务A锁定,于是等待,这样事务A能够读取到正确的数据。但是如果事务B执行的操作是从账户987转50元到账户123的时候,事务B先操作第342023行,发现没有被锁定,于是锁定将金额由100修改成50,然后操作第一行,发现锁定了于是等待。而事务A读取到342023行的时候,发现这一行已经被事务B锁定于是等待,这样就陷入了死锁。
b.
丢失更新
在采用共享读锁的数据库中,这种隔离级别可以防止丢失更新,比如事务1先读取了第A行然后修改了这一行的C列(其他列也修改了只是值还和以前一样,因为程序员都是整行的更新)。这个时候事务2想也想修改A行的时候会被阻塞,防止事务1的更新被覆盖。
4.
SEAIALIZABLE
不允许脏读,重复读与幻想读,最高的隔离级别。这种隔离级别标明事务A在操作数据库的时候好像就只有事务A在操作,没有其他事务在操作数据库一样。
Oracle
中是这样实现
SERIALIZABLE
事务的:原本通常在语句级得到的读一致性现在可以扩展到事务级。也就是在事务执行的那一刻,将这个事务将要操作的数据拍了一张照片。
从上面的例子我们可以看出,其他数据库采用共享读锁来解决统计总金额问题是没有oracle多版本机制灵活的,其一严重影响了程序的并发性,读阻塞了写。其二可能引起死锁。
⑵ oraclesql执行顺序优先级
Oracle SQL执行顺序优先级为:
1. 解析与编译阶段。
2. 执行计划生成。
3. 执行阶段。
接下来是对这一执行顺序的
解析与编译阶段: 这是SQL语句处理的第一个阶段。在这一阶段,Oracle会检查语法,识别并验证SQL语句中的表和列名,同时还会解析使用到的任何函数或过程。此外,还会根据对象定义和数据统计信息生成执行计划的基础结构。如果SQL语句被缓存或包含在一个命名块中,那么Oracle可能会跳过解析阶段,直接使用已编译的代码。
执行计划生成阶段: 在解析和编译之后,Oracle会生成执行计划,这是根据解析和编译过程中收集到的数据统计信息和对象定义来决定的。Oracle优化器会考虑多种可能的执行路径并选择成本最低的一种。这个计划详细说明了如何检索数据以及如何以最高效的方式执行查询。
执行阶段: 这是最后一个阶段,Oracle根据生成的执行计划开始获取数据并返回结果。根据计划中的指示,它会访问磁盘上的数据或将数据从内存缓存中检索出来,对数据进行必要的操作并返回结果集。执行阶段的具体细节取决于查询的复杂性以及数据库的配置和数据分布。
整体上,Oracle SQL的执行过程涉及多个阶段和多个复杂的决策过程,从解析和编译到执行计划的生成和执行。了解这些阶段的顺序和每个阶段的作用对于有效地编写和优化SQL查询至关重要。同时,还需要考虑到数据库的性能和资源管理等方面的影响。
⑶ 什么是事务,oracle和sql server 在事务处理上有何不同
事务可以看作是由对数据库的若干操作组成的一个单元,这些操作要么都完成,要么都取消,从而保证数据满足一致性的要求。事务的一个典型例子是银行中的转帐操作,帐户A把一定数量的款项转到帐户B上,这个操作包括两个步骤,一个是从帐户A上把存款减去一定数量,二是在帐户B上把存款加上相同的数量。这两个步骤显然要么都完成,要么都取消,否则银行就会受损失。显然,这个转帐操作中的两个步骤就构成一个事务。
数据库中的事务还有如下ACID特征。
ACID分别是四个英文单词的首写字母,这四个英文单词是Atomicity、Consistency、Isolation、Durability,分别翻译为原子性、一致性、隔离性、持久性。
在SQL Server中有三种事务类型,分别是:隐式事务、显式事务、自动提交事务,缺省为自动提交。
在Oracle中没有SQL Server的这些事务类型,缺省情况下任何一个DML语句都会开始一个事务,直到用户发出Commit或Rollback操作,这个事务才会结束,这与SQL Server的隐式事务模式相似。