sqlplus之多版本

oracle采用了一种多版本、读一致(read-consistent)的并发模型。

读一致查询:对于一个时间点(point in time),查询会产生一致的结果

非阻塞查询:查询不会被写入器阻塞


多版本(multi-versioning),oracle能够同时物化多个版本的数据。

-----------------------------------------------------------------------------------------------

创建一个测试表T,并把all_users表的一些数据加载到T表中

scott@ORCL>create table t
  2  as
  3  select *
  4  from all_users;

表已创建。


然后在这个表上 打开一个游标。在此没有从该游标读取数据,只是打开游标而已。

打开游标时,oracle不复制任何数据,它会边行进边回答查询。

只在获取数据时它才从表中读取数据

scott@ORCL>variable x refcursor
scott@ORCL>begin
  2  open :x for select * from t;
  3  end;
  4  /

PL/SQL 过程已成功完成。


在同一会话中,再从表中删除所有数据,甚至commit提交了删除所做的工作。

scott@ORCL>delete from t;
已删除37行。

scott@ORCL>commit;
提交完成。

记录行都没有了,但是,还是可以通过游标获取数据

scott@ORCL>print x

USERNAME                          USER_ID CREATED
------------------------------ ---------- --------------
PERFSTAT                               93 15-3月 -18
BI                                     90 14-3月 -18
PM                                     89 14-3月 -18
SH                                     88 14-3月 -18
IX                                     87 14-3月 -18
OE                                     86 14-3月 -18
HR                                     85 14-3月 -18
SCOTT                                  84 30-3月 -10
OWBSYS_AUDIT                           83 30-3月 -10
OWBSYS                                 79 30-3月 -10
APEX_030200                            78 30-3月 -10
APEX_PUBLIC_USER                       76 30-3月 -10
FLOWS_FILES                            75 30-3月 -10
MGMT_VIEW                              74 30-3月 -10
SYSMAN                                 72 30-3月 -10
SPATIAL_CSW_ADMIN_USR                  70 30-3月 -10
SPATIAL_WFS_ADMIN_USR                  67 30-3月 -10
MDDATA                                 65 30-3月 -10
MDSYS                                  57 30-3月 -10
SI_INFORMTN_SCHEMA                     56 30-3月 -10
ORDPLUGINS                             55 30-3月 -10
ORDDATA                                54 30-3月 -10
ORDSYS                                 53 30-3月 -10
OLAPSYS                                61 30-3月 -10
ANONYMOUS                              46 30-3月 -10
XDB                                    45 30-3月 -10
CTXSYS                                 43 30-3月 -10
EXFSYS                                 42 30-3月 -10
XS$NULL                        2147483638 30-3月 -10
WMSYS                                  32 30-3月 -10
APPQOSSYS                              31 30-3月 -10
DBSNMP                                 30 30-3月 -10
ORACLE_OCM                             21 30-3月 -10
DIP                                    14 30-3月 -10
OUTLN                                   9 30-3月 -10
SYSTEM                                  5 30-3月 -10
SYS                                     0 30-3月 -10

已选择37行。

open命令返回的结果集那一刻(时间点)就已经确定了。

打开时,我们根本没有碰过表中的任何数据块,但答案是固定的。

获取数据之前,我们无法知道答案会是什么;但从游标角度看,结果则是固定不变了。

打开游标时,并非oracle将所有数据复制到另外某个位置;实际上是delete命令为我们把数据保留下来,把它放在一个称之为undo段(undo segment)的数据区,也称之为回滚段(rollback segment)。

-------------------------------------------------------------------------------

1.多版本和闪回

SCN(System Change Number or System Commit Number)

是oracle的内部时钟:每次发生提交时,这个时钟就会向上滴答(递增)

授权给Scott:

sys@ORCL>grant "CONNECT" to scott;
授权成功。

sys@ORCL>grant "DBA" to scott;
授权成功。

sys@ORCL>grant execute on dbms_flashback to scott;
授权成功。


scott@ORCL>variable SCN number
scott@ORCL>exec :scn := dbms_flashback.get_system_change_number
PL/SQL 过程已成功完成。

scott@ORCL>print scn

       SCN
----------
   1364868

现在可以让oracle提供SCN值所表示时间点的数据。以后再查询oracle,就能看看这一时刻表中的内容。

首先看看emp表:

scott@ORCL>select count(1) from emp;

  COUNT(1)
----------
        14

下面把这些信息都删除,并验证数据是否确实"没有了":

scott@ORCL>delete from emp;

已删除14行。

scott@ORCL>select count(1) from emp;

  COUNT(1)
----------
         0

此外,使用闪回查询(即 AS OF SCN 或 AS OF TIMESTAMP子句),可以让oracle告诉我们SCN值为1364868的时间点上表中有什么:

scott@ORCL>select count(1) from emp AS OF SCN :scn;

  COUNT(1)
----------
        14

不仅如此,这个功能还能跨事务边界。我们甚至可以在统一查询中得到同一个对象在"两个时间点"上的结果:

scott@ORCL>commit;
提交完成。

scott@ORCL>select *
  2  from (select count(*) from emp),
  3  (select count(*) from emp as of scn:scn)
  4  /

  COUNT(*)   COUNT(*)
