CSDN博客

img biti_rainy

关于oracle怎么保证读一致性

发表于2004/7/5 18:14:00  8737人阅读

 

假设这样一种情况,一个大查询开始FTS查询大表 ,然后另外一个session更新了表末尾的某条数据并提交,更新了N次 ,由于多次事务的DML使得该block上的相关XID和LOCK信息早已荡然无存 ,该记录行上的 XID 已经被设置为 0  了,也就是根本找不着  ITL 信息 ,那这样的情况下,oracle 是怎么找出事务的 before  image ?
  
根据 dump 出来的数据是看不出端倪的。 
  
简单一致性验证测试
  
session 1 :
  
SQL> create table t nologging as select rownum a,aa.* from dba_objects aa,dba_objects bb where rownu
m < 1000000;
  
Table created.
  
SQL> create index t_index on t(a) nologging;
  
Index created.
  
SQL> select max(a) from t;
  
     MAX(A)
----------
     999999
SQL> declare
   2  v1 number;
   3  v2 number;
   4  begin
   5  v1 := 0;
   6  v2:= 0;
   7  for c in (select * from t) loop
   8  if c.a > v1 then
   9  v1:=c.a;
  10  end if;
  11  end loop;
  12  dbms_output.put_line(to_char(v1));
  13  end;
  14  /
  
这时我打开 session 2 :
session 2:  
SQL> update t set a = a+1 where a = (select max(a) from t);
  
1 row updated.
  
SQL> commit;
  
Commit complete.
  
SQL>  update t set a = a+1 where a = (select max(a) from t);
  
1 row updated.
  
SQL> commit;
  
Commit complete.
  
SQL> update t set a = a+1 where a = (select max(a) from t);
  
1 row updated.
  
SQL> commit;
  
Commit complete.
  
SQL> update t set a = a+1 where a = (select max(a) from t);
  
1 row updated.
  
SQL> commit;
  
Commit complete.
  
SQL>  update t set a = a+1 where a = (select max(a) from t);
  
1 row updated.
  
SQL> commit;
  
Commit complete.
  
SQL>  update t set a = a+1 where a = (select max(a) from t);
  
1 row updated.
  
SQL> commit;
  
Commit complete.
  
SQL>  update t set a = a+1 where a = (select max(a) from t);
  
1 row updated.
  
SQL> commit;
  
Commit complete.
  
SQL> select max(a) from t;
  
     MAX(A)
----------
    1000006
  
SQL>  update t set a = a+1 where a = (select max(a) from t);
  
1 row updated.
  
SQL>  commit;
  
Commit complete.
  
SQL>  update t set a = a+1 where a = (select max(a) from t);
  
1 row updated.
  
SQL>  commit;
  
Commit complete.
  
SQL>  update t set a = a+1 where a = (select max(a) from t);
  
1 row updated.
  
SQL>  commit;
  
Commit complete.
  
SQL>  update t set a = a+1 where a = (select max(a) from t);
  
1 row updated.
  
SQL>  commit;
  
Commit complete.
  
SQL>  
  
这时session 1 依然还没有执行完
  
一会输出结果 :
999999
  
PL/SQL procedure successfully completed.
  
如果在session 2  更新数据后再 dump 数据将会看见 dump 结果中不能提供足够的信息。那我就疑惑了,这个时候一致性是怎么来维护的,淡然 before image 应该是来自  回滚段 ,但是,oracle这个时候怎么知道这行数据跟回滚段哪部分数据相关?这个信息保存在哪里? 难道 dump 出来的内容实际上掩盖了一些内容?
  
也就是说即使我们读到某个 行 的数据,上面没有lock,没有XID信息,但是oracle 依然会去找回滚段中有没有相关的before image,并确认block 是干净的 ?这个代价是不是太高了?

 

另外一个测试,可以看出
  
同一个BLOCK中的数据,只要有一条被更新,即使只查询没有被更新的数据,甚至通过索引直接由  ROWID 查询数据,我们可以想象为根本就不看被更新的数据一眼,但是,依然会产生 CR  block  
  
SQL> create table t1(a number, b number);
  
Table created.
  
SQL> insert into t1 select rownum ,rownum from t where rownum < 11;
  
10 rows created.
  
SQL> create index t1_index on t1(a);
  
Index created.
  
SYS在这里做一个更新b字段
SQL> update rainy.t1  set b = 0 where a = 1;
  
1 row updated.
  
SQL>  select count(*) from x$bh where state = 3;  
  
   COUNT(*)
----------
         50
  

  
SQL> select * from t1 where a = 2;
  
          A          B
---------- ----------
          2          2
  
          A          B
---------- ----------
          2          2
  
Execution Plan
----------------------------------------------------------
    0      SELECT STATEMENT Optimizer=CHOOSE
    1    0   TABLE ACCESS (BY INDEX ROWID) OF 'T1'
    2    1     INDEX (RANGE SCAN) OF 'T1_INDEX' (NON-UNIQUE)
  
Statistics
----------------------------------------------------------
           0  recursive calls
           0  db block gets
           5  consistent gets
           0  physical reads
          52  redo size
         424  bytes sent via SQL*Net to client
         503  bytes received via SQL*Net from client
           2  SQL*Net roundtrips to/from client
           0  sorts (memory)
           0  sorts (disk)
           1  rows processed
  
