<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
		>
<channel>
	<title>Comments on: _SMALL_TABLE_THRESHOLD Parameter and Buffer Cache &#8211; What is Wrong with this Quote?</title>
	<atom:link href="http://hoopercharles.wordpress.com/2010/06/17/_small_table_threshold-parameter-and-buffer-cache-what-is-wrong-with-this-quote/feed/" rel="self" type="application/rss+xml" />
	<link>http://hoopercharles.wordpress.com/2010/06/17/_small_table_threshold-parameter-and-buffer-cache-what-is-wrong-with-this-quote/</link>
	<description>Miscellaneous Random Oracle Topics: Stop, Think, ... Understand</description>
	<lastBuildDate>Mon, 13 May 2013 14:10:06 +0000</lastBuildDate>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
	<item>
		<title>By: talebzadeh</title>
		<link>http://hoopercharles.wordpress.com/2010/06/17/_small_table_threshold-parameter-and-buffer-cache-what-is-wrong-with-this-quote/#comment-4379</link>
		<dc:creator><![CDATA[talebzadeh]]></dc:creator>
		<pubDate>Tue, 07 Feb 2012 15:00:46 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=2663#comment-4379</guid>
		<description><![CDATA[Charles,

No doubt when buffer cache is busy, direct path reads (DPR) will be faster than conventional path reads (CPR). As suggested, I tried that as well. Exactly the same code on identical tables on different schemas. I started running the query in second schema and then I kicked off the query in the main schema. With DPR disabled, CPR results were slower compared to doing exactly the same concurrent run tests with DPR. 

My current argument is that the optimiser costing will alway favour DPR to CPR when a table scan of &gt; 10% of buffer cache is involved. In other words it does not matter how much free buffers are there (case a single session running nothing else), the run time engine will perform DPR and will always put the table in the LRU end of the chain to be discarded immediately. 

So in summary with DPR, any table &gt; 10% of buffer cache will be fetch and discard and will not be promoted to hot area. 

I trust that this makes sense.

Cheers,

Mich]]></description>
		<content:encoded><![CDATA[<p>Charles,</p>
<p>No doubt when buffer cache is busy, direct path reads (DPR) will be faster than conventional path reads (CPR). As suggested, I tried that as well. Exactly the same code on identical tables on different schemas. I started running the query in second schema and then I kicked off the query in the main schema. With DPR disabled, CPR results were slower compared to doing exactly the same concurrent run tests with DPR. </p>
<p>My current argument is that the optimiser costing will alway favour DPR to CPR when a table scan of &gt; 10% of buffer cache is involved. In other words it does not matter how much free buffers are there (case a single session running nothing else), the run time engine will perform DPR and will always put the table in the LRU end of the chain to be discarded immediately. </p>
<p>So in summary with DPR, any table &gt; 10% of buffer cache will be fetch and discard and will not be promoted to hot area. </p>
<p>I trust that this makes sense.</p>
<p>Cheers,</p>
<p>Mich</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Charles Hooper</title>
		<link>http://hoopercharles.wordpress.com/2010/06/17/_small_table_threshold-parameter-and-buffer-cache-what-is-wrong-with-this-quote/#comment-4378</link>
		<dc:creator><![CDATA[Charles Hooper]]></dc:creator>
		<pubDate>Tue, 07 Feb 2012 11:54:40 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=2663#comment-4378</guid>
		<description><![CDATA[Hi Mich,

I think that this is the OTN thread where we discussed the topic of serial direct path reads (direct path read waits) versus conventional, buffered reads (db file scattered read waits) for table scans:
https://forums.oracle.com/forums/message.jspa?messageID=10077663

My opening paragraph in that thread stated:

&lt;blockquote&gt;
... Keep in mind that the numbers that you are seeing are slightly distorted due to the fact that the buffer cache was empty when the test started. To understand how this may affect the test results, see the first comment on this page: http://dioncho.wordpress.com/2009/04/22/strong-doubt-on-small-table_small_table_threshold/
&lt;/blockquote&gt;

In short, you saw the significant 20% performance improvement with db file scattered reads because the buffer cache was empty at the start of the test, which is unlikely in a production environment.  If you want to test this theory:
&lt;pre&gt;
CREATE TABLE TDASH2 AS SELECT * FROM TDASH;
&lt;/pre&gt;

Bounce the database, execute a modified version of your script:
&lt;pre&gt;
DECLARE
        type array is table of tdash%ROWTYPE index by binary_integer;
        l_data array;
        l_rec tdash%rowtype;
BEGIN
        SELECT
                a.*
                ,RPAD(&#039;*&#039;,4000,&#039;*&#039;) AS PADDING1
                ,RPAD(&#039;*&#039;,4000,&#039;*&#039;) AS PADDING2
        BULK COLLECT INTO
        l_data
        FROM ALL_OBJECTS a;

        FOR rs IN 1 .. 100
        LOOP
                BEGIN
                        SELECT * INTO l_rec FROM TDASH2 WHERE object_id = l_data(rs).object_id;
                EXCEPTION
                  WHEN NO_DATA_FOUND THEN NULL;
                END;
        END LOOP;
END;
&lt;/pre&gt;

Finally, execute your script with and without serial direct path reads enabled.  You should now see that the buffer cache had less of an opportunity to cache frequently accessed blocks read during the full table scan of your tdash table, and you should find that the serial direct path read now not only uses less CPU, but also requires less elapsed time for its execution than the conventional buffered reads.

Jonathan, as usual, has provided excellent information in the comment section of this blog article, and on the OTN thread, and has mentioned items that I had not considered.  For example, I did not consider that your cacher table example would result in chained rows (out of curiosity, I might test this just to see if I am able to identify the chained rows - I am assuming that Jonathan noticed that counting 67,500 rows required 136,006 consistent gets during the COUNT(*) FROM CACHER;).]]></description>
		<content:encoded><![CDATA[<p>Hi Mich,</p>
<p>I think that this is the OTN thread where we discussed the topic of serial direct path reads (direct path read waits) versus conventional, buffered reads (db file scattered read waits) for table scans:<br />
<a href="https://forums.oracle.com/forums/message.jspa?messageID=10077663" rel="nofollow">https://forums.oracle.com/forums/message.jspa?messageID=10077663</a></p>
<p>My opening paragraph in that thread stated:</p>
<blockquote><p>
&#8230; Keep in mind that the numbers that you are seeing are slightly distorted due to the fact that the buffer cache was empty when the test started. To understand how this may affect the test results, see the first comment on this page: <a href="http://dioncho.wordpress.com/2009/04/22/strong-doubt-on-small-table_small_table_threshold/" rel="nofollow">http://dioncho.wordpress.com/2009/04/22/strong-doubt-on-small-table_small_table_threshold/</a>
</p></blockquote>
<p>In short, you saw the significant 20% performance improvement with db file scattered reads because the buffer cache was empty at the start of the test, which is unlikely in a production environment.  If you want to test this theory:</p>
<pre>
CREATE TABLE TDASH2 AS SELECT * FROM TDASH;
</pre>
<p>Bounce the database, execute a modified version of your script:</p>
<pre>
DECLARE
        type array is table of tdash%ROWTYPE index by binary_integer;
        l_data array;
        l_rec tdash%rowtype;
BEGIN
        SELECT
                a.*
                ,RPAD('*',4000,'*') AS PADDING1
                ,RPAD('*',4000,'*') AS PADDING2
        BULK COLLECT INTO
        l_data
        FROM ALL_OBJECTS a;

        FOR rs IN 1 .. 100
        LOOP
                BEGIN
                        SELECT * INTO l_rec FROM TDASH2 WHERE object_id = l_data(rs).object_id;
                EXCEPTION
                  WHEN NO_DATA_FOUND THEN NULL;
                END;
        END LOOP;
END;
</pre>
<p>Finally, execute your script with and without serial direct path reads enabled.  You should now see that the buffer cache had less of an opportunity to cache frequently accessed blocks read during the full table scan of your tdash table, and you should find that the serial direct path read now not only uses less CPU, but also requires less elapsed time for its execution than the conventional buffered reads.</p>
<p>Jonathan, as usual, has provided excellent information in the comment section of this blog article, and on the OTN thread, and has mentioned items that I had not considered.  For example, I did not consider that your cacher table example would result in chained rows (out of curiosity, I might test this just to see if I am able to identify the chained rows &#8211; I am assuming that Jonathan noticed that counting 67,500 rows required 136,006 consistent gets during the COUNT(*) FROM CACHER;).</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: talebzadeh</title>
		<link>http://hoopercharles.wordpress.com/2010/06/17/_small_table_threshold-parameter-and-buffer-cache-what-is-wrong-with-this-quote/#comment-4377</link>
		<dc:creator><![CDATA[talebzadeh]]></dc:creator>
		<pubDate>Tue, 07 Feb 2012 09:36:07 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=2663#comment-4377</guid>
		<description><![CDATA[Hi,

If you recall a while back we discussed direct path reads vs conventional reads in OTN. The query was pretty simple:
&lt;pre&gt;
DECLARE
        type array is table of tdash%ROWTYPE index by binary_integer;
        l_data array;
        l_rec tdash%rowtype;
BEGIN
        SELECT
                a.*
                ,RPAD(&#039;*&#039;,4000,&#039;*&#039;) AS PADDING1
                ,RPAD(&#039;*&#039;,4000,&#039;*&#039;) AS PADDING2
        BULK COLLECT INTO
        l_data
        FROM ALL_OBJECTS a;
 
        DBMS_MONITOR.SESSION_TRACE_ENABLE ( waits=&gt;true );
        FOR rs IN 1 .. 100
        LOOP
                BEGIN
                        SELECT * INTO l_rec FROM tdash WHERE object_id = l_data(rs).object_id;
                EXCEPTION
                  WHEN NO_DATA_FOUND THEN NULL;
                END;
        END LOOP;
END;
&lt;/pre&gt;

The base table tdash had 1.7 million rows and I tried using direct path read (default) and db scattered file read. with conventional path it was 20% faster.
At that time the argument was that the run time engine preferred direct path read to save CPU usage in multi-user environment. Fair enough. My test was every time after reboot, one run only. However, we are looping over table 100 times fatching rows matching object_id. tkprof output showed:

for direct path read:
&lt;pre&gt;
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          7         47          0           0
Execute    100      0.00       0.00          2         51          0           0
Fetch      100     10.52    6358.83  194142802  194831012          0         100
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total      201     10.53    6358.84  194142811  194831110          0         100

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 96  (SSDTESTER)   (recursive depth: 1)

Rows     Row Source Operation
-------  ---------------------------------------------------
      1  TABLE ACCESS FULL TDASH (cr=1948310 pr=1941430 pw=0 time=0 us cost=526908 size=8091 card=1)
&lt;/pre&gt;

There was full table scan for every read in the loop. With conventional path we had
&lt;pre&gt;
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute    100      0.02       0.00          0          0          0           0
Fetch      100    150.74    5103.34  131458857  194490912          0         100
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total      201    150.76    5103.34  131458857  194490912          0         100
&lt;/pre&gt;

So we seems to have 194142811 - 131458857 = 62683954 or around 32% less physical reads. Table tdash had 1,729,204  rows in 2,709,153 blocks and Table size as percent of buffer size was 123%.  So with more caching and less waits that 20% performance gain may be explained now?

Cheers,

Mich]]></description>
		<content:encoded><![CDATA[<p>Hi,</p>
<p>If you recall a while back we discussed direct path reads vs conventional reads in OTN. The query was pretty simple:</p>
<pre>
DECLARE
        type array is table of tdash%ROWTYPE index by binary_integer;
        l_data array;
        l_rec tdash%rowtype;
BEGIN
        SELECT
                a.*
                ,RPAD('*',4000,'*') AS PADDING1
                ,RPAD('*',4000,'*') AS PADDING2
        BULK COLLECT INTO
        l_data
        FROM ALL_OBJECTS a;
 
        DBMS_MONITOR.SESSION_TRACE_ENABLE ( waits=&gt;true );
        FOR rs IN 1 .. 100
        LOOP
                BEGIN
                        SELECT * INTO l_rec FROM tdash WHERE object_id = l_data(rs).object_id;
                EXCEPTION
                  WHEN NO_DATA_FOUND THEN NULL;
                END;
        END LOOP;
END;
</pre>
<p>The base table tdash had 1.7 million rows and I tried using direct path read (default) and db scattered file read. with conventional path it was 20% faster.<br />
At that time the argument was that the run time engine preferred direct path read to save CPU usage in multi-user environment. Fair enough. My test was every time after reboot, one run only. However, we are looping over table 100 times fatching rows matching object_id. tkprof output showed:</p>
<p>for direct path read:</p>
<pre>
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          7         47          0           0
Execute    100      0.00       0.00          2         51          0           0
Fetch      100     10.52    6358.83  194142802  194831012          0         100
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total      201     10.53    6358.84  194142811  194831110          0         100

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 96  (SSDTESTER)   (recursive depth: 1)

Rows     Row Source Operation
-------  ---------------------------------------------------
      1  TABLE ACCESS FULL TDASH (cr=1948310 pr=1941430 pw=0 time=0 us cost=526908 size=8091 card=1)
</pre>
<p>There was full table scan for every read in the loop. With conventional path we had</p>
<pre>
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute    100      0.02       0.00          0          0          0           0
Fetch      100    150.74    5103.34  131458857  194490912          0         100
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total      201    150.76    5103.34  131458857  194490912          0         100
</pre>
<p>So we seems to have 194142811 &#8211; 131458857 = 62683954 or around 32% less physical reads. Table tdash had 1,729,204  rows in 2,709,153 blocks and Table size as percent of buffer size was 123%.  So with more caching and less waits that 20% performance gain may be explained now?</p>
<p>Cheers,</p>
<p>Mich</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: talebzadeh</title>
		<link>http://hoopercharles.wordpress.com/2010/06/17/_small_table_threshold-parameter-and-buffer-cache-what-is-wrong-with-this-quote/#comment-4376</link>
		<dc:creator><![CDATA[talebzadeh]]></dc:creator>
		<pubDate>Mon, 06 Feb 2012 11:40:28 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=2663#comment-4376</guid>
		<description><![CDATA[Hi Jonathan,

Many thanks for your input.  To test the case again I decided to create the cacher table with PCTFREE = 0 as follows:
&lt;pre&gt;
create table cacher(c1 char(2000), c2 char(2000), c3 char(2000), c4 char(2000))PCTFREE 0
nologging;
insert /*+ append */ into cacher
select
          RPAD(&#039;*&#039;,2000)
         ,RPAD(&#039;*&#039;,2000)
         ,RPAD(&#039;*&#039;,2000)
         ,RPAD(&#039;*&#039;,2000)
from dual
connect by level&lt;=1350000;
exec dbms_stats.gather_table_stats(ownname=&gt;user,tabname=&gt;&#039;CACHER&#039;,estimate_percent=&gt;100);
commit;
alter system flush buffer_cache;
EXEC DBMS_MONITOR.SESSION_TRACE_ENABLE ( waits=&gt;true, plan_stat=&gt;&#039;ALL_EXECUTIONS&#039; );
ALTER SESSION SET EVENTS &#039;10949 trace name context forever, level 1&#039;  -- No Direct path read ;
select count(*) from cacher;
select count(*) from cacher;
ALTER SESSION SET TRACEFILE_IDENTIFIER = &#039;catcher&#039;;
EXEC DBMS_MONITOR.SESSION_TRACE_ENABLE ( waits=&gt;true, plan_stat=&gt;&#039;ALL_EXECUTIONS&#039; );
select count(*) from cacher;
&lt;/pre&gt;
With that in mind In the case above we have a table with the following properties. I also added the simple ratios of buffer cache to block size to give an indication of how many blocks fit in to this cache
&lt;pre&gt;
Parameter                      buffer cache size/MB blocks fit in this cache
------------------------------ -------------------- ------------------------
buffer_cache                                 11,072                1,417,216


Small table threshold at 2% of buffer cache size/MB Small table block limit
--------------------------------------------------- -----------------------
                                                221                  28,344

name                                     value                isdefault            description
---------------------------------------- -------------------- -------------------- -------------------------------------------------------
_small_table_threshold                   27295                TRUE                 lower threshold level of table size for direct reads
&lt;/pre&gt;

My table details
&lt;pre&gt;
OWNER                            TABLE_NAME                               rows block size/KB       blocks avg free space/KB Table size/MB
-------------------------------- -------------------------------- ------------ ------------- ------------ ----------------- -------------
SSDTESTER                        CACHER                              1,350,000             8    1,351,819                 0        10,552


Table size as percent of buffer size
------------------------------------
                               95.30
&lt;/pre&gt;

So almost one block per row and no free space. Tkprof reports shows:
&lt;pre&gt;
select count(*)
from
 cacher


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      1.31       1.32          2    1350039          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      1.31       1.32          2    1350039          0           1

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 96

Rows     Row Source Operation
-------  ---------------------------------------------------
      1  SORT AGGREGATE (cr=1350039 pr=2 pw=0 time=0 us)
1350000   TABLE ACCESS FULL CACHER (cr=1350039 pr=2 pw=0 time=2925915 us cost=366427 size=0 card=1350000)
&lt;/pre&gt;

So there we go 135,0039 CR with 2 physical reads for a  table that is around 95% of the buffer cache.

Obviously what really matters is the default optimizer behaviour when choosing ‘direct path read’. Let us what it shows now:

With direct path read the magic percentage of ~ 10% holds. Let us look at the below case. This table at 9.2% of buffer size in. Anything above is recycled

My table details
&lt;pre&gt;
OWNER                            TABLE_NAME                               rows block size/KB       blocks avg free space/KB Table size/MB
-------------------------------- -------------------------------- ------------ ------------- ------------ ----------------- -------------
SSDTESTER                        CACHER                                130,000             8      130,612                 0         1,016


Table size as percent of buffer size
------------------------------------
                                9.18

Table block size/threshold limit 
------------------------------- 
                              5 



select count(*)
from
 cacher


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.11       0.11          0     130022          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.11       0.11          0     130022          0           1

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 96

Rows     Row Source Operation
-------  ---------------------------------------------------
      1  SORT AGGREGATE (cr=130022 pr=0 pw=0 time=0 us)
 130000   TABLE ACCESS FULL CACHER (cr=130022 pr=0 pw=0 time=252448 us cost=35406 size=0 card=130000)
&lt;/pre&gt;

I believe now that you assertions about  10% boundary below which repeating a scan WILL increment the touch count holds as per optimizer use of direct path reads. For conventional path reads if table fits into the cache then Oracle allows that. 

In a way it perhaps explains why Oracle prefers direct path reads to conventional reads for FTS. I think because Oracle has more control in place on the use of PGA than SGA.. It also shows that threshold limit of 2% does not mean much. 


Regards,

Mich]]></description>
		<content:encoded><![CDATA[<p>Hi Jonathan,</p>
<p>Many thanks for your input.  To test the case again I decided to create the cacher table with PCTFREE = 0 as follows:</p>
<pre>
create table cacher(c1 char(2000), c2 char(2000), c3 char(2000), c4 char(2000))PCTFREE 0
nologging;
insert /*+ append */ into cacher
select
          RPAD('*',2000)
         ,RPAD('*',2000)
         ,RPAD('*',2000)
         ,RPAD('*',2000)
from dual
connect by level&lt;=1350000;
exec dbms_stats.gather_table_stats(ownname=&gt;user,tabname=&gt;'CACHER',estimate_percent=&gt;100);
commit;
alter system flush buffer_cache;
EXEC DBMS_MONITOR.SESSION_TRACE_ENABLE ( waits=&gt;true, plan_stat=&gt;'ALL_EXECUTIONS' );
ALTER SESSION SET EVENTS '10949 trace name context forever, level 1'  -- No Direct path read ;
select count(*) from cacher;
select count(*) from cacher;
ALTER SESSION SET TRACEFILE_IDENTIFIER = 'catcher';
EXEC DBMS_MONITOR.SESSION_TRACE_ENABLE ( waits=&gt;true, plan_stat=&gt;'ALL_EXECUTIONS' );
select count(*) from cacher;
</pre>
<p>With that in mind In the case above we have a table with the following properties. I also added the simple ratios of buffer cache to block size to give an indication of how many blocks fit in to this cache</p>
<pre>
Parameter                      buffer cache size/MB blocks fit in this cache
------------------------------ -------------------- ------------------------
buffer_cache                                 11,072                1,417,216


Small table threshold at 2% of buffer cache size/MB Small table block limit
--------------------------------------------------- -----------------------
                                                221                  28,344

name                                     value                isdefault            description
---------------------------------------- -------------------- -------------------- -------------------------------------------------------
_small_table_threshold                   27295                TRUE                 lower threshold level of table size for direct reads
</pre>
<p>My table details</p>
<pre>
OWNER                            TABLE_NAME                               rows block size/KB       blocks avg free space/KB Table size/MB
-------------------------------- -------------------------------- ------------ ------------- ------------ ----------------- -------------
SSDTESTER                        CACHER                              1,350,000             8    1,351,819                 0        10,552


Table size as percent of buffer size
------------------------------------
                               95.30
</pre>
<p>So almost one block per row and no free space. Tkprof reports shows:</p>
<pre>
select count(*)
from
 cacher


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      1.31       1.32          2    1350039          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      1.31       1.32          2    1350039          0           1

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 96

Rows     Row Source Operation
-------  ---------------------------------------------------
      1  SORT AGGREGATE (cr=1350039 pr=2 pw=0 time=0 us)
1350000   TABLE ACCESS FULL CACHER (cr=1350039 pr=2 pw=0 time=2925915 us cost=366427 size=0 card=1350000)
</pre>
<p>So there we go 135,0039 CR with 2 physical reads for a  table that is around 95% of the buffer cache.</p>
<p>Obviously what really matters is the default optimizer behaviour when choosing ‘direct path read’. Let us what it shows now:</p>
<p>With direct path read the magic percentage of ~ 10% holds. Let us look at the below case. This table at 9.2% of buffer size in. Anything above is recycled</p>
<p>My table details</p>
<pre>
OWNER                            TABLE_NAME                               rows block size/KB       blocks avg free space/KB Table size/MB
-------------------------------- -------------------------------- ------------ ------------- ------------ ----------------- -------------
SSDTESTER                        CACHER                                130,000             8      130,612                 0         1,016


Table size as percent of buffer size
------------------------------------
                                9.18

Table block size/threshold limit 
------------------------------- 
                              5 



select count(*)
from
 cacher


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.11       0.11          0     130022          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.11       0.11          0     130022          0           1

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 96

Rows     Row Source Operation
-------  ---------------------------------------------------
      1  SORT AGGREGATE (cr=130022 pr=0 pw=0 time=0 us)
 130000   TABLE ACCESS FULL CACHER (cr=130022 pr=0 pw=0 time=252448 us cost=35406 size=0 card=130000)
</pre>
<p>I believe now that you assertions about  10% boundary below which repeating a scan WILL increment the touch count holds as per optimizer use of direct path reads. For conventional path reads if table fits into the cache then Oracle allows that. </p>
<p>In a way it perhaps explains why Oracle prefers direct path reads to conventional reads for FTS. I think because Oracle has more control in place on the use of PGA than SGA.. It also shows that threshold limit of 2% does not mean much. </p>
<p>Regards,</p>
<p>Mich</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Jonathan Lewis</title>
		<link>http://hoopercharles.wordpress.com/2010/06/17/_small_table_threshold-parameter-and-buffer-cache-what-is-wrong-with-this-quote/#comment-4375</link>
		<dc:creator><![CDATA[Jonathan Lewis]]></dc:creator>
		<pubDate>Mon, 06 Feb 2012 09:16:55 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=2663#comment-4375</guid>
		<description><![CDATA[Mich,

An important point to consider in this test case is that every row in your table is chained (row length is more than 8,000, blocksize is 8KB,  pctfree seems to be at the default of 10, means you have about 7,200 bytes per block  - and then you can see that your table of 700,000 rows has used 1.4M blocks).

You may have discovered yet another type of boundary case for full tablescans, direct path reads, and caching, but you&#039;re not looking at the general case. You&#039;re query is simply select count(*), so Oracle shouldn&#039;t have to follow the chain for each row, but it&#039;s possible (and please take this as a flight of fancy at this point) that when Oracle reads a block which holds nothing but the target of a chained, it doesn&#039;t count it is a &quot;proper&quot; read, and therefore manages to exclude it from the more generic arithmetic.]]></description>
		<content:encoded><![CDATA[<p>Mich,</p>
<p>An important point to consider in this test case is that every row in your table is chained (row length is more than 8,000, blocksize is 8KB,  pctfree seems to be at the default of 10, means you have about 7,200 bytes per block  &#8211; and then you can see that your table of 700,000 rows has used 1.4M blocks).</p>
<p>You may have discovered yet another type of boundary case for full tablescans, direct path reads, and caching, but you&#8217;re not looking at the general case. You&#8217;re query is simply select count(*), so Oracle shouldn&#8217;t have to follow the chain for each row, but it&#8217;s possible (and please take this as a flight of fancy at this point) that when Oracle reads a block which holds nothing but the target of a chained, it doesn&#8217;t count it is a &#8220;proper&#8221; read, and therefore manages to exclude it from the more generic arithmetic.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: talebzadeh</title>
		<link>http://hoopercharles.wordpress.com/2010/06/17/_small_table_threshold-parameter-and-buffer-cache-what-is-wrong-with-this-quote/#comment-4374</link>
		<dc:creator><![CDATA[talebzadeh]]></dc:creator>
		<pubDate>Sun, 05 Feb 2012 22:22:08 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=2663#comment-4374</guid>
		<description><![CDATA[Hi,

I tried to do find out simply at what table size (as percent of buffer size), Oracle decides to cache table blocks. The approach I used was based on what Kyle Hailey devised. First

Let us get the environment: Oracle version installed is 11.2.0.1.0 -64 bit, block size is 8K.  buffer_cache = 11,072 MB
&lt;pre&gt;
Small table threshold at 2% of buffer cache size/MB Small table block limit
--------------------------------------------------- -----------------------
                                                221                  28,344
 
hidden parameter _small_table_threshold

name                                     value (blocks)               
---------------------------------------- -------------------
_small_table_threshold                   27295     
&lt;/pre&gt;

To force Oracle to choose the conventional path I disabled direct path read first
&lt;pre&gt;
drop table cacher;
create table cacher(c1 char(2000), c2 char(2000), c3 char(2000), c4 char(2000)) nologging;
insert /*+ append */ into cacher
select
          RPAD(&#039;*&#039;,2000)
         ,RPAD(&#039;*&#039;,2000)
         ,RPAD(&#039;*&#039;,2000)
         ,RPAD(&#039;*&#039;,2000)
from dual
connect by level user,tabname=&gt;&#039;CACHER&#039;,estimate_percent=&gt;100);
commit;
alter system flush buffer_cache;
ALTER SESSION SET EVENTS &#039;10949 trace name context forever, level 1&#039;  -- No Direct path read ;
select count(*) from cacher;
select count(*) from cacher;
set autot on stat
 select count(*) from cacher;
 set autot off;
&lt;/pre&gt;

When I run the above I get:
&lt;pre&gt;
  COUNT(*)
----------
    700000
 
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
    1400040  consistent gets
     224581  physical reads
          1  rows processed
&lt;/pre&gt;

OK so we are just above the threshold in here. At this stage table cacher details is
&lt;pre&gt;
OWNER                            TABLE_NAME                               rows block size/KB       blocks avg free space/KB Table size/MB
-------------------------------- -------------------------------- ------------ ------------- ------------ ----------------- -------------
SSDTESTER                        CACHER                                700,000             8    1,401,868                 0         5,471


Table size as percent of buffer size
------------------------------------
                               49.42
&lt;/pre&gt;

So at almost 50% we are seeing some discard activity. If I relax the size of table to be ~45% of buffer size the table is fully cached. 
&lt;pre&gt; 
 COUNT(*)
----------
    650000


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
    1300038  consistent gets
          0  physical reads
 
OWNER                            TABLE_NAME                               rows block size/KB       blocks avg free space/KB Table size/MB
-------------------------------- -------------------------------- ------------ ------------- ------------ ----------------- -------------
SSDTESTER                        CACHER                                650,000             8    1,301,762                 0         5,081


Table size as percent of buffer size
------------------------------------
                               45.89
&lt;/pre&gt;

So with conventional path and FTS when table size is roughly around 45% we have fully caching and block promotion to hot area

Now if we leave the optimiser to choose the default direct path read with FTS, we notice that up around 4.8% of buffer size, the table is fully cached.
&lt;pre&gt;
Example:

  COUNT(*)
----------
     67500


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
     135022  consistent gets
          0  physical reads
 
  OWNER                            TABLE_NAME                               rows block size/KB       blocks avg free space/KB Table size/MB
-------------------------------- -------------------------------- ------------ ------------- ------------ ----------------- -------------
SSDTESTER                        CACHER                                 67,500             8      135,620                 0           528


Table size as percent of buffer size
------------------------------------
                                4.77
&lt;/pre&gt;

Above this figure, the table blocks are put at LRU end and discarded. At around 4.8% of buffer size the table is read in and discarded every time. Tkprof output shows this.
&lt;pre&gt;
select count(*)
from
 cacher


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.00       4.02     136000     136006          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.00       4.02     136000     136006          0           1

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 96

Rows     Row Source Operation
-------  ---------------------------------------------------
      1  SORT AGGREGATE (cr=136006 pr=136000 pw=0 time=0 us)
  68000   TABLE ACCESS FULL CACHER (cr=136006 pr=136000 pw=0 time=179583 us cost=37034 size=0 card=68000)


Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  SQL*Net message to client                       2        0.00          0.00
  direct path read                             1073        0.00          3.79
  SQL*Net message from client                     2        0.00          0.00
&lt;/pre&gt;

So in summary as per default optimiser behaviour ‘direct path read’,the expectation is that any table below 5% of buffer cache is promoted to hot area. When the table size is larger than this figure blocks with full table scans go to LRU end and are fully discarded.

Regards,

Mich]]></description>
		<content:encoded><![CDATA[<p>Hi,</p>
<p>I tried to do find out simply at what table size (as percent of buffer size), Oracle decides to cache table blocks. The approach I used was based on what Kyle Hailey devised. First</p>
<p>Let us get the environment: Oracle version installed is 11.2.0.1.0 -64 bit, block size is 8K.  buffer_cache = 11,072 MB</p>
<pre>
Small table threshold at 2% of buffer cache size/MB Small table block limit
--------------------------------------------------- -----------------------
                                                221                  28,344
 
hidden parameter _small_table_threshold

name                                     value (blocks)               
---------------------------------------- -------------------
_small_table_threshold                   27295     
</pre>
<p>To force Oracle to choose the conventional path I disabled direct path read first</p>
<pre>
drop table cacher;
create table cacher(c1 char(2000), c2 char(2000), c3 char(2000), c4 char(2000)) nologging;
insert /*+ append */ into cacher
select
          RPAD('*',2000)
         ,RPAD('*',2000)
         ,RPAD('*',2000)
         ,RPAD('*',2000)
from dual
connect by level user,tabname=&gt;'CACHER',estimate_percent=&gt;100);
commit;
alter system flush buffer_cache;
ALTER SESSION SET EVENTS '10949 trace name context forever, level 1'  -- No Direct path read ;
select count(*) from cacher;
select count(*) from cacher;
set autot on stat
 select count(*) from cacher;
 set autot off;
</pre>
<p>When I run the above I get:</p>
<pre>
  COUNT(*)
----------
    700000
 
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
    1400040  consistent gets
     224581  physical reads
          1  rows processed
</pre>
<p>OK so we are just above the threshold in here. At this stage table cacher details is</p>
<pre>
OWNER                            TABLE_NAME                               rows block size/KB       blocks avg free space/KB Table size/MB
-------------------------------- -------------------------------- ------------ ------------- ------------ ----------------- -------------
SSDTESTER                        CACHER                                700,000             8    1,401,868                 0         5,471


Table size as percent of buffer size
------------------------------------
                               49.42
</pre>
<p>So at almost 50% we are seeing some discard activity. If I relax the size of table to be ~45% of buffer size the table is fully cached. </p>
<pre> 
 COUNT(*)
----------
    650000


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
    1300038  consistent gets
          0  physical reads
 
OWNER                            TABLE_NAME                               rows block size/KB       blocks avg free space/KB Table size/MB
-------------------------------- -------------------------------- ------------ ------------- ------------ ----------------- -------------
SSDTESTER                        CACHER                                650,000             8    1,301,762                 0         5,081


Table size as percent of buffer size
------------------------------------
                               45.89
</pre>
<p>So with conventional path and FTS when table size is roughly around 45% we have fully caching and block promotion to hot area</p>
<p>Now if we leave the optimiser to choose the default direct path read with FTS, we notice that up around 4.8% of buffer size, the table is fully cached.</p>
<pre>
Example:

  COUNT(*)
----------
     67500


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
     135022  consistent gets
          0  physical reads
 
  OWNER                            TABLE_NAME                               rows block size/KB       blocks avg free space/KB Table size/MB
-------------------------------- -------------------------------- ------------ ------------- ------------ ----------------- -------------
SSDTESTER                        CACHER                                 67,500             8      135,620                 0           528


Table size as percent of buffer size
------------------------------------
                                4.77
</pre>
<p>Above this figure, the table blocks are put at LRU end and discarded. At around 4.8% of buffer size the table is read in and discarded every time. Tkprof output shows this.</p>
<pre>
select count(*)
from
 cacher


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.00       4.02     136000     136006          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.00       4.02     136000     136006          0           1

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 96

Rows     Row Source Operation
-------  ---------------------------------------------------
      1  SORT AGGREGATE (cr=136006 pr=136000 pw=0 time=0 us)
  68000   TABLE ACCESS FULL CACHER (cr=136006 pr=136000 pw=0 time=179583 us cost=37034 size=0 card=68000)


Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  SQL*Net message to client                       2        0.00          0.00
  direct path read                             1073        0.00          3.79
  SQL*Net message from client                     2        0.00          0.00
</pre>
<p>So in summary as per default optimiser behaviour ‘direct path read’,the expectation is that any table below 5% of buffer cache is promoted to hot area. When the table size is larger than this figure blocks with full table scans go to LRU end and are fully discarded.</p>
<p>Regards,</p>
<p>Mich</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Plan is same, Wait profile is different ? &#171; Coskan&#8217;s Approach to Oracle</title>
		<link>http://hoopercharles.wordpress.com/2010/06/17/_small_table_threshold-parameter-and-buffer-cache-what-is-wrong-with-this-quote/#comment-3536</link>
		<dc:creator><![CDATA[Plan is same, Wait profile is different ? &#171; Coskan&#8217;s Approach to Oracle]]></dc:creator>
		<pubDate>Fri, 24 Jun 2011 10:42:36 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=2663#comment-3536</guid>
		<description><![CDATA[[...] the memory the buffer cache and small table threshold (for more information about this threshold Charles Hooper-great discussion with Jonathan Lewis on comments, Tanel Poder [...]]]></description>
		<content:encoded><![CDATA[<p>[...] the memory the buffer cache and small table threshold (for more information about this threshold Charles Hooper-great discussion with Jonathan Lewis on comments, Tanel Poder [...]</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Charles Hooper</title>
		<link>http://hoopercharles.wordpress.com/2010/06/17/_small_table_threshold-parameter-and-buffer-cache-what-is-wrong-with-this-quote/#comment-3007</link>
		<dc:creator><![CDATA[Charles Hooper]]></dc:creator>
		<pubDate>Sat, 26 Mar 2011 02:23:03 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=2663#comment-3007</guid>
		<description><![CDATA[Interesting... I will do a bit of experimentation with your test case.]]></description>
		<content:encoded><![CDATA[<p>Interesting&#8230; I will do a bit of experimentation with your test case.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Kyle Hailey</title>
		<link>http://hoopercharles.wordpress.com/2010/06/17/_small_table_threshold-parameter-and-buffer-cache-what-is-wrong-with-this-quote/#comment-3005</link>
		<dc:creator><![CDATA[Kyle Hailey]]></dc:creator>
		<pubDate>Fri, 25 Mar 2011 19:45:09 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=2663#comment-3005</guid>
		<description><![CDATA[Great point. My reaction was, &quot;wow, that probably explains it!&quot; but alas I tried and got the same results:
&lt;pre&gt;
 drop table cacher;
  create table cacher(c1 char(2000), c2 char(2000), c3 char(2000)) nologging;
  insert /*+ append */ into cacher
    select &#039;x&#039;, &#039;x&#039;, &#039;x&#039;
    from dual
       connect by level &lt;= 220000     ;
  EXEC DBMS_STATS.gather_table_stats(NULL, &#039;CACHER&#039;);
  commit;
  alter system flush buffer_cache;
  set autot on stat
  select count(*) from cacher;
  select count(*) from cacher;
  select count(*) from cacher;
  set autot off;
&lt;/pre&gt;
with
  _db_block_buffers                   273102
  _small_table_threshold              5462
and the results were:
&lt;pre&gt;
80% of buffer cache Statistics with stats gathered on table
----------------------------------------------------------
     220033  consistent gets
     220005  physical reads
----------------------------------------------------------
     220023  consistent gets
          0  physical reads
----------------------------------------------------------
     220023  consistent gets
          0  physical reads
&lt;/pre&gt;]]></description>
		<content:encoded><![CDATA[<p>Great point. My reaction was, &#8220;wow, that probably explains it!&#8221; but alas I tried and got the same results:</p>
<pre>
 drop table cacher;
  create table cacher(c1 char(2000), c2 char(2000), c3 char(2000)) nologging;
  insert /*+ append */ into cacher
    select 'x', 'x', 'x'
    from dual
       connect by level &lt;= 220000     ;
  EXEC DBMS_STATS.gather_table_stats(NULL, &#039;CACHER&#039;);
  commit;
  alter system flush buffer_cache;
  set autot on stat
  select count(*) from cacher;
  select count(*) from cacher;
  select count(*) from cacher;
  set autot off;
</pre>
<p>with<br />
  _db_block_buffers                   273102<br />
  _small_table_threshold              5462<br />
and the results were:</p>
<pre>
80% of buffer cache Statistics with stats gathered on table
----------------------------------------------------------
     220033  consistent gets
     220005  physical reads
----------------------------------------------------------
     220023  consistent gets
          0  physical reads
----------------------------------------------------------
     220023  consistent gets
          0  physical reads
</pre>
]]></content:encoded>
	</item>
	<item>
		<title>By: Charles Hooper</title>
		<link>http://hoopercharles.wordpress.com/2010/06/17/_small_table_threshold-parameter-and-buffer-cache-what-is-wrong-with-this-quote/#comment-3003</link>
		<dc:creator><![CDATA[Charles Hooper]]></dc:creator>
		<pubDate>Fri, 25 Mar 2011 18:37:34 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=2663#comment-3003</guid>
		<description><![CDATA[Hi Kyle,

Thanks for posting a follow up comment with a test case.  

I noticed in the test case script that table statistics were not collected for the CACHER table.  Do you think that skipping the statistics collection step might cause the runtime engine problems where it believes that the table is very small, resulting in too many of the blocks being cached, or do you think that the automatic dynamic sampling would have prevented problems like that?  I am more than a little surprised by the results you posted for 10.2.0.4.

In the above comments I see that Jonathan had intended to write up a blog article about the _SMALL_TABLE_THRESHOLD parameter - it appears that he had a chance to finish up that article:
http://jonathanlewis.wordpress.com/2011/03/24/small-tables/]]></description>
		<content:encoded><![CDATA[<p>Hi Kyle,</p>
<p>Thanks for posting a follow up comment with a test case.  </p>
<p>I noticed in the test case script that table statistics were not collected for the CACHER table.  Do you think that skipping the statistics collection step might cause the runtime engine problems where it believes that the table is very small, resulting in too many of the blocks being cached, or do you think that the automatic dynamic sampling would have prevented problems like that?  I am more than a little surprised by the results you posted for 10.2.0.4.</p>
<p>In the above comments I see that Jonathan had intended to write up a blog article about the _SMALL_TABLE_THRESHOLD parameter &#8211; it appears that he had a chance to finish up that article:<br />
<a href="http://jonathanlewis.wordpress.com/2011/03/24/small-tables/" rel="nofollow">http://jonathanlewis.wordpress.com/2011/03/24/small-tables/</a></p>
]]></content:encoded>
	</item>
</channel>
</rss>
