代码改变世界

Asktom:Tom对Consitent Get的解释及分析

2011-08-31 16:27  Tracy.  阅读(317)  评论(0编辑  收藏  举报

You Asked

Tom:

create table test( a int);
begin
for i in 1..10000 loop
insert into test values (i);
end loop;
end;

set autotrace on
select count(0) from test;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   SORT (AGGREGATE)
   2    1     TABLE ACCESS (FULL) OF 'TEST'

Statistics
----------------------------------------------------------
          0  recursive calls
          4  db block gets
         20  consistent gets
          0  physical reads
          0  redo size
        369  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed


select * from test where a=10;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (FULL) OF 'TEST'




Statistics
----------------------------------------------------------
          0  recursive calls
          4  db block gets
         21  consistent gets
          0  physical reads
          0  redo size
        360  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed


select * from test;
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (FULL) OF 'TEST'




Statistics
----------------------------------------------------------
          0  recursive calls
          4  db block gets
        686  consistent gets
          0  physical reads
          0  redo size
     185864  bytes sent via SQL*Net to client
      74351  bytes received via SQL*Net from client
        668  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      10001  rows processed



question:
1.
when query count(0), what exactly did oracle do? since we don't have index, did oracle 
went to every block and query the number?
if so, why consistent gets is much lower that select * from test?
both are full scan table.

2.
why select * from test have much higher consistent gets than
select * from test where a=10?
since there are no index, oracle need to go to every block to get the value, it should be 
same consistent gets, although the first one return more rows, but it does't matter.

Regards,

 

and we said...

q1) select count(0) is just like 

select count(*)
  from ( select 0 from t )
/

yes, oracle went to each block to find the rows to give you a zero

q2) its a side effect of your arraysize.  You must have an 8k blocksize cause I 
reproduced this exactly.

We were expecting about 20 consistent gets right?  Well, the default array size in 
sqlplus is 15 rows / fetch.  10000/15 = 666.66666.  Well, 666+20 = 686 -- whoah there -- 
686 is our consistent gets!

Thats what happened.  When you fetched 15 rows, Oracle paused, gave you the data.  When 
you went back to get the next 15, it got the buffer again to resume your query. 

Watch what happens with different array sizes, starting with 15:


ops$tkyte@8i> select * from test;

10000 rows selected.

Statistics
----------------------------------------------------------
          0  recursive calls
          4  db block gets
        686  consistent gets
          0  physical reads
          0  redo size
     108813  bytes sent via SQL*Net to client
      46265  bytes received via SQL*Net from client
        668  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      10000  rows processed

ops$tkyte@8i> set arraysize 1000
ops$tkyte@8i> select * from test;

10000 rows selected.


Statistics
----------------------------------------------------------
          0  recursive calls
          4  db block gets
         30  consistent gets
          0  physical reads
          0  redo size
      86266  bytes sent via SQL*Net to client
        942  bytes received via SQL*Net from client
         11  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      10000  rows processed

ops$tkyte@8i> set arraysize 5000
ops$tkyte@8i> select * from test;

10000 rows selected.


Statistics
----------------------------------------------------------
          0  recursive calls
          4  db block gets
         22  consistent gets
          0  physical reads
          0  redo size
     149793  bytes sent via SQL*Net to client
        382  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      10000  rows processed



Other interesting thing to note is that as the array size gets too large -- the amount of 
data transferred goes up.  there is a diminishing marginal return on the array size so 
don't go OVERBOARD.

followup to comment oneSet the arraysize to some constant as it will not matter for single row fetches whether 
it is too big but having it too small for lots of rows (NOT just full scans -- lots of 
rows) does impact you.

Followup -Excellent. May 1, 2002 - 7am Central time zone:

The count(*) returns how many rows?  1.

The array size does even begin to kick in.  That statement is processed in ONE call.

Also, the count(*) may very well *never be touching the table*.  It might be just using an index.

Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t as select * from all_objects;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> 
ops$tkyte@ORA817DEV.US.ORACLE.COM> 
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec show_space( 'T' )
Free Blocks.............................0
Total Blocks............................320
Total Bytes.............................2621440
Unused Blocks...........................4
Unused Bytes............................32768
Last Used Ext FileId....................7
Last Used Ext BlockId...................40969
Last Used Block.........................60

PL/SQL procedure successfully completed.

so the table consumes about 316 blocks..


ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;
22905 rows selected.


