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 UncommittedVVVOracle不允许脏读,因此不支持此隔离级
Read CommittedXVV支持且为默认隔离级,保证了语句级的一致性。这意味着每个 DML 命令在开始之前都可以看到保存的所有数据。 其他会话在启动后保存的任何更改都将隐藏。Oracle使用多版本并发控制 (MVCC) 来做到这一点。 当您更新或删除一行时,这会将行的当前状态存储在undo中。 其他事务可以使用undo来查看过去存在的数据。因此,除了在事务内部,Oracle 数据库中的单个语句中不可能出现任何读取现象。
Repeatable ReadsXXV此隔离级的目的是提供一致的查询结果。 但是 Oracle 数据库已经在Read Committed中做到了这一点!因此也就没有实现它。如果是没有 MVCC 的数据库,您可能需要这种模式才能获得正确的结果。
SerializableXXX您可以在 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课(读取一致性)实验的主要内容,如果未能解决你的问题,请参考以下文章

Oracle开发者中级第3课(如何对行进行排序)实验

Oracle开发者中级第4课(分析函数)实验

Oracle开发者中级第8课(Merge)实验

Oracle开发者中级第8课(Merge)实验

Oracle开发者中级第2课(子查询)实验

Oracle开发者中级第1课(Null)实验