《Oracle逻 辑读写深入分析》之分析逻辑读次数受哪些因素的影响(笔记)[以跟踪文件中的事件来计数consistent gets次数,非用公式计算]


跟踪文件里的一些字符串表示的意思:

kdstgr:表示数据块,即表对应的段中除了段头块外的块(存储数据行的)或是索引的对应的段中除了段头块外的块(存储索引条目的)。

Kdusru:  以当前模式读入,以用于更新操作(Read>(这里的(一致性读过程)例子是避免了用undo块来回滚数据这一情况的)

一、全表扫描 I
select *> //读取段头两次不是因为表里有两条数据行的关系,而是因为要进行一次>3.   
4.  2>7.   
8.  2 rows updated.   
9.   
10.  -- Session 2:   
11.  HELLODBA.COM>conn demo/demo   
12.  Connected.   
13.  HELLODBA.COM>alter system flush buffer_cache;   
14.   
15.  System altered.  
HELLODBA.COM>select * from tt;

                 1  recursive calls   
31.            0  db block gets   
32.           13  consistent gets   
33.            8  physical reads  
同样还是之前的那个小表的全表扫描,这里有 13 次逻辑读,比正常情况下多了 6 次。看看从跟踪文件中我们可以发现什么。
首先,和前面一样,它还是读取了 2 次段头(2 次读取表段头//读取段头两次不是因为表里有两条数据行的关系,而是因为要进行一次 kteinicnt 操作 kteinpscan 操作的关系。),然后读取高水位线下的普通数据块(4 次读取表数据块)。当读取到(内存)地址为140e64f的普通数据块 时,这个数据块是第一块被读取到的含有被其他事务修改过数据的数据块。这时,Oracle读取了回滚段的段头(1 次读取回滚段头)中事务表(TransactionTable)的数据,从中找到用于一致性回滚的 UNDO 数据块。我们之前做了两次 UPDATE,尽管这两次的 UNDO 内容都在同一个 UNDO 数据块中(从 UBA 可以看出,有兴趣的读者可以将这个 UNDO 块 dump 出来进行观察),这里有两次’Applying CR undo’,每一条 ITL  entry 都导致了增加了一次对 UNDO 数据块的读(2 次读取UNDO 数据块。)至此,已经完成了 9 次逻辑读:2 次读取表段头,4 次读取表数据块,1次读取回滚段头,2 次读取 UNDO 数据块。

注释:这里,2 次读取UNDO 数据块,是假设该被修改的普通数据块里的被修改的数据行是只是被一个事务所修改,是被该事务里的两次操作所修改,对应产生了两条undo记录,故而有了2 次读取(UNDO 数据块)。如果一个被修改的普通数据块不同行被不同事务修改,或是同一行被先后多个事务修改,且每个事务都操作多次对该行,即会产生多条undo记录对应每个事务,那么回滚该普通数据块要几次读取呢?

还有,

Applying CR undo to block 5 : 140e64f itl entry 02:   
2.            xid:  0x0005.00b.00023460 uba: 0x008012aa.7970.05   
3.            flg: ----    lkc:  1     fsc: 0x0000.00000000   
4.   
5.  Then the 2nd ITL in it, read the UNDO to apply, increase 1 Logic reads   
6.   
7.  Applying CR undo to block 5 : 140e64f itl entry 02:   
8.            xid:  0x0005.00b.00023460 uba: 0x008012aa.7970.03   
9.            flg: ----    lkc:  1     fsc: 0x0000.00000000    

为什么同一个xid对应不同的uba?

接下来继续回滚下一个普通数据块(即地址为140e650的普通数据块),导致了另外 4 次逻辑读:1 次读取表的普通数据块,1 次读取回滚段头,2 次读取
UNDO 数据块(也是由于被修改的普通数据块里的被修改的数据行是只是被一个事务所修改,是被该事务里的两次操作所修改,对应产生了两条undo记录,故而有了2 次读取(UNDO 数据块))。


这里我们还要注意到:在物理读中有 2 次对回滚段的读(一次段头、一次 UNDO 数据块)。


五、当前模式
当修改数据时,Oracle 会将被修改数据所在的数据块以当前模式(Current  Mode)读取到>
13.   
14.  2 rows updated.   
15.   
16.  Statistics
17.  ----------------------------------------------------------   
18.            0  recursive calls   
19.            4  db block gets   
20.            7  consistent gets   
21.            8  physical reads  
30.  HELLODBA.COM>alter>
35.   
36.  2>Secret of oracle logic IO: Current Mode


在这里,还需要提醒大家注意两点:
1、  逻辑读的 db block gets 和 consistent gets 是被分别统计的,也就是说 SQL Trace 中的统计数据要
将两者相加才得到该语句的逻辑读的数量;而两者的物理读则是被合并统计的(也就是说db block gets 和 consistent gets两个读取的相同的数据块(即数据块的地址相同)部分(这部分是两者重合的)不做两次计数,而是当做一次);

2、  我们这次是用 10.2.0.3 测试的, 而如果你用 10.2.0.4 或 11g 做相同实验的话, 你会发现第二次 UPDATE
中可能没有增加新的对 UNDO 数据块的当前读。那是因为 IMU(In Memory Undo)导致的:IMU 用私有内
存存储 UNDO 数据块,因而无需 pin,也不会被统计逻辑读。而 10.2.0.3 由于 Bug 的原因,IMU 并未
际被激活。


六、排序

12.  HELLODBA.COM>select * from t_test2 order by table_name;   
13.     
14.  2072 rows selected.   
15.   
16.  Statistics
17.  ----------------------------------------------------------   
18.            7  recursive calls   
19.           21  db block gets   
20.           65  consistent gets   
21.          201  physical reads   
22.            0  redo size
23.       131049  bytes sent via SQL*Net to client   
24.         1903  bytes received via SQL*Net from client   
25.          140  SQL*Net roundtrips to/from client   
26.            0  sorts (memory)   
27.            1  sorts (disk)   
28.         2072  rows processed

我们还可以看到,数据的 FETCH 是在排序完成后再进行的。这一点很重要,这就意味着存在排序操作时,
arraysize 将不再影响逻辑读的次数
一个数据块的内容都是被一次读入以用于排序而非返回客户端。所以,从数
据上看, 尽管这里也是对和前面测试中同一个表进行全表扫描, 但由于排序的需要, 逻辑读的次数反而减少了。


再仔细看跟踪文件内容,可以发现多出了一种 pin 操作:stsswr,不难猜测这是用排序读写的(Sort  Segment  Write  Read)。由于需要写入(修改)数据,这一逻辑读是以当前模式读(方式进行的)入的,即方式为>              //其中,/a里的a应该是命令set的参数,而a=0里的a是变量。                                 
2.  0   


3.  C:\oracle\product\10.2.0\admin\hellodba.com>for /f "tokens=1>r /C:"pin>pin stswh00pin 操作:stsswr时会在跟踪文件里会出现的字符串。以此字符串来对应一次当前模式读。
4.   


5.  C:\oracle\product\10.2.0\admin\hellodba.com>echo %a%   
6.  21   



最后,统计一下物理读:从"db>2.  0   
3.  C:\oracle\product\10.2.0\admin\hellodba.com>for /f "tokens=1>r /C:"direct>4.   
5.  C:\oracle\product\10.2.0\admin\hellodba.com>echo %a%   
6.  138 


       因为排序时,客户端对数据的 FETCH 是在排序完成后再进行的,所以在排序前结果集的所有数据会放在服务器进程对应的pga内存上,如果服务器进程对应的pga内存上放不下所有的结果集,即pga内存放满结果集的数据行后,新来的结果集的数据行会放在数据文件上的临时表空间上,故而会产生一些属于"direct path readtemp"事件的物理读。当排序时,排序的过程也会产生一些属于"direct path readtemp"事件的物理读。排序后,当pga内存上的结果集的一部分数据行传送给客户端后,要读取结果集存放在临时表空间上的数据行到pga内存上,再将之传送给客户端,这样也会产生一些属于"direct path readtemp"事件的物理读。(只是推测,未证实)
最后,要注意的是,如果排序工作需要通过读写临时表空间来完成的话,你会发现,无论该语句被重复执行
多少次,"direct  path  read  temp"导致的物理读始终不会减少,因为这种“直接读”是不会将数据读入 buffer
cache 中(读入也没有任何意义)去,而是将数据读入 pga内存。因而它所导致的物理读不会被统计到逻辑读当中去。


七、索引扫描


通过前面的分析, 我们基本上可以了解查询中一些基本操作(如排序,一致性读,当前模式读)所导致的逻辑读是如何得出的。 我们最后再分析
一下索引扫描(注意:索引全扫描和全表扫描的行为相似,不在这里的分析范围之内)的情况。

13.  HELLODBA.COM>select *>
14.   
15.   
16.  Execution Plan   
17.  ----------------------------------------------------------   
18.  Plan>32.   
33.   
34.  Statistics
35.  ----------------------------------------------------------   
36.            0 >

由此我们可以说下物理读的数量为什么为5?因为逻辑读时虽然读取次数为6,但是有两次是读取同一个块的(2 次读取叶子节点 (尽管是同一个节点)),所以物理读只要读取五个块即可,1 次读取分支节点+1次读取叶子节点 +3 次通过 ROWID 访问表= 5 次物理读。

物理读的次数就是等于读取的块的次数。

我们还注意到,kdiixs 和 kdifxs pin 住的同一个叶子节点,那为什么会产生 2 次呢?我们再通过一个大表
返回更多数据的测试就能明白了:

我们不难发现,和全表扫描一样,也由于 arraysize 的作用:第一次 kdiixs 后读取表的第一条记录以获取
字段信息;之后每获取 15 条(arraysize)记录就会将数据返回客户端,
然后再次读取索引节点。


附加:数据文件上空的数据块也是要先被读取到内存上后,才能被比如DML操作使用的。数据文件上空的数据块也是要先被读取到内存上,相当于该DML操作在数据文件上占用了该空的数据块。

详见:《Oracle逻 辑读写深入分析》

对其跟踪文件如何分析还不很理解。


你可能感兴趣的:(一致性读,当前模式读,undo机制)