oracle如何保持读写一致性,关于oracle怎么保证读一致性

假设这样一种情况,一个大查询开始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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值