Statistics
----------------------------------------------------------
          0  recursive calls
         12  db block gets
       1823  consistent gets
         46  physical reads
          0  redo size
    2704019  bytes sent via SQL*Net to client
     169811  bytes received via SQL*Net from client
       1528  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      22905  rows processed


22905/15 = 1527+316 = 1843 which is about our consistent gets (what we were expecting)....

ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from t;


Statistics
----------------------------------------------------------
          0  recursive calls
         12  db block gets
        319  consistent gets
         46  physical reads
          0  redo size
        369  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

And the count(*), since it was done in a SINGLE CALL -- no arraysize -- only did a consistent get 
on each block ONE TIME.  Hence it did 319 consistent gets (which is about the size of the table)

Now, adding a primary key to the table:


ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t add constraint t_pk primary key(object_id);

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> analyze table t compute statistics;

Table analyzed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from t;


Statistics
----------------------------------------------------------
         79  recursive calls
          4  db block gets
         82  consistent gets
          1  physical reads
          0  redo size
        369  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          1  rows processed

we can see that the consistent gets go way down.  why?  because the optimizer counted the rows in 
the index, not the table in this case.
 
db_block_gets + consistent_gets = LOGICAL IO
physical_reads                  = PHYSICAL IO
A consistent get is a block gotten in read consistent mode (point in time mode).  It MAY or MAY NOT 
involve reconstruction (rolling back).

Db Block Gets are CURRENT mode gets -- blocks read "as of right now".


Some blocks are processed more then once, yes, the blocks will have more then 1 consistent read in 
the process.  Consider:


ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t as select * from all_objects;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec show_space( 'T')
Free Blocks.............................0
Total Blocks............................320
Total Bytes.............................2621440
Unused Blocks...........................4
Unused Bytes............................32768
Last Used Ext FileId....................7
Last Used Ext BlockId...................40969
Last Used Block.........................60

PL/SQL procedure successfully completed.

Table has 316 blocks, 22,908 rows..


ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly statistics;

ops$tkyte@ORA817DEV.US.ORACLE.COM> set arraysize 15
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;
22908 rows selected.

here with an array size of 15, we expect

22908/15 + 316 = 1843 consistent mode gets.  db block gets -- they were for performing the FULL 
SCAN, they had nothing to do with the data itself we selected


Statistics
----------------------------------------------------------
          0  recursive calls
         12  db block gets
       1824  consistent gets
        170  physical reads
          0  redo size
    2704448  bytes sent via SQL*Net to client
     169922  bytes received via SQL*Net from client
       1529  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      22908  rows processed

ops$tkyte@ORA817DEV.US.ORACLE.COM> set arraysize 100
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

22908 rows selected.

Now, with 100 as the arraysize, we expect

22908/100 + 316 = 545 consistent mode gets.

Statistics
----------------------------------------------------------
          0  recursive calls
         12  db block gets
        546  consistent gets
        180  physical reads
          0  redo size
    2557774  bytes sent via SQL*Net to client
      25844  bytes received via SQL*Net from client
        231  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      22908  rows processed

ops$tkyte@ORA817DEV.US.ORACLE.COM> set arraysize 1000
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

22908 rows selected.

now, with arraysize = 1000, we expect:

22908/1000+316 = 338 consistent mode gets...

Statistics
----------------------------------------------------------
          0  recursive calls
         12  db block gets
        342  consistent gets
        222  physical reads
          0  redo size
    2534383  bytes sent via SQL*Net to client
       2867  bytes received via SQL*Net from client
         24  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      22908  rows processed




so yes, the blocks are gotten in consistent mode MORE THEN ONCE when the array fetch size is 
lower then the number of rows to be retrieved in this case

This is because we'll be 1/2 way through processing a block -- have enough rows to return to the 
client -- and we'll give UP that block.  When they ask for the next N rows, we need to get that 
halfway processed block again and pick up where we left off.
Sorry --- I've explained this beyond my best ability at this point.  I've explained many many 
times.  This is my last try:

The table has 20 blocks in it.
You would expect 20 consistent gets on a full scan.
But, with an array size of 15 we will revisit some blocks MORE THEN ONCE.

In fact, we might revisit upto 10000/15 = 666 blocks more then ONCE.

This is because we "start" and "stop" the query in the middle of a block.  (see the two followups 
immediately above this).


If you full scan a table T that has N blocks and R rows and you use an array fetch size of A, we 
will typically perform the following number of consistent gets:

N + R/A


We have to read N blocks (that should be obvious)
We might revisit R/A blocks more then once (since we stop in the middle)