---------- ----------
         0         14

oracle 10g以及以上版本,有个“闪回”(flashback)命令,它使用了这种底层多版本技术,可以把对象返回到以前某个时间点的状态:

scott@ORCL>flashback table emp to scn :scn;
flashback table emp to scn :scn
                *
第 1 行出现错误:
ORA-08189: 因为未启用行移动功能, 不能闪回表


scott@ORCL>alter table emp enable row movement;    #允许oracle修改分配给行的rowid
表已更改。

scott@ORCL>flashback table emp to scn :scn;
闪回完成。

scott@ORCL>select *
  2  from (select count(*) from emp),
  3  (select count(*) from emp as of scn:scn)
  4  /

  COUNT(*)   COUNT(*)
---------- ----------
        14         14

在oracle中,插入一行时就会为它分配一个rowid,而且这一行永远拥有这个rowid。

闪回表处理 会对emp完成delete,并且重新插入行,这样就会为这些行分配一个新的rowid。

要支持闪回必须允许oracle执行这个操作。


2.读一致性和非阻塞读

假设我们读取的表在每个数据库块(数据库中最小的存储单元)中只存放一行


scott@ORCL>create table accounts
  2  (account_number number primary key,
  3  account_balance number
  4  );

表已创建。


scott@ORCL>insert into accounts values(123,500);
已创建 1 行。

...
scott@ORCL>select * from accounts;

ACCOUNT_NUMBER ACCOUNT_BALANCE
-------------- ---------------
           123             500
           234             250
           345             400
           456             100
scott@ORCL>select sum(account_balance) from accounts;

SUM(ACCOUNT_BALANCE)
--------------------
                1250


场景:如果我们现在读了第1行,准备读第2行和第3行时,一台ATM针对这个表发生了一个事务,将400从账号123转到账户456,结果会怎么样?读一致性 就是oracle为了避免发生这种情况所采用的方法。


在几乎所有的其他数据库中,如果想得到"一致"和"正确"的查询答案,就必须在计算总额时锁定整个表,或者在读取记录行时对其锁定。这样一来,获取结果时 就可以防止别人再做修改。如果提前锁定表,就会得到查询开始时数据库中的结果。如果在读取数据时锁定(共享读锁,可以防止更新,但不妨碍读取器读取数据库),就会得到查询结束时数据库中的结果。这两种方法都会影响并发性。


oracle利用多版本来得到结果,也就是查询开始时那个时间点的结果,然后完成查询,而不做任何锁定(转账事务更新第1行和第4行时,这些行会对其他写入器锁定,但不会对读取器锁定,如这里的 select sum...)。实际上,oracle根本没有"共享锁"。

oracle事务机制:

只要你修改数据,oracle就会创建撤销(undo)条目,这些undo条目写至undo段(撤销段,undo segment)。

如果事务失败,需要撤销,oracle就会从这个回滚段读取"之前"的映像,并恢复数据。

除了使用回滚段数据 撤销事务外,oracle还会用它撤销 读取块时对块所做的修改,使之恢复到查询开始前的时间点。

时间 查询 转账事务
T1 读取第1行;到目前为止 sum=500  
T2   更新第1行;对第1行加一个排他锁,阻止其他更新第1行。现有100
T3 读取第2行;到目前为止sum=750  
T4 读取第3行;到目前为止sum=1150  
T5   更新第4行;对第4行加一个排他锁,阻止其他更新第4行(但不阻止读操作)。现有500
T6 读取第4行,发现第4行已经修改,这会将块回滚到T1时刻的状态。查询从这个块读到值100  
T7 得到答案1250  













在T6时,oracle有效的"摆脱'了事务加在第4行上的锁。非阻塞性读是这样实现的:oracle只看数据是否改变,它并不关心数据当前是否锁定(锁定意味着数据已经改变)。oracle只是从回滚段中取回原来的值,并继续处理下一个数据块。


数据的读一致视图总是在sql语句级执行sql语句的结果 对于查询开始的时间点 来说是一致的

正因为这一点,所以下面的语句可以插入可预知的数据集:

scott@ORCL>select * from t;

USERNAME                          USER_ID CREATED
------------------------------ ---------- --------------
yin                               1         18-3月 -18


scott@ORCL>begin
  2  for x in( select * from t)
  3  loop
  4  insert into t values(x.username,x.user_id,x.created);
  5  end loop;
  6  end;
  7  /

PL/SQL 过程已成功完成。

scott@ORCL>select * from t;

USERNAME                          USER_ID CREATED
------------------------------ ---------- --------------
yin                                     1 18-3月 -18
yin                                     1 18-3月 -18

select * from t的结果在查询开始执行时 就已经确定了。这个select并不看insert生成的任何新数据。

oracle为所有语句提供了这种读一致性,所以如下的insert也是可预知的:

scott@ORCL>insert into t select * from t;

已创建2行。

scott@ORCL>select * from t;

USERNAME                          USER_ID CREATED
------------------------------ ---------- --------------
yin                                     1 18-3月 -18
yin                                     1 18-3月 -18
yin                                     1 18-3月 -18
yin                                     1 18-3月 -18

猜你喜欢

转载自blog.csdn.net/a0001aa/article/details/79599971