SQL>  
  
SQL>  select * from t1 where a = 2;
  
          A          B
---------- ----------
          2          2
  
SYS查询
SQL>  select count(*) from x$bh where state = 3;  
  
   COUNT(*)
----------
         51
  

  
SQL>  select * from t1 where a = 2;
  
          A          B
---------- ----------
          2          2
  
SQL>  
  
SYS查询
SQL> select count(*) from x$bh where state = 3;  
  
   COUNT(*)
----------
         52
  
SQL>  

  
SQL>  select * from t1 where a > 8;
  
          A          B
---------- ----------
          9          9
         10         10
  
Execution Plan
----------------------------------------------------------
    0      SELECT STATEMENT Optimizer=CHOOSE
    1    0   TABLE ACCESS (BY INDEX ROWID) OF 'T1'
    2    1     INDEX (RANGE SCAN) OF 'T1_INDEX' (NON-UNIQUE)
  
Statistics
----------------------------------------------------------
           0  recursive calls
           0  db block gets
           6  consistent gets
           0  physical reads
          52  redo size
         462  bytes sent via SQL*Net to client
         503  bytes received via SQL*Net from client
           2  SQL*Net roundtrips to/from client
           0  sorts (memory)
           0  sorts (disk)
           2  rows processed
  
SQL>  
  
SYS查询
SQL> select count(*) from x$bh where state = 3;  
  
   COUNT(*)
----------
         54
  
SQL>  

  
这证明一个过程,那就是 CR 的产生,我们原来以为是这样的流程:
查看到某行,发现行上有XID信息(XID>0),根据XID找到ITL,根据ITL找到回滚段信息……  产生 CR  BLOCK
  
现在看来这是错误的!
  
CR并不是因为查看到的数据被更改过才去产生一致读,而是只要block里面存在活动事务、或者 block SCN > 查询SCN 都会产生 CR block
若存在没有活动的事务但是还没有产生commit SCN 的(ITL 中 fsc 项为0) 则先产生 delay  block  cleanout
  
那关于 CR 到底怎么从 block 定位到 undo block ,至今还没有很明确的答案(有ITL存在很容易理解,但是没有呢?), transaction table 中也只是一个  链表头 的结构……  

 

搞了半天,发现

  
ITL 中
0x1 xid: 0x0005.040.00000117 uba: 0x0080233e.01f9.11 --U- 10 fsc 0x0000.0272a3b7  
  
XID 是当前事务的事务表信息
而 uba 该事务的回滚段的地址

忽略了这样一个事实,那就是 回滚段中记录的关于block的改变,除了数据的改变还有 ITL 的改变

  
所以根据当前 ITL 信息可以产生CR block,CR block 里面包含了原来的ITL信息, 这样根据before ITL 信息产生 CR block ,这个CR block 里面包含了before  ITL ,这样又继续重复这个过程,直到 commit scn 都小于 查询SCN 的 block 或者不再有ITL为止,产生 CR 的过程结束,若没有找到就返回 1555 错误
  
当然,不一定非要是查询到的记录上有XID 信息,这依然成立,行上的 lock 主要是针对 DML 起作用

 

顺便补一句也许是废话的话
对于select 是以时间点为准的,对于 DML 不是以时间点为准的,也就是说current  mode ,当前看见的是什么就是什么,而不管时间点的一致性和过去是什么
  
SQL> select count(*) from t;
  
   COUNT(*)
----------
     999999
  
SQL>  
SQL> desc t
  Name                                                  Null?    Type
  ----------------------------------------------------- -------- ----------------------
  A                                                              NUMBER
  OWNER                                                          VARCHAR2(30)
  OBJECT_NAME                                                    VARCHAR2(128)
  SUBOBJECT_NAME                                                 VARCHAR2(30)
  OBJECT_ID                                                      NUMBER
  DATA_OBJECT_ID                                                 NUMBER
  OBJECT_TYPE                                                    VARCHAR2(18)
  CREATED                                                        DATE
  LAST_DDL_TIME                                                  DATE
  TIMESTAMP                                                      VARCHAR2(19)
  STATUS                                                         VARCHAR2(7)
  TEMPORARY                                                      VARCHAR2(1)
  GENERATED                                                      VARCHAR2(1)
  SECONDARY                                                      VARCHAR2(1)
  
SQL>  select max(a) from t;
  
     MAX(A)
----------
    1000010
  
SQL> select rowid from t where a = 1000010;
  
ROWID
------------------
AAAHhXAAJAAAHinAAX
  
该表无索引!
打开session 1 执行
SQL>  update t set owner = 'QQQQQQ'  where a = 1000010 ;
  
立即打开session 2 执行
  
SQL>  update  t set owner = 'WWWWWW' ,a = 1000011 where rowid = 'AAAHhXAAJAAAHinAAX';
  
1 row updated.
  
SQL> commit;
  
Commit complete.
  
这是 session 1 还在执行的过程中,一会就返回结果:
SQL>  update t set owner = 'QQQQQQ'  where a = 1000010 ;
  
0 rows updated.
  
SQL>  
  
这就是  query  mode 和 current mode 的重大差异
query mode  --------  consistent gets
current mode -------- db block gets

 

 

 


 

 

0 0

相关博文

我的热门文章

img
取 消
img