Oracle开发者中级第9课(读取一致性)实验
Posted dingdingfish
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Oracle开发者中级第9课(读取一致性)实验相关的知识,希望对你有一定的参考价值。
概述
本实验参考DevGym中的实验指南。
创建环境
create table toys (
toy_name varchar2(30),
price number(10, 2)
);
insert into toys values ( 'Miss Snuggles', 9.99 );
commit;
数据为:
select * from toys;
TOY_NAME PRICE
________________ ________
Miss Snuggles 9.99
Autonomous Transaction
Autonomous Transactions在生产环境中极少使用,此处只是由于LiveSQL的限制,需要用它在一个会话中来模拟两个会话。
insert into toys values ( 'Baby Turtle', 7.95 );
declare
pragma autonomous_transaction;
begin
insert into toys values ( 'Blue Dinosaur', 15.95 );
commit;
end;
/
select * from toys;
TOY_NAME PRICE
________________ ________
Baby Turtle 7.95
Blue Dinosaur 15.95
Miss Snuggles 9.99
-- 此rollback并不涉及autonomous transaction中的事务
rollback;
select * from toys;
TOY_NAME PRICE
________________ ________
Blue Dinosaur 15.95
Miss Snuggles 9.99
实际上你也可以起两个窗口,分别执行:
会话1 | 会话2 |
---|---|
insert into toys values ( ‘Baby Turtle’, 7.95 ); | insert into toys values ( ‘Blue Dinosaur’, 15.95 ); |
commit; | |
rollback; |
Read Phenomena
SQL 标准定义了三种读取现象,即多人对同一行进行读写时可能发生的问题,他们是:
- 脏读
- 不可重复(或模糊)读
- 幻像读
Dirty Reads(脏读)
脏读是指看到在另一个事务中未提交的行。 由于其未必提交。 因此,您可以返回最终未保存到数据库中的数据!
在 Oracle 数据库中,脏读是不可能的。 您只能在自己的事务中查看未提交的行。
Non-repeatable (Fuzzy) Reads(不可重复读或模糊读)
不可重复读取是指两次查询同一行返回不同的结果。 当他人在查询之间更新行时会发生这种情况。
例如:
会话1 | 会话2 |
---|---|
create table bricks(colour varchar2(16), shape varchar2(16)); | |
insert into bricks (colour, shape) values (‘red’, ‘cube’); | |
commit; | |
select shape from bricks where colour = ‘red’; cube | |
update bricks set shape = ‘pyramid’; | |
commit; | |
select shape from bricks where colour = ‘red’; pyramid | |
SQL 标准还允许在单个查询中进行不可重复读。例如对于一个运行时间非常长的统计查询,在查询过程中数据可能被别人修改,因此导致结果的不一致性。
-- 会话1的查询,耗时10秒
select colour, count(*)
from bricks
group by colour;
-- 会话2在第5秒时进行更新
update bricks
set colour = 'red';
Oracle 数据库始终具有语句级一致性。 因此,在一个查询中不可能发生不可重复读。
Phantom Reads(幻象读)
幻象读是不可重复读的一种特殊形式。 当另一个会话插入或删除与查询的 where 子句匹配的行时,就会发生这种情况。 所以重复查询时可以返回不同的行。
会话1 | 会话2 |
---|---|
truncate table bricks; | |
insert into bricks (colour, shape) values (‘red’, ‘cube’); | |
commit; | |
select shape from bricks where colour = ‘red’; cube | |
insert into bricks ( colour, shape ) values ( ‘red’, ‘pyramid’ ); | |
commit; | |
select shape from bricks where colour = ‘red’; pyramid cube |
索引,幻象读和不可重复读的本质区别是结果集的行数发生了变化。
Isolation Levels
为了帮助您管理读取问题,SQL 标准定义了四个隔离级别,以说明哪些现象是可能的,如下表所示:
脏读 | 不可重复读 | 幻象读 | Oracle是否支持 | |
---|---|---|---|---|
Read Uncommitted | V | V | V | Oracle不允许脏读,因此不支持此隔离级 |
Read Committed | X | V | V | 支持且为默认隔离级,保证了语句级的一致性。这意味着每个 DML 命令在开始之前都可以看到保存的所有数据。 其他会话在启动后保存的任何更改都将隐藏。Oracle使用多版本并发控制 (MVCC) 来做到这一点。 当您更新或删除一行时,这会将行的当前状态存储在undo中。 其他事务可以使用undo来查看过去存在的数据。因此,除了在事务内部,Oracle 数据库中的单个语句中不可能出现任何读取现象。 |
Repeatable Reads | X | X | V | 此隔离级的目的是提供一致的查询结果。 但是 Oracle 数据库已经在Read Committed中做到了这一点!因此也就没有实现它。如果是没有 MVCC 的数据库,您可能需要这种模式才能获得正确的结果。 |
Serializable | X | X | X | 您可以在 Oracle 数据库中使用它来获得事务级一致性。 您只能查看在事务开始时在数据库中提交的更改。 在此之后其他交易所做的任何更改都会对您的交易隐藏。 |
Set Transaction Isolation Level
语法示例:
set transaction isolation level read committed;
只能是事务中的第一条语句:
insert into toys values ( 'Purple Ninja', 19.99 );
set transaction isolation level read committed;
Error starting at line : 1 in command -
set transaction isolation level read committed
Error report -
ORA-01453: SET TRANSACTION must be first statement of transaction
01453. 00000 - "SET TRANSACTION must be first statement of transaction"
*Cause: self-evident
*Action: commit (or rollback) transaction, and re-execute
rollback;
Read Committed
Read Committed提供语句一级的一致性。每个命令都可以查看在它启动时保存在数据库中的所有更改。
没有看懂这个示例想说明什么。感觉结果不是理所当然的吗。接着看就明白了。
set transaction isolation level read committed;
select * from toys;
TOY_NAME PRICE
________________ ________
Miss Snuggles 9.99
declare
pragma autonomous_transaction;
begin
update toys set price = 1.61;
insert into toys values ( 'Baby Turtle', 19.99 );
commit;
end;
/
select * from toys;
TOY_NAME PRICE
________________ ________
Baby Turtle 19.99
Miss Snuggles 1.61
commit;
select * from toys;
TOY_NAME PRICE
________________ ________
Baby Turtle 19.99
Miss Snuggles 1.61
Serializable
在Serializable中,您具有事务级一致性。 这就像您是数据库的唯一用户一样。 其他交易所做的更改对您是隐藏的。 其他一些数据库将 Oracle 数据库的Serializable实现称为快照隔离。
set transaction isolation level serializable;
select * from toys;
TOY_NAME PRICE
________________ ________
Baby Turtle 19.99
Miss Snuggles 1.61
declare
pragma autonomous_transaction;
begin
update toys set price = 2.71;
insert into toys values ( 'Purple Ninja', 7.95 );
commit;
end;
/
select * from toys;
TOY_NAME PRICE
________________ ________
Baby Turtle 19.99
Miss Snuggles 1.61
commit;
select * from toys;
TOY_NAME PRICE
________________ ________
Baby Turtle 2.71
Purple Ninja 7.95
Miss Snuggles 2.71
以上转换为2个会话会更清楚:
会话1 | 会话2 |
---|---|
truncate table toys; | |
insert into toys values ( ‘Baby Turtle’, 19.99 ); | |
insert into toys values ( ‘Miss Snuggles’, 1.61 ); | |
commit; | |
set transaction isolation level serializable; | |
select * from toys; | |
update toys set price = 2.71; | |
insert into toys values ( ‘Purple Ninja’, 7.95 ); | |
insert into bricks ( colour, shape ) values ( ‘red’, ‘pyramid’ ); | |
commit; | |
select * from toys; | |
commit; | |
select * from toys; |
自治事务(第二个会话)发生在父事务(第一个事务)开始之后。 所以父级看不到新值。 PL/SQL 块之后的查询返回与开始时的查询相同的行。 您只能在父事务提交结束后才能看到更新和插入的行。
Serializable 还可以阻止您更改由其他事务修改的行:
set transaction isolation level serializable;
select * from toys;
declare
pragma autonomous_transaction;
begin
update toys set price = 3.14;
commit;
end;
/
update toys
set price = price * 10
where toy_name = 'Miss Snuggles';
Error starting at line : 1 in command -
update toys
set price = price * 10
where toy_name = 'Miss Snuggles'
Error report -
ORA-08177: can't serialize access for this transaction
delete toys
where toy_name = 'Baby Turtle';
Error starting at line : 1 in command -
delete toys
where toy_name = 'Baby Turtle'
Error report -
ORA-08177: can't serialize access for this transaction
commit;
select * from toys;
一点困惑,虽然Serializable 成功的规避了3种读取现象,但却看不到其它会话已提交的修改,那么它的应用场景是什么?技术上称为快照隔离是容易理解的,和Read Committed的区别并不大。
以下是作者的建议:
当事务多次访问相同的行时,而且会有很多人同时运行交易,您应该考虑使用可序列化。 。
要选择最佳隔离级,您需要了解您的数据要求是什么。
Serializable 会减少可以同时使用您的应用程序的人数。 因此,您可能需要在数据一致性和性能之间进行一些权衡。
Read Only(只读)
除了Read Committed和Serializable ,Oracle 数据库还提供了另一种事务模式:只读。
对于查询,这与Serializable的工作方式相同。 您具有事务级一致性。 查询返回事务开始时存在的数据。它有一个额外的限制,即您只能运行SELECT。 任何更改行的尝试都将引发异常。例如:
set transaction read only;
select * from toys;
update toys
set price = price + 1;
Error starting at line : 1 in command -
update toys
set price = price + 1
Error report -
ORA-01456: may not perform insert/delete/update operation inside a READ ONLY transaction
declare
pragma autonomous_transaction;
begin
update toys set price = 99.00;
commit;
end;
/
select * from toys;
commit;
select * from toys;
此模式在报表环境中很有用。 一组报表可能需要多次查询相同的表。 为确保结果正确,您必须忽略报告运行时其他用户所做的任何更改。 用户还必须只能读取数据。 您需要停止所有非SELECT的 DML。
将事务模式设置为只读可以满足这两个需求。
最后,下面语句等同于设为read committed隔离级:
set transaction read write;
环境清理
drop table toys;
以上是关于Oracle开发者中级第9课(读取一致性)实验的主要内容,如果未能解决你的问题,请参考以下文章