<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-2194093596149383181</id><updated>2011-09-13T04:19:48.143-07:00</updated><category term='Mutex'/><category term='Index Prefetch'/><category term='Function Based Index'/><category term='SQL*Trace'/><category term='Plan Diff'/><category term='Or-Expansion'/><category term='db file scattered read'/><category term='並列実行、V$PQ_TQSTAT'/><category term='errorstack'/><category term='X$テーブル'/><category term='PL/SQL'/><category term='Execute Immediate'/><category term='DBA_EXTENTS'/><category term='CLOB'/><category term='Cardinality Feedback'/><category term='Preliminary Connection'/><category term='troubleshooting'/><category term='MAXTRANS'/><category term='error_stack'/><category term='short_stack'/><category term='Alert Log'/><category term='ORA-01555'/><category term='Ref Cursor'/><category term='QB_NAME'/><category term='varchar2'/><category term='NO_NLJ_BATCHING'/><category term='Physical Reads'/><category term='AWR'/><category term='_nlj_batching_enabled'/><category term='oradebug dump errorstack、dbms_system、parameter'/><category term='Advanced Query Rewrite'/><category term='dbms_stats'/><category term='Filter Predicate'/><category term='LOB'/><category term='oradebug'/><category term='ashdump'/><category term='cursor: pin S on X'/><category term='Metalink、My Oracle Support'/><category term='Zero-Size Unusable Index、Table Expansion、_optimizer_table_expansion'/><category term='fetch'/><category term='parameters'/><category term='Batching NLJ'/><category term='block dump'/><category term='direct path read'/><category term='TKPROF'/><category term='パラメータ'/><category term='メモリリーク'/><category term='raw'/><category term='診断イベント'/><category term='HASH GROUP BY、SORT GROUP BY、_gby_hash_aggregation_enabled'/><category term='Logical Reads'/><category term='Oracle11gR2'/><category term='10949 event'/><category term='10032'/><category term='ORA-4030、'/><category term='Query Transformation'/><category term='V$SESSION_LONGOPS'/><category term='utl_raw'/><category term='Write Consistency'/><category term='INITRANS'/><category term='dbms_sql'/><category term='ORA-4030'/><category term='Native Full Outer Hash Join、ROWNUM'/><category term='APPEND'/><category term='10033'/><category term='テストケース'/><category term='バインド'/><category term='V$ROWCACHE_PARENT'/><category term='ORA-4031'/><category term='oradebug direct access'/><category term='PGA Heap Dump'/><category term='dbms_xplan.display'/><category term='ORA-01792'/><category term='dbms_job'/><category term='Global Hint'/><category term='_nlj_batching_misses_enabled'/><category term='実行計画'/><category term='10046、sql_trace'/><category term='dbms_pipe'/><category term='stored outline'/><category term='index tree dump'/><category term='PLAN_HASH_VALUE'/><category term='parse'/><category term='dbms_lock'/><category term='execute'/><category term='Parallel Query'/><category term='SQL*net message from dblink'/><category term='Row Cache Lock'/><category term='SQL*Plus'/><category term='Describe'/><category term='full table scan'/><category term='V$ビュー'/><category term='バインドピーキング'/><category term='Callstack'/><category term='dbms_advanced_rewrite'/><category term='Fixed Table'/><category term='Regular Expression'/><category term='SORT_AREA_SIZE'/><category term='Deferred Segment Creation'/><category term='db file parallel read'/><title type='text'>Dion Cho - Oracle Performance Storyteller</title><subtitle type='html'>Oracle性能に関する実用的な高級知識</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default?start-index=101&amp;max-results=100'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>111</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-7247411902931576123</id><published>2010-12-16T22:19:00.000-08:00</published><updated>2010-12-16T22:38:42.593-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Global Hint'/><category scheme='http://www.blogger.com/atom/ns#' term='dbms_xplan.display'/><category scheme='http://www.blogger.com/atom/ns#' term='QB_NAME'/><title type='text'>グローバルヒントの制約の面白いケース</title><content type='html'>次はグローバルヒントで結合順序を制御できないテストケースです。&lt;br /&gt;&lt;p /&gt;&lt;br /&gt;&lt;b&gt;1. &lt;/b&gt;表T1, T2, T3を作ります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;SQL&gt; create table t1(c1 number, c2 number);&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt; create table t2(c1 number, c2 number);&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt; create table t3(c1 number, c2 number);&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;2. &lt;/b&gt;グローバルヒントを使って、結合順序を&lt;b&gt;T1-&gt;T2-&gt;T3&lt;/b&gt;にしますが、なぜか予想通りに動作しません。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;SQL&gt; explain plan for&lt;br /&gt;  2  select * from&lt;br /&gt;  3  (&lt;br /&gt;  4  select&lt;br /&gt;  5        /*+ leading(v.t1 v.t2 t3) */&lt;br /&gt;  6        v.c1 as v_c1,&lt;br /&gt;  7        v.c2 as v_c2,&lt;br /&gt;  8        t3.c2 as t3_c2&lt;br /&gt;  9  from&lt;br /&gt; 10        (select&lt;br /&gt; 11         t1.c1,&lt;br /&gt; 12         t2.c2&lt;br /&gt; 13        from&lt;br /&gt; 14         t1, t2&lt;br /&gt; 15        where&lt;br /&gt; 16         t1.c1 = t2.c1) v,&lt;br /&gt; 17        t3&lt;br /&gt; 18  where&lt;br /&gt; 19        v.c1 = t3.c1&lt;br /&gt; 20  ) x&lt;br /&gt; 21  ;&lt;br /&gt;&lt;br /&gt;------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation             | Name | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT      |      |     1 |    65 |     7  (15)| 00:00:01 |&lt;br /&gt;|*  1 |  HASH JOIN            |      |     1 |    65 |     7  (15)| 00:00:01 |&lt;br /&gt;|   2 |   MERGE JOIN CARTESIAN|      |     1 |    52 |     4   (0)| 00:00:01 |&lt;br /&gt;|   3 |    TABLE ACCESS FULL  | T2   |     1 |    26 |     2   (0)| 00:00:01 |&lt;br /&gt;|   4 |    BUFFER SORT        |      |     1 |    26 |     2   (0)| 00:00:01 |&lt;br /&gt;|   5 |     TABLE ACCESS FULL | T3   |     1 |    26 |     2   (0)| 00:00:01 |&lt;br /&gt;|   6 |   TABLE ACCESS FULL   | T1   |     1 |    13 |     2   (0)| 00:00:01 |&lt;br /&gt;------------------------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;テストの結果から、Optimizerはグローバルヒントがグローバルではないヒントとは正常的に動作しないことがわります。インラインビューでヒントを与える方法もありますが、この例ではグローバルヒントだけで制御するのが目的です。&lt;br /&gt;&lt;p /&gt;&lt;br /&gt;&lt;b&gt;3. &lt;/b&gt;ここで適用できるのがOracleの内部的なグローバルヒントの表記法です。DBMS_XPLAN.DISPLAY関数にADVANCEDオプションを使えばOracleの内部的なヒント表記法が出力されます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;select * from table(dbms_xplan.display(null, null, 'advanced'));&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;Query Block Name / Object Alias (identified by operation id):&lt;br /&gt;-------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;   1 - SEL$5C160134&lt;br /&gt;   3 - SEL$5C160134 / T1@SEL$3&lt;br /&gt;   4 - SEL$5C160134 / T2@SEL$3&lt;br /&gt;   5 - SEL$5C160134 / T3@SEL$2&lt;br /&gt;&lt;br /&gt;Outline Data&lt;br /&gt;-------------&lt;br /&gt;&lt;br /&gt;  /*+&lt;br /&gt;      BEGIN_OUTLINE_DATA&lt;br /&gt;      USE_HASH(@"SEL$5C160134" "T3"@"SEL$2")&lt;br /&gt;      USE_HASH(@"SEL$5C160134" "T2"@"SEL$3")&lt;br /&gt;      LEADING(@"SEL$5C160134" "T1"@"SEL$3" "T2"@"SEL$3" "T3"@"SEL$2")&lt;br /&gt;      FULL(@"SEL$5C160134" "T3"@"SEL$2")&lt;br /&gt;      FULL(@"SEL$5C160134" "T2"@"SEL$3")&lt;br /&gt;      FULL(@"SEL$5C160134" "T1"@"SEL$3")&lt;br /&gt;      OUTLINE(@"SEL$3")&lt;br /&gt;      OUTLINE(@"SEL$2")&lt;br /&gt;      MERGE(@"SEL$3")&lt;br /&gt;      OUTLINE(@"SEL$335DD26A")&lt;br /&gt;      OUTLINE(@"SEL$1")&lt;br /&gt;      MERGE(@"SEL$335DD26A")&lt;br /&gt;      OUTLINE_LEAF(@"SEL$5C160134")&lt;br /&gt;      ALL_ROWS&lt;br /&gt;      DB_VERSION('11.2.0.1')&lt;br /&gt;      OPTIMIZER_FEATURES_ENABLE('11.2.0.1')&lt;br /&gt;      IGNORE_OPTIM_EMBEDDED_HINTS&lt;br /&gt;      END_OUTLINE_DATA&lt;br /&gt;  */&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;4. &lt;/b&gt;その結果を応用してグローバルヒントを与えれば、結合順序を完全に制御できます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;SQL&gt; explain plan for&lt;br /&gt;  2  select * from&lt;br /&gt;  3  (&lt;br /&gt;  4  select&lt;br /&gt;  5        /*+ LEADING(@"SEL$5C160134" "T1"@"SEL$3" "T2"@"SEL$3" "T3"@"SEL$2" ) */&lt;br /&gt;  6        v.c1 as v_c1,&lt;br /&gt;  7        v.c2 as v_c2,&lt;br /&gt;  8        t3.c2 as t3_c2&lt;br /&gt;  9  from&lt;br /&gt; 10        (select&lt;br /&gt; 11         t1.c1,&lt;br /&gt; 12         t2.c2&lt;br /&gt; 13        from&lt;br /&gt; 14         t1, t2&lt;br /&gt; 15        where&lt;br /&gt; 16         t1.c1 = t2.c1) v,&lt;br /&gt; 17        t3&lt;br /&gt; 18  where&lt;br /&gt; 19        v.c1 = t3.c1&lt;br /&gt; 20  ) x&lt;br /&gt; 21  ;&lt;br /&gt;&lt;br /&gt;----------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;----------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT    |      |     1 |    65 |     7  (15)| 00:00:01 |&lt;br /&gt;|*  1 |  HASH JOIN          |      |     1 |    65 |     7  (15)| 00:00:01 |&lt;br /&gt;|*  2 |   HASH JOIN         |      |     1 |    39 |     5  (20)| 00:00:01 |&lt;br /&gt;|   3 |    TABLE ACCESS FULL| T1   |     1 |    13 |     2   (0)| 00:00:01 |&lt;br /&gt;|   4 |    TABLE ACCESS FULL| T2   |     1 |    26 |     2   (0)| 00:00:01 |&lt;br /&gt;|   5 |   TABLE ACCESS FULL | T3   |     1 |    26 |     2   (0)| 00:00:01 |&lt;br /&gt;----------------------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;5. &lt;/b&gt;もっと良い方法は&lt;b&gt;QB_NAME&lt;/b&gt;ヒントでしょう。このヒントを使用すればもっと読みやすくて変更しやしはずです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;SQL&gt; explain plan for&lt;br /&gt;  2  select * from&lt;br /&gt;  3  (&lt;br /&gt;  4  select&lt;br /&gt;  5        /*+ leading(t1@inline t2@inline t3) */&lt;br /&gt;  6        v.c1 as v_c1,&lt;br /&gt;  7        v.c2 as v_c2,&lt;br /&gt;  8        t3.c2 as t3_c2&lt;br /&gt;  9  from&lt;br /&gt; 10        (select /*+ qb_name(inline) */&lt;br /&gt; 11         t1.c1,&lt;br /&gt; 12         t2.c2&lt;br /&gt; 13        from&lt;br /&gt; 14         t1, t2&lt;br /&gt; 15        where&lt;br /&gt; 16         t1.c1 = t2.c1) v,&lt;br /&gt; 17        t3&lt;br /&gt; 18  where&lt;br /&gt; 19        v.c1 = t3.c1&lt;br /&gt; 20  ) x&lt;br /&gt; 21  ;&lt;br /&gt;&lt;br /&gt;----------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;----------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT    |      |     1 |    65 |     7  (15)| 00:00:01 |&lt;br /&gt;|*  1 |  HASH JOIN          |      |     1 |    65 |     7  (15)| 00:00:01 |&lt;br /&gt;|*  2 |   HASH JOIN         |      |     1 |    39 |     5  (20)| 00:00:01 |&lt;br /&gt;|   3 |    TABLE ACCESS FULL| T1   |     1 |    13 |     2   (0)| 00:00:01 |&lt;br /&gt;|   4 |    TABLE ACCESS FULL| T2   |     1 |    26 |     2   (0)| 00:00:01 |&lt;br /&gt;|   5 |   TABLE ACCESS FULL | T3   |     1 |    26 |     2   (0)| 00:00:01 |&lt;br /&gt;----------------------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;グローバルヒントの使い方を理解するに役に立つ面白いテストケースですね。&lt;br /&gt;&lt;p /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-7247411902931576123?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/7247411902931576123/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/12/blog-post.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/7247411902931576123'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/7247411902931576123'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/12/blog-post.html' title='グローバルヒントの制約の面白いケース'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-7372690686366484683</id><published>2010-12-12T01:48:00.000-08:00</published><updated>2010-12-12T20:14:22.598-08:00</updated><title type='text'>並列MERGE文章の実行計画の解析</title><content type='html'>数日前に同僚の一人が並列MERGE文章の実行計画の解析に困っていることを見て、次のようにステップーバイーステップの説明をしました。&lt;br /&gt;&lt;p /&gt;&lt;br /&gt;&lt;b&gt;1. &lt;/b&gt;まずテーブルを作ります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;SQL&gt; create table t1&lt;br /&gt;  2  as&lt;br /&gt;  3  select&lt;br /&gt;  4         level as c1,&lt;br /&gt;  5         level as c2,&lt;br /&gt;  6         rpad('x',100) as c3&lt;br /&gt;  7  from&lt;br /&gt;  8         dual&lt;br /&gt;  9  connect by level &lt;= 10000&lt;br /&gt; 10  ;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt; create table t2&lt;br /&gt;  2  as&lt;br /&gt;  3  select&lt;br /&gt;  4         level as c1,&lt;br /&gt;  5         level as c2,&lt;br /&gt;  6         rpad('x', 100) as c3&lt;br /&gt;  7  from&lt;br /&gt;  8         dual&lt;br /&gt;  9  connect by level &lt;= 10000&lt;br /&gt; 10  ;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;2. &lt;/b&gt;次のSQL文は並列に実行されるでしょうか。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;SQL&gt; explain plan for&lt;br /&gt;  2  merge /*+ parallel */ into t1&lt;br /&gt;  3   using (select c1, c2 from t2) t2&lt;br /&gt;  4   on (t1.c1 = t2.c1)&lt;br /&gt;  5  when matched then&lt;br /&gt;  6         update set t1.c2 = t1.c2&lt;br /&gt;  7  when not matched then&lt;br /&gt;  8         insert(c1, c2) values(t2.c1, t2.c2)&lt;br /&gt;  9  ;&lt;br /&gt;&lt;br /&gt;Explained.&lt;br /&gt;&lt;br /&gt;-------------------------------------------------------&lt;br /&gt;| Id  | Operation                  | Name     | Rows  |&lt;br /&gt;-------------------------------------------------------&lt;br /&gt;|   0 | MERGE STATEMENT            |          |  9356 |&lt;br /&gt;|   1 |  MERGE                     | T1       |       |&lt;br /&gt;|   2 |   PX COORDINATOR           |          |       |&lt;br /&gt;|   3 |    PX SEND QC (RANDOM)     | :TQ10001 |  9356 |&lt;br /&gt;|   4 |     VIEW                   |          |       |&lt;br /&gt;|*  5 |      HASH JOIN OUTER       |          |  9356 |&lt;br /&gt;|   6 |       PX BLOCK ITERATOR    |          |  9356 |&lt;br /&gt;|   7 |        TABLE ACCESS FULL   | T2       |  9356 |&lt;br /&gt;|   8 |       BUFFER SORT          |          |       |&lt;br /&gt;|   9 |        PX RECEIVE          |          | 11234 |&lt;br /&gt;|  10 |         PX SEND BROADCAST  | :TQ10000 | 11234 |&lt;br /&gt;|  11 |          PX BLOCK ITERATOR |          | 11234 |&lt;br /&gt;|  12 |           TABLE ACCESS FULL| T1       | 11234 |&lt;br /&gt;-------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;答えは&lt;b&gt;NO&lt;/b&gt;です。段階２番から分かるように、MERGEパート自体は直列に実行されます。&lt;br /&gt;&lt;p /&gt;&lt;br /&gt;&lt;b&gt;3. &lt;/b&gt;理由はたぶん&lt;b&gt;PARALLEL　DML&lt;/b&gt;が活性化されていないからでしょう。PARALLEL　DMLを活性化したらどうなれるか確認してみます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;SQL&gt; alter session enable parallel dml;&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; explain plan for&lt;br /&gt;  2  merge /*+ parallel */ into t1&lt;br /&gt;  3   using (select c1, c2 from t2) t2&lt;br /&gt;  4   on (t1.c1 = t2.c1)&lt;br /&gt;  5  when matched then&lt;br /&gt;  6         update set t1.c2 = t1.c2&lt;br /&gt;  7  when not matched then&lt;br /&gt;  8         insert(c1, c2) values(t2.c1, t2.c2)&lt;br /&gt;  9  ;&lt;br /&gt;&lt;br /&gt;-------------------------------------------------------&lt;br /&gt;| Id  | Operation                  | Name     | Rows  |&lt;br /&gt;-------------------------------------------------------&lt;br /&gt;|   0 | MERGE STATEMENT            |          |  9356 |&lt;br /&gt;|   1 |  MERGE                     | T1       |       |&lt;br /&gt;|   2 |   PX COORDINATOR           |          |       |&lt;br /&gt;|   3 |    PX SEND QC (RANDOM)     | :TQ10001 |  9356 |&lt;br /&gt;|   4 |     VIEW                   |          |       |&lt;br /&gt;|*  5 |      HASH JOIN OUTER       |          |  9356 |&lt;br /&gt;|   6 |       PX BLOCK ITERATOR    |          |  9356 |&lt;br /&gt;|   7 |        TABLE ACCESS FULL   | T2       |  9356 |&lt;br /&gt;|   8 |       BUFFER SORT          |          |       |&lt;br /&gt;|   9 |        PX RECEIVE          |          | 11234 |&lt;br /&gt;|  10 |         PX SEND BROADCAST  | :TQ10000 | 11234 |&lt;br /&gt;|  11 |          PX BLOCK ITERATOR |          | 11234 |&lt;br /&gt;|  12 |           TABLE ACCESS FULL| T1       | 11234 |&lt;br /&gt;-------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;また、MERGEパートが直列に実行されています。&lt;br /&gt;&lt;p /&gt;&lt;br /&gt;&lt;b&gt;4. &lt;/b&gt;たぶん、並列MERGEはもっと正確なヒントを必要にするみたいです。これは当たり前でしょう。MERGE文章は２つ以上のテーブルを使用するから。PARALLELヒントにエイリアスを追加してみましょう。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;SQL&gt; explain plan for&lt;br /&gt;  2  merge /*+ parallel(t1) */ into t1&lt;br /&gt;  3   using (select c1, c2 from t2) t2&lt;br /&gt;  4   on (t1.c1 = t2.c1)&lt;br /&gt;  5  when matched then&lt;br /&gt;  6         update set t1.c2 = t1.c2&lt;br /&gt;  7  when not matched then&lt;br /&gt;  8         insert(c1, c2) values(t2.c1, t2.c2)&lt;br /&gt;  9  ;&lt;br /&gt;&lt;br /&gt;----------------------------------------------------&lt;br /&gt;| Id  | Operation                       | Name     |&lt;br /&gt;----------------------------------------------------&lt;br /&gt;|   0 | MERGE STATEMENT                 |          |&lt;br /&gt;|   1 |  PX COORDINATOR                 |          |&lt;br /&gt;|   2 |   PX SEND QC (RANDOM)           | :TQ10003 |&lt;br /&gt;|   3 |    MERGE                        | T1       |&lt;br /&gt;|   4 |     PX RECEIVE                  |          |&lt;br /&gt;|   5 |      PX SEND HYBRID (ROWID PKEY)| :TQ10002 |&lt;br /&gt;|   6 |       VIEW                      |          |&lt;br /&gt;|*  7 |        HASH JOIN OUTER BUFFERED |          |&lt;br /&gt;|   8 |         BUFFER SORT             |          |&lt;br /&gt;|   9 |          PX RECEIVE             |          |&lt;br /&gt;|  10 |           PX SEND HASH          | :TQ10000 |&lt;br /&gt;|  11 |            TABLE ACCESS FULL    | T2       |&lt;br /&gt;|  12 |         PX RECEIVE              |          |&lt;br /&gt;|  13 |          PX SEND HASH           | :TQ10001 |&lt;br /&gt;|  14 |           PX BLOCK ITERATOR     |          |&lt;br /&gt;|  15 |            TABLE ACCESS FULL    | T1       |&lt;br /&gt;----------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;今度こそ、MERGEパートがちゃんと並列に実行されています。&lt;br /&gt;&lt;p /&gt;&lt;br /&gt;&lt;b&gt;5. &lt;/b&gt;注意すべきのことがもう１つあります。対象テーブルT1に新しいインデックスをつけ、どんな変化が起こるか確認してみましょう。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;SQL&gt; create index t1_n1 on t1(c1);&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;SQL&gt; explain plan for&lt;br /&gt;  2  merge /*+ parallel(t1) */ into t1&lt;br /&gt;  3   using (select c1, c2 from t2) t2&lt;br /&gt;  4   on (t1.c1 = t2.c1)&lt;br /&gt;  5  when matched then&lt;br /&gt;  6         update set t1.c2 = t1.c2&lt;br /&gt;  7  when not matched then&lt;br /&gt;  8         insert(c1, c2) values(t2.c1, t2.c2)&lt;br /&gt;  9  ;&lt;br /&gt;&lt;br /&gt;---------------------------------------------------------------&lt;br /&gt;| Id  | Operation                          | Name     | Rows  |&lt;br /&gt;---------------------------------------------------------------&lt;br /&gt;|   0 | MERGE STATEMENT                    |          |  9356 |&lt;br /&gt;|   1 |  PX COORDINATOR                    |          |       |&lt;br /&gt;|   2 |   PX SEND QC (RANDOM)              | :TQ10004 |  9356 |&lt;br /&gt;|   3 |    INDEX MAINTENANCE               | T1       |       |&lt;br /&gt;|   4 |     PX RECEIVE                     |          |  9356 |&lt;br /&gt;|   5 |      PX SEND RANGE                 | :TQ10003 |  9356 |&lt;br /&gt;|   6 |       MERGE                        | T1       |       |&lt;br /&gt;|   7 |        PX RECEIVE                  |          |  9356 |&lt;br /&gt;|   8 |         PX SEND HYBRID (ROWID PKEY)| :TQ10002 |  9356 |&lt;br /&gt;|   9 |          VIEW                      |          |       |&lt;br /&gt;|* 10 |           HASH JOIN OUTER BUFFERED |          |  9356 |&lt;br /&gt;|  11 |            BUFFER SORT             |          |       |&lt;br /&gt;|  12 |             PX RECEIVE             |          |  9356 |&lt;br /&gt;|  13 |              PX SEND HASH          | :TQ10000 |  9356 |&lt;br /&gt;|  14 |               TABLE ACCESS FULL    | T2       |  9356 |&lt;br /&gt;|  15 |            PX RECEIVE              |          | 11234 |&lt;br /&gt;|  16 |             PX SEND HASH           | :TQ10001 | 11234 |&lt;br /&gt;|  17 |              PX BLOCK ITERATOR     |          | 11234 |&lt;br /&gt;|  18 |               TABLE ACCESS FULL    | T1       | 11234 |&lt;br /&gt;---------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;INDEX MAINTENANCE&lt;/b&gt;と言う新しい段階が現れます。インデックスメンテナンスのためにもう一つのテーブルキューTQ10004が使用されることも分かります。問題は、なぜここでインデックスメンテナンスをするのかということです。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;並列MERGEは並列UPDATEと並列INSERTで構成されます。&lt;br /&gt;&lt;li&gt;並列INSERTとはダイレクトパスINSERTです。&lt;br /&gt;&lt;li&gt;ダイレクトパスINSERTが終わったら、インデックスに変更内容を反映しなければなりません。&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;OracleはダイレクトパスINSERTの後で、インデックスのメンテナンスをするようになっています。この機能は&lt;b&gt;_idl_conventional_index_maintenance&lt;/b&gt;という隠しパラメーターで制御されます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;SQL&gt; select * from table(tpack.param('_idl_conventional_index_maintenance'));&lt;br /&gt;&lt;br /&gt;NAME                                VALUE&lt;br /&gt;----------------------------------- ----------------------------------------&lt;br /&gt;Name #1                             _idl_conventional_index_maintenance&lt;br /&gt;  Value                             TRUE&lt;br /&gt;  Is Default                        TRUE&lt;br /&gt;  Sess Modifiable                   false&lt;br /&gt;  Sys Modifiable                    false&lt;br /&gt;  Description                       enable conventional index maintenance fo&lt;br /&gt;                                    r insert direct load&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p /&gt;&lt;br /&gt;INDEX MAINTENANCEの段階はインデックスをUNUSABLEさせても消えません。でも、実際に実行する際にはUNUSABLE状態のインデックスはメンテナンスされないはずです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;SQL&gt; alter index t1_n1 unusable;&lt;br /&gt;&lt;br /&gt;Index altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; explain plan for&lt;br /&gt;  2  merge /*+ parallel(t1) */ into t1&lt;br /&gt;  3   using (select c1, c2 from t2) t2&lt;br /&gt;  4   on (t1.c1 = t2.c1)&lt;br /&gt;  5  when matched then&lt;br /&gt;  6         update set t1.c2 = t1.c2&lt;br /&gt;  7  when not matched then&lt;br /&gt;  8         insert(c1, c2) values(t2.c1, t2.c2)&lt;br /&gt;  9  ;&lt;br /&gt;&lt;br /&gt;---------------------------------------------------------------&lt;br /&gt;| Id  | Operation                          | Name     | Rows  |&lt;br /&gt;---------------------------------------------------------------&lt;br /&gt;|   0 | MERGE STATEMENT                    |          |  9356 |&lt;br /&gt;|   1 |  PX COORDINATOR                    |          |       |&lt;br /&gt;|   2 |   PX SEND QC (RANDOM)              | :TQ10004 |  9356 |&lt;br /&gt;|   3 |    INDEX MAINTENANCE               | T1       |       |&lt;br /&gt;|   4 |     PX RECEIVE                     |          |  9356 |&lt;br /&gt;|   5 |      PX SEND RANGE                 | :TQ10003 |  9356 |&lt;br /&gt;|   6 |       MERGE                        | T1       |       |&lt;br /&gt;|   7 |        PX RECEIVE                  |          |  9356 |&lt;br /&gt;|   8 |         PX SEND HYBRID (ROWID PKEY)| :TQ10002 |  9356 |&lt;br /&gt;|   9 |          VIEW                      |          |       |&lt;br /&gt;|* 10 |           HASH JOIN OUTER BUFFERED |          |  9356 |&lt;br /&gt;|  11 |            BUFFER SORT             |          |       |&lt;br /&gt;|  12 |             PX RECEIVE             |          |  9356 |&lt;br /&gt;|  13 |              PX SEND HASH          | :TQ10000 |  9356 |&lt;br /&gt;|  14 |               TABLE ACCESS FULL    | T2       |  9356 |&lt;br /&gt;|  15 |            PX RECEIVE              |          | 11234 |&lt;br /&gt;|  16 |             PX SEND HASH           | :TQ10001 | 11234 |&lt;br /&gt;|  17 |              PX BLOCK ITERATOR     |          | 11234 |&lt;br /&gt;|  18 |               TABLE ACCESS FULL    | T1       | 11234 |&lt;br /&gt;---------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;私の説明が役に立ったのか分かりませんが、ともかくこのようにブログに残します。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-7372690686366484683?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/7372690686366484683/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/12/merge.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/7372690686366484683'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/7372690686366484683'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/12/merge.html' title='並列MERGE文章の実行計画の解析'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-6065837219805429428</id><published>2010-11-02T01:17:00.000-07:00</published><updated>2010-11-02T01:59:48.341-07:00</updated><title type='text'>Oracle 11gのSerial Direct Path Readと_very_large_object_thresholdパラメータパラメータ</title><content type='html'>次のポストを通じてOracle 11gのSerial Direct Path Readを制御する方法を紹介したことがあります。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://dioncho.blogspot.com/2009/07/11serialdirect-path-read.html"&gt;オラクル11ｇのserial全表スキャンに対するdirect path read非活性化しよう。&lt;/a&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;ポストの核心は&lt;b&gt;10949診断イベント&lt;/b&gt;を利用してSerial Direct Path Readを不活性化することが可能だということです。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;最近Serial Direct Path Readを制御するもう１つの隠しパラメータを知るようになりました。&lt;b&gt;_VERY_LARGE_OBJECT_THRESHOLD&lt;/b&gt;隠しパラメータです。例えば、このパラメータの値が「500」ならば、セグメントのサイズが500MNB以上だったら10949診断イベントとは無関係にSerail Direct Path Readが使用されるようになります。これが意味するのは&lt;b&gt;大きすぎるテーブルは可能なかぎりSerial Direct Path Readを使用しろ&lt;/b&gt;ということです。とても合理的な決定だと思います。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;簡単なテストケースで説明してみます。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;b&gt;1. &lt;/b&gt;Oracleのバージョンは11.2.0.1です。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;SQL&gt; select * from v$version where rownum = 1;&lt;br /&gt;&lt;br /&gt;BANNER&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;2. &lt;/b&gt;_VERY_LARGE_OBJECT_THRESHOLDパラメータの値は500(MB)です。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;SQL&gt; @para very_large_object&lt;br /&gt;SQL&gt; set echo off&lt;br /&gt;old   9: and i.ksppinm like '%&amp;1%'&lt;br /&gt;new   9: and i.ksppinm like '%very_large_object%'&lt;br /&gt;&lt;br /&gt;NAME                           VALUE                IS_DEFAUL SES_MODIFI SYS_MODIFI&lt;br /&gt;------------------------------ -------------------- --------- ---------- ----------&lt;br /&gt;DESCRIPTION&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;_very_large_object_threshold   500                  TRUE      true       deferred&lt;br /&gt;upper threshold level of object size for direct reads&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;3. &lt;/b&gt;約104MBのサイズのテーブルT_VLOTを作り、セッションレベルで94MBを_VERY_LARGE_OBJECT_THRESHOLDパラメータの値で指定します。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;SQL&gt; create table t_vlot&lt;br /&gt;  2  as&lt;br /&gt;  3  select&lt;br /&gt;  4   rpad('x',2000) as c1,&lt;br /&gt;  5   rpad('x',2000) as c2,&lt;br /&gt;  6   rpad('x',2000) as c3,&lt;br /&gt;  7   rpad('x',2000) as c4&lt;br /&gt;  8  from dual&lt;br /&gt;  9  connect by level &lt;= 6500&lt;br /&gt; 10  ;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt; col tsize new_value tsize&lt;br /&gt;SQL&gt; select trunc(blocks*8*1024/1024/1024) - 10 as tsize&lt;br /&gt;  2  from dba_segments&lt;br /&gt;  3  where owner = user and segment_name = 'T_VLOT'&lt;br /&gt;  4  ;&lt;br /&gt;&lt;br /&gt;     TSIZE&lt;br /&gt;----------&lt;br /&gt;        94&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; alter session set "_very_large_object_threshold" = &amp;tsize;&lt;br /&gt;old   1: alter session set "_very_large_object_threshold" = &amp;tsize&lt;br /&gt;new   1: alter session set "_very_large_object_threshold" =         94&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;10949診断イベントを活性化し、T_VLOTテーブルに対してTable Full Scanを行ないます。そして10046診断イベントを通じて待機イベントを分析します。&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;SQL&gt; -- even when 10949 is enabled&lt;br /&gt;SQL&gt; alter session set events '10949 trace name context forever, level 1';&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; alter system flush buffer_cache;&lt;br /&gt;&lt;br /&gt;System altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; exec tpack.begin_diag_trace(userenv('sid'), 10046, 8);&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; select count(*) from t_vlot;&lt;br /&gt;&lt;br /&gt;  COUNT(*)&lt;br /&gt;----------&lt;br /&gt;      6500&lt;br /&gt;&lt;br /&gt;SQL&gt; exec tpack.end_diag_trace(userenv('sid'), 10046);&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; select * from table(tpack.get_diag_trace(userenv('sid'), 'TKPROF', 'sys=no'));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;5. &lt;/b&gt;次にその結果があります。10949イベントが活性化されていますが、&lt;b&gt;direct path read&lt;/b&gt;待機イベントが現れます。Serial Direct Path Readが動作したという意味です。&lt;br /&gt;&lt;pre class="brush: sql; highlight:[31];"&gt;&lt;br /&gt;SQL ID: 1n87ukuyyv5h2&lt;br /&gt;Plan Hash: 2969598161&lt;br /&gt;select count(*)&lt;br /&gt;from&lt;br /&gt; t_vlot&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;call     count       cpu    elapsed       disk      query    current        rows&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;Parse        1      0.00       0.00          1          1          0           0&lt;br /&gt;Execute      1      0.00       0.00          0          0          0           0&lt;br /&gt;Fetch        2      0.09       2.86      13000      13004          0           1&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;total        4      0.09       2.86      13001      13005          0           1&lt;br /&gt;&lt;br /&gt;Misses in library cache during parse: 1&lt;br /&gt;Optimizer mode: ALL_ROWS&lt;br /&gt;Parsing user id: 97&lt;br /&gt;&lt;br /&gt;Rows     Row Source Operation&lt;br /&gt;-------  ---------------------------------------------------&lt;br /&gt;      1  SORT AGGREGATE (cr=13004 pr=13000 pw=0 time=0 us)&lt;br /&gt;   6500   TABLE ACCESS FULL T_VLOT (cr=13004 pr=13000 pw=0 time=60657 us cost=3575 size=0 card=6473)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Elapsed times include waiting on following events:&lt;br /&gt;  Event waited on                             Times   Max. Wait  Total Waited&lt;br /&gt;  ----------------------------------------   Waited  ----------  ------------&lt;br /&gt;  db file sequential read                         1        0.00          0.00&lt;br /&gt;  SQL*Net message to client                       2        0.00          0.00&lt;br /&gt;  direct path read                              412        0.04          2.73&lt;br /&gt;  asynch descriptor resize                        1        0.00          0.00&lt;br /&gt;  SQL*Net message from client                     2        0.00          0.00&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;たとえ診断イベントと隠しパラメータを通じてSerial Direct Path Readを完全に不活性化できますが、この機能は基本的に&lt;b&gt;良い&lt;/b&gt;機能です。バッチI/Oの性能が優れた多くのストレージで良い性能を見せてくれるのを期待できます。本番環境で色々なファクターを考えて適切に制御する必要があります。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-6065837219805429428?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/6065837219805429428/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/11/oracle-11gserial-direct-path.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/6065837219805429428'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/6065837219805429428'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/11/oracle-11gserial-direct-path.html' title='Oracle 11gのSerial Direct Path Readと_very_large_object_thresholdパラメータパラメータ'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-833252257554170700</id><published>2010-10-25T23:22:00.000-07:00</published><updated>2010-11-02T01:17:02.707-07:00</updated><title type='text'>結合の手順の制御</title><content type='html'>Oracleが提供するヒントが強すぎて、基本的なことを忘れてしまうことがあります。たまにはヒントがないとしたらどんな方法で実行計画を制御しようかを考えてみれば面白いことを見つけるようになるでしょう。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;例えば、次のような五つのテーブルがあります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;create table t1(c1, c2)&lt;br /&gt;as &lt;br /&gt;select&lt;br /&gt;  level, level&lt;br /&gt;from dual&lt;br /&gt;connect by level &lt;= 5000;&lt;br /&gt;&lt;br /&gt;create table t2(c1, c2)&lt;br /&gt;as &lt;br /&gt;select&lt;br /&gt;  level, level&lt;br /&gt;from dual&lt;br /&gt;connect by level &lt;= 4000;&lt;br /&gt;&lt;br /&gt;create table t3(c1, c2)&lt;br /&gt;as &lt;br /&gt;select&lt;br /&gt;  level, level&lt;br /&gt;from dual&lt;br /&gt;connect by level &lt;= 3000;&lt;br /&gt;&lt;br /&gt;create table t4(c1, c2)&lt;br /&gt;as &lt;br /&gt;select&lt;br /&gt;  level, level&lt;br /&gt;from dual&lt;br /&gt;connect by level &lt;= 2000;&lt;br /&gt;&lt;br /&gt;create table t5(c1, c2)&lt;br /&gt;as &lt;br /&gt;select&lt;br /&gt;  level, level&lt;br /&gt;from dual&lt;br /&gt;connect by level &lt;= 1000;&lt;br /&gt;&lt;br /&gt;create index t1_n1 on t1(c1);&lt;br /&gt;create index t2_n1 on t2(c1);&lt;br /&gt;create index t3_n1 on t3(c1);&lt;br /&gt;create index t4_n1 on t4(c1);&lt;br /&gt;create index t5_n1 on t5(c1);&lt;br /&gt;&lt;br /&gt;exec dbms_stats.gather_table_stats(user, 't1');&lt;br /&gt;exec dbms_stats.gather_table_stats(user, 't2');&lt;br /&gt;exec dbms_stats.gather_table_stats(user, 't3');&lt;br /&gt;exec dbms_stats.gather_table_stats(user, 't4');&lt;br /&gt;exec dbms_stats.gather_table_stats(user, 't5');&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;テーブルT1, T2, T3, T4, T5を結合するクエリで&lt;b&gt;結合手順&lt;/b&gt;はどうなりましょうか。テーブルのデータ分布を見るとT1(c1 between 1 and 10 条件のため）がドライビングテーブルになり、その次はT5(1000件）、T4(2000件）、T3(3000件）、T2(4000件）になるのが分かります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;explain plan for&lt;br /&gt;select &lt;br /&gt; *&lt;br /&gt;from&lt;br /&gt; t1, t2, t3, t5, t4&lt;br /&gt;where&lt;br /&gt; t1.c1 = t2.c1&lt;br /&gt; and t1.c1 = t3.c1&lt;br /&gt; and t1.c1 = t4.c1&lt;br /&gt; and t1.c1 = t5.c1&lt;br /&gt; and t1.c2 between 1 and 10&lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;--------------------------------------&lt;br /&gt;| Id  | Operation             | Name |&lt;br /&gt;--------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT      |      |&lt;br /&gt;|*  1 |  HASH JOIN            |      |&lt;br /&gt;|*  2 |   HASH JOIN           |      |&lt;br /&gt;|*  3 |    HASH JOIN          |      |&lt;br /&gt;|*  4 |     HASH JOIN         |      |&lt;br /&gt;|*  5 |      TABLE ACCESS FULL| T1   |&lt;br /&gt;|   6 |      TABLE ACCESS FULL| T5   |&lt;br /&gt;|   7 |     TABLE ACCESS FULL | T4   |&lt;br /&gt;|   8 |    TABLE ACCESS FULL  | T3   |&lt;br /&gt;|   9 |   TABLE ACCESS FULL   | T2   |&lt;br /&gt;--------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;結合の手順を&lt;b&gt;T1 -&gt; T2 -&gt; T3 -&gt; T4 -&gt; T5&lt;/b&gt;としたいと言ったらどう？次のように&lt;b&gt;ORDERED&lt;/b&gt;ヒントを使えばいいでしょう。またはLEADING(t1 t2 t3 t4 t5)ヒントを使ってもいいです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;explain plan for&lt;br /&gt;select /*+ ordered */&lt;br /&gt; *&lt;br /&gt;from&lt;br /&gt; t1, t2, t3, t5, t4&lt;br /&gt;where&lt;br /&gt; t1.c1 = t2.c1&lt;br /&gt; and t1.c1 = t3.c1&lt;br /&gt; and t1.c1 = t4.c1&lt;br /&gt; and t1.c1 = t5.c1&lt;br /&gt; and t1.c2 between 1 and 10&lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;--------------------------------------&lt;br /&gt;| Id  | Operation             | Name |&lt;br /&gt;--------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT      |      |&lt;br /&gt;|*  1 |  HASH JOIN            |      |&lt;br /&gt;|*  2 |   HASH JOIN           |      |&lt;br /&gt;|*  3 |    HASH JOIN          |      |&lt;br /&gt;|*  4 |     HASH JOIN         |      |&lt;br /&gt;|*  5 |      TABLE ACCESS FULL| T1   |&lt;br /&gt;|   6 |      TABLE ACCESS FULL| T2   |&lt;br /&gt;|   7 |     TABLE ACCESS FULL | T3   |&lt;br /&gt;|   8 |    TABLE ACCESS FULL  | T4   |&lt;br /&gt;|   9 |   TABLE ACCESS FULL   | T5   |&lt;br /&gt;--------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;仮にヒントを使用せず&lt;/b&gt;（そしてRBOではなくてCBOと言えば）、結合の手順が常に上のように出るようにしたいといったらどうすればいいでしょうか。ヒントに慣れてしまえばこのような簡単な質問に答えることが難しくなりかねません。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;最も典型的な方法は次のように&lt;b&gt;t1.c1 = t3.c1&lt;/b&gt;の条件を&lt;b&gt;t1.c1 + 0*t2.c1 = t3.c1&lt;/b&gt;のような形で使用することです。t3.c1で結合が可能になるためにはt2.c1の値を知らなければならないので、T2 -&gt; T3の手順しか結合方法がありません。同じ原理で条件を付けば全ての結合手順を制御できます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;explain plan for&lt;br /&gt;select &lt;br /&gt; *&lt;br /&gt;from&lt;br /&gt; t1, t2, t3, t4, t5&lt;br /&gt;where&lt;br /&gt; t1.c1 = t2.c1&lt;br /&gt; and t1.c1 + 0*t2.c1 = t3.c1  -- t2.c1値なしは t3.c1の結合不可能&lt;br /&gt; and t1.c1 + 0*t3.c1= t4.c1  -- t3.c1値なしはt4.c1の結合不可能&lt;br /&gt; and t1.c1 + 0*t4.c1 = t5.c1  -- t4.c1値なしは t5.c1結合不可能&lt;br /&gt; and t1.c2 between 1 and 10&lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;--------------------------------------&lt;br /&gt;| Id  | Operation             | Name |&lt;br /&gt;--------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT      |      |&lt;br /&gt;|*  1 |  HASH JOIN            |      |&lt;br /&gt;|*  2 |   HASH JOIN           |      |&lt;br /&gt;|*  3 |    HASH JOIN          |      |&lt;br /&gt;|*  4 |     HASH JOIN         |      |&lt;br /&gt;|*  5 |      TABLE ACCESS FULL| T1   |&lt;br /&gt;|   6 |      TABLE ACCESS FULL| T2   |&lt;br /&gt;|   7 |     TABLE ACCESS FULL | T3   |&lt;br /&gt;|   8 |    TABLE ACCESS FULL  | T4   |&lt;br /&gt;|   9 |   TABLE ACCESS FULL   | T5   |&lt;br /&gt;--------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;このような方法を応用すれば結合の手順を自由に制御できます。例えば結合の手順を&lt;b&gt;T1 -&gt; T2 -&gt; T4 -&gt; T3 -&gt; T5&lt;/b&gt;（T4がT3より先に結合されるように）とするためにはどうすればいいでしょうか。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;explain plan for&lt;br /&gt;select &lt;br /&gt; *&lt;br /&gt;from&lt;br /&gt; t1, t2, t3, t4, t5&lt;br /&gt;where&lt;br /&gt; t1.c1 = t2.c1&lt;br /&gt; { ここにどんな条件があれば下のような結合手順が？ }&lt;br /&gt; and t1.c2 between 1 and 10&lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;--------------------------------------&lt;br /&gt;| Id  | Operation             | Name |&lt;br /&gt;--------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT      |      |&lt;br /&gt;|*  1 |  HASH JOIN            |      |&lt;br /&gt;|*  2 |   HASH JOIN           |      |&lt;br /&gt;|*  3 |    HASH JOIN          |      |&lt;br /&gt;|*  4 |     HASH JOIN         |      |&lt;br /&gt;|*  5 |      TABLE ACCESS FULL| T1   |&lt;br /&gt;|   6 |      TABLE ACCESS FULL| T2   |&lt;br /&gt;|   7 |     TABLE ACCESS FULL | T4   |&lt;br /&gt;|   8 |    TABLE ACCESS FULL  | T3   |&lt;br /&gt;|   9 |   TABLE ACCESS FULL   | T5   |&lt;br /&gt;--------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;解答はもう公開されていたも同然ですからここに書くことはしません。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;でもやっぱりヒントが便利ですね。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-833252257554170700?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/833252257554170700/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/10/blog-post.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/833252257554170700'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/833252257554170700'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/10/blog-post.html' title='結合の手順の制御'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-7650886116226169527</id><published>2010-10-12T00:28:00.000-07:00</published><updated>2010-10-12T00:40:05.716-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='utl_raw'/><category scheme='http://www.blogger.com/atom/ns#' term='raw'/><category scheme='http://www.blogger.com/atom/ns#' term='dbms_stats'/><title type='text'>RAW値の切り替え</title><content type='html'>ディクショナリーを検索してみるとRAWの形で提供されるコラムが時々あります。例えばUSER_TAB_COLSビューでコラムの最小値と最大値を見ると次のように意味の分からない変な値と見えます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;SQL&gt; col column_name format a10&lt;br /&gt;SQL&gt; col data_type format a10&lt;br /&gt;SQL&gt; select&lt;br /&gt;  2   column_name,&lt;br /&gt;  3   data_type,&lt;br /&gt;  4   low_value,&lt;br /&gt;  5   high_value&lt;br /&gt;  6  from&lt;br /&gt;  7   user_tab_cols&lt;br /&gt;  8  where&lt;br /&gt;  9   table_name = 'T1'&lt;br /&gt; 10  ;&lt;br /&gt;&lt;br /&gt;COLUMN_NAM DATA_TYPE  LOW_VALUE            HIGH_VALUE&lt;br /&gt;---------- ---------- -------------------- --------------------&lt;br /&gt;C1         NUMBER     C102                 C302&lt;br /&gt;C2         VARCHAR2   6D616E79             6F6E65&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;このような値たちは&lt;b&gt;UTL_RAW&lt;/b&gt;　パッケージで切り替えられます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;SQL&gt; select utl_raw.cast_to_number('C102') from dual;&lt;br /&gt;&lt;br /&gt;UTL_RAW.CAST_TO_NUMBER('C102')&lt;br /&gt;------------------------------&lt;br /&gt;                             1&lt;br /&gt;&lt;br /&gt;SQL&gt; select utl_raw.cast_to_varchar2('6D616E79') from dual;&lt;br /&gt;&lt;br /&gt;UTL_RAW.CAST_TO_VARCHAR2('6D616E79')&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;many&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;次のように使用されるでしょう。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;SQL&gt; select&lt;br /&gt;  2   column_name,&lt;br /&gt;  3   data_type,&lt;br /&gt;  4   decode(data_type,&lt;br /&gt;  5    'NUMBER', utl_raw.cast_to_number(low_value)||'',&lt;br /&gt;  6    'VARCHAR2', utl_raw.cast_to_varchar2(low_value), low_value||'') as low_value,&lt;br /&gt;  7   decode(data_type,&lt;br /&gt;  8    'NUMBER', utl_raw.cast_to_number(high_value)||'',&lt;br /&gt;  9    'VARCHAR2', utl_raw.cast_to_varchar2(high_value), high_value||'') as high_value&lt;br /&gt; 10  from&lt;br /&gt; 11   user_tab_cols&lt;br /&gt; 12  where&lt;br /&gt; 13   table_name = 'T1'&lt;br /&gt; 14  ;&lt;br /&gt;&lt;br /&gt;COLUMN_NAM DATA_TYPE  LOW_VALUE            HIGH_VALUE&lt;br /&gt;---------- ---------- -------------------- --------------------&lt;br /&gt;C1         NUMBER     1                    10000&lt;br /&gt;C2         VARCHAR2   many                 one&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;DBMS_STATS&lt;/b&gt;　パッケージも同一な役割の関数を提供します。でも、パラメータの指定しかたが少し違います。使用の便利性のために次のように使用者定義関数を作ります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;SQL&gt; -- dbms_stats (conversion)&lt;br /&gt;SQL&gt; create or replace function convert_me(p_value in raw, p_type in varchar2)&lt;br /&gt;  2  return varchar2&lt;br /&gt;  3  is&lt;br /&gt;  4     v_number  number;&lt;br /&gt;  5     v_varchar2   varchar2(4000);&lt;br /&gt;  6  begin&lt;br /&gt;  7     if (p_type = 'NUMBER') then&lt;br /&gt;  8        dbms_stats.convert_raw_value(p_value, v_number);&lt;br /&gt;  9        return v_number|| '';&lt;br /&gt; 10     elsif (p_type = 'VARCHAR2') then&lt;br /&gt; 11        dbms_stats.convert_raw_value(p_value,v_varchar2);&lt;br /&gt; 12        return v_varchar2;&lt;br /&gt; 13     /* other data types */&lt;br /&gt; 14     else&lt;br /&gt; 15        return p_value || '';&lt;br /&gt; 16     end if;&lt;br /&gt; 17  end;&lt;br /&gt; 18  /&lt;br /&gt;&lt;br /&gt;Function created.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; select convert_me('C102', 'NUMBER') from dual;&lt;br /&gt;&lt;br /&gt;CONVERT_ME('C102','NUMBER')&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;1&lt;br /&gt;&lt;br /&gt;SQL&gt; select convert_me('6D616E79', 'VARCHAR2') from dual;&lt;br /&gt;&lt;br /&gt;CONVERT_ME('6D616E79','VARCHAR2')&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;many&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;そうすると次のようにSQL文内で使用できます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;SQL&gt; select&lt;br /&gt;  2   column_name,&lt;br /&gt;  3   data_type,&lt;br /&gt;  4   convert_me(low_value, data_type) as low_value,&lt;br /&gt;  5   convert_me(high_value, data_type) as high_value&lt;br /&gt;  6  from&lt;br /&gt;  7   user_tab_cols&lt;br /&gt;  8  where&lt;br /&gt;  9   table_name = 'T1'&lt;br /&gt; 10  ;&lt;br /&gt;&lt;br /&gt;COLUMN_NAM DATA_TYPE  LOW_VALUE            HIGH_VALUE&lt;br /&gt;---------- ---------- -------------------- --------------------&lt;br /&gt;C1         NUMBER     1                    10000&lt;br /&gt;C2         VARCHAR2   many                 one&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;上のような面倒なことをしなくても良いようにOracleが自ら切り替えてくれたら良かったんですけどね。ともかく時折こんな切り替え作業が必要なところがあります。その際、活用すれば良いでしょう。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-7650886116226169527?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/7650886116226169527/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/10/raw.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/7650886116226169527'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/7650886116226169527'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/10/raw.html' title='RAW値の切り替え'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-3797132801102498925</id><published>2010-10-11T02:59:00.000-07:00</published><updated>2010-10-11T03:07:28.229-07:00</updated><title type='text'>Batching NLJに対するOracleマニュアルの説明</title><content type='html'>Oracle 11gのBatching NLJにより物理I/Oが発生する時、整列が壊れてしまうように見える現象を&lt;a href="http://dioncho.blogspot.com/2010/08/batching-nlj.html"&gt;このポスト&lt;/a&gt;で紹介したことがあります。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;今日偶然にOracleマニュアル(Performance Tuning Guide)でOracle 11gのBatching NLJに対して説明している部分を見つかったんです。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://download.oracle.com/docs/cd/E11882_01/server.112/e16638/optimops.htm#BABFCIAI"&gt;11.3.3.1.2 New Implementation for Nested Loop Joins&lt;/a&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;単純で明確に説明してあります。この説明と共に私が説明したヒントと隠しパラメーターを理解していただければ本番環境で問題が発生した際、効果的に対処できるはずです。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-3797132801102498925?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/3797132801102498925/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/10/batching-nljoracle.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/3797132801102498925'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/3797132801102498925'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/10/batching-nljoracle.html' title='Batching NLJに対するOracleマニュアルの説明'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-6728510925473613266</id><published>2010-10-06T23:07:00.000-07:00</published><updated>2010-10-06T23:57:08.252-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PLAN_HASH_VALUE'/><title type='text'>PLAN_HASH_VALUE</title><content type='html'>SQL AとSQL Bの実行計画がお互いに同じか違うか比べられる一番易い方法は何でしょうか。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;Oracleがて提供するPLAN_HASH_VALUEが正解ではないかと思います。この値は文字通り実行計画に対するハッシュねであります。ハッシュねなので100%の唯一性が保障されてはいないが、殆ど大部分の場合識別するに使えます。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;Oracleでは同一なSQLテキストのSQLカーソルが複数の実行計画が持てます。こんな柔らかのおかげでいろいろな性能問題が現れますが...&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;簡単な例えを通して説明してみます。この例えはOracle 11gの&lt;b&gt;Adaptive Cursor Sharing&lt;/b&gt;機能を利用しています。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;Oracleのバージョンは11gR2です。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1120&gt; select * from v$version where rownum = 1;&lt;br /&gt;&lt;br /&gt;BANNER&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;偏りのある表を持つテーブルT1を作ります。列C1に対して索引を作り、ヒストグラムも作ります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1120&gt; &lt;br /&gt;TPACK@ukja1120&gt; create table t1&lt;br /&gt;  2  as&lt;br /&gt;  3  select level as c1 from dual connect by level &lt;= 10000&lt;br /&gt;  4  union all&lt;br /&gt;  5  select 1 as c1 from dual connect by level &lt;= 100000&lt;br /&gt;  6  ;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; &lt;br /&gt;TPACK@ukja1120&gt; create index t1_n1 on t1(c1);&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; &lt;br /&gt;TPACK@ukja1120&gt; exec dbms_stats.gather_table_stats(user, 't1', method_opt=&gt;'for all columns size skewonly');&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;「100」の値が条件で使われる時はIndex Range Scanを使用します。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1120&gt; explain plan for&lt;br /&gt;  2  select count(*) from t1 where c1 = 100;&lt;br /&gt;&lt;br /&gt;Explained.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; select * from table(dbms_xplan.display);&lt;br /&gt;---------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation         | Name  | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;---------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT  |       |     1 |     3 |     1   (0)| 00:00:01 |&lt;br /&gt;|   1 |  SORT AGGREGATE   |       |     1 |     3 |            |          |&lt;br /&gt;|*  2 |   INDEX RANGE SCAN| T1_N1 |     1 |     3 |     1   (0)| 00:00:01 |&lt;br /&gt;---------------------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;「1」の値が条件で使われる時はTable FullScanを使用します。&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;TPACK@ukja1120&gt; explain plan for&lt;br /&gt;  2  select count(*) from t1 where c1 = 1;&lt;br /&gt;&lt;br /&gt;Explained.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; select * from table(dbms_xplan.display);&lt;br /&gt;---------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;---------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT   |      |     1 |     3 |    53   (4)| 00:00:01 |&lt;br /&gt;|   1 |  SORT AGGREGATE    |      |     1 |     3 |            |          |&lt;br /&gt;|*  2 |   TABLE ACCESS FULL| T1   | 99173 |   290K|    53   (4)| 00:00:01 |&lt;br /&gt;---------------------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;「100」と「1」と値を繰り返しながらクエリを実行します。バインド変数を使用してAdpative Cursor Sharingが働くようにします。&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;TPACK@ukja1120&gt; var b1 number;&lt;br /&gt;TPACK@ukja1120&gt; exec :b1 := 100;&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; select count(*) from t1 where c1 = :b1;&lt;br /&gt;&lt;br /&gt;  COUNT(*)&lt;br /&gt;----------&lt;br /&gt;         1&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; &lt;br /&gt;TPACK@ukja1120&gt; exec :b1 := 1;&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; select count(*) from t1 where c1 = :b1;&lt;br /&gt;&lt;br /&gt;  COUNT(*)&lt;br /&gt;----------&lt;br /&gt;    100001&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; &lt;br /&gt;TPACK@ukja1120&gt; exec :b1 := 100;&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; select count(*) from t1 where c1 = :b1;&lt;br /&gt;&lt;br /&gt;  COUNT(*)&lt;br /&gt;----------&lt;br /&gt;         1&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; &lt;br /&gt;TPACK@ukja1120&gt; exec :b1 := 1;&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; select count(*) from t1 where c1 = :b1;&lt;br /&gt;&lt;br /&gt;  COUNT(*)&lt;br /&gt;----------&lt;br /&gt;    100001&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; &lt;br /&gt;TPACK@ukja1120&gt; exec :b1 := 100;&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; select count(*) from t1 where c1 = :b1;&lt;br /&gt;&lt;br /&gt;  COUNT(*)&lt;br /&gt;----------&lt;br /&gt;         1&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; &lt;br /&gt;TPACK@ukja1120&gt; exec :b1 := 1;&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; select count(*) from t1 where c1 = :b1;&lt;br /&gt;&lt;br /&gt;  COUNT(*)&lt;br /&gt;----------&lt;br /&gt;    100001&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; &lt;br /&gt;TPACK@ukja1120&gt; exec :b1 := 100;&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; select count(*) from t1 where c1 = :b1;&lt;br /&gt;&lt;br /&gt;  COUNT(*)&lt;br /&gt;----------&lt;br /&gt;         1&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; &lt;br /&gt;TPACK@ukja1120&gt; exec :b1 := 1;&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; select count(*) from t1 where c1 = :b1;&lt;br /&gt;&lt;br /&gt;  COUNT(*)&lt;br /&gt;----------&lt;br /&gt;    100001&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;四つのチャイルドカーソルができました。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1120&gt; col sql_id new_value sql_id&lt;br /&gt;TPACK@ukja1120&gt; select sql_id, version_count, plan_hash_value&lt;br /&gt;  2  from v$sqlarea&lt;br /&gt;  3  where sql_text = 'select count(*) from t1 where c1 = :b1';&lt;br /&gt;&lt;br /&gt;SQL_ID        VERSION_COUNT PLAN_HASH_VALUE&lt;br /&gt;------------- ------------- ---------------&lt;br /&gt;7dwqb1wjmp5hm             4        73337487&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;チャイルドカーソルを表すV$SQLビューを見るとチャイルドは四つですけどPLAN_HASH_VALUEは2つが存在します。すなわち、実際の実行計画は2つ(Index Range Scan + Table Full Scan)だけです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1120&gt; select sql_id, child_number, plan_hash_value&lt;br /&gt;  2  from v$sql&lt;br /&gt;  3  where sql_id = '&amp;sql_id';&lt;br /&gt;old   3: where sql_id = '&amp;sql_id'&lt;br /&gt;new   3: where sql_id = '7dwqb1wjmp5hm'&lt;br /&gt;&lt;br /&gt;SQL_ID        CHILD_NUMBER PLAN_HASH_VALUE&lt;br /&gt;------------- ------------ ---------------&lt;br /&gt;7dwqb1wjmp5hm            0        73337487&lt;br /&gt;7dwqb1wjmp5hm            1        73337487&lt;br /&gt;7dwqb1wjmp5hm            2        73337487&lt;br /&gt;7dwqb1wjmp5hm            3      3724264953&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;DBMS_XPLAN.DISPLAY_CURSOR関数を利用して実行計画を検索してみると、詳細な情報が得られます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1120&gt; select * from table(dbms_xplan.display_cursor('&amp;sql_id', null));&lt;br /&gt;old   1: select * from table(dbms_xplan.display_cursor('&amp;sql_id', null))&lt;br /&gt;new   1: select * from table(dbms_xplan.display_cursor('7dwqb1wjmp5hm', null))&lt;br /&gt;&lt;br /&gt;Plan hash value: 73337487&lt;br /&gt;&lt;br /&gt;---------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation         | Name  | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;---------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT  |       |       |       |     1 (100)|          |&lt;br /&gt;|   1 |  SORT AGGREGATE   |       |     1 |     3 |            |          |&lt;br /&gt;|*  2 |   INDEX RANGE SCAN| T1_N1 |     1 |     3 |     1   (0)| 00:00:01 |&lt;br /&gt;---------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   2 - access("C1"=:B1)&lt;br /&gt;&lt;br /&gt;SQL_ID  7dwqb1wjmp5hm, child number 1&lt;br /&gt;-------------------------------------&lt;br /&gt;select count(*) from t1 where c1 = :b1&lt;br /&gt;&lt;br /&gt;Plan hash value: 73337487&lt;br /&gt;&lt;br /&gt;---------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation         | Name  | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;---------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT  |       |       |       |     1 (100)|          |&lt;br /&gt;|   1 |  SORT AGGREGATE   |       |     1 |     3 |            |          |&lt;br /&gt;|*  2 |   INDEX RANGE SCAN| T1_N1 |     1 |     3 |     1   (0)| 00:00:01 |&lt;br /&gt;---------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   2 - access("C1"=:B1)&lt;br /&gt;&lt;br /&gt;SQL_ID  7dwqb1wjmp5hm, child number 2&lt;br /&gt;-------------------------------------&lt;br /&gt;select count(*) from t1 where c1 = :b1&lt;br /&gt;&lt;br /&gt;Plan hash value: 73337487&lt;br /&gt;&lt;br /&gt;---------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation         | Name  | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;---------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT  |       |       |       |     1 (100)|          |&lt;br /&gt;|   1 |  SORT AGGREGATE   |       |     1 |     3 |            |          |&lt;br /&gt;|*  2 |   INDEX RANGE SCAN| T1_N1 |     1 |     3 |     1   (0)| 00:00:01 |&lt;br /&gt;---------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   2 - access("C1"=:B1)&lt;br /&gt;&lt;br /&gt;SQL_ID  7dwqb1wjmp5hm, child number 3&lt;br /&gt;-------------------------------------&lt;br /&gt;select count(*) from t1 where c1 = :b1&lt;br /&gt;&lt;br /&gt;Plan hash value: 3724264953&lt;br /&gt;&lt;br /&gt;---------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;---------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT   |      |       |       |    53 (100)|          |&lt;br /&gt;|   1 |  SORT AGGREGATE    |      |     1 |     3 |            |          |&lt;br /&gt;|*  2 |   TABLE ACCESS FULL| T1   | 99173 |   290K|    53   (4)| 00:00:01 |&lt;br /&gt;---------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   2 - filter("C1"=:B1)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;76 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;PLAN_HASH_VALUEが実行計画を分けるに使用できるという特徴を利用したら、実行計画の変更を分析するに使用することまできます。例えば、AWRに格納されているTop SQLとSQLテキストは同様ですがPLAN_HASH_VALUEは違う新しいSQL文が現れたら、実行計画の変更が発生したのではないか疑われます。&lt;br /&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;1つ注意していただきたいこてゃ、1つのSQLテキストに複数の実行計画が持てるから、精密な分析は必要だということです。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-6728510925473613266?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/6728510925473613266/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/10/planhashvalue.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/6728510925473613266'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/6728510925473613266'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/10/planhashvalue.html' title='PLAN_HASH_VALUE'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-8917155752303236607</id><published>2010-09-27T23:23:00.000-07:00</published><updated>2010-09-27T23:52:21.946-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='10046、sql_trace'/><title type='text'>待機イベントが含まれている10046診断イベントの実行ーOracle11g</title><content type='html'>Oracle 11gで待機イベントが含まれている10046診断イベントの実行方法を整えてみました。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;b&gt;1. &lt;/b&gt;伝統的な10046診断イベントの使い方は次のとおりです。レベル８以上なら待機イベントが記録されます。 &lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;alter session set events '10046 trace name context forever, level 8';&lt;br /&gt;&lt;br /&gt;select /* 10046 */ count(*) from user_objects;&lt;br /&gt;&lt;br /&gt;alter session set events '10046 trace name context off';&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;2. &lt;/b&gt;11gからは次のようにsql_traceという「認識できる」名前の診断イベントが使えます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- sql_trace + level 8&lt;br /&gt;alter session set events 'sql_trace level 8';&lt;br /&gt;&lt;br /&gt;select /* sql_trace_1 */ count(*) from user_objects;&lt;br /&gt;&lt;br /&gt;alter session set events 'sql_trace off';&lt;br /&gt;&lt;br /&gt;-- sql_trace + wait=true&lt;br /&gt;alter session set events 'sql_trace wait=true';&lt;br /&gt;&lt;br /&gt;select /* sql_trace_2 */ count(*) from user_objects;&lt;br /&gt;&lt;br /&gt;alter session set events 'sql_trace off';&lt;br /&gt;&lt;br /&gt;-- sql_trace + wait=true, bind=true&lt;br /&gt;alter session set events 'sql_trace wait=true, bind=true';&lt;br /&gt;&lt;br /&gt;select /* sql_trace_3 */ count(*) from user_objects;&lt;br /&gt;&lt;br /&gt;alter session set events 'sql_trace off';&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;11gから追加された新しい診断イベント機能は強すぎて、次のように特定のSQLを特定することもできます。あまりに有効な機能であるんでしょう。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- SQL_IDを得て...&lt;br /&gt;select /* sql_trace_4 */ count(*) from user_objects;&lt;br /&gt;select /* sql_trace_5 */ count(*) from user_objects;&lt;br /&gt;&lt;br /&gt;col sql_id new_value sql_id1&lt;br /&gt;select sql_id &lt;br /&gt;from v$sqlarea &lt;br /&gt;where sql_text = 'select /* sql_trace_4 */ count(*) from user_objects';&lt;br /&gt;&lt;br /&gt;col sql_id new_value sql_id2&lt;br /&gt;select sql_id &lt;br /&gt;from v$sqlarea &lt;br /&gt;where sql_text = 'select /* sql_trace_5 */ count(*) from user_objects';&lt;br /&gt;&lt;br /&gt;-- 1つのSQLに対して&lt;br /&gt;alter session set events 'sql_trace [sql: &amp;sql_id1] wait=true';&lt;br /&gt;select /* sql_trace_4 */ count(*) from user_objects;&lt;br /&gt;select /* sql_trace_5 */ count(*) from user_objects;&lt;br /&gt;alter session set events 'sql_trace off';&lt;br /&gt;&lt;br /&gt;-- 複数のSQLに対して&lt;br /&gt;alter session set events 'sql_trace [sql: &amp;sql_id1 | &amp;sql_id2] wait=true';&lt;br /&gt;select /* sql_trace_4 */ count(*) from user_objects;&lt;br /&gt;select /* sql_trace_5 */ count(*) from user_objects;&lt;br /&gt;alter session set events 'sql_trace off';&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;3. &lt;/b&gt;DBMS_MONITORパッケージこそオラクルの公式的なお勧めの方法です。DBMS_MONITORパッケージもSQLを特定する機能が提供したらいいけど、開発者等がまだそこまでは考えていないようです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;exec dbms_monitor.session_trace_enable(waits=&gt;true);&lt;br /&gt;&lt;br /&gt;select /* dbms_monitor */ count(*) from user_objects;&lt;br /&gt;&lt;br /&gt;exec dbms_monitor.session_trace_disable;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;4. &lt;/b&gt;DBMS_SYSTEMパッケージやDBMS_SUPPORTパッケージなどの隠しパッケージも同様な機能を提供しています。でも、DBMS_MONITORパッケージが登場した以上必要がなくなったといえます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;col sid new_value sid&lt;br /&gt;col serial# new_value se&lt;br /&gt;select sid, serial# &lt;br /&gt;from v$session &lt;br /&gt;where sid = userenv('sid');&lt;br /&gt;&lt;br /&gt;exec sys.dbms_system.set_ev(&amp;sid, &amp;se, 10046, 8, null);&lt;br /&gt;&lt;br /&gt;select /* dbms_system */ count(*) from user_objects;&lt;br /&gt;&lt;br /&gt;exec sys.dbms_system.set_ev(&amp;sid, &amp;se, 10046, 0, null);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;SQL_ID値を利用して特定SQLに対してのみ診断イベントを行なう機能が特に有効に見えます。Oracle 11gの拡張された診断イベント機能は次の文書で詳細に紹介しています。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://tech.e2sn.com/oracle/troubleshooting/oradebug-doc"&gt;ORADEBUG DOC&lt;/a&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-8917155752303236607?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/8917155752303236607/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/09/10046oracle11g.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/8917155752303236607'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/8917155752303236607'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/09/10046oracle11g.html' title='待機イベントが含まれている10046診断イベントの実行ーOracle11g'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-2405704916951483540</id><published>2010-09-15T18:15:00.000-07:00</published><updated>2010-09-15T18:37:26.656-07:00</updated><title type='text'>Library Cache Pinに対するSelf Deadlockを作ってみよう。</title><content type='html'>Library Cache Pinに対するSelf Deadlockをとても易く作ってみます。まず次のように空いたプロシージャであるTEST_PROC1を作ります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;create or replace procedure test_proc1 &lt;br /&gt;is&lt;br /&gt;begin&lt;br /&gt; null;&lt;br /&gt;end;&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;次のPL/SQLブロックでTEST_PROC1を実行し、まのなくTEST_PROC1をコンパイルします。そうすると、セッションはハング状態に落ちってしまいます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1120&gt; begin&lt;br /&gt;  2     test_proc1;&lt;br /&gt;  3&lt;br /&gt;  4     execute immediate 'alter procedure test_proc1 compile';&lt;br /&gt;  5&lt;br /&gt;  6  end;&lt;br /&gt;  7  /&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;(Hang)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ASH(Active Session History)を通じて該当セッションの状態を分析してみると、WaiterとBlockerが一致していることが分かります。Self Deadlockという状態です。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;select * &lt;br /&gt;from (&lt;br /&gt; select&lt;br /&gt;  h.session_id as sid,&lt;br /&gt;  to_char(h.sample_time,'mi:ss') as sample_time,&lt;br /&gt;  h.sql_id,&lt;br /&gt;  (select sql_text from v$sqlarea a where a.sql_id = h.sql_id) as sql_text,&lt;br /&gt;  event,&lt;br /&gt;  blocking_session as blocker&lt;br /&gt; from&lt;br /&gt;  v$active_session_history h&lt;br /&gt; where&lt;br /&gt;  h.session_id = &amp;sid&lt;br /&gt; order by h.sample_time desc   &lt;br /&gt;) where rownum &lt;= 20 &lt;br /&gt;;     &lt;br /&gt;&lt;br /&gt; SID SAMPL SQL_ID        SQL_TEXT             EVENT         BLOCKER&lt;br /&gt;---- ----- ------------- -------------------- ---------- ----------&lt;br /&gt; 136 49:10                                    library ca        136&lt;br /&gt;                                              che pin&lt;br /&gt;&lt;br /&gt; 136 49:09                                    library ca        136&lt;br /&gt;                                              che pin&lt;br /&gt;&lt;br /&gt; 136 49:08                                    library ca        136&lt;br /&gt;                                              che pin&lt;br /&gt;&lt;br /&gt; 136 49:07                                    library ca        136&lt;br /&gt;                                              che pin&lt;br /&gt;&lt;br /&gt; 136 49:06                                    library ca        136&lt;br /&gt;                                              che pin&lt;br /&gt;&lt;br /&gt; 136 49:05                                    library ca        136&lt;br /&gt;                                              che pin&lt;br /&gt;&lt;br /&gt; 136 49:04                                    library ca        136&lt;br /&gt;                                              che pin&lt;br /&gt;&lt;br /&gt; 136 49:03                                    library ca        136&lt;br /&gt;                                              che pin&lt;br /&gt;&lt;br /&gt; 136 49:02                                    library ca        136&lt;br /&gt;                                              che pin&lt;br /&gt;&lt;br /&gt; 136 49:01                                    library ca        136&lt;br /&gt;                                              che pin&lt;br /&gt;&lt;br /&gt; 136 49:00                                    library ca        136&lt;br /&gt;                                              che pin&lt;br /&gt;&lt;br /&gt; 136 48:59                                    library ca        136&lt;br /&gt;                                              che pin&lt;br /&gt;&lt;br /&gt; 136 48:58                                    library ca        136&lt;br /&gt;                                              che pin&lt;br /&gt;&lt;br /&gt; 136 48:57                                    library ca        136&lt;br /&gt;                                              che pin&lt;br /&gt;&lt;br /&gt; 136 48:56                                    library ca        136&lt;br /&gt;                                              che pin&lt;br /&gt;&lt;br /&gt; 136 48:55                                    library ca        136&lt;br /&gt;                                              che pin&lt;br /&gt;&lt;br /&gt; 136 48:54                                    library ca        136&lt;br /&gt;                                              che pin&lt;br /&gt;&lt;br /&gt; 136 48:53                                    library ca        136&lt;br /&gt;                                              che pin&lt;br /&gt;&lt;br /&gt; 136 48:52                                    library ca        136&lt;br /&gt;                                              che pin&lt;br /&gt;&lt;br /&gt; 136 48:51                                    library ca        136&lt;br /&gt;                                              che pin&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;20 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://sites.google.com/site/tpackjp/"&gt;ティパック&lt;/a&gt;が提供する待機イベントの詳細情報からもっと詳しい状態が得られます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1120&gt; select * from table(tpack.session_detail(136,'wait_detail'))&lt;br /&gt;&lt;br /&gt;NAME                           VALUE&lt;br /&gt;------------------------------ --------------------&lt;br /&gt;SID                            136&lt;br /&gt;Serial#                        2797&lt;br /&gt;SPID                           5148&lt;br /&gt;Program                        sqlplus.exe&lt;br /&gt;Process                        5404:672&lt;br /&gt;Module                         SQL*Plus&lt;br /&gt;SQL ID                         9pbva4bn2m25b&lt;br /&gt;Child No                       0&lt;br /&gt;SQL Text                       alter procedure test&lt;br /&gt;                               _proc1 compile&lt;br /&gt;&lt;br /&gt;Status                         ACTIVE&lt;br /&gt;Blocking Instance              1&lt;br /&gt;Blocking Session               136&lt;br /&gt;SQL Exec Start                 2010/09/15 13:45:34&lt;br /&gt;Event                          library cache pin&lt;br /&gt;Seq#                           130&lt;br /&gt;P1(P1raw)                      384372376(0000000016&lt;br /&gt;                               E90E98)&lt;br /&gt;&lt;br /&gt;P2(P2raw)                      384372376(0000000016&lt;br /&gt;                               DAB608)&lt;br /&gt;&lt;br /&gt;P3(P3raw)                      384372376(00014F8500&lt;br /&gt;                               010003)&lt;br /&gt;&lt;br /&gt;Seconds in wait                40&lt;br /&gt;State                          WAITING&lt;br /&gt;Wait Event                     library cache pin&lt;br /&gt;Holder SID                     136&lt;br /&gt;Namespace                      TABLE/PROCEDURE&lt;br /&gt;Object                         TEST_PROC1&lt;br /&gt;Holding Mode                   2(S)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;理由は何と考えますか？&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-2405704916951483540?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/2405704916951483540/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/09/library-cache-pinself-deadlock.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/2405704916951483540'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/2405704916951483540'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/09/library-cache-pinself-deadlock.html' title='Library Cache Pinに対するSelf Deadlockを作ってみよう。'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-654635540217972488</id><published>2010-09-09T00:40:00.000-07:00</published><updated>2010-09-09T01:34:55.805-07:00</updated><title type='text'>Remote SQL</title><content type='html'>数日前に&lt;b&gt;データベースリンクを使う分散クエリ(Distributed Query)の動作原理&lt;/b&gt;に対する質問を受けました。それに対する答えを簡単なテストでします。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;テスト環境はOracle 11.1.0.6です。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1106&gt; -- version&lt;br /&gt;TPACK@ukja1106&gt; select * from v$version where rownum = 1;&lt;br /&gt;&lt;br /&gt;BANNER&lt;br /&gt;-----------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 - Production&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;分散クエリのためにループバックデータベースリンクを作ります。ループバックデータベースリンクとは自己自身がリモートデータベースとなるデータベースリンクとことを意味します。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1106&gt; -- create loopback database link&lt;br /&gt;TPACK@ukja1106&gt; create public database link loopback&lt;br /&gt;  2  connect to {user}&lt;br /&gt;  3  identified by {password}&lt;br /&gt;  4  using '{service_name}';&lt;br /&gt;&lt;br /&gt;Database link created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;テーブルT1、T2を作ります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1106&gt; -- create table&lt;br /&gt;TPACK@ukja1106&gt; create table t1(c1, c2)&lt;br /&gt;  2  as&lt;br /&gt;  3  select level, level&lt;br /&gt;  4  from dual&lt;br /&gt;  5  connect by level &lt;= 1000&lt;br /&gt;  6  ;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1106&gt; &lt;br /&gt;TPACK@ukja1106&gt; create table t2(c1, c2)&lt;br /&gt;  2  as&lt;br /&gt;  3  select level, level&lt;br /&gt;  4  from dual&lt;br /&gt;  5  connect by level &lt;= 1000&lt;br /&gt;  6  ;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1106&gt; &lt;br /&gt;TPACK@ukja1106&gt; create index t2_n1 on t2(c1);&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1106&gt; &lt;br /&gt;TPACK@ukja1106&gt; exec dbms_stats.gather_table_stats(user, 't1');&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1106&gt; exec dbms_stats.gather_table_stats(user, 't2');&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;テーブルT1(ローカル)が先行テーブルとなり、テーブルT2(リモート)が結合対象となる分散くえりの実行計画です。&lt;b&gt;Remote SQL Information&lt;/b&gt;という部分に注意してください。&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql; highlight:[29,30,31,32,33];"&gt;&lt;br /&gt;TPACK@ukja1106&gt; -- explain plan&lt;br /&gt;TPACK@ukja1106&gt; -- nested loops join&lt;br /&gt;TPACK@ukja1106&gt; &lt;br /&gt;TPACK@ukja1106&gt; -- execute it, but 0 row&lt;br /&gt;TPACK@ukja1106&gt; explain plan for&lt;br /&gt;  2  select /*+ leading(t1) use_nl(d) */&lt;br /&gt;  3   *&lt;br /&gt;  4  from t1, t2@loopback d&lt;br /&gt;  5  where t1.c1 = d.c1&lt;br /&gt;  6   and t1.c1 &lt; 0&lt;br /&gt;  7  ;&lt;br /&gt;&lt;br /&gt;TPACK@ukja1106&gt; select * from table(dbms_xplan.display);&lt;br /&gt;&lt;br /&gt;---------------------------------------------------&lt;br /&gt;| Id  | Operation          | Name | Inst   |IN-OUT|&lt;br /&gt;---------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT   |      |        |      |&lt;br /&gt;|   1 |  NESTED LOOPS      |      |        |      |&lt;br /&gt;|*  2 |   TABLE ACCESS FULL| T1   |        |      |&lt;br /&gt;|   3 |   REMOTE           | T2   | LOOPB~ | R-&gt;S |&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   2 - filter("T1"."C1"&lt;0)&lt;br /&gt;&lt;br /&gt;Remote SQL Information (identified by operation id):&lt;br /&gt;----------------------------------------------------&lt;br /&gt;&lt;br /&gt;   3 - SELECT /*+ USE_NL ("D") */ "C1","C2" FROM "T2" "D" WHERE "C1"&lt;0 AND :1="C1"&lt;br /&gt;       (accessing 'LOOPBACK' )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Remote SQLとは分散くえりを実行する時、リモートデータベースからデータを伝送させられうためにリモートデータベースで実行するSQLのことです。すなわち、ローカルデータベースはRemote SQLをリモートデータベースに発行してほしいデータを受け付けます。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;ローカルデータベースはリモートデータベースからリモートテーブルとインデックスの基本統計情報を伝送されます。その情報を利用して実行計画を作ります。この過程で&lt;b&gt;Remote SQL&lt;/b&gt;を作ります。Remote SQLは&lt;b&gt;実際にデータを伝送されるために実行するまでは&lt;/b&gt;リモートデータベースに送りません。&lt;br /&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;これを証明するために、クエリを実行した後、リモートデータベースでRemote SQLが実行されたかどうか確認してみます。次のクエリを実行したら、先行テーブルから一件の行もでないので（t1.c1 &lt; 0 の条件のため）リモートデータベースへのデータリクエストもないはずです。つぎの結果を見ると、この推測があたることが分かります。&lt;br /&gt;&lt;pre class="brush: sql; highlight:[15]; "&gt;&lt;br /&gt;TPACK@ukja1106&gt; select /*+ leading(t1) use_nl(d) */&lt;br /&gt;  2   *&lt;br /&gt;  3  from t1, t2@loopback d&lt;br /&gt;  4  where t1.c1 = d.c1&lt;br /&gt;  5   and t1.c1 &lt; 0&lt;br /&gt;  6  ;&lt;br /&gt;&lt;br /&gt;no rows selected&lt;br /&gt;&lt;br /&gt;TPACK@ukja1106&gt; &lt;br /&gt;TPACK@ukja1106&gt; select sql_id, executions&lt;br /&gt;  2  from v$sqlarea&lt;br /&gt;  3  where sql_text = 'SELECT /*+ USE_NL ("D") */ "C1","C2" FROM "T2" "D" WHERE "C1"&lt;0 AND :1="C1"';&lt;br /&gt;&lt;br /&gt;no rows selected&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;今度は同一なテストを1,000回のデータリクエストをリモートデータベースへ送る分散クエリに対して修行してみます。&lt;br /&gt;&lt;pre class="brush: sql; highlight:[23,24,25,26,27]; "&gt;&lt;br /&gt;TPACK@ukja1106&gt; -- execute it, for 1000 rows&lt;br /&gt;TPACK@ukja1106&gt; explain plan for&lt;br /&gt;  2  select /*+ leading(t1) use_nl(d) */&lt;br /&gt;  3   *&lt;br /&gt;  4  from t1, t2@loopback d&lt;br /&gt;  5  where t1.c1 = d.c1&lt;br /&gt;  6  ;&lt;br /&gt;&lt;br /&gt;Explained.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1106&gt; &lt;br /&gt;TPACK@ukja1106&gt; select * from table(dbms_xplan.display);&lt;br /&gt;&lt;br /&gt;---------------------------------------------------&lt;br /&gt;| Id  | Operation          | Name | Inst   |IN-OUT|&lt;br /&gt;---------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT   |      |        |      |&lt;br /&gt;|   1 |  NESTED LOOPS      |      |        |      |&lt;br /&gt;|   2 |   TABLE ACCESS FULL| T1   |        |      |&lt;br /&gt;|   3 |   REMOTE           | T2   | LOOPB~ | R-&gt;S |&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;Remote SQL Information (identified by operation id):&lt;br /&gt;----------------------------------------------------&lt;br /&gt;&lt;br /&gt;   3 - SELECT /*+ USE_NL ("D") */ "C1","C2" FROM "T2" "D" WHERE :1="C1" (accessing&lt;br /&gt;       'LOOPBACK' )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;1,000回のデータリクエストを送った結果、Remote SQLがリモートデータベースで1,000回修行されました。これはまるでSQL*Plusで該当くえりを1,000回修行したのは同一です。&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1106&gt; select /*+ leading(t1) use_nl(d) */&lt;br /&gt;  2   *&lt;br /&gt;  3  from t1, t2@loopback d&lt;br /&gt;  4  where t1.c1 = d.c1&lt;br /&gt;  5  ;&lt;br /&gt;&lt;br /&gt;        C1         C2         C1         C2&lt;br /&gt;---------- ---------- ---------- ----------&lt;br /&gt;         1          1          1          1&lt;br /&gt;         2          2          2          2&lt;br /&gt;         3          3          3          3&lt;br /&gt;...&lt;br /&gt;       999        999        999        999&lt;br /&gt;      1000       1000       1000       1000&lt;br /&gt;&lt;br /&gt;1000 rows selected.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1106&gt; &lt;br /&gt;TPACK@ukja1106&gt; col sql_id new_value sql_id&lt;br /&gt;TPACK@ukja1106&gt; select sql_id, executions&lt;br /&gt;  2  from v$sqlarea&lt;br /&gt;  3  where sql_text = 'SELECT /*+ USE_NL ("D") */ "C1","C2" FROM "T2" "D" WHERE :1="C1"';&lt;br /&gt;&lt;br /&gt;SQL_ID        EXECUTIONS&lt;br /&gt;------------- ----------&lt;br /&gt;6skxmvb24s6v4       1000&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;DBMS_XPLAN.DISPLAY_CURSOR関数を利用したら、リモートデータベースでのRemote SQLの実行計画も分かります。次のようにIndex Range Scanが選択されました。&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1106&gt; select * from table(dbms_xplan.display_cursor('&amp;sql_id', null));&lt;br /&gt;&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT            |       |       |       |     2 (100)|          |&lt;br /&gt;|   1 |  TABLE ACCESS BY INDEX ROWID| T2    |     1 |     7 |     2   (0)| 00:00:01 |&lt;br /&gt;|*  2 |   INDEX RANGE SCAN          | T2_N1 |     1 |       |     1   (0)| 00:00:01 |&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   2 - access("C1"=:1)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;今度はNested Loops JoinではなくてHash Joinに対して同一なテストを修行してみます。Remote SQLは次のようです。&lt;br /&gt;&lt;pre class="brush: sql; highlight:[28,29,30,31]; "&gt;&lt;br /&gt;TPACK@ukja1106&gt; explain plan for&lt;br /&gt;  2  select /*+ leading(t1) use_hash(d) */&lt;br /&gt;  3   *&lt;br /&gt;  4  from t1, t2@loopback d&lt;br /&gt;  5  where t1.c1 = d.c1&lt;br /&gt;  6  ;&lt;br /&gt;&lt;br /&gt;Explained.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1106&gt; &lt;br /&gt;TPACK@ukja1106&gt; select * from table(dbms_xplan.display);&lt;br /&gt;&lt;br /&gt;---------------------------------------------------&lt;br /&gt;| Id  | Operation          | Name | Inst   |IN-OUT|&lt;br /&gt;---------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT   |      |        |      |&lt;br /&gt;|*  1 |  HASH JOIN         |      |        |      |&lt;br /&gt;|   2 |   TABLE ACCESS FULL| T1   |        |      |&lt;br /&gt;|   3 |   REMOTE           | T2   | LOOPB~ | R-&gt;S |&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   1 - access("T1"."C1"="D"."C1")&lt;br /&gt;&lt;br /&gt;Remote SQL Information (identified by operation id):&lt;br /&gt;----------------------------------------------------&lt;br /&gt;&lt;br /&gt;   3 - SELECT /*+ USE_HASH ("D") */ "C1","C2" FROM "T2" "D" (accessing 'LOOPBACK' )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;たとえ1,000件をフェッチするのは同様ですが、Hash Joinの特性上リモートデータベースへRemote SQLを1,000回送ることではなく、ただ一回の実行でほしいデータを受け付けることができます。したがって実行回数(EXECUTIONS)は”１”となっています。&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1106&gt; select /*+ leading(t1) use_hash(d) */&lt;br /&gt;  2   *&lt;br /&gt;  3  from t1, t2@loopback d&lt;br /&gt;  4  where t1.c1 = d.c1&lt;br /&gt;  5  ;&lt;br /&gt;&lt;br /&gt;        C1         C2         C1         C2&lt;br /&gt;---------- ---------- ---------- ----------&lt;br /&gt;         1          1          1          1&lt;br /&gt;         2          2          2          2&lt;br /&gt;         3          3          3          3&lt;br /&gt;...&lt;br /&gt;       999        999        999        999&lt;br /&gt;      1000       1000       1000       1000&lt;br /&gt;&lt;br /&gt;1000 rows selected.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1106&gt; col sql_id new_value sql_id&lt;br /&gt;TPACK@ukja1106&gt; select sql_id, executions&lt;br /&gt;  2  from v$sqlarea&lt;br /&gt;  3  where sql_text = 'SELECT /*+ USE_HASH ("D") */ "C1","C2" FROM "T2" "D"';&lt;br /&gt;&lt;br /&gt;SQL_ID        EXECUTIONS&lt;br /&gt;------------- ----------&lt;br /&gt;0uksumbhuswyx          1&lt;br /&gt;&lt;br /&gt;1 row selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;そしてRemote SQLはTable Full Scanの実行計画を持ちます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1106&gt; select * from table(dbms_xplan.display_cursor('&amp;sql_id', null));&lt;br /&gt;&lt;br /&gt;SQL_ID  0uksumbhuswyx, child number 0&lt;br /&gt;-------------------------------------&lt;br /&gt;SELECT /*+ USE_HASH ("D") */ "C1","C2" FROM "T2" "D"&lt;br /&gt;&lt;br /&gt;Plan hash value: 1513984157&lt;br /&gt;&lt;br /&gt;--------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;--------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT  |      |       |       |     3 (100)|          |&lt;br /&gt;|   1 |  TABLE ACCESS FULL| T2   |  1000 |  7000 |     3   (0)| 00:00:01 |&lt;br /&gt;--------------------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;上の一連のテスト結果を見ると次のような結論が下せます。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt; &lt;li&gt;分散くえりの動作方式はローカルクエリの動作方式とほとんど同一。&lt;br /&gt; &lt;li&gt;リモートデータベースからデータを伝送されるためRemote SQLをリモートデータベースの送る。&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;これから、分散クエリをテストする時、上のような方法の分析が役に立つことをお願いします。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-654635540217972488?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/654635540217972488/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/09/remote-sql.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/654635540217972488'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/654635540217972488'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/09/remote-sql.html' title='Remote SQL'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-5142380199877856582</id><published>2010-08-23T23:40:00.000-07:00</published><updated>2010-08-24T00:48:08.028-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Deferred Segment Creation'/><title type='text'>Deferred Segment Creation機能の面白い2つのバグ</title><content type='html'>Oracle 11gR2では&lt;b&gt;Deferred Segment Creation&lt;/b&gt;という機能が追加されました。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://download.oracle.com/docs/cd/E11882_01/server.112/e10592/statements_7002.htm#CEGJHDEB"&gt;マニュアルはこちら&lt;/a&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;簡単に言えば、CREATE TABLE文を実行しても実際にデータが追加されるまでにはセグメントが生成されない機能です。このポストではDeferred Segment CreationとINSERT ... SELECT文に関する面白い2つのバグを紹介します。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;バグ9078678&lt;/b&gt;: セグメントのないテーブルへの並列INSERT ... SELECT文に対しる予想実行計画（Explain Plan）は並列ではなく直列実行計画に表示されます。しかし、実際には並列で行なわれます。&lt;br /&gt;&lt;li&gt;&lt;b&gt;バグ9329566&lt;/b&gt;: セグメントのないテーブルに対してINSERT ...SELECT文を実行すれば、SELECT文を二回実行するバグです。&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;2つのバグはすべてセグメントかないという特徴から発生するバグです。簡単なテストケースを見ましょう。まず&lt;b&gt;バグ9078678&lt;/b&gt;に当たる現象です。&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;b&gt;1. &lt;/b&gt;オラクルバージョンは11.2.0.1です。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1120&gt; -- version&lt;br /&gt;TPACK@ukja1120&gt; select * from v$version where rownum = 1;&lt;br /&gt;&lt;br /&gt;BANNER&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;2. &lt;/b&gt;テーブルT1を作ります。テーブルT1はセグメントが存在しない状態です。テーブルT1に対して並列INSERT文章を行なう場合実行計画がどうなるか見ましょう。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1120&gt; create table t1(c1 number);&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; alter session enable parallel dml;&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; explain plan for&lt;br /&gt;  2  insert /*+ parallel(t1 4) */ into t1&lt;br /&gt;  3  select level from dual connect by level &lt;= 10000;&lt;br /&gt;&lt;br /&gt;Explained.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; select * from table(dbms_xplan.display);&lt;br /&gt;&lt;br /&gt;------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                     | Name | Rows  | Cost (%CPU)| Time     |&lt;br /&gt;------------------------------------------------------------------------------&lt;br /&gt;|   0 | INSERT STATEMENT              |      |     1 |     2   (0)| 00:00:01 |&lt;br /&gt;|   1 |  LOAD AS SELECT               | T1   |       |            |          |&lt;br /&gt;|*  2 |   CONNECT BY WITHOUT FILTERING|      |       |            |          |&lt;br /&gt;|   3 |    FAST DUAL                  |      |     1 |     2   (0)| 00:00:01 |&lt;br /&gt;------------------------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ヒントを付けて並列実行を指定したが直列実行の実行計画が選択されました。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;b&gt;3. &lt;/b&gt;予想実行計画ではなくて&lt;b&gt;実際にSQL文を実行した後&lt;/b&gt;並列で実行するかどうかを見ましょう。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1120&gt; insert /*+ parallel(t1 4) */ into t1&lt;br /&gt;  2  select level from dual connect by level &lt;= 10000;&lt;br /&gt;&lt;br /&gt;10000 rows created.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; &lt;br /&gt;TPACK@ukja1120&gt; commit;&lt;br /&gt;&lt;br /&gt;Commit complete.&lt;br /&gt;&lt;br /&gt;-- https://sites.google.com/site/ukja/sql-scripts-1/o-s/pqstat&lt;br /&gt;TPACK@ukja1120&gt; @pq_stat&lt;br /&gt;&lt;br /&gt;DFO_NUMBER      TQ_ID SERVER_TYP PROCESS      NUM_ROWS &lt;br /&gt;---------- ---------- ---------- ---------- ---------- &lt;br /&gt;         1          0 Consumer   P000             2500 &lt;br /&gt;         1          0 Consumer   P001             2500 &lt;br /&gt;         1          0 Consumer   P002             2500 &lt;br /&gt;         1          0 Consumer   P003             2500 &lt;br /&gt;         1          0 Producer   QC              10000 &lt;br /&gt;         1          1 Consumer   QC                  4 &lt;br /&gt;         1          1 Producer   P000                1 &lt;br /&gt;         1          1 Producer   P001                1 &lt;br /&gt;         1          1 Producer   P002                1 &lt;br /&gt;         1          1 Producer   P003                1 &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;10 rows selected.&lt;br /&gt;&lt;br /&gt;STATISTIC                      LAST_QUERY SESSION_TOTAL&lt;br /&gt;------------------------------ ---------- -------------&lt;br /&gt;Queries Parallelized                    0             0&lt;br /&gt;DML Parallelized                        1             1&lt;br /&gt;DDL Parallelized                        0             0&lt;br /&gt;DFO Trees                               1             1&lt;br /&gt;Server Threads                          4             0&lt;br /&gt;Allocation Height                       4             0&lt;br /&gt;Allocation Width                        1             0&lt;br /&gt;Local Msgs Sent                        44            44&lt;br /&gt;Distr Msgs Sent                         0             0&lt;br /&gt;Local Msgs Recv'd                      44            44&lt;br /&gt;Distr Msgs Recv'd                       0             0&lt;br /&gt;&lt;br /&gt;11 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Explain Planをよる予想実行計画では直列実行という結果がでてきたが、実際の結果からは並列に実行されたのがわかります。もっと面白いのはこの状態で（すなわち、テーブルT1にデータが追加された状態）予想実行計画を見直すと並列実行計画にかえるとくうことです。&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1120&gt; explain plan for&lt;br /&gt;  2  insert /*+ append parallel(t1 4) */ into t1&lt;br /&gt;  3  select level from dual connect by level &lt;= 10000;&lt;br /&gt;&lt;br /&gt;Explained.&lt;br /&gt;&lt;br /&gt;------------------------------------------------------&lt;br /&gt;| Id  | Operation                         | Name     |&lt;br /&gt;------------------------------------------------------&lt;br /&gt;|   0 | INSERT STATEMENT                  |          |&lt;br /&gt;|   1 |  PX COORDINATOR                   |          |&lt;br /&gt;|   2 |   PX SEND QC (RANDOM)             | :TQ10001 |&lt;br /&gt;|   3 |    LOAD AS SELECT                 | T1       |&lt;br /&gt;|   4 |     PX RECEIVE                    |          |&lt;br /&gt;|   5 |      PX SEND ROUND-ROBIN          | :TQ10000 |&lt;br /&gt;|*  6 |       CONNECT BY WITHOUT FILTERING|          |&lt;br /&gt;|   7 |        FAST DUAL                  |          |&lt;br /&gt;------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;これはバグです。セグメントがまだ生成されていないテーブルに対する並列実行文章の予想実行計画はまるで直列に実行されうように見えるが、実際には並列に実行されるバグです。すなわち、セグメントのないテーブルに対する並列実行文章の予想実行計画を正確に見せてくれないバグです。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;b&gt;バグ9329566&lt;/b&gt;に該当するテストケースも見ましょう。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;b&gt;1. &lt;/b&gt;テーブルT1とテーブルT2を同一に生成します。ただ、テーブルT2には一件の行を追加してセグメントもあらかじめ作っておきます。テーブルT1はセグメントがまだ存在しない状態です。10,000ブロックのサイズのテーブルT3も作っておきます。&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1120&gt; create table t1(c1 varchar2(2000), c2 varchar2(2000), c3 varchar2(2000), c4 varchar2(1000));&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; create table t2(c1 varchar2(2000), c2 varchar2(2000), c3 varchar2(2000), c4 varchar2(1000));&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; &lt;br /&gt;TPACK@ukja1120&gt; -- insert 1 row into table t2 to create the segment&lt;br /&gt;TPACK@ukja1120&gt; insert into t2 values('1','1','1','1');&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; -- table size 10000 block&lt;br /&gt;TPACK@ukja1120&gt; create table t3&lt;br /&gt;  2  as&lt;br /&gt;  3  select rpad('x',2000) as c1, rpad('x',2000) as c2, rpad('x',2000) as c3, rpad('x',1000) as c4&lt;br /&gt;  4  from dual&lt;br /&gt;  5  connect by level &lt;= 10000;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;2. &lt;/b&gt;テーブルT3から最高値を読み込んでテーブルT1（セグメントない）、テーブルT2（セグメントある）へINSERTするクエリのそれぞれのConsistent Getsを比べてみましょう。手軽なテストのために&lt;b&gt;&lt;a href="https://sites.google.com/site/otpack/"&gt;ティパックのSession Snapshot Report&lt;/a&gt;&lt;/b&gt;を利用します。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1120&gt; exec tpack.begin_session_snapshot;&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; insert into t1&lt;br /&gt;  2  select max(c1), max(c2), max(c3), max(c4) from t3;&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; exec tpack.add_session_snapshot;&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; insert into t2&lt;br /&gt;  2  select max(c1), max(c2), max(c3), max(c4) from t3;&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; &lt;br /&gt;TPACK@ukja1120&gt; exec tpack.add_session_snapshot;&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;次の結果を見たら、テーブルT1（セグメントない）に対するINSERTがテーブルT2（セグメントある）に対するINSERTに比べて2倍程度のConsistentを見せます。&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1120&gt; col item format a40&lt;br /&gt;TPACK@ukja1120&gt; col deltas format a20&lt;br /&gt;TPACK@ukja1120&gt; select item, deltas from table(tpack.session_snapshot_report)&lt;br /&gt;  2  where type = 'STAT';&lt;br /&gt;&lt;br /&gt;TPACK@ukja1120&gt; select item, deltas from table(tpack.session_snapshot_report)&lt;br /&gt;  2  where type = 'STAT';&lt;br /&gt;&lt;br /&gt;ITEM                                     DELTAS&lt;br /&gt;---------------------------------------- --------------------&lt;br /&gt;...&lt;br /&gt;physical read bytes                      164339712-&gt;82305024&lt;br /&gt;consistent gets                          20451-&gt;10327&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;これもやはりバグです。&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;テーブルT3からデータを読んでからそのデータをテーブルT1へINSERTしようとします。&lt;br /&gt;&lt;li&gt;どころが、テーブルT1はまだセグメントがありません。したがってまずセグメントを作ります。&lt;br /&gt;&lt;li&gt;ここでオラクルは1番段階で獲得したデータを再活用できずまたテーブルT3からデータを読み込みます。現在のクエリはテーブルT3から最高値(MAX)を取り出しているから、クエリを実行するたびにテーブルのサイズである10,000ブロックを全部読み込まなければなりません。このために、テーブルT1に対するINSERTの際には20,000ブロック（二回読み込むから）、テーブルT2に対するINSERTは10,000ブロック（1回読み込むから）を読み込むようになるのです。&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;このバグたちはセグメントのないテーブルに対してのみ発生するから致命的なバグとは言いかねます。むしろ&lt;b&gt;開発者がいくら基本的な罠に陥るのかをわかる良い例&lt;/b&gt;だといえます。オラクルで発生する多い性能問題がこのようなロジック穴から発生します。性能問題が起こった時このようなロジック穴を見破るテスト能力備えることが重要だといえます。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-5142380199877856582?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/5142380199877856582/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/08/deferred-segment-creation2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/5142380199877856582'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/5142380199877856582'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/08/deferred-segment-creation2.html' title='Deferred Segment Creation機能の面白い2つのバグ'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-4796158756140187158</id><published>2010-08-16T19:12:00.000-07:00</published><updated>2010-08-16T21:17:33.002-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='_nlj_batching_enabled'/><category scheme='http://www.blogger.com/atom/ns#' term='NO_NLJ_BATCHING'/><category scheme='http://www.blogger.com/atom/ns#' term='Batching NLJ'/><category scheme='http://www.blogger.com/atom/ns#' term='_nlj_batching_misses_enabled'/><title type='text'>Batching NLJ最適化と整列</title><content type='html'>Batching NLJ最適化って聞いたこたあります？Batching NLJとはOracle 11gで紹介されたNested Loops Joinの最適化技法であります。例えば、次の２つの実行計画を見てください。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- Oracle 10g&lt;br /&gt;------------------------------------------------&lt;br /&gt;| Id  | Operation                      | Name  |&lt;br /&gt;------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT               |       |&lt;br /&gt;|*  1 |  COUNT STOPKEY                 |       |&lt;br /&gt;|   2 |   TABLE ACCESS BY INDEX ROWID  | T2    |  &lt;-- Here&lt;br /&gt;|   3 |    NESTED LOOPS                |       |&lt;br /&gt;|   4 |     TABLE ACCESS BY INDEX ROWID| T1    |&lt;br /&gt;|*  5 |      INDEX RANGE SCAN          | T1_N1 |&lt;br /&gt;|*  6 |     INDEX RANGE SCAN           | T2_N1 |&lt;br /&gt;------------------------------------------------&lt;br /&gt;&lt;br /&gt;-- Oracle 11g&lt;br /&gt;------------------------------------------------&lt;br /&gt;| Id  | Operation                      | Name  |&lt;br /&gt;------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT               |       |&lt;br /&gt;|*  1 |  COUNT STOPKEY                 |       |&lt;br /&gt;|   2 |   NESTED LOOPS                 |       |&lt;br /&gt;|   3 |    NESTED LOOPS                |       |&lt;br /&gt;|   4 |     TABLE ACCESS BY INDEX ROWID| T1    |&lt;br /&gt;|*  5 |      INDEX RANGE SCAN          | T1_N1 |&lt;br /&gt;|*  6 |     INDEX RANGE SCAN           | T2_N1 |&lt;br /&gt;|   7 |    TABLE ACCESS BY INDEX ROWID | T2    |   &lt;-- And here&lt;br /&gt;------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;TABLE ACCESS BY INDEX ROWID (T2)&lt;/b&gt;のオペレーションの位置の違いに気付いたんでしょうか。これがOracle 11gのNested Loops Join最適化(Batching NLJ)の現れ方です。Batching NLJによって、論理読み込みが減り、性能が効率的になるはずです。&lt;br /&gt;&lt;br /&gt;さて、ほんの数日前、Batching NLJ関連の変な整列問題に出会いました。下記は再現のできるテストケースです。ORDER BYのオーバーヘッドなく整列をするために、索引T1_N1を利用していることに注意してください。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;create table t1&lt;br /&gt;as&lt;br /&gt;select 1 as c1, mod(level, 4) as c2, level as c3, level as c4, rpad('x',1000) as dummy&lt;br /&gt;from dual&lt;br /&gt;connect by level &lt;= 1000;&lt;br /&gt;&lt;br /&gt;create table t2&lt;br /&gt;as&lt;br /&gt;select 1001-level as c1, level as c2, rpad('x',1000) as dummy&lt;br /&gt;from dual&lt;br /&gt;connect by level &lt;= 100;&lt;br /&gt;&lt;br /&gt;create index t1_n1 on t1(c1, c2, c3);&lt;br /&gt;create index t2_n1 on t2(c1);&lt;br /&gt;&lt;br /&gt;exec dbms_stats.gather_table_stats(user, 't1');&lt;br /&gt;exec dbms_stats.gather_table_stats(user, 't2');&lt;br /&gt;&lt;br /&gt;explain plan for&lt;br /&gt;select /*+ leading(t1 t2) use_nl(t2) index_asc(t1) index_asc(t2) */&lt;br /&gt; rownum as rnum,&lt;br /&gt; t2.c1,&lt;br /&gt; t1.c4,&lt;br /&gt; t2.c2&lt;br /&gt;from t1, t2&lt;br /&gt;where&lt;br /&gt; t1.c3 = t2.c1&lt;br /&gt; and t1.c1 = 1&lt;br /&gt; and t1.c2 = 0&lt;br /&gt; and rownum &lt;= 20&lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;select * from table(dbms_xplan.display);&lt;br /&gt;&lt;br /&gt;-- Read from the disk&lt;br /&gt;alter system flush buffer_cache;&lt;br /&gt;&lt;br /&gt;select * from (&lt;br /&gt; select /*+ leading(t1 t2) use_nl(t2) index_asc(t1) index_asc(t2) */&lt;br /&gt;  rownum as rnum,&lt;br /&gt;  t2.c1,&lt;br /&gt;  t1.c4,&lt;br /&gt;  t2.c2&lt;br /&gt; from t1, t2&lt;br /&gt; where&lt;br /&gt;  t1.c3 = t2.c1&lt;br /&gt;  and t1.c1 = 1&lt;br /&gt;  and t1.c2 = 0&lt;br /&gt;  and rownum &lt;= 20&lt;br /&gt;) where rnum &gt;= 15&lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;-- Read from the buffer cache&lt;br /&gt;select * from (&lt;br /&gt; select /*+ leading(t1 t2) use_nl(t2) index_asc(t1) index_asc(t2) */&lt;br /&gt;  rownum as rnum,&lt;br /&gt;  t2.c1,&lt;br /&gt;  t1.c4,&lt;br /&gt;  t2.c2&lt;br /&gt; from t1, t2&lt;br /&gt; where&lt;br /&gt;  t1.c3 = t2.c1&lt;br /&gt;  and t1.c1 = 1&lt;br /&gt;  and t1.c2 = 0&lt;br /&gt;  and rownum &lt;= 20&lt;br /&gt;) where rnum &gt;= 15&lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;-- Disable exceptions for buffer cache misses&lt;br /&gt;alter session set "_nlj_batching_misses_enabled" = 0;&lt;br /&gt;&lt;br /&gt;-- Read from the disk&lt;br /&gt;alter system flush buffer_cache;&lt;br /&gt;&lt;br /&gt;select * from (&lt;br /&gt; select /*+ leading(t1 t2) use_nl(t2) index_asc(t1) index_asc(t2) */&lt;br /&gt;  rownum as rnum,&lt;br /&gt;  t2.c1,&lt;br /&gt;  t1.c4,&lt;br /&gt;  t2.c2&lt;br /&gt; from t1, t2&lt;br /&gt; where&lt;br /&gt;  t1.c3 = t2.c1&lt;br /&gt;  and t1.c1 = 1&lt;br /&gt;  and t1.c2 = 0&lt;br /&gt;  and rownum &lt;= 20&lt;br /&gt;) where rnum &gt;= 15&lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;-- Read from the buffer cache&lt;br /&gt;select * from (&lt;br /&gt; select /*+ leading(t1 t2) use_nl(t2) index_asc(t1) index_asc(t2) */&lt;br /&gt;  rownum as rnum,&lt;br /&gt;  t2.c1,&lt;br /&gt;  t1.c4,&lt;br /&gt;  t2.c2&lt;br /&gt; from t1, t2&lt;br /&gt; where&lt;br /&gt;  t1.c3 = t2.c1&lt;br /&gt;  and t1.c1 = 1&lt;br /&gt;  and t1.c2 = 0&lt;br /&gt;  and rownum &lt;= 20&lt;br /&gt;) where rnum &gt;= 15&lt;br /&gt;;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;時間の節約のために、コメントが付いた簡単な結果を見ましょう。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- Case1 : batching NLJ enabled&lt;br /&gt;-- when the query reads from the disk&lt;br /&gt;      RNUM         C1         C4         C2&lt;br /&gt;---------- ---------- ---------- ----------&lt;br /&gt;        15        960        960         41&lt;br /&gt;        16        964        964         37&lt;br /&gt;        17        980        980         21  &lt;-- Why 980 here?&lt;br /&gt;        18        968        968         33&lt;br /&gt;        19        972        972         29&lt;br /&gt;        20        976        976         25&lt;br /&gt;&lt;br /&gt;-- when the query reads from the buffer cache&lt;br /&gt;      RNUM         C1         C4         C2&lt;br /&gt;---------- ---------- ---------- ----------&lt;br /&gt;        15        960        960         41&lt;br /&gt;        16        964        964         37&lt;br /&gt;        17        968        968         33&lt;br /&gt;        18        972        972         29&lt;br /&gt;        19        976        976         25&lt;br /&gt;        20        980        980         21&lt;br /&gt;&lt;br /&gt;-- Case 2: batching NLJ disabled&lt;br /&gt;-- when the query reads from the disk&lt;br /&gt;      RNUM         C1         C4         C2&lt;br /&gt;---------- ---------- ---------- ----------&lt;br /&gt;        15        960        960         41&lt;br /&gt;        16        964        964         37&lt;br /&gt;        17        968        968         33&lt;br /&gt;        18        972        972         29&lt;br /&gt;        19        976        976         25&lt;br /&gt;        20        980        980         21&lt;br /&gt;&lt;br /&gt;-- when the query reads from the buffer cache&lt;br /&gt;      RNUM         C1         C4         C2&lt;br /&gt;---------- ---------- ---------- ----------&lt;br /&gt;        15        960        960         41&lt;br /&gt;        16        964        964         37&lt;br /&gt;        17        968        968         33&lt;br /&gt;        18        972        972         29&lt;br /&gt;        19        976        976         25&lt;br /&gt;        20        980        980         21&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;いかがでしょう。整列の順序に変わったこと&lt;br /&gt;&lt;br /&gt;これは次のように解説できます。&lt;br /&gt;「新しいNested Loops Joinの最適化コードは行が整列されたまま返されることを保障しない。特にディスクからデータを読み出している際には」&lt;br /&gt;&lt;br /&gt;これはページネーションクエリ(Pagination Query)を索引とNested Loops Joinを利用して作成したい時に制約になる可能性があると思います。では、この制約がバグというのではありません。オラクルで整列順序を保障する雄一な方法はORDER BY句を与えることだけからです。&lt;br /&gt;&lt;br /&gt;この制約を抜けていくためには次の方法で1つくらいを進めます。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt; &lt;li&gt;隠しパラメーター_nlj_batching_misses_enabledを0にセット&lt;br /&gt; &lt;li&gt;隠しパラメーター_nlj_batching_enabledを0にセット&lt;br /&gt; &lt;li&gt;NO_NLJ_BATCHING(T2)ヒントの追加&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-4796158756140187158?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/4796158756140187158/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/08/batching-nlj.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/4796158756140187158'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/4796158756140187158'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/08/batching-nlj.html' title='Batching NLJ最適化と整列'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-8729181139829158662</id><published>2010-08-10T21:36:00.000-07:00</published><updated>2010-08-15T23:57:08.383-07:00</updated><title type='text'>「ティパック」知能的な待機イベントの分析 - Part 1</title><content type='html'>オラクルが提供する&lt;b&gt;待機イベント&lt;/b&gt;とは本?に?特です。どんなDBMSでも待機現象をこんなに詳細に報告してくれる製品はありません。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;これはオラクルの性能問題を分析かつ解決すべき私たちには大きいプレゼントである同時?みであります。まるで初?の運?者にポルシェを?えるのと同じだと言えないでしょうか。ポルシェのような車を走らせようとしたらまず丈夫な運?わざが必要です。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;待機イベント分析も同?です。正確な分析のためにはオラクルのア?キテクチャ?に?する相?な知識が必要です。これが待機イベント分析の技法がより?範?に使用されうことに邪魔となっています。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;待機イベント分析で1つ?をつけなければならないのが大?イベントだけでは意味のある情報を引き出し難しいということです。?連のあるほかの情報が必要である場合がたくさんあります。ティパックではこれを&lt;b&gt;知能的な待機イベントの分析&lt;/b&gt;と呼びます。例えをあげてみればこのような追加的な情報が必要です。&lt;br /&gt;&lt;ol&gt;&lt;br /&gt; &lt;li&gt;cache buffers chainsラッチで競合が?生する場合、おもにどんなブロックで?生しているかわかる？&lt;br /&gt; &lt;li&gt;　row cache objectsラッチで競合が?生する場合、おもにどんなディクショナリ?オブジェクトなのかわかる？&lt;br /&gt; &lt;li&gt;db file sequential readのようなI/O待機いの場合、どんなセグメントのどのような種類のブロックで?生するかわかる？&lt;br /&gt; &lt;li&gt;ラッチ、ロック（Enqueue）、library cache lock、library cache pin、row cache lock、Mutexなどの同期化客?で競合が?生する場合、ホルダ?情報および?連の客?の情報が得られる？&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;これから?回にわたって待機イベントをもっと知能的に分析するためにどんなデ?タを追加的に分析する必要があらのかいくつかの例を見るつもりです。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;b&gt;1. ホルダ?を突き止め！　&lt;/b&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;オラクルでの競合は次の六つ程度の同期化客?により?生します。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;ラッチ&lt;br /&gt;&lt;li&gt;ロック&lt;br /&gt;&lt;li&gt;Library cache lock&lt;br /&gt;&lt;li&gt;Library cache pin&lt;br /&gt;&lt;li&gt;Row cache lock&lt;br /&gt;&lt;li&gt;Mutex&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;個?の客?が何を意味しているのかすでにわかっている方?も多いはずなので、詳細な?明は足らないと思います。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;同期化客?の種類別に提供されるビュ?が違いですから、ホルダ?を突き止めるときも違うビュ?を?索しなければなりません。幸いに最近バ?ジョンのオラクルでは&lt;b&gt;V$SESSIONビィ?のBLOCKING_SESSION、BLOCKING_INSTANCE&lt;/b&gt;&lt;br /&gt;などの列から大部分の?況でホルダ?情報が提供されます。大?有?な情報だと言えます。しかし、待機イベントによっては?純にホルダ?を知るだけでは不足である場合もあります。正確にどんなオブジェクトで、どんなモ?ドで?生しているのかなどの情報が必要である場合もあります。&lt;br /&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;これから公開する情報はたぶん（！）ホルダ?を突き止める方法を今までのどんな文書より一番?範?で完璧にまとめていることであるはずです。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;b&gt;1.1 ラッチのホルダ?&lt;/b&gt; &lt;br /&gt;&lt;p/&gt;&lt;br /&gt;ラッチホルダ?は&lt;b&gt;V$LATCHHOLDER&lt;/b&gt;ビュ?から突き止められます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- ラッチホルダ?&lt;br /&gt;select h.pid, h.sid, h.laddr, h.name, h.gets&lt;br /&gt;from v$latchholder h, v$session_wait s&lt;br /&gt;where s.sid = &amp;sid&lt;br /&gt;   and s.p1raw = h.laddr;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ラッチはとても短い時間に獲得されうため、上のような?純なクエリだけでは欲しい結果を得ることができない場合がたくさんあります。（もちろんオラクルのバグなどによって特定のラッチを長い間持っている場合もあります）&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;この時に使用できるのがプロファイル方法です。ティパックは&lt;b&gt;1)スナップショット、2)プロファイル&lt;/b&gt;2つの方法を使用しています。この中で、プロファイルとは短い時間に記?され、消えてしまう値を可能な限りよく取り出して、要約する方法を意味します。&lt;b&gt;V$LATCHHOLDERビュ?も次のようにプロファイリング&lt;/b&gt;できます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- ラッチホルダ?プロファイリング&lt;br /&gt;TPACK@ukja1021&gt; select /*+ ordered use_nl(x) */&lt;br /&gt;  2     x.sid, x.name, count(*)&lt;br /&gt;  3  from&lt;br /&gt;  4     (select /*+ no_merge */ level from dual connect by level &lt;= 10000) t1,&lt;br /&gt;  5     (select  /*+ no_merge */ h.pid, h.sid, h.laddr, h.name, h.gets&lt;br /&gt;  6             from v$latchholder h, v$session_wait s&lt;br /&gt;  7             where s.sid = &amp;sid&lt;br /&gt;  8                             and s.p1raw = h.laddr) x&lt;br /&gt;  9  group by x.sid, x.name&lt;br /&gt; 10  ;&lt;br /&gt;old   7:                where s.sid = &amp;sid&lt;br /&gt;new   7:                where s.sid =        138&lt;br /&gt;&lt;br /&gt;       SID NAME                             COUNT(*)&lt;br /&gt;---------- ------------------------------ ----------&lt;br /&gt;       134 shared pool                            33&lt;br /&gt;       134 library cache                         308&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1.3 ロックのホルダ?&lt;/b&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;ロックホルダ?はV$LOCKビュ?で十分です。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- ロックホルダ?&lt;br /&gt;select&lt;br /&gt; h.sid,  -- ホルダ?SID&lt;br /&gt; h.type,   -- ロックタイプ&lt;br /&gt; h.id1,  -- ID1&lt;br /&gt; h.id2,  -- ID2&lt;br /&gt; h.lmode, &lt;br /&gt; t.name,&lt;br /&gt; t.id1_tag,&lt;br /&gt; t.id2_tag,&lt;br /&gt; t.description&lt;br /&gt;from v$lock h, v$lock w, v$lock_type t&lt;br /&gt;where w.sid = { waiter_sid }&lt;br /&gt; and h.id1 = w.id1&lt;br /&gt; and h.id2 = w.id2&lt;br /&gt; and h.lmode &gt; 0 &lt;br /&gt; and h.block &gt; 0&lt;br /&gt; and h.type = t.type&lt;br /&gt;;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1.3 Library cache lockのホルダ?&lt;/b&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;Library cache lockとはLCO(Library Cache Object)を保護するシステムロックです。競合が?生すればlibrary cache lock待機イベントを持ちます。Library cache lockのホルダ?はX$KGLLKビュ?を通じて突き止められます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- Library cache lock ホルダ?　　　　　　　　　　&lt;br /&gt;select&lt;br /&gt; (select sid from v$session where saddr = k.kgllkuse) as sid,  -- ホルダ?SID&lt;br /&gt; k.kglhdnsp,  -- 客?の種類&lt;br /&gt; k.kglnaobj,  -- 客?名（SQL文章や表、プロシ?ジャ名など）&lt;br /&gt; decode(k.kgllkmod, 3, '3(X)', 2, '2(S)', 1, '1(N)', k.kgllkmod) as lkmode&lt;br /&gt;from x$kgllk k&lt;br /&gt;where k.kgllkhdl = { v$session_wait.p1raw }&lt;br /&gt;   and k.kgllkmod &gt; 0&lt;br /&gt;;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1.4 Library cache pinのホルダ?&lt;/b&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;Library cache pinとはカ?ソルやプロシ?ジャの?行を保護するシステムロックです。競合が?生すればlibrary cache pin待機イベントを待ちます。Library cache pinホルダ?はX$LGLPNビュ?を通じて突き止められます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- Library cache pin　ホルダ?&lt;br /&gt;select&lt;br /&gt;  (select sid from v$session where saddr = n.kglpnuse) as sid, &lt;br /&gt;  o.kglnaobj,&lt;br /&gt;  o.kglhdnsp,&lt;br /&gt;  decode(n.kglpnmod, 3, '3(X)', 2, '2(S)', 1, '1(N)', n.kglpnmod) as lkmode&lt;br /&gt;from x$kglpn n, x$kglob o&lt;br /&gt;where n.kglpnhdl = { v$session_wait.p1raw }&lt;br /&gt;   and n.kglpnmod &gt; 0&lt;br /&gt;   and o.kglhdadr = n.kglpnhdl&lt;br /&gt;;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1.5 Row cache lockのホルダ?&lt;/b&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;Row cache lockとはディクショナリ?オブジェクトを保護するロックです。競合が?生すればrow cache lock待機イベントを待ちます。Row cache lockのホルダ?はV$ROWCACHE_PARENTビュ?から突き止められます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- Row cache lock ホルダ?&lt;br /&gt;select &lt;br /&gt; (select sid from v$session where saddr = h.saddr) as sid,  -- ホルダ?SID&lt;br /&gt; h.cache_name,  -- ディクショナリ?オブジェクトの種類&lt;br /&gt; h.lock_mode,    &lt;br /&gt; h.inst_lock_type &lt;br /&gt;from v$rowcache_parent h, v$rowcache_parent w, v$session s&lt;br /&gt;where h.address = w.address&lt;br /&gt;   and w.saddr = s.saddr &lt;br /&gt;   and s.sid = { waiter_sid }&lt;br /&gt;   and h.lock_mode &gt; 0&lt;br /&gt;;   &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1.6 Mutexのホルダ?&lt;/b&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;Mutexのホルダ?は&lt;b&gt;V$MUTEX_SLEEP_HISTORY&lt;/b&gt;ビュ?を通じて突き止められます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- Mutexのホルダ?&lt;br /&gt;select * from (&lt;br /&gt; select&lt;br /&gt;   blocking_session as sid,  -- ホルダ?SID&lt;br /&gt;  (select kglnaobj from x$kglob &lt;br /&gt;  where kglnahsh = mutex_identifier &lt;br /&gt;  and rownum = 1) as obj_name,   -- オブジェクト命(11gで追加)&lt;br /&gt;   mutex_type,  -- Mutexのタイプ&lt;br /&gt;   location,  -- Mutexを獲得した位置、すなわち何のためにMutexを獲得しようとしているのか？&lt;br /&gt;   sleeps,&lt;br /&gt;   gets,&lt;br /&gt;   to_char(sleep_timestamp,'yyyy/mm/dd hh24:mi:ss') as sleep_timestamp&lt;br /&gt; from v$mutex_sleep_history&lt;br /&gt; where requesting_session = session_id&lt;br /&gt; order by sleep_timestamp desc&lt;br /&gt;) where rownum &lt;= 1 ;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;11gからは&lt;b&gt;MUTEX_IDENTIFIER&lt;/b&gt;という有?なコラムが追加されMutex競合の分析がもっと易くなりました。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;上の情報からわかるようにホルダ?の?索も大事ものの、もっと具?的にどんな情報で、どんあ客?で問題が?生しているのかを把握することも重要です。このような理由から、文書化されていないV$ビュ?やX$ビュ?を?索するしかありません。&lt;br /&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;ティパックでは&lt;b&gt;Session Detail Report&lt;/b&gt;を通じて上の情報を取り出すことができます。ほぼ同じクエリを使用します。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;Library cache pin競合の例で?明してみます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- とても長いSQL文章を作ります。正確にいえばハ?ドパ?スの時間が長いSQL文章です。&lt;br /&gt;-- http://sites.google.com/site/ukja/sql-scripts-1/j-m/make_long&lt;br /&gt;TPACK@ukja1021&gt; @make_long&lt;br /&gt;select count(*) from TPACK_REPORT_PARAMS, TPACK_REPORT_CONDITIONS,&lt;br /&gt;TPACK_REPORT_JOB_HIST, TPACK_REPORT_SESSION_TEMP, TPACK_FUNCTION_NAMES,&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;-- セッション＃１で上のクエリを行います。（私のテスト環境で２分５０秒くらいかかります）&lt;br /&gt;TPACK@ukja1021&gt; exec dbms_application_info.set_client_info('session1');&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.39&lt;br /&gt;TPACK@ukja1021&gt; @long_parse&lt;br /&gt;....&lt;br /&gt;&lt;br /&gt;-- セッション＃１と同時にセッション＃２で同じクエリを行います。&lt;br /&gt;TPACK@ukja1021&gt; exec dbms_application_info.set_client_info('session2');&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.04&lt;br /&gt;TPACK@ukja1021&gt; @long_parse&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;セッション＃２はセッション＃１がハ?ドパ?スを行なう間、待機?態になります。セッション＃２に?して&lt;b&gt;Session Detail Report&lt;/b&gt;を?集して待機イベントを詳細に分析します。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- セッション＃２のSID&lt;br /&gt;TPACK@ukja1021&gt; col sid new_value sid&lt;br /&gt;TPACK@ukja1021&gt; select sid from v$session where client_info = 'session2';&lt;br /&gt;&lt;br /&gt;       SID&lt;br /&gt;----------&lt;br /&gt;       138&lt;br /&gt;&lt;br /&gt;1 row selected.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.14&lt;br /&gt;&lt;br /&gt;-- セッション＃２のSession Detail Report&lt;br /&gt;TPACK@ukja1021&gt; col name format a30&lt;br /&gt;TPACK@ukja1021&gt; col value format a45&lt;br /&gt;TPACK@ukja1021&gt; set pages 200&lt;br /&gt;TPACK@ukja1021&gt; set long 10000000&lt;br /&gt;TPACK@ukja1021&gt; select * from table(tpack.session_detail(&amp;sid, 'wait_detail'));&lt;br /&gt;old   1: select * from table(tpack.session_detail(&amp;sid, 'wait_detail'))&lt;br /&gt;new   1: select * from table(tpack.session_detail(       138, 'wait_detail'))&lt;br /&gt;&lt;br /&gt;NAME                           VALUE&lt;br /&gt;------------------------------ ---------------------------------------------&lt;br /&gt;SID                            138&lt;br /&gt;Serial#                        711&lt;br /&gt;SPID                           3724&lt;br /&gt;Program                        sqlplus.exe&lt;br /&gt;Process                        5364:5208&lt;br /&gt;Module                         SQL*Plus&lt;br /&gt;SQL ID&lt;br /&gt;Child No&lt;br /&gt;SQL Text&lt;br /&gt;Status                         ACTIVE&lt;br /&gt;Blocking Instance              1&lt;br /&gt;Blocking Session               134&lt;br /&gt;Event                          library cache pin&lt;br /&gt;Seq#                           4137&lt;br /&gt;P1(P1raw)                      970230240(39D489E0)&lt;br /&gt;P2(P2raw)                      970230240(36A9AB34)&lt;br /&gt;P3(P3raw)                      970230240(000000C8)&lt;br /&gt;Seconds in wait                10&lt;br /&gt;State                          WAITING&lt;br /&gt;Wait Event                     library cache pin&lt;br /&gt;Holder SID                     134&lt;br /&gt;Namespace                      CURSOR&lt;br /&gt;Object                         select count(*) from TPACK_SGA_STAT, TPACK_SG&lt;br /&gt;                               A_STAT, TPACK_SGA_STAT, TPACK_SGA_STAT, TPACK&lt;br /&gt;                               _SGA_STAT, TPACK_SGA_STAT, TPACK_SGA_STAT, TP&lt;br /&gt;                               ACK_SGA_STAT, TPACK_SGA_STAT, TPACK_SGA_STAT,&lt;br /&gt;                                TPACK_SGA_STAT, TPACK_SGA_STAT, TPACK_SGA_ST&lt;br /&gt;                               AT, TPACK_SGA_STAT, TPACK_SGA_STAT, TPACK_SGA&lt;br /&gt;                               _STAT, TPACK_SGA_STAT, TPACK_SGA_STAT, TPACK_&lt;br /&gt;                               SGA_STAT, TPACK_SGA_STAT, TPACK_SGA_STAT, TPACK_S&lt;br /&gt;&lt;br /&gt;                               GA_STAT, TPACK_SGA_STAT, TPACK_SGA_STAT, TPAC&lt;br /&gt;                               K_SGA_STAT, TPACK_SGA_STAT, TPACK_SGA_STAT, T&lt;br /&gt;                               PACK_HEAP_DUMP, TPACK_HEAP_DUMP, TPACK_HEAP_D&lt;br /&gt;                               UMP, TPACK_HEAP_DUMP, TPACK_HEAP_DUMP, TPACK_&lt;br /&gt;                               HEAP_DUMP, TPACK_HEAP_DUMP, TPACK_HEAP_DUMP,&lt;br /&gt;                               TPACK_HEAP_DUMP, TPACK_HEAP_DUMP, TPACK_HEAP_&lt;br /&gt;                               DUMP, TPACK_HEAP_DUMP, TPACK_HEAP_DUMP, TPACK&lt;br /&gt;                               _HEAP_DUMP, TPACK_HEAP_DUMP, TPACK_HEAP_DUMP, TPA&lt;br /&gt;&lt;br /&gt;                               CK_HEAP_DUMP, TPACK_HEAP_DUMP, TPACK_HEAP_DUM&lt;br /&gt;                               P, TPACK_HEAP_DUMP, TPACK_HEAP_DUMP, TPACK_HE&lt;br /&gt;                               AP_DUMP, TPACK_HEAP_DUMP, TPACK_HEAP_DUMP, TP&lt;br /&gt;                               ACK_HEAP_DUMP, TPACK_HEAP_DUMP, TPACK_HEAP_DU&lt;br /&gt;                               MP, TPACK_HEAP_DUMP_SUM, TPACK_HEAP_DUMP_SUM,&lt;br /&gt;                                TPACK_HEAP_DUMP_SUM, TPACK_HEAP_DUMP_SUM, TP&lt;br /&gt;                               AC&lt;br /&gt;&lt;br /&gt;Holding Mode                   3(X)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;NamespaceとObject情報からlibrary cache pin待機に?するより正確な分析が可能です。Oracle 11gではlibrary cache pinではなくてMutexに?する競合が?生します。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1106&gt; select * from table(tpack.session_detail(&amp;sid, 'wait_detail'));&lt;br /&gt;old   1: select * from table(tpack.session_detail(&amp;sid, 'wait_detail'))&lt;br /&gt;new   1: select * from table(tpack.session_detail(       127, 'wait_detail'))&lt;br /&gt;&lt;br /&gt;NAME                           VALUE&lt;br /&gt;------------------------------ ---------------------------------------------&lt;br /&gt;SID                            127&lt;br /&gt;Serial#                        1642&lt;br /&gt;SPID                           2656&lt;br /&gt;Program                        sqlplus.exe&lt;br /&gt;Process                        5364:5208&lt;br /&gt;Module                         SQL*Plus&lt;br /&gt;SQL ID&lt;br /&gt;Child No&lt;br /&gt;SQL Text&lt;br /&gt;Status                         ACTIVE&lt;br /&gt;Blocking Instance              1&lt;br /&gt;Blocking Session               139&lt;br /&gt;SQL Exec Start&lt;br /&gt;Event                          cursor: pin S wait on X&lt;br /&gt;Seq#                           631&lt;br /&gt;P1(P1raw)                      3859422310(00000000E60A1C66)&lt;br /&gt;P2(P2raw)                      3859422310(00000000008B0000)&lt;br /&gt;P3(P3raw)                      3859422310(0000000000050256)&lt;br /&gt;Seconds in wait                0&lt;br /&gt;State                          WAITING&lt;br /&gt;Wait Event                     cursor: pin S wait on X&lt;br /&gt;Holder SID                     139&lt;br /&gt;Mutex Type                     Cursor Pin&lt;br /&gt;Location&lt;br /&gt;Target Object                  select count(*) from TPACK_SGA_STAT, TPACK_SG&lt;br /&gt;                               A_STAT, TPACK_SGA_STAT, TPACK_SGA_STAT, TPACK&lt;br /&gt;                               _SGA_STAT, TPACK_SGA_STAT, TPACK_SGA_STAT, TP&lt;br /&gt;                               ACK_SGA_STAT, TPACK_SGA_STAT, TPACK_SGA_STAT,&lt;br /&gt;                                TPACK_SGA_STAT, TPACK_SGA_STAT, TPACK_SGA_ST&lt;br /&gt;                               AT, TPACK_SGA_STAT, TPACK_SGA_STAT, TPACK_SGA&lt;br /&gt;                               _STAT, TPACK_SGA_STAT, TPACK_SGA_STAT, TPACK_&lt;br /&gt;                               SGA_STAT, TPACK_SGA_STAT, TPACK_SGA_STAT, TPACK_S&lt;br /&gt;&lt;br /&gt;                               GA_STAT, TPACK_SGA_STAT, TPACK_SGA_STAT, TPAC&lt;br /&gt;                               K_SGA_STAT, TPACK_SGA_STAT, TPACK_SGA_STAT, T&lt;br /&gt;                               PACK_HEAP_DUMP, TPACK_HEAP_DUMP, TPACK_HEAP_D&lt;br /&gt;                               UMP, TPACK_HEAP_DUMP, TPACK_HEAP_DUMP, TPACK_&lt;br /&gt;                               HEAP_DUMP, TPACK_HEAP_DUMP, TPACK_HEAP_DUMP,&lt;br /&gt;                               TPACK_HEAP_DUMP, TPACK_HEAP_DUMP, TPACK_HEAP_&lt;br /&gt;                               DUMP, TPACK_HEAP_DUMP, TPACK_HEAP_DUMP, TPACK&lt;br /&gt;                               _HEAP_DUMP, TPACK_HEAP_DUMP, TPACK_HEAP_DUMP, TPA&lt;br /&gt;&lt;br /&gt;                               CK_HEAP_DUMP, TPACK_HEAP_DUMP, TPACK_HEAP_DUM&lt;br /&gt;                               P, TPACK_HEAP_DUMP, TPACK_HEAP_DUMP, TPACK_HE&lt;br /&gt;                               AP_DUMP, TPACK_HEAP_DUMP, TPACK_HEAP_DUMP, TP&lt;br /&gt;                               ACK_HEAP_DUMP, TPACK_HEAP_DUMP, TPACK_HEAP_DU&lt;br /&gt;                               MP, TPACK_HEAP_DUMP_SUM, TPACK_HEAP_DUMP_SUM,&lt;br /&gt;                                TPACK_HEAP_DUMP_SUM, TPACK_HEAP_DUMP_SUM, TP&lt;br /&gt;                               AC&lt;br /&gt;&lt;br /&gt;Last Sleep Time                2010/06/29 16:03:51&lt;br /&gt;Gets                           1&lt;br /&gt;Sleeps                         611&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;重要なのは&lt;b&gt;待機現象が?生するとき適切なビュ?から適切なデ?タを?索することができるか&lt;/b&gt;ということです。ティパックはその中核心的なデ?タを自動に?集してくれるだけです。&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;&lt;b&gt;以前のポスト&lt;/b&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://dioncho.blogspot.com/2010/07/blog-post_4048.html"&gt;「ティパック」性能問題をトラブルシュ?ティングする２つのフレ?ム&lt;/a&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://dioncho.blogspot.com/2010/08/blog-post_05.html"&gt;「ティパック」oradebug&lt;/a&gt;&lt;br /&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-8729181139829158662?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/8729181139829158662/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/08/part-1.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/8729181139829158662'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/8729181139829158662'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/08/part-1.html' title='「ティパック」知能的な待機イベントの分析 - Part 1'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-8349138147798107843</id><published>2010-08-05T23:03:00.003-07:00</published><updated>2010-08-06T00:35:26.996-07:00</updated><title type='text'>「ティパック」oradebug</title><content type='html'>このポストの目的はoradebugの使用法全?を?明することではなくて&lt;b&gt;ティパック&lt;/b&gt;で使われているoradebugコマンド&lt;/b&gt;を簡?に紹介することです。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;b&gt;1. 特定のプロセスにバインディング&lt;/b&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;ティパックがoradebugコマンドを使用する理由は特定のプロセスに自由にバインディングできるからです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- SYSDBA ユ?ザ?でログイン&lt;br /&gt;sqlplus "/as sysdba"&lt;br /&gt;&lt;br /&gt;-- 現在のプロセスにバインディング&lt;br /&gt;SQL&gt; oradebug setmypid&lt;br /&gt;&lt;br /&gt;-- 特定のセッションのOS Process IDでバインディング&lt;br /&gt;SQL&gt; select sid, serial#,&lt;br /&gt;   (select spid from v$process where addr = paddr) as ospid&lt;br /&gt;from v$session &lt;br /&gt;where sid = (select sid from v$mystat where rownum = 1);&lt;br /&gt;&lt;br /&gt;       SID    SERIAL# OSPID&lt;br /&gt;---------- ---------- ------------&lt;br /&gt;       144       2446 4872&lt;br /&gt;&lt;br /&gt;SQL&gt; oradebug setospid 4872&lt;br /&gt;Oracle pid: 16, Windows thread id: 4872, image: ORACLE.EXE (SHAD)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;特定のプロセスにバインディングするには次の４つの方法があります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;SQL&gt; oradebug help&lt;br /&gt;SETMYPID                                 Debug current process&lt;br /&gt;SETOSPID       {ospid}                   Set OS pid of process to debug&lt;br /&gt;SETORAPID      {orapid} ['force']        Set Oracle pid of process to debug&lt;br /&gt;SETORAPNAME    {orapname}                Set Oracle process name to debug  -- 11g에서 추가&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;SETOSPIDコマンドを主に使用する理由はUnix/Linux環境ではProcess ID(PID)を得るのが易いからです。でも、ティパックでは特定のセッションを指定するためにSession ID(SID)をパラメ?タ?で使用します。オラクルではSIDが一番認識しやすい値からです。&lt;br /&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;b&gt;2. トレ?スファイルの名前の獲得&lt;/b&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;ティパックでは特定のプロセスに?してイベントやダンプを行い、その中身を?み?む作業がいくつかあります。したがって、トレ?スファイルの名前を正確にえるのが大事です。oradebug tracefile_name名前がそれに?たります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;SQL&gt; oradebug tracefile_name&lt;br /&gt;c:\oracle\admin\ukja1021\udump\ukja1021_ora_4872.trc&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;上の作業をティパックでは次のように行なえます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- 143番セッションのトレ?スファイルの名前&lt;br /&gt;SQL&gt; select tpack.get_tracefile_name(143) from dual;&lt;br /&gt;c:\oracle\admin\ukja1021\udump\ukja1021_ora_4872.trc&lt;br /&gt;&lt;br /&gt;-- レ?スファイルの中身&lt;br /&gt;SQL&gt; select * from table(tpack.get_tracefile_contents('c:\oracle\admin\ukja1021\udump\ukja1021_ora_4872.trc'))&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;あるいは次のようなクエリを通じても得られます。しかし、ファイル名の正確なフォ?マットはバ?ジョンやＯＳにより?わられます。もう１つの短所は該?プロセスがサ?バ?プロセスなのか、バックグラウンドプロセスなのかを事前に知ることが必要で、プロセスの種類によりどんなディレクトリ?にファイルが書き?まれるのかも判?したければならないということです。このような複?性のためにティパックではoradebug tracefile_nameコマンドを利用します。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;select&lt;br /&gt;  d.value||'\'||p.value||'_ora_'||s.spid||'.trc'   as trace_file_name&lt;br /&gt;from &lt;br /&gt;  (&lt;br /&gt;  select value &lt;br /&gt;  from v$parameter&lt;br /&gt;  where name = 'instance_name'&lt;br /&gt;  ) p, &lt;br /&gt;  (&lt;br /&gt;  select value&lt;br /&gt;  from v$parameter&lt;br /&gt;  where name = 'user_dump_dest'&lt;br /&gt;  ) d,&lt;br /&gt;  (&lt;br /&gt;  select spid&lt;br /&gt;  from v$process&lt;br /&gt;  where addr = (&lt;br /&gt;    select paddr &lt;br /&gt;    from v$session&lt;br /&gt;    where sid = {sid here}&lt;br /&gt;    )&lt;br /&gt;  ) s&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;3. 診?イベントの修行&lt;/b&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;特定のプロセスにバインディングした後は該?プロセスに?して診?イベントが行なわれます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- 診?イベントの活性化&lt;br /&gt;SQL&gt; oradebug event 10046 trace name context forever, level 12&lt;br /&gt;&lt;br /&gt;-- 診?イベントの非活性化&lt;br /&gt;SQL&gt; oradebug event 10046 context off&lt;br /&gt;&lt;br /&gt;-- トレ?スファイルの確認&lt;br /&gt;SQL&gt; oradebug tracefile_name&lt;br /&gt;SQL&gt; ed {tracefile_name}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;上の作業をティパックでは次のように行われます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- 診?イベントの活性化&lt;br /&gt;SQL&gt; exec tpack.begin_diag_trace(143, 10046, 12);&lt;br /&gt;&lt;br /&gt;-- 診?イベントの非活性化&lt;br /&gt;SQL&gt; exec tpack.end_diag_trace(143, 10046);&lt;br /&gt;&lt;br /&gt;-- トレ?スファイルの確認&lt;br /&gt;SQL&gt; select * from table(tpack.get_diag_trace(143));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ティパックを利用すればoradebugを利用するためにテルネットで接?する必要かないし、&lt;b&gt;クライアントでSQL*Plusですべての作業&lt;/b&gt;ができます。ティパックのもう１つの長所は診?イベントを活性した後に生成された中身だけを?み?むということです。すなわち、診?イベントを活性する前に、もうほかの作業により書き?まれた多くのデ?タわスキップされ、今度の作業により書き?まれた中身だけを?み?みます。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;b&gt;4. ダンプの修行&lt;/b&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;特定のプロセスにバインディングした後、該?プロセスに?して多?なダンプファイルを生成できます。次に簡?な例があります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- レベル１でCallstackダンプの修行&lt;br /&gt;SQL&gt; oradebug dump callstack 1&lt;br /&gt;&lt;br /&gt;-- PGA Heap Dump。Level 0x20000001とは最上位ヒ?プだけではなくてサイズが大きい五つのサブヒ?プに?して再?的にダンプを修行しろという意味です。非常に有?な機能だと言えます。&lt;br /&gt;SQL&gt; oradebug dump heapdump 0x20000001&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ティパックではヒ?プダンプファイルから?み?んだデ?タを加工して分析レポ?トを提供します。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- 143番セッションに?してCallstackダンプを1秒?たり10回行い、その結果を要約してレポ?ト&lt;br /&gt;SQL&gt; select * from table(tpack.callstack_prof_report(143));&lt;br /&gt;&lt;br /&gt;-- 143番セッションに?してPGA Heap Dumpをレベル0x20000001で行い、この結果をレポ?ト&lt;br /&gt;SQL&gt; select * from table(tpack.pga_heap_report(143, 2));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ティパックは性能トラブルシュ?ティングに必要な基本的なデ?タの一部を診?イベントやダンプを通じて得ています。PGA Heap DumpやCallstack Dumpが代表的な例です。このようなデ?タを得るための一番易い（もしかしたら唯一&lt;br /&gt;な）方法としてoradebugを使用しています。&lt;br /&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;1つの技術的な難しさはSQLコマンドからどうすればoradebugを自由に呼び出すのかということです。ティパックでは&lt;b&gt;Java Stored Procedure&lt;/b&gt;を利用しています。これに?する詳細なお話は次のポストで進める予定です。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;性能トラブルシュ?ティングの段階が深まるほどoradebugの魅力におぼれるはずです。特にOracle 11gでは完全に新しく設計されたOracle Debugging Frameworkが提供され、それに連れてoradebugの機能ももっと?くなりました。詳細な?容は次の文書を?考してください。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://tech.e2sn.com/oracle/troubleshooting/oradebug-doc"&gt;Oradebug DOC&lt;/a&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;&lt;b&gt;以前のポスト&lt;/b&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://dioncho.blogspot.com/2010/07/blog-post_4048.html"&gt;「ティパック」性能問題をトラブルシュ?ティングする２つのフレ?ム&lt;/a&gt;&lt;br /&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-8349138147798107843?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/8349138147798107843/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/08/blog-post_05.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/8349138147798107843'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/8349138147798107843'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/08/blog-post_05.html' title='「ティパック」oradebug'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-1205714334727456375</id><published>2010-07-26T23:42:00.000-07:00</published><updated>2010-07-26T23:43:15.830-07:00</updated><title type='text'>「ティパック」性能問題をトラブルシューティングする２つのフレーム</title><content type='html'>クイズであります。&lt;br /&gt;&lt;table&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td bgcolor="lightgrey" margin="5"&gt;&lt;br /&gt;&lt;b&gt;1. &lt;/b&gt;オラクルで特定のセッション（あるいはシステム）の現在の状態をすぐにわかる一番良い方法はなんでしょうか。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;b&gt;2. &lt;/b&gt;オラクルで特定のセッション（あるいはシステム）が何をしているのかを追跡できる一番良い方法は何でしょうか。&lt;br /&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/table&gt;&lt;br /&gt;&lt;b&gt;性能問題を分析する2つのフレーム&lt;/b&gt;で上の質問に対する私の答えを代わります。私はオラクル性能問題をトラブルシューティングする全てのツールと技法を次の２つのフレームに分けます。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;&lt;b&gt;スナップショット（Snapshot）&lt;/b&gt; - 特定の時点の作業の&lt;b&gt;現在の状態&lt;/b&gt;を検索する方法&lt;br /&gt;  &lt;li&gt;&lt;b&gt;プロファイル（Profile）&lt;/b&gt; - 特定の作業を&lt;b&gt;時間の流れ&lt;/b&gt;で追跡する方法&lt;br /&gt; &lt;/ul&gt;&lt;br /&gt;例をあげていましょうか。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;10053診断イベント&lt;/b&gt; - オプティマイザの作業を時間の順序で追跡するプロファイル機能&lt;br /&gt;&lt;li&gt;&lt;b&gt;V$SESSION_WAIT&lt;/b&gt; - セッションの待機状態を検索するスナップショット機能&lt;br /&gt;&lt;li&gt;&lt;b&gt;Heap Dump&lt;/b&gt; - 特定のセッションやシステムの現在のメモリー使用程度を検索するスナップショット機能&lt;br /&gt;&lt;li&gt;&lt;b&gt;System State Dump&lt;/b&gt; - システムの現在の状態を検索するスナップショット機能&lt;br /&gt;&lt;li&gt;&lt;b&gt;Call Stack(oradebug dump callstack)&lt;/b&gt; - 特定のセッションの現在呼び出されているファンクションのコールツリーなのでスナップショットとプロファイルの中間性格&lt;br /&gt;&lt;li&gt;&lt;b&gt;Active Session History&lt;/b&gt; - アクティブセッションのリストを秒あたり一度づつサンプリングしたことなのでスナップショットとプロファイルの中間性格&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;スナップショットデータを時間の流れによって全体あるいは一部をサンプリングしたらそれがプロファイルになります。スナップショットは特定の時点の状態をいみするので一番基本的なデータだと言えます。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;スナップショットは特定の時点の状態を示しているから、ほとんど大部分&lt;b&gt;差異（Delta）と比較（Diff）&lt;/b&gt;を通じてだけ意味を持ちます。AWRレポートをみればスナップショット間の値を差異（Delta）を計算してくれます。差異と比較を通じて直感的にシステムの現在の状態がわかります。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;プロファイルは時間の流れによってデータを追跡する方式だから、ほとんど大部分&lt;b&gt;集計と要約&lt;/b&gt;を通じてだけ意味を持ちます。10046イベントにより生成されたプロファイルデータをTKPROFレポートというツールを利用して集計して見るのが代表的な例です。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;ティパックは上のような簡単明瞭なフレームの上で&lt;b&gt;状況によって適当なスナップショットとプロファイルだけ存在すれば比較と要約を通じて大部分の性能問題をトラブルシューティングできる&lt;/b&gt;という考えに基づいています。これからブログを通じてより具体的な事例たちとともにオラクルで使用できる多様な技法たちを論議するようにします。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-1205714334727456375?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/1205714334727456375/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/07/blog-post_4048.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/1205714334727456375'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/1205714334727456375'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/07/blog-post_4048.html' title='「ティパック」性能問題をトラブルシューティングする２つのフレーム'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-1214421112669806916</id><published>2010-07-26T22:35:00.000-07:00</published><updated>2010-07-26T23:41:56.418-07:00</updated><title type='text'>「ティパック」を紹介します。</title><content type='html'>&lt;b&gt;ティパック&lt;/b&gt;とはオラクル性能トラブルシューティング機能を提供するPL/SQLライブラリーであります。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;a href="http://sites.google.com/site/tpackjp/"&gt;ティパックウェブページ&lt;/a&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;これからブログを通じてティパック機能の使用方法を説明するつもりです。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-1214421112669806916?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/1214421112669806916/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/07/blog-post_26.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/1214421112669806916'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/1214421112669806916'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/07/blog-post_26.html' title='「ティパック」を紹介します。'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-5503219648369797264</id><published>2010-07-14T23:41:00.000-07:00</published><updated>2010-07-15T00:05:03.611-07:00</updated><title type='text'>性能改善の絶対的な原則！</title><content type='html'>オラクル性能問題の関する最高の専門家で平価されている&lt;a href="http://carymillsap.blogspot.com/"&gt;Carry Milsap&lt;/a&gt;さんは性能を改善する絶対的な原則を言及したことがあります。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;ある作業の性能を改善する最高の方法はその作業自体をしないということだ。&lt;/b&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;本当に名言の中の名言です。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;次に簡単な例があります。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;b&gt;1. &lt;/b&gt;これ以上チューニング不可能に見える完璧に最適化された文章です。ほぼ3.4秒がかかりました。&lt;br /&gt;&lt;pre class="brush: sql; highlight:[12]"&gt;&lt;br /&gt;TPACK@ukja1106&gt; declare&lt;br /&gt;  2   v_value number;&lt;br /&gt;  3  begin&lt;br /&gt;  4   for idx in 1 .. 100000 loop&lt;br /&gt;  5    select trunc(idx) into v_value from dual;&lt;br /&gt;  6   end loop;&lt;br /&gt;  7  end;&lt;br /&gt;  8  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:03.42&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;本当にそうでしょうか。上で話した絶対原則を適用したら？不必要なSELECT ... FROM DUAL分をなくしたら？&lt;br /&gt;&lt;pre class="brush: sql; highlight:[12]"&gt;&lt;br /&gt;TPACK@ukja1106&gt; declare&lt;br /&gt;  2   v_value number;&lt;br /&gt;  3  begin&lt;br /&gt;  4   for idx in 1 .. 100000 loop&lt;br /&gt;  5    v_value := trunc(idx);&lt;br /&gt;  6   end loop;&lt;br /&gt;  7  end;&lt;br /&gt;  8  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.07&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;0.07秒だけで作業が終わります。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;b&gt;2. &lt;/b&gt;DECODE関数はSQL文章内のみで修行可能です。従ってCASE...文に変換すれば同じ効果を享受できます。&lt;br /&gt;&lt;pre class="brush: sql; highlight:[13,27]"&gt;&lt;br /&gt;&lt;br /&gt;TPACK@ukja1106&gt; -- decode&lt;br /&gt;TPACK@ukja1106&gt; declare&lt;br /&gt;  2   v_value varchar2(1);&lt;br /&gt;  3  begin&lt;br /&gt;  4   for idx in 1 .. 100000 loop&lt;br /&gt;  5    select decode(mod(idx,2),0,'A','B') into v_value from dual;&lt;br /&gt;  6   end loop;&lt;br /&gt;  7  end;&lt;br /&gt;  8  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:03.59&lt;br /&gt;&lt;br /&gt;TPACK@ukja1106&gt; -- case&lt;br /&gt;TPACK@ukja1106&gt; declare&lt;br /&gt;  2   v_value varchar2(1);&lt;br /&gt;  3  begin&lt;br /&gt;  4   for idx in 1 .. 100000 loop&lt;br /&gt;  5    v_value := case mod(idx,2) when 0 then 'A' else 'B' end;&lt;br /&gt;  6   end loop;&lt;br /&gt;  7  end;&lt;br /&gt;  8  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.15&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;3. &lt;/b&gt;Oracle 11gからはSequenceの値もSELECT ... FROM DUALを通じなくて直接えることがでいます。でも性能に与える影響はほとんどありませんね。&lt;br /&gt;&lt;pre class="brush: sql; highlight:[13,27]"&gt;&lt;br /&gt;TPACK@ukja1106&gt; -- SELECT... FROM DUAL&lt;br /&gt;TPACK@ukja1106&gt; declare&lt;br /&gt;  2   v_value number;&lt;br /&gt;  3  begin&lt;br /&gt;  4   for idx in 1 .. 100000 loop&lt;br /&gt;  5    select s1.nextval into v_value from dual;&lt;br /&gt;  6   end loop;&lt;br /&gt;  7  end;&lt;br /&gt;  8  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:03.73&lt;br /&gt;&lt;br /&gt;TPACK@ukja1106&gt; -- 直接&lt;br /&gt;TPACK@ukja1106&gt; declare&lt;br /&gt;  2   v_value number;&lt;br /&gt;  3  begin&lt;br /&gt;  4   for idx in 1 .. 100000 loop&lt;br /&gt;  5    v_value := s1.nextval;&lt;br /&gt;  6   end loop;&lt;br /&gt;  7  end;&lt;br /&gt;  8  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:03.64&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;うん。。。なぜでしょうか。それに対する応えはSQL*Traceを通じて得られます。SQL*Traceを実行してみたら{ v_value := s1.nextval }作業は{ Select S1.NEXTVAL from dual }&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;もう一度言いますが、性能改善の一番の原則は！&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;ある作業の性能を改善する最高の方法はその作業自体をしないということだ。&lt;/b&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-5503219648369797264?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/5503219648369797264/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/07/blog-post.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/5503219648369797264'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/5503219648369797264'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/07/blog-post.html' title='性能改善の絶対的な原則！'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-4577643648289110159</id><published>2010-07-06T18:51:00.000-07:00</published><updated>2010-07-06T20:46:24.062-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='HASH GROUP BY、SORT GROUP BY、_gby_hash_aggregation_enabled'/><title type='text'>_gby_hash_aggregation_enabledバグ</title><content type='html'>カスタムーからの問い合わせ：オラクルがどんな状況でもHASH GROUP BYを選択しないが、理由は何?&lt;br /&gt;&lt;br /&gt;オラクルバージョンは10gR2（10.2.0.1）であります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1021&gt; select * from v$version where rownum = 1;&lt;br /&gt;&lt;br /&gt;BANNER&lt;br /&gt;----------------------------------------------------------------&lt;br /&gt;Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;次のクエリを見れば、一番簡単なGroup By文でもHASH GROUP BYではなくてSORT GROUP BYを使っています。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;create table t1&lt;br /&gt;as&lt;br /&gt;select level as c1&lt;br /&gt;from dual&lt;br /&gt;connect by level &lt;= 10;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;explain plan for &lt;br /&gt;select count(*)&lt;br /&gt;from tpack.t1&lt;br /&gt;group by c1;&lt;br /&gt;&lt;br /&gt;-------------------------------------------&lt;br /&gt;| Id  | Operation          | Name | Rows  |&lt;br /&gt;-------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT   |      |    10 |&lt;br /&gt;|   1 |  SORT GROUP BY     |      |    10 |&lt;br /&gt;|   2 |   TABLE ACCESS FULL| T1   |    10 |&lt;br /&gt;-------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;隠しパラメーターである_gby_hash_aggregation_enabledの値がFalseである可能性があるでしょう。確かそうです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1021&gt; col value format a10&lt;br /&gt;TPACK@ukja1021&gt; @para gby_hash&lt;br /&gt;TPACK@ukja1021&gt; set echo off&lt;br /&gt;old   9: and i.ksppinm like '%&amp;1%'&lt;br /&gt;new   9: and i.ksppinm like '%gby_hash%'&lt;br /&gt;&lt;br /&gt;NAME                           VALUE                IS_DEFAUL SES_MODIFI&lt;br /&gt;------------------------------ -------------------- --------- ----------&lt;br /&gt;SYS_MODIFI&lt;br /&gt;----------&lt;br /&gt;DESCRIPTION&lt;br /&gt;-------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;_gby_hash_aggregation_enabled  FALSE                FALSE     true&lt;br /&gt;immediate&lt;br /&gt;enable group-by and aggregation using hash scheme&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;しかし、_gby_hash_aggregation_enabledパラメーターをTrueに変え、USE_HASH_AGGREGATIONヒントを与えても、かわりなくSORT GROUP BYが選べられます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;alter session set "_gby_hash_aggregation_enabled" = true;&lt;br /&gt;&lt;br /&gt;explain plan for &lt;br /&gt;select /*+ use_hash_aggregation */ count(*)&lt;br /&gt;from t1&lt;br /&gt;group by c1;&lt;br /&gt;&lt;br /&gt;-------------------------------------------&lt;br /&gt;| Id  | Operation          | Name | Rows  |&lt;br /&gt;-------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT   |      |    10 |&lt;br /&gt;|   1 |  SORT GROUP BY     |      |    10 |&lt;br /&gt;|   2 |   TABLE ACCESS FULL| T1   |    10 |&lt;br /&gt;-------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;これは正常的な状況とは言えません。MOSで検索してみたら、完璧に一致するバグが見つかりました。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;Bug 8631742: ALTER SESSION SET ... NOT HAVING EFFECT IN 10.2.0.4&lt;br /&gt;...&lt;br /&gt;RELEASE NOTES:&lt;br /&gt;]] Setting _gby_hash_aggregation_enabled at the session level did not&lt;br /&gt;]] always take effect&lt;br /&gt;REDISCOVERY INFORMATION:&lt;br /&gt;If you change the use of hash aggregation at the session level, but this does&lt;br /&gt;not affect the choice of aggregation method in subsequebtly parsed SQL,&lt;br /&gt;you are probably hitting this bug.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;このバグはパラメーターファイルで_gby_hash_aggregation_enabledパラメーターの値を指定する時にのみ起こります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;*._gby_hash_aggregation_enabled=FALSE&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;初期のHASH GROUP BY機能が多くのバグを持っていたから、パラメーターファイルでこのパラメーターをFalseで指定しているシステムが多いと思います。でもUSE_HASH_AGGREGATIONヒントを使うにもかかわらずHASH GROUP BY機能が動作しないというのは予想しなかった副作用でしょう。&lt;br /&gt;&lt;br /&gt;このバグは10.2.0.5でパッチされました。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-4577643648289110159?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/4577643648289110159/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/07/gbyhashaggregationenabled.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/4577643648289110159'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/4577643648289110159'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/07/gbyhashaggregationenabled.html' title='_gby_hash_aggregation_enabledバグ'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-2789788722139466023</id><published>2010-06-20T23:14:00.000-07:00</published><updated>2010-06-21T00:34:43.827-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='APPEND'/><title type='text'>INSERT...VALUESコマンドとAPPENDヒント</title><content type='html'>1つの行のインサートにAPPENDヒントを用いたDirect Path Modeが必要なのかよく分かりません。とにかく、オラクルは11gからこのようなモードを支援するようにしました。すなわちINSERT...VALUES...コマンドでもAPPENDヒントが動作するように修正されました。もっと詳細に整理してみれば...&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Oracle 10gまではINSERT...VALUES...コマンドにAPPENDヒントを与えても無視されます。すなわち、APPENDヒントはINSERT...SELECT...コマンドでだけ動作します。&lt;br /&gt;&lt;br /&gt;&lt;li&gt;APPENDヒントによってDirect Path Modeで動作すると行をインサートするとき現在セグメントの空きブロックを無視し（同時にバッファーキャッシュを経緯しなくて）セグメントのHigh Water MarkのあとにAppendさせてしまいます。したがって大量のデータを追加する時速度は速いがデータファイルにその分不要な空き容量が残りかねません。（でもこの空き容量もあとで使用されるはずです）&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Oracle 11gR1ではINSERT...VALUES...コマンドでもAPPENDヒントが動作します。隠しパラメーター_direct_path_insert_featuresで制御できます。&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Oracle 11gR2ではINSERT...VALUES...コマンドでAPPENDヒントを与えて無視されます。かわりにAPPEND_VALUESというヒントが追加されました。すなわちAPPEND_VALUESヒントを使用すればINSERT...VALUES...コマンドでもDirect Path Modeでインサートされます。&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;少し複雑になったでしょう。下記は簡単なテスト結果です。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;create table t1(c1 number);&lt;br /&gt;&lt;br /&gt;insert into t1 values(1);&lt;br /&gt;commit;&lt;br /&gt;insert into t1 values(2);&lt;br /&gt;commit;&lt;br /&gt;&lt;br /&gt;-- （2件の行をインサートして、Conventional Path Modeであるため二つの行が同じブロックにあるはずです）&lt;br /&gt;select&lt;br /&gt; dbms_rowid.rowid_relative_fno(rowid) as file#,&lt;br /&gt; dbms_rowid.rowid_block_number(rowid) as block#&lt;br /&gt;from t1;&lt;br /&gt;&lt;br /&gt;-- 10.2.0.1&lt;br /&gt;     FILE#     BLOCK#&lt;br /&gt;---------- ----------&lt;br /&gt;         4        853&lt;br /&gt;         4        853&lt;br /&gt;&lt;br /&gt;-- 11.1.0.6&lt;br /&gt;     FILE#     BLOCK#&lt;br /&gt;---------- ----------&lt;br /&gt;         4        758&lt;br /&gt;         4        758&lt;br /&gt;&lt;br /&gt;-- （APPENDヒントが与えられたINSERT...VALUES...コマンドです）&lt;br /&gt;insert /*+ append */ into t1 values(3);&lt;br /&gt;commit;&lt;br /&gt;&lt;br /&gt;-- 10.2.0.1ではAPPENDヒントが無視されるためやはり同じブロックにインサートされます。&lt;br /&gt;     FILE#     BLOCK#&lt;br /&gt;---------- ----------&lt;br /&gt;         4        853&lt;br /&gt;         4        853&lt;br /&gt;         4        853&lt;br /&gt;&lt;br /&gt;-- でも11.1.0.6ではAPPENDヒントが動作し、その結果High Water Markの以後にインサートされてしまいます。&lt;br /&gt;     FILE#     BLOCK#&lt;br /&gt;---------- ----------&lt;br /&gt;         4        758&lt;br /&gt;         4        758&lt;br /&gt;         4        769&lt;br /&gt;&lt;br /&gt;-- 11.2.0.1ではAPPEND_VALUESヒントがその役割を代わります。&lt;br /&gt;iinsert /*+ append_values */ into t1 values(3);&lt;br /&gt;commit;&lt;br /&gt;&lt;br /&gt;     FILE#     BLOCK#&lt;br /&gt;---------- ----------&lt;br /&gt;         4        526&lt;br /&gt;         4        526&lt;br /&gt;         4        528&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;なぜオラクル開発者たちが急にINSERT...VALUES...コマンドでもAPPENDヒントが動作することに変えたんでしょうか。いくら考えてみてもわかりません。誰かその理由が浮かんでくる方あります?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-2789788722139466023?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/2789788722139466023/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/06/insertvaluesappend.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/2789788722139466023'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/2789788722139466023'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/06/insertvaluesappend.html' title='INSERT...VALUESコマンドとAPPENDヒント'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-8472960127130944017</id><published>2010-06-15T21:18:00.000-07:00</published><updated>2010-06-15T22:15:50.926-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ORA-4030、'/><category scheme='http://www.blogger.com/atom/ns#' term='PGA Heap Dump'/><title type='text'>PGAサイズの非正常的な増加</title><content type='html'>PGAサイズが多すぎるとシステムに多い問題を起こします。非正常的なPGAサイズの問題を分析する最高の方法はPGAヒープダンプを落としてそれを精密分析することです。しかし、PGAのサイズ増加が速すぎる場合は手動的にダンプコマンドを実行するのはむずかしいはずです。&lt;br /&gt;&lt;p /&gt;&lt;br /&gt;&lt;br /&gt;幸い、オラクルが提供する診断イベントを活用すればPGAヒープダンプを自動的かすることができます。&lt;br /&gt;&lt;p /&gt;&lt;br /&gt;&lt;b&gt;1.&lt;/b&gt; まず、10261診断イベントを通じてPGAヒープダンプサイズを制限します。たとえば、下記のコマンドでPGAヒープサイズを100000KBに制限できます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;alter system set events '10261 trace name context forever, level 100000';&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. &lt;/b&gt;10261イベントが存在する場合、PGAのサイズが設定したサイズを超えるプロセスはORA-600 [723]エラーとともに失敗します。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- make big pga&lt;br /&gt;declare&lt;br /&gt; type varchar2_array is table of varchar2(32767) index by pls_integer;&lt;br /&gt; vc  varchar2_array;&lt;br /&gt; v  varchar2(32767);&lt;br /&gt;begin&lt;br /&gt; for idx in 1 .. 10000 loop&lt;br /&gt; v := rpad('x',32767,'x');&lt;br /&gt; vc(idx) := v;&lt;br /&gt; end loop;&lt;br /&gt;end;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-00600: internal error code, arguments: [723], [41000], [pga heap], [], [],&lt;br /&gt;[], [], []&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. &lt;/b&gt;この時、600診断イベントをPGAヒープダンプを実行するように設定すれば、10261→600→ヒープダンプの手順で自動的にPGAヒープダンプが実行されます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;alter system set events '600 trace name heapdump level 0x20000001';&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;4. &lt;/b&gt;下記は自動的に生成されたPGAヒープダンプの一部です。特に、ダンプレベル0x20000001のおかげで下位Subheapを含んだダンプが記録されます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;DDE: Problem Key 'ORA 600 [723]' was flood controlled (0x2) (incident: 44800)&lt;br /&gt;ORA-00600: internal error code, arguments: [723], [41000], [pga heap], [], [], [], [], []&lt;br /&gt;****** ERROR: PGA size limit exceeded: 102450812 &gt; 102400000 *****&lt;br /&gt;******************************************************&lt;br /&gt;HEAP DUMP heap name="pga heap"  desc=11AFB098&lt;br /&gt; extent sz=0x206c alt=92 het=32767 rec=0 flg=2 opc=1&lt;br /&gt; parent=00000000 owner=00000000 nex=00000000 xsz=0xfff8 heap=00000000&lt;br /&gt; fl2=0x60, nex=00000000&lt;br /&gt;EXTENT 0 addr=39150008&lt;br /&gt;  Chunk 39150010 sz=    24528    free      "               "&lt;br /&gt;  Chunk 39155fe0 sz=    40992    freeable  "koh-kghu call  "  ds=0D4D9A60&lt;br /&gt;EXTENT 1 addr=39140008&lt;br /&gt;  Chunk 39140010 sz=    24528    free      "               "&lt;br /&gt;  Chunk 39145fe0 sz=    40992    freeable  "koh-kghu call  "  ds=0D4D9A60&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;5. &lt;/b&gt;最後の段階はヒープダンプを分析してどんなオブジェクトがヒープを使用しているのかを見破ることです。例えば、私は&lt;b&gt;ティパック&lt;/b&gt;という個人的なライブラリーを使用します。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;select * from table(tpack.heap_file_report('C:\oracle\diag\rdbms\ukja1106\ukja1106\trace\ukja1106_ora_3640.trc'));&lt;br /&gt;&lt;br /&gt;TYPE     HEAP_NAME        ITEM             ITEM_COUNT  ITEM_SIZE  HEAP_SIZE      RATIO&lt;br /&gt;-------- ---------------- ---------------- ---------- ---------- ---------- ----------&lt;br /&gt;HEAP     pga heap                                   0      97.14      97.14        100&lt;br /&gt;HEAP     top call heap                              0        .18        .18        100&lt;br /&gt;HEAP     top uga heap                               0        .31        .31        100&lt;br /&gt;CHUNK    pga heap         free                   1554       36.2       97.1       37.3&lt;br /&gt;CHUNK    pga heap         recreate                  9          0       97.1          0&lt;br /&gt;CHUNK    pga heap         perm                     14          0       97.1          0&lt;br /&gt;CHUNK    pga heap         freeable               1597       60.7       97.1       62.5&lt;br /&gt;CHUNK    top uga heap     recreate                  1          0         .3       19.9&lt;br /&gt;CHUNK    top uga heap     free                      5          0         .3          0&lt;br /&gt;CHUNK    top uga heap     freeable                  4         .2         .3       79.9&lt;br /&gt;CHUNK    top call heap    free                      3         .1         .1       65.5&lt;br /&gt;CHUNK    top call heap    recreate                  2          0         .1          1&lt;br /&gt;CHUNK    top call heap    freeable                  1          0         .1       33.3&lt;br /&gt;CHUNK    top call heap    perm                      1          0         .1          0&lt;br /&gt;OBJECT   pga heap         kews sqlstat st           1          0       97.1          0&lt;br /&gt;OBJECT   pga heap         pesom.c:Proces            3          0       97.1          0&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;6. &lt;/b&gt;オラクルの診断イベントのほかに、V$SESSTATビューをモニターリングしながらメモリーサイズがあるサイズを超える時、PGAヒープダンプを行なう方法もあります。例えば、前述の&lt;b&gt;ティパック&lt;/b&gt;ライブラリーを次のように利用できます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;col report_id new_value report_id&lt;br /&gt;&lt;br /&gt;select tpack_server.create_report('Heap Dump') as report_id from dual;&lt;br /&gt;&lt;br /&gt;exec tpack_server.add_parameter('&amp;report_id', 'dump_level', '0x20000001');&lt;br /&gt;exec tpack_server.add_parameter('&amp;report_id', 'get_whole_contents', 0);&lt;br /&gt;&lt;br /&gt;exec tpack_server.add_condition('&amp;report_id', 'STAT', 'session pga memory', '&gt;100000000', 'SUM');&lt;br /&gt;&lt;br /&gt;exec tpack_server.register_report('&amp;report_id');&lt;br /&gt;&lt;br /&gt;-- start server&lt;br /&gt;exec tpack_server.start_server;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;PGAヒープサイズが100000000Bに達すると、ティパックはあらかじめ定義されているプロシージャを実行してヒープダンプを落とします。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;Fri Jun 11 06:19:10 GMT+00:00 2010 : Session 142 got! sum=659645392, name = session pga memory&lt;br /&gt;...&lt;br /&gt;Fri Jun 11 06:27:50 GMT+00:00 2010 : executing report 1:142:1973827792 for session 142&lt;br /&gt;Fri Jun 11 06:27:55 GMT+00:00 2010 : executing report = begin tpack.heap_dump(  dump_level=&gt;'0x20000001', get_whole_contents=&gt;0,  session_id =&gt; 142); end;&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;10261と600診断イベントは仮の方便にすぎないでしょう。最も重要なのはヒープダンプを注意深く分析してPGAサイズの非正常的な増加を防ぐことです。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-8472960127130944017?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/8472960127130944017/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/06/pga.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/8472960127130944017'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/8472960127130944017'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/06/pga.html' title='PGAサイズの非正常的な増加'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-7892616078351440362</id><published>2010-05-11T00:24:00.000-07:00</published><updated>2010-05-11T00:59:55.158-07:00</updated><title type='text'>Library cache pin競合の面白いケース</title><content type='html'>最近、面白いlibrary cache pin（もしくはMutex）の競合の事例を経験しました。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;（この例では&lt;b&gt;TPack&lt;/b&gt;という個人的なライブラリーを使用していますが、すべての情報はX$View,V$View,Trace Fileなどから取得できます。）&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;b&gt;１．&lt;/b&gt;次のようにテーブルを作ります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;drop table t1 purge;&lt;br /&gt;create table t1(c1 int);&lt;br /&gt;insert into t1 values(1);&lt;br /&gt;commit;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;２．&lt;/b&gt;セッション＃１で次のようなSQLコマンドを行います。１００８エーらが発生するのに注意してください。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;       SID    SERIAL# PID&lt;br /&gt;---------- ---------- ----------&lt;br /&gt;       147      23608 11556&lt;br /&gt;&lt;br /&gt;1 row selected.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:05.12&lt;br /&gt;&lt;br /&gt;TPACK@ukja1021&gt; declare&lt;br /&gt;  2    v_value   number;&lt;br /&gt;  3  begin&lt;br /&gt;  4    execute immediate 'select 1 from t1 where c1 = :b1'&lt;br /&gt;  5        into v_value;&lt;br /&gt;  6&lt;br /&gt;  7    dbms_output.put_line('value = ' || v_value);&lt;br /&gt;  8&lt;br /&gt;  9  end;&lt;br /&gt; 10  /&lt;br /&gt;declare&lt;br /&gt;*&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-01008: not all variables bound&lt;br /&gt;ORA-06512: at line 4&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;３．&lt;/b&gt;セッション＃２が同じコマンドを実行します。でも、ブロックされて待機ようになります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;       SID    SERIAL# PID&lt;br /&gt;---------- ---------- ----------&lt;br /&gt;       148      56408 11736&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.00&lt;br /&gt;&lt;br /&gt;TPACK@ukja1021&gt; declare&lt;br /&gt;  2    v_value   number;&lt;br /&gt;  3  begin&lt;br /&gt;  4    execute immediate 'select 1 from t1 where c1 = :b1'&lt;br /&gt;  5        into v_value;&lt;br /&gt;  6&lt;br /&gt;  7    dbms_output.put_line('value = ' || v_value);&lt;br /&gt;  8&lt;br /&gt;  9  end;&lt;br /&gt; 10  /&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;４．&lt;/b&gt;なぜセッション＃２が待機するのでしょうか。詳細な待機情報を分析してみます。セッション＃１（１４７）がCURSORタイプ（SQL）のオブジェクトに対してlibrary cache pinをExclusiveモードで獲得しているのが原因であることがわらります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1021&gt; select * from table(tpack.session_detail(&amp;sid));&lt;br /&gt;Enter value for sid: 148&lt;br /&gt;old   1: select * from table(tpack.session_detail(&amp;sid))&lt;br /&gt;new   1: select * from table(tpack.session_detail(148))&lt;br /&gt;&lt;br /&gt;NAME                           VALUE&lt;br /&gt;------------------------------ --------------------&lt;br /&gt;SID                            148&lt;br /&gt;Serial#                        56408&lt;br /&gt;SPID                           11736&lt;br /&gt;Program                        sqlplus.exe&lt;br /&gt;Process                        7940:1228&lt;br /&gt;Module                         SQL*Plus&lt;br /&gt;SQL ID&lt;br /&gt;Child No&lt;br /&gt;SQL Text&lt;br /&gt;Status                         ACTIVE&lt;br /&gt;Blocking Instance              1&lt;br /&gt;Blocking Session               147&lt;br /&gt;Event                          library cache pin&lt;br /&gt;Seq#                           29&lt;br /&gt;P1(P1raw)                      764496508(2D914A7C)&lt;br /&gt;P2(P2raw)                      764496508(2CDC1330)&lt;br /&gt;P3(P3raw)                      764496508(000000C8)&lt;br /&gt;Seconds in wait                81&lt;br /&gt;State                          WAITING&lt;br /&gt;Wait Event                     library cache pin&lt;br /&gt;Holder SID                     147&lt;br /&gt;Namespace                      CURSOR&lt;br /&gt;Object                         select 1 from t1 whe&lt;br /&gt;                               re c1 = :b1&lt;br /&gt;&lt;br /&gt;Holding Mode                   3(X)&lt;br /&gt;&lt;br /&gt;24 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;５．&lt;/b&gt;より正確な情報のために待機しているセッション＃１に対してコールスタックをプロファイリングしてみます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TPACK@ukja1021&gt; select * from table(tpack.callstack_prof_report(&amp;sid));&lt;br /&gt;Enter value for sid: 148&lt;br /&gt;old   1: select * from table(tpack.callstack_prof_report(&amp;sid))&lt;br /&gt;new   1: select * from table(tpack.callstack_prof_report(148))&lt;br /&gt;&lt;br /&gt;STACK_TRACE&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;   HIT_CNT    HIT_PCT&lt;br /&gt;---------- ----------&lt;br /&gt;7C80B710&lt;br /&gt; -&gt;_OracleThreadStart@4&lt;br /&gt;  -&gt;_opimai&lt;br /&gt;   -&gt;_opimai_real&lt;br /&gt;    -&gt;_sou2o&lt;br /&gt;     -&gt;_opidrv&lt;br /&gt;      -&gt;_opiodr&lt;br /&gt;       -&gt;_opiino&lt;br /&gt;        -&gt;_opitsk&lt;br /&gt;         -&gt;_ttcpip&lt;br /&gt;          -&gt;_opiodr&lt;br /&gt;           -&gt;_kpoal8&lt;br /&gt;            -&gt;_opiexe&lt;br /&gt;             -&gt;_kkxexe&lt;br /&gt;              -&gt;_peicnt&lt;br /&gt;               -&gt;_plsql_run&lt;br /&gt;                -&gt;_pfrrun&lt;br /&gt;                 -&gt;_pfrrun_no_tool&lt;br /&gt;                  -&gt;_pfrinstr_EXIM&lt;br /&gt;                   -&gt;_pevm_EXIM&lt;br /&gt;                    -&gt;_psdnal&lt;br /&gt;                     -&gt;_psddr0&lt;br /&gt;                      -&gt;_rpidrv&lt;br /&gt;                       -&gt;_rpiswu2&lt;br /&gt;                        -&gt;_rpidru&lt;br /&gt;                         -&gt;_rpidrus&lt;br /&gt;                          -&gt;_opiodr&lt;br /&gt;                           -&gt;_opipls&lt;br /&gt;                            -&gt;_opiosq0&lt;br /&gt;                             -&gt;_kksParseCursor&lt;br /&gt;                              -&gt;_kkspsc0&lt;br /&gt;                               -&gt;_kksfbc&lt;br /&gt;                                -&gt;_kksSearchChildList&lt;br /&gt;                                 -&gt;_kksCheckCursor&lt;br /&gt;                                  -&gt;_kkslce&lt;br /&gt;                                   -&gt;_kxsGetRuntimeLock&lt;br /&gt;                                    -&gt;_kglpin&lt;br /&gt;                                     -&gt;_kglpnal&lt;br /&gt;                                      -&gt;__PGOSF169__ksfwat&lt;br /&gt;                                       -&gt;_kslwait&lt;br /&gt;                                        -&gt;_kskthbwt&lt;br /&gt;                                         -&gt;_kslwaitns&lt;br /&gt;                                          -&gt;_ksliwat&lt;br /&gt;                                           -&gt;_skgpwwait&lt;br /&gt;                                            -&gt;7C80253D&lt;br /&gt;                                             -&gt;00000000&lt;br /&gt;                                              -&gt;7C80B710&lt;br /&gt;                                               -&gt;_OracleOradebugThreadStart@4&lt;br /&gt;                                                -&gt;_ssthreadsrgruncallback&lt;br /&gt;                                                 -&gt;_ksdxcb&lt;br /&gt;                                                  -&gt;_ksdxfdmp&lt;br /&gt;                                                   -&gt;_ksedst_tracecb&lt;br /&gt;        10        100&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;関数名_pevm_EXIMに注意してください。これはExecute Immediateコマンドが実行しているのを意味します。複雑な状況でこのような待機が発生したらコールスタック情報が特に有効です。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;これは疑いなくバグであるはずです。MOS（Metalink）で次のようなKeywordで検索してみます。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;library cache pin&lt;br /&gt;&lt;li&gt;execute immediate&lt;br /&gt;&lt;li&gt;pevm_EXIM&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_QmSegOSVHT8/S-kMEAAXp9I/AAAAAAAAAC8/xp1-uZWEpvg/s1600/execute_immediate.jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 640px; height: 174px;" src="http://3.bp.blogspot.com/_QmSegOSVHT8/S-kMEAAXp9I/AAAAAAAAAC8/xp1-uZWEpvg/s320/execute_immediate.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5469916485278738386" /&gt;&lt;/a&gt;&lt;br /&gt;いくつかの簡単な情報を適切に取得すれば問題の分析がより正確で便利にできるようになることが分かります。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-7892616078351440362?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/7892616078351440362/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/05/library-cache-pin.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/7892616078351440362'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/7892616078351440362'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/05/library-cache-pin.html' title='Library cache pin競合の面白いケース'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_QmSegOSVHT8/S-kMEAAXp9I/AAAAAAAAAC8/xp1-uZWEpvg/s72-c/execute_immediate.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-5883382823777806277</id><published>2010-04-25T18:27:00.000-07:00</published><updated>2010-04-25T19:12:45.896-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='oradebug'/><category scheme='http://www.blogger.com/atom/ns#' term='ashdump'/><title type='text'>Oracle Hang状態でActive Session Listを獲得しよう。</title><content type='html'>&lt;a href="http://www.maxgauge.jp/"&gt;Maxgauge&lt;/a&gt;のようなツールの最高のメリットはOracle Hang状態でもActive Session Listを得ることができるということです。待機イベントとSQL情報が含まれたActive Session Listこそがすべての性能トラブルシューティングの初めです。ここからすべてのトラブルシューティングが始まります。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;MaxgaugeのようなツールがOracle Hang状況でもデータを収集できるのはDMA（Direct Memory Access）を使用するためです。この方法はOracleのCOE（Center of Expertise)チームが極限の状況、すなわちSQLで必要な情報を収集できない際使用していた方法です。これがMaxgaugeのようなツールのおかげで普遍化されたのです。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;もしMaxgaugeのようなツールがなければどうすればいいでしょうか。Oracleが提供するASHDUMP機能をPreliminary Connectionとともに使えば同じ効果が得られます。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Preliminary ConnectionとはSQL*Net方式ではない、Direct Memory Access方式で接続することです。&lt;br /&gt;&lt;li&gt;ASHDUMPとはActive Session ListのメモリーバージョンであるASH（Active Session History）をテキストファイルに書き込む機能です。&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;従って、この二つの機能を一緒に使えばまるでDMA方式でActive Session Listを得るのは同じ効果があります。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;簡単な例で説明します。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;まずPreliminary Connectionを結びます。&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;#&gt; sqlplus -prelim sys/oracle@ukja1106 as sysdba&lt;br /&gt;SQL*Plus: Release 11.1.0.6.0 - Production on Mon Apr 26 09:43:35 2010&lt;br /&gt;Copyright (c) 1982, 2007, Oracle.  All rights reserved.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;一般的なクエリは動きません。&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;alter session set nls_date_format='yyyy/mm/dd hh24:mi:ss'&lt;br /&gt;*&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-01012: not logged on&lt;br /&gt;Process ID: 0&lt;br /&gt;Session ID: 0 Serial number: 0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Preliminary Connection状態でASHDUMPを行ないます。レベル（10）は10分を意味します。すなわち、過去の10分間のASHを意味します。&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SYS@ukja1106&gt; oradebug setmypid&lt;br /&gt;Statement processed.&lt;br /&gt;SYS@ukja1106&gt; oradebug dump ashdump 10&lt;br /&gt;Statement processed.&lt;br /&gt;SYS@ukja1106&gt; oradebug tracefile_name&lt;br /&gt;c:\oracle\diag\rdbms\ukja1106\ukja1106\trace\ukja1106_ora_12152.trc&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ダンプファイルの内容は次のようです。11gからはActive Session Listとともに該当リストをテーブルに格納するためのスクリプトまで提供します。ハングの悪夢が終わったあと（大部分リスタート）、正確な分析をする目的です。&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;Processing Oradebug command 'dump ashdump 10'&lt;br /&gt;ASH dump&lt;br /&gt;&lt;&lt;&lt;ACTIVE SESSION HISTORY - PROCESS TRACE DUMP HEADER BEGIN&gt;&gt;&gt;&lt;br /&gt;****************&lt;br /&gt;SCRIPT TO IMPORT&lt;br /&gt;****************&lt;br /&gt;------------------------------------------&lt;br /&gt;Step 1: Create destination table &lt;ashdump&gt;&lt;br /&gt;------------------------------------------&lt;br /&gt;CREATE TABLE ashdump AS&lt;br /&gt;SELECT * FROM SYS.WRH$_ACTIVE_SESSION_HISTORY WHERE rownum &lt; 0&lt;br /&gt;----------------------------------------------------------------&lt;br /&gt;Step 2: Create the SQL*Loader control file &lt;ashldr.ctl&gt; as below&lt;br /&gt;----------------------------------------------------------------&lt;br /&gt;load data &lt;br /&gt;infile * "str '\n####\n'"&lt;br /&gt;append&lt;br /&gt;into table ashdump&lt;br /&gt;fields terminated by ',' optionally enclosed by '"'&lt;br /&gt;(                               &lt;br /&gt;SNAP_ID  CONSTANT 0           , &lt;br /&gt;DBID                          , &lt;br /&gt;INSTANCE_NUMBER               , &lt;br /&gt;SAMPLE_ID                     , &lt;br /&gt;SAMPLE_TIME TIMESTAMP ENCLOSED BY '"' AND '"' "TO_TIMESTAMP(:SAMPLE_TIME   ,'MM-DD-YYYY HH24:MI:SSXFF')"   , &lt;br /&gt;SESSION_ID                    , &lt;br /&gt;SESSION_SERIAL#               , &lt;br /&gt;SESSION_TYPE                  , &lt;br /&gt;USER_ID                       , &lt;br /&gt;SQL_ID                        , &lt;br /&gt;SQL_CHILD_NUMBER              , &lt;br /&gt;SQL_OPCODE                    , &lt;br /&gt;FORCE_MATCHING_SIGNATURE      , &lt;br /&gt;TOP_LEVEL_SQL_ID              , &lt;br /&gt;TOP_LEVEL_SQL_OPCODE          , &lt;br /&gt;SQL_PLAN_HASH_VALUE           , &lt;br /&gt;SQL_PLAN_LINE_ID              , &lt;br /&gt;SQL_PLAN_OPERATION#           , &lt;br /&gt;SQL_PLAN_OPTIONS#             , &lt;br /&gt;SQL_EXEC_ID                   , &lt;br /&gt;SQL_EXEC_START DATE 'MM/DD/YYYY HH24:MI:SS' ENCLOSED BY '"' AND '"' ":SQL_EXEC_START"   , &lt;br /&gt;PLSQL_ENTRY_OBJECT_ID         , &lt;br /&gt;PLSQL_ENTRY_SUBPROGRAM_ID     , &lt;br /&gt;PLSQL_OBJECT_ID               , &lt;br /&gt;PLSQL_SUBPROGRAM_ID           , &lt;br /&gt;QC_INSTANCE_ID                , &lt;br /&gt;QC_SESSION_ID                 , &lt;br /&gt;QC_SESSION_SERIAL#            , &lt;br /&gt;EVENT_ID                      , &lt;br /&gt;SEQ#                          , &lt;br /&gt;P1                            , &lt;br /&gt;P2                            , &lt;br /&gt;P3                            , &lt;br /&gt;WAIT_TIME                     , &lt;br /&gt;TIME_WAITED                   , &lt;br /&gt;BLOCKING_SESSION              , &lt;br /&gt;BLOCKING_SESSION_SERIAL#      , &lt;br /&gt;CURRENT_OBJ#                  , &lt;br /&gt;CURRENT_FILE#                 , &lt;br /&gt;CURRENT_BLOCK#                , &lt;br /&gt;CURRENT_ROW#                  , &lt;br /&gt;CONSUMER_GROUP_ID             , &lt;br /&gt;XID                           , &lt;br /&gt;REMOTE_INSTANCE#              , &lt;br /&gt;TIME_MODEL                    , &lt;br /&gt;SERVICE_HASH                  , &lt;br /&gt;PROGRAM                       , &lt;br /&gt;MODULE                        , &lt;br /&gt;ACTION                        , &lt;br /&gt;CLIENT_ID                       &lt;br /&gt;)                               &lt;br /&gt;---------------------------------------------------&lt;br /&gt;Step 3: Load the ash rows dumped in this trace file&lt;br /&gt;---------------------------------------------------&lt;br /&gt;sqlldr userid/password control=ashldr.ctl data=&lt;this_trace_filename&gt; errors=1000000&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;&lt;&lt;ACTIVE SESSION HISTORY - PROCESS TRACE DUMP HEADER END&gt;&gt;&gt;&lt;br /&gt;&lt;&lt;&lt;ACTIVE SESSION HISTORY - PROCESS TRACE DUMP BEGIN&gt;&gt;&gt;&lt;br /&gt;####&lt;br /&gt;58646642,1,12519562,"04-26-2010 09:44:01.822000000",160,1,2,0,&lt;br /&gt;"",0,0,0,"",0,0,0,0,0,&lt;br /&gt;0,"",0,0,0,0,0,0,0,&lt;br /&gt;3213517201,9858,0,3,1,0,69788,4294967295,0,&lt;br /&gt;4294967295,0,0,0,0,,0,0,165959219,&lt;br /&gt;"ORACLE.EXE (CKPT)","",&lt;br /&gt;"",""&lt;br /&gt;####&lt;br /&gt;58646642,1,12519486,"04-26-2010 09:42:45.808000000",160,1,2,0,&lt;br /&gt;"",0,0,0,"",0,0,0,0,0,&lt;br /&gt;0,"",0,0,0,0,0,0,0,&lt;br /&gt;3213517201,9767,1,1,1,0,38825,4294967295,0,&lt;br /&gt;4294967295,0,0,0,0,,0,0,165959219,&lt;br /&gt;"ORACLE.EXE (CKPT)","",&lt;br /&gt;"",""&lt;br /&gt;####&lt;br /&gt;58646642,1,12519416,"04-26-2010 09:41:35.770000000",160,1,2,0,&lt;br /&gt;"",0,0,0,"",0,0,0,0,0,&lt;br /&gt;0,"",0,0,0,0,0,0,0,&lt;br /&gt;4078387448,9683,3,3,3,0,11083,4294967295,0,&lt;br /&gt;4294967295,0,0,0,0,,0,0,165959219,&lt;br /&gt;"ORACLE.EXE (CKPT)","",&lt;br /&gt;"",""&lt;br /&gt;####&lt;br /&gt;58646642,1,12519384,"04-26-2010 09:41:03.774000000",163,1,2,0,&lt;br /&gt;"",0,0,0,"",0,0,0,0,0,&lt;br /&gt;0,"",0,0,0,0,0,0,0,&lt;br /&gt;3176176482,40612,5,1,1000,999888,0,4294967291,0,&lt;br /&gt;4294967295,0,0,0,0,,0,0,165959219,&lt;br /&gt;"ORACLE.EXE (DIA0)","",&lt;br /&gt;"",""&lt;br /&gt;####&lt;br /&gt;58646642,1,12519304,"04-26-2010 09:39:43.719000000",160,1,2,0,&lt;br /&gt;"",0,0,0,"",0,0,0,0,0,&lt;br /&gt;0,"",0,0,0,0,0,0,0,&lt;br /&gt;3213517201,9551,1,1,1,0,38798,4294967295,0,&lt;br /&gt;4294967295,0,0,0,0,,0,0,165959219,&lt;br /&gt;"ORACLE.EXE (CKPT)","",&lt;br /&gt;"",""&lt;br /&gt;####&lt;br /&gt;58646642,1,12519265,"04-26-2010 09:39:04.663000000",163,1,2,0,&lt;br /&gt;"",0,0,0,"",0,0,0,0,0,&lt;br /&gt;0,"",0,0,0,0,0,0,0,&lt;br /&gt;3176176482,40493,5,1,1000,999941,0,4294967291,0,&lt;br /&gt;4294967295,0,0,0,0,,0,0,165959219,&lt;br /&gt;"ORACLE.EXE (DIA0)","",&lt;br /&gt;"",""&lt;br /&gt;####&lt;br /&gt;58646642,1,12519183,"04-26-2010 09:37:42.554000000",160,1,2,0,&lt;br /&gt;"",0,0,0,"",0,0,0,0,0,&lt;br /&gt;0,"",0,0,0,0,0,0,0,&lt;br /&gt;3213517201,9406,0,1,1,0,54077,4294967295,0,&lt;br /&gt;4294967295,0,0,0,0,,0,0,165959219,&lt;br /&gt;"ORACLE.EXE (CKPT)","",&lt;br /&gt;"",""&lt;br /&gt;####&lt;br /&gt;&lt;&lt;&lt;ACTIVE SESSION HISTORY - PROCESS TRACE DUMP END&gt;&gt;&gt;&lt;br /&gt;&lt;br /&gt;*** 2010-04-26 09:45:51.625&lt;br /&gt;Oradebug command 'dump ashdump 10' console output: &lt;none&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;事後分析用で使用したら有効であるはずです。ただし、Preliminary ConnectionとOradebugは非公式的な支援機能だから徹底なテストのあとに使用しなければならないし、可能な限りオラクル支援エンジニアの承認の上で使用しなければなりません。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;もっと詳しい情報は次のリンクを参照してください。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://dioncho.blogspot.com/2009/08/preliminary-connectionoradebug-direct.html"&gt;Preliminary ConnectionとDirect Access&lt;/a&gt;&lt;br /&gt;&lt;li&gt;MLノート: 243132.1&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-5883382823777806277?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/5883382823777806277/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/04/oracle-hangactive-session-list.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/5883382823777806277'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/5883382823777806277'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/04/oracle-hangactive-session-list.html' title='Oracle Hang状態でActive Session Listを獲得しよう。'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-6606205013196564173</id><published>2010-03-23T00:23:00.000-07:00</published><updated>2010-03-23T00:39:28.234-07:00</updated><title type='text'>SQLでOSの資源を操作したい時　-　Java Stored Procedureの活用</title><content type='html'>SQLとPL/SQLの多様な機能に慣れてみればOracleがインストールされているOSシステムの資源をSQLやPL/SQLで制御したくなります。SQLとPL/SQLの活用を極大化したい自然な願いだと思います。&lt;b&gt;Java Stored Procedure&lt;/b&gt;がこのような要求事項を解決するに一番強力なツールと言えます。&lt;br /&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;実際に私が開発している&lt;b&gt;トラブルシューチングパック&lt;/b&gt;というライブラリーも内部的にJava Stored Procedureを幅広く活用しています。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;簡単な例を通じて説明してみます。次のような要求事項があります。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;オラクルが設置されたシステムの特定のフォルダーのファイルリストをSQL分を通じて得たいです。&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;次のように解決できます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1106&gt; connect sys/oracle@ukja1106 as sysdba&lt;br /&gt;Connected.&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.00&lt;br /&gt;&lt;br /&gt;       SID    SERIAL# PID&lt;br /&gt;---------- ---------- ----------&lt;br /&gt;       129      12732 12376&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.00&lt;br /&gt;SYS@ukja1106&gt; &lt;br /&gt;SYS@ukja1106&gt; exec dbms_java.grant_permission('UKJA', 'SYS:java.io.FilePermission', -&lt;br /&gt;&gt;   'c:\temp', 'read ,write, execute, delete');&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.03&lt;br /&gt;SYS@ukja1106&gt; &lt;br /&gt;SYS@ukja1106&gt; connect ukja/ukja@ukja1106&lt;br /&gt;Connected.&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.00&lt;br /&gt;&lt;br /&gt;       SID    SERIAL# PID&lt;br /&gt;---------- ---------- ----------&lt;br /&gt;       129      12734 10400&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.00&lt;br /&gt;UKJA@ukja1106&gt; &lt;br /&gt;UKJA@ukja1106&gt; create or replace and compile java source named FileList&lt;br /&gt;  2  as&lt;br /&gt;  3    import java.io.*;&lt;br /&gt;  4    import java.util.*;&lt;br /&gt;  5    import java.sql.*;&lt;br /&gt;  6    import oracle.sql.*;&lt;br /&gt;  7  &lt;br /&gt;  8    public class FileList {&lt;br /&gt;  9  &lt;br /&gt; 10       public static oracle.sql.ARRAY getFileList() throws Exception {&lt;br /&gt; 11  &lt;br /&gt; 12         File f = new File("c:\\temp");&lt;br /&gt; 13         String[] flist = f.list();&lt;br /&gt; 14  &lt;br /&gt; 15         Connection conn = DriverManager.getConnection("jdbc:default:connection:");&lt;br /&gt; 16  &lt;br /&gt; 17         ArrayDescriptor desc = ArrayDescriptor.createDescriptor("VARCHAR2_ARRAY", conn);&lt;br /&gt; 18         ARRAY array = new ARRAY(desc, conn, flist);&lt;br /&gt; 19         return array;&lt;br /&gt; 20       }&lt;br /&gt; 21  &lt;br /&gt; 22    }&lt;br /&gt; 23  ;&lt;br /&gt; 24  /&lt;br /&gt;&lt;br /&gt;Java created.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.18&lt;br /&gt;UKJA@ukja1106&gt; &lt;br /&gt;UKJA@ukja1106&gt; create or replace type varchar2_array as table of varchar2(1000);&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Type created.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.01&lt;br /&gt;UKJA@ukja1106&gt; &lt;br /&gt;UKJA@ukja1106&gt; &lt;br /&gt;UKJA@ukja1106&gt; create or replace function get_file_list&lt;br /&gt;  2    return varchar2_array&lt;br /&gt;  3    as language java&lt;br /&gt;  4    name 'FileList.getFileList() return oracle.sql.ARRAY';&lt;br /&gt;  5  /&lt;br /&gt;&lt;br /&gt;Function created.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.00&lt;br /&gt;UKJA@ukja1106&gt; &lt;br /&gt;UKJA@ukja1106&gt; select * from table(get_file_list);&lt;br /&gt;&lt;br /&gt;COLUMN_VALUE&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;1269319491031.sql&lt;br /&gt;1269319491031.txt&lt;br /&gt;20100323.log&lt;br /&gt;343225818.out&lt;br /&gt;343225818.trc&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.03&lt;br /&gt;UKJA@ukja1106&gt; &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;本当に簡単で強いせす。活用によって多くの作業を自動化できるはずです。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-6606205013196564173?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/6606205013196564173/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/03/sqlos-java-stored-procedure.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/6606205013196564173'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/6606205013196564173'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/03/sqlos-java-stored-procedure.html' title='SQLでOSの資源を操作したい時　-　Java Stored Procedureの活用'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-4682363028078498278</id><published>2010-03-11T20:55:00.000-08:00</published><updated>2010-03-11T22:33:06.462-08:00</updated><title type='text'>Oracle 11.1.0.6のトリガーバグ分析　－　ティパックの活用</title><content type='html'>この頃&lt;b&gt;トラブルシューティングパック（Troubleshooting Pack。以下ティパック）&lt;/b&gt;という名前のライブラリーを開発しています。オラクルが提供するトラブルシューティング機能（診断イベント、ダンプ、X$ビュー）を利用して普遍的ないくつかの性能問題について意味のあるレポートを提供する目的で企画されました。&lt;br /&gt;&lt;p&gt;&lt;br /&gt;次のアーティクルにOracle 11.1.0.6の面白いトリガー関係のバグが紹介されています。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;&lt;a href="http://antognini.ch/2010/03/inserts-experiencing-an-increasing-cpu-consumption/"&gt;Inserts Experiencing an Increasing CPU Consumption&lt;/a&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;このバグで現れる現象ががティパックを活用して問題を分析できる良い例に見えて簡単に紹介します。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;まず上のアーティクルで説明している問題は次のようです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1106&gt; create table t1(c1 int);&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;UKJA@ukja1106&gt; create table t2(c1 int);&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;次のように二つのトリガーを作ります。二つ目のトリガーをどんな意味もないトリガーだけれど開発規則によって追加されました。何もしないトリガーなので性能に与える影響はほとんどないはずです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1106&gt; create or replace trigger t1_trg1 after insert on t1 for each row&lt;br /&gt;  2  begin&lt;br /&gt;  3    insert into t2 values(:new.c1);&lt;br /&gt;  4  end;&lt;br /&gt;  5  /&lt;br /&gt;&lt;br /&gt;Trigger created.&lt;br /&gt;&lt;br /&gt;UKJA@ukja1106&gt; &lt;br /&gt;UKJA@ukja1106&gt; create or replace trigger t1_trg2 after insert on t2 for each row&lt;br /&gt;  2  begin&lt;br /&gt;  3    declare&lt;br /&gt;  4    numrows number;&lt;br /&gt;  5    begin&lt;br /&gt;  6    numrows := 1;&lt;br /&gt;  7    end;&lt;br /&gt;  8  end;&lt;br /&gt;  9  /&lt;br /&gt;&lt;br /&gt;Trigger created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;どころが、Oracle 11.1.0.6で次のように繰り返しにテーブルT1に対してInsertを行うとだんだんその性能が遅れていきます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1106&gt; declare&lt;br /&gt;  2    v_start_time  number := dbms_utility.get_time;&lt;br /&gt;  3  begin&lt;br /&gt;  4    for idx in 1 .. 50000 loop&lt;br /&gt;  5    insert into t1 values(idx);&lt;br /&gt;  6  &lt;br /&gt;  7    if mod(idx, 1000) = 0 then&lt;br /&gt;  8      dbms_output.put_line(idx || 'th exe = ' || (dbms_utility.get_time - v_start_time)/100);&lt;br /&gt;  9      v_start_time := dbms_utility.get_time;&lt;br /&gt; 10    end if;&lt;br /&gt; 11    end loop;&lt;br /&gt; 12  end;&lt;br /&gt; 13  /&lt;br /&gt;1000th exe = .14&lt;br /&gt;2000th exe = .17&lt;br /&gt;3000th exe = .16&lt;br /&gt;4000th exe = .17&lt;br /&gt;5000th exe = .15&lt;br /&gt;6000th exe = .19&lt;br /&gt;7000th exe = .16&lt;br /&gt;8000th exe = .2&lt;br /&gt;9000th exe = .19&lt;br /&gt;10000th exe = .2&lt;br /&gt;11000th exe = .21&lt;br /&gt;12000th exe = .23&lt;br /&gt;13000th exe = .22&lt;br /&gt;14000th exe = .23&lt;br /&gt;15000th exe = .25&lt;br /&gt;16000th exe = .24&lt;br /&gt;17000th exe = .23&lt;br /&gt;18000th exe = .27&lt;br /&gt;19000th exe = .28&lt;br /&gt;20000th exe = .26&lt;br /&gt;21000th exe = .27&lt;br /&gt;22000th exe = .28&lt;br /&gt;23000th exe = .3&lt;br /&gt;24000th exe = .31&lt;br /&gt;25000th exe = .3&lt;br /&gt;26000th exe = .34&lt;br /&gt;27000th exe = .31&lt;br /&gt;28000th exe = .35&lt;br /&gt;29000th exe = .36&lt;br /&gt;30000th exe = .43&lt;br /&gt;31000th exe = .52&lt;br /&gt;32000th exe = .45&lt;br /&gt;33000th exe = .49&lt;br /&gt;34000th exe = .43&lt;br /&gt;35000th exe = .68&lt;br /&gt;36000th exe = .64&lt;br /&gt;37000th exe = .89&lt;br /&gt;38000th exe = .53&lt;br /&gt;39000th exe = .44&lt;br /&gt;40000th exe = .47&lt;br /&gt;41000th exe = .48&lt;br /&gt;42000th exe = .52&lt;br /&gt;43000th exe = .56&lt;br /&gt;44000th exe = .59&lt;br /&gt;45000th exe = .64&lt;br /&gt;46000th exe = .69&lt;br /&gt;47000th exe = .7&lt;br /&gt;48000th exe = .77&lt;br /&gt;49000th exe = .78&lt;br /&gt;50000th exe = .84&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Oracle 10gR2やOracle 11.1.0.7ではこのような現象は発生しません。Oracle 11.1.0.6のバグに見えます。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;上のアーティクルでは次のような二つの現象を報告しています。&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;  &lt;li&gt;実行回数が増えるほどCPU使用時間が増えていく（SQL*Trace）&lt;br /&gt;  &lt;li&gt;実行回数が増えるほどPGA使用量が増えていく（V$SESSTAT）&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;上の現象から見るとたぶんわけのわからない理由のためメモリーを使いすぎて、その過程でCPU使用時間も増えるように見えます。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;この問題をティパック（Troubleshooting Pack）を通じて分析してみます。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;Session Snapshot機能を通じてどんな性能指標がおもに増加するか観察する。&lt;br /&gt;  &lt;li&gt;PGA Heap Dump分析を通じてPGAのサイズが増加する原因を分析します。&lt;br /&gt;  &lt;li&gt;Call Stack Traceの分析を通じてどんな係数をおもに呼び出しながらCPUを使用するか分析する。&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;下にその結果があります。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;Session Snapshotで得た結果はアーティクルの結果と一致します。500,000件のInsertが終わったあとPGAのサイズが1.6Mから45Mまで増加したのがわかります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;01. statistics&lt;br /&gt;&lt;br /&gt;NAME                                          DIFF         VALUE1         VALUE2&lt;br /&gt;----------------------------------- -------------- -------------- --------------&lt;br /&gt;session pga memory                      45,285,376      1,663,952     46,949,328&lt;br /&gt;session pga memory max                  45,154,304      1,795,024     46,949,328&lt;br /&gt;session uga memory                      45,104,696        891,300     45,995,996&lt;br /&gt;session uga memory max                  44,973,768      1,022,228     45,995,996&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;PGAのサイズが増加した原因を分析するためにPGAレポートを生成します。&lt;br /&gt;&lt;pre class="brush: sql; highlight: [1]"&gt;&lt;br /&gt;UKJA@ukja1106&gt; select * from table(tpack.get_pga_report);&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;02. Size per Chunk Type&lt;br /&gt;&lt;br /&gt;Heap Name            Chunk Type Count      Chunk Size Heap Size  Ratio(%)&lt;br /&gt;-------------------- ---------- ---------- ---------- ---------- ----------&lt;br /&gt;...&lt;br /&gt;kxs-heap-p           perm                1          0       20.9          0&lt;br /&gt;kxs-heap-p           freeable       250005       20.9       20.9       99.6&lt;br /&gt;pesom.c:Proces       free                2          0          0       68.3&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;03. Size Per Object Type&lt;br /&gt;Heap Name            Obj Type             Count      Chunk Si Heap Siz Ratio&lt;br /&gt;-------------------- -------------------- ---------- -------- -------- -----&lt;br /&gt;...&lt;br /&gt;kxs-heap-p           kxt.c: PL/SQL p          150000     16.3     20.9  77.7&lt;br /&gt;kxs-heap-p           kxt.c: Trigger           100000      4.5     20.9  21.8&lt;br /&gt;kxs-heap-p           none                       3334        0     20.9    .3&lt;br /&gt;kxs-heap-p           kxtcin:kxscplx                1        0     20.9     0&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Kxs-heap-pヒープのfreeable Chunk（再活用可能なChunk）の数が250,005個に達して、20M程度のサイズを占めています。メモリーりーくが疑われます。KXSはKernel Execution Shared Cursorの略語です。Shared Cursorと関連したメモリー問題の可能性があります。PGA内のShared Cursorの領域はSESSION_CACHED_CURSORパラメータで制御されます。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;Ksx-heap-pヒープのkxt.c: PL/SQL p、kxt.c: Triggerオブジェクトの数が250,000個です。この値はkxs-heap-pヒープのfreeable Chunkの数とほとんど一致しています。KXTはKernel Execution Triggerの略語です。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;情報を総合してみると、トリガー実行関係のオブジェクトがShared Cursor領域で再活用されないまま繰り返しに追加され、メモリーとCPUの使用量が一緒に増加していると仮定できます。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;Call Stack Trace分析を通じて実際にどんなコードがおもに呼び出されているのか確認してみます。&lt;br /&gt;&lt;pre class="brush: sql; highlight: [1]"&gt;&lt;br /&gt;UKJA@ukja1106&gt; select * from table(tpack.stacktrace_profile_report(&amp;1,5,0.1));&lt;br /&gt;old   1: select * from table(tpack.stacktrace_profile_report(&amp;1,5,0.1))&lt;br /&gt;new   1: select * from table(tpack.stacktrace_profile_report(134,5,0.1))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;STACK_TRACE&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;   HIT_CNT    HIT_PCT&lt;br /&gt;---------- ----------&lt;br /&gt;-&gt;00000000-&gt;7C80B710-&gt;_OracleThreadStart@4()+764-&gt;_opimai()+92-&gt;_opimai_real()+1&lt;br /&gt;30-&gt;_sou2o()+45-&gt;_opidrv()+807-&gt;_opiodr()+1224-&gt;_opiino()+1067-&gt;_opitsk()+1278-&gt;&lt;br /&gt;_ttcpip()+2733-&gt;_opiodr()+1224-&gt;_kpoal8()+2299-&gt;_opiexe()+14438-&gt;_kkxexe()+1046-&lt;br /&gt;&gt;_peicnt()+250-&gt;_plsql_run()+729-&gt;_pfrrun()+875-&gt;_pfrrun_no_tool()+56-&gt;_pfrinstr&lt;br /&gt;_EXECC()+58-&gt;_pevm_EXECC()+295-&gt;_psdnal()+348-&gt;_psddr0()+487-&gt;_rpidrv()+1357-&gt;_r&lt;br /&gt;piswu2()+560-&gt;_rpidru()+88-&gt;_rpidrus()+178-&gt;_opiodr()+1224-&gt;_opipls()+1312-&gt;_opi&lt;br /&gt;exe()+5344-&gt;_insexe()+1027-&gt;_qerltcFetch()+896-&gt;_qerltcInsertValuesRop()+243-&gt;_q&lt;br /&gt;erltcLoadStateMachine()+609-&gt;__PGOSF492__qerltcNoKdtBufferedInsRowCBK()+336-&gt;__V&lt;br /&gt;Infreq__qesltcAfterRowProcessing()+238-&gt;_qesltcExecuteAfterRowTriggers()+1299-&gt;_&lt;br /&gt;kxtexe()+483-&gt;_rpiswu2()+560-&gt;_kxtex1()+478-&gt;_kkxtexe()+901-&gt;_peiet_execute_trig&lt;br /&gt;ger()+47-&gt;_peicnt()+250-&gt;_plsql_run()+729-&gt;_pfrrun()+875-&gt;_pfrrun_no_tool()+56-&gt;&lt;br /&gt;_pfrinstr_EXECC()+58-&gt;_pevm_EXECC()+295-&gt;_psdnal()+348-&gt;_psddr0()+487-&gt;_rpidrv()&lt;br /&gt;+1357-&gt;_rpiswu2()+560-&gt;_rpidru()+88-&gt;_rpidrus()+178-&gt;_opiodr()+1224-&gt;_opipls()+1&lt;br /&gt;312-&gt;_opiexe()+1823-&gt;_kksfbc()+12485-&gt;_kxtInstantiateAllTriggers()+77-&gt;_kxtCreat&lt;br /&gt;eTriggerInst()+250-&gt;_kxsPersistentHeapAllocFree()+42-&gt;_kghalf()+188-&gt;00000000-&gt;7&lt;br /&gt;C80B710-&gt;_OracleOradebugThreadStart@4()+825-&gt;_ssthreadsrgruncallback()+432-&gt;_ksd&lt;br /&gt;xcb()+1780-&gt;_ksdxfdmp()+850-&gt;_ksedst_tracecb()+53-&gt;_ksedst1()+91-&gt;_skdstdst()+11&lt;br /&gt;4&lt;br /&gt;         8         16&lt;br /&gt;&lt;br /&gt;-&gt;00000000-&gt;7C80B710-&gt;_OracleThreadStart@4()+764-&gt;_opimai()+92-&gt;_opimai_real()+1&lt;br /&gt;30-&gt;_sou2o()+45-&gt;_opidrv()+807-&gt;_opiodr()+1224-&gt;_opiino()+1067-&gt;_opitsk()+1278-&gt;&lt;br /&gt;_ttcpip()+2733-&gt;_opiodr()+1224-&gt;_kpoal8()+2299-&gt;_opiexe()+14438-&gt;_kkxexe()+1046-&lt;br /&gt;&gt;_peicnt()+250-&gt;_plsql_run()+729-&gt;_pfrrun()+875-&gt;_pfrrun_no_tool()+56-&gt;_pfrinstr&lt;br /&gt;_EXECC()+58-&gt;_pevm_EXECC()+295-&gt;_psdnal()+348-&gt;_psddr0()+487-&gt;_rpidrv()+1357-&gt;_r&lt;br /&gt;piswu2()+560-&gt;_rpidru()+88-&gt;_rpidrus()+178-&gt;_opiodr()+1224-&gt;_opipls()+1312-&gt;_opi&lt;br /&gt;exe()+1823-&gt;_kksfbc()+12485-&gt;_kxtInstantiateAllTriggers()+77-&gt;_kxtCreateTriggerI&lt;br /&gt;nst()+250-&gt;_kxsPersistentHeapAllocFree()+42-&gt;_kghalf()+188-&gt;00000000-&gt;7C80B710-&gt;&lt;br /&gt;_OracleOradebugThreadStart@4()+825-&gt;_ssthreadsrgruncallback()+432-&gt;_ksdxcb()+178&lt;br /&gt;0-&gt;_ksdxfdmp()+850-&gt;_ksedst_tracecb()+53-&gt;_ksedst1()+91-&gt;_skdstdst()+114&lt;br /&gt;         3          6&lt;br /&gt;&lt;br /&gt;-&gt;00000000-&gt;7C80B710-&gt;_OracleThreadStart@4()+764-&gt;_opimai()+92-&gt;_opimai_real()+1&lt;br /&gt;30-&gt;_sou2o()+45-&gt;_opidrv()+807-&gt;_opiodr()+1224-&gt;_opiino()+1067-&gt;_opitsk()+1278-&gt;&lt;br /&gt;_ttcpip()+2733-&gt;_opiodr()+1224-&gt;_kpoal8()+2299-&gt;_opiexe()+14438-&gt;_kkxexe()+1046-&lt;br /&gt;&gt;_peicnt()+250-&gt;_plsql_run()+729-&gt;_pfrrun()+875-&gt;_pfrrun_no_tool()+56-&gt;_pfrinstr&lt;br /&gt;_EXECC()+58-&gt;_pevm_EXECC()+295-&gt;_psdnal()+348-&gt;_psddr0()+487-&gt;_rpidrv()+1357-&gt;_r&lt;br /&gt;piswu2()+560-&gt;_rpidru()+88-&gt;_rpidrus()+178-&gt;_opiodr()+1224-&gt;_opipls()+1312-&gt;_opi&lt;br /&gt;exe()+5344-&gt;_insexe()+1027-&gt;_qerltcFetch()+896-&gt;_qerltcInsertValuesRop()+243-&gt;_q&lt;br /&gt;erltcLoadStateMachine()+609-&gt;__PGOSF492__qerltcNoKdtBufferedInsRowCBK()+336-&gt;__V&lt;br /&gt;Infreq__qesltcAfterRowProcessing()+238-&gt;_qesltcExecuteAfterRowTriggers()+1299-&gt;_&lt;br /&gt;kxtexe()+483-&gt;_rpiswu2()+560-&gt;_kxtex1()+478-&gt;_kkxtexe()+901-&gt;_peiet_execute_trig&lt;br /&gt;ger()+47-&gt;_peicnt()+250-&gt;_plsql_run()+729-&gt;_pfrrun()+875-&gt;_pfrrun_no_tool()+56-&gt;&lt;br /&gt;_pfrinstr_EXECC()+58-&gt;_pevm_EXECC()+295-&gt;_psdnal()+348-&gt;_psddr0()+487-&gt;_rpidrv()&lt;br /&gt;+1357-&gt;_rpiswu2()+560-&gt;_rpidru()+88-&gt;_rpidrus()+178-&gt;_opiodr()+1224-&gt;_opipls()+1&lt;br /&gt;312-&gt;_opiexe()+1823-&gt;_kksfbc()+12485-&gt;_kxtInstantiateAllTriggers()+77-&gt;_kxtCreat&lt;br /&gt;eTriggerInst()+304-&gt;_kxsPersistentHeapAllocFree()+42-&gt;_kghalf()+188-&gt;00000000-&gt;7&lt;br /&gt;C80B710-&gt;_OracleOradebugThreadStart@4()+825-&gt;_ssthreadsrgruncallback()+432-&gt;_ksd&lt;br /&gt;xcb()+1780-&gt;_ksdxfdmp()+850-&gt;_ksedst_tracecb()+53-&gt;_ksedst1()+91-&gt;_skdstdst()+11&lt;br /&gt;4&lt;br /&gt;         3          6&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Call Stack Traceを見ると下の呼びさしパターンが一番普遍的に発生していることがわかります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;_kxtInstantiateAllTriggers&lt;br /&gt;_kxtCreateTriggerInst&lt;br /&gt;_kxsPersistentHeapAllocFree&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;PGAレポートと完璧に一致する係数呼び出しパターンです。トリガーオブジェクトを続けて生成し、その過程でメモリーを続けて割り当てされています。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;確かに、メモリー関係のバグの可能性が高そうです。こんな情報があればメタリンクをもっと正確に検索できます。下にメタリンクの検索結果があります。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;img class="txc-image-c" src="http://cfile6.uf.tistory.com/image/173B50124B99BF5E2E5E5F"  ld="1C|cfile6.uf@173B50124B99BF5E2E5E5F.jpg|width=&amp;quot;640&amp;quot; height=&amp;quot;251&amp;quot; alt=&amp;quot;&amp;quot; filename=&amp;quot;trigger.jpg&amp;quot; filemime=&amp;quot;image/jpeg&amp;quot;|"  /&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;バグ番号6400175が見えるでしょう。ほとんど完璧に一致する現象を報告しています。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;オラクルが提供するいくつかの基本的な性能データを通じてオラクルでの性能異常現象を体系的に分析できるということを見せてくれる良い例だと思われます。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;b&gt;PS) &lt;/b&gt;上の例で紹介されたティパックの機能は次のようなデータを利用しています。このデータたちを直接検索し、分析できると誰でも同一な分析が可能です。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;V$SESSTAT&lt;br /&gt;&lt;li&gt;PGA Heap Dump (oradebug dump heapdump 0x20000001)&lt;br /&gt;&lt;li&gt;Call Stack (oradebug dump callstack 1)&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-4682363028078498278?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/4682363028078498278/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/03/oracle-11106.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/4682363028078498278'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/4682363028078498278'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/03/oracle-11106.html' title='Oracle 11.1.0.6のトリガーバグ分析　－　ティパックの活用'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-6422339608374900684</id><published>2010-03-04T17:09:00.000-08:00</published><updated>2010-03-04T18:13:05.343-08:00</updated><title type='text'>Errorstackダンプで問題のSQLを突き止めること</title><content type='html'>オラクルでエラーが発生したとき、どのSQL文が問題なのかを突き止める必要があります。例えば、alert.logファイルに次のようなエラーメッセージが記録されています。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;Fri Mar 05 09:47:53 2010&lt;br /&gt;ORA-1652: unable to extend temp segment by 128 in tablespace                 VERY_SMALL_TBS &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;どんなSQL文が犯人なのかを知らなければ、解決は難しいでしょ。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;このような場合でやってみれるのが&lt;b&gt;ErrorStackダンプ&lt;/b&gt;です。ErrorStackダンプを診断イベントと一緒に使えばエラーを起こしたSQL文がトレースファイルに記録されるようにすることができます。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;簡単な例で説明します。まず小さなサイズ（10m）のテーブルスペースを作ります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; create tablespace very_small_tbs&lt;br /&gt;  2  datafile size 10m;&lt;br /&gt;&lt;br /&gt;Tablespace created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ORA-01652エラーが発生したら、ErrorStackダンプを実行するように診断イベントを掛けます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; alter system set events '1652 trace name errorstack level 1, forever';&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;10mより大きいテーブルを作ればORA-01652エラーが発生します。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; create table tbig(c1)&lt;br /&gt;  2  tablespace very_small_tbs&lt;br /&gt;  3  as&lt;br /&gt;  4  select rpad('x',1000) from dual&lt;br /&gt;  5  connect by level &lt;= 10000&lt;br /&gt;  6  ;&lt;br /&gt;select rpad('x',1000) from dual&lt;br /&gt;                           *&lt;br /&gt;ERROR at line 4:&lt;br /&gt;ORA-01652: unable to extend temp segment by 128 in tablespace VERY_SMALL_TBS&lt;br /&gt;&lt;br /&gt;UKJA@ukja1021&gt; alter system set events '1652 trace name context off';&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Alert.logファイルには次のようなエラーメッセージが残ります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;Fri Mar 05 09:47:53 2010&lt;br /&gt;ORA-1652: unable to extend temp segment by 128 in tablespace                 VERY_SMALL_TBS &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;プロセスのダンプファイルにはエラー発生時のSQL文とCallStackトレースが記録してあります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;ORA-01652: unable to extend temp segment by 128 in tablespace VERY_SMALL_TBS&lt;br /&gt;Current SQL statement for this session:&lt;br /&gt;create table tbig(c1)&lt;br /&gt;tablespace very_small_tbs&lt;br /&gt;as&lt;br /&gt;select rpad('x',1000) from dual&lt;br /&gt;connect by level &lt;= 10000&lt;br /&gt;----- Call Stack Trace -----&lt;br /&gt;calling              call     entry                argument values in hex      &lt;br /&gt;location             type     point                (? means dubious value)     &lt;br /&gt;-------------------- -------- -------------------- ----------------------------&lt;br /&gt;_ksedst+38           CALLrel  _ksedst1+0           0 1&lt;br /&gt;_ksedmp+898          CALLrel  _ksedst+0            0&lt;br /&gt;_ksddoa+2088         CALLreg  00000000             1&lt;br /&gt;_ksdpcg+238          CALLrel  _ksddoa+0            A9615C0 93C78C0&lt;br /&gt;_ksdpec+230          CALLrel  _ksdpcg+0            674 C04A478 1&lt;br /&gt;__PGOSF89__ksfpec+1  CALLrel  _ksdpec+0            674&lt;br /&gt;18                                                 &lt;br /&gt;_kgesev+88           CALLreg  00000000             A0C6760 674&lt;br /&gt;_ksesec2+39          CALLrel  _kgesev+0            A0C6760 93C0020 674 2 C04A4E4&lt;br /&gt;_ktsxterr+316        CALLrel  _ksesec2+0           674 0 80 0 1 E C04A55E&lt;br /&gt;_ktfbtgex1+969       CALLrel  _ktsxterr+0          792DE5C 80 0&lt;br /&gt;_ktsxs_add+1766      CALLrel  _ktfbtgex1+0         C04AD8C 3D C04AA50 80 18 A 3&lt;br /&gt;                                                   0 0 C04AD50 37B3EE88&lt;br /&gt;_ktsxssr_sadd+1409   CALLrel  _ktsxs_add+0         C04B048 C04AD8C 80 A 3 0 18 1&lt;br /&gt;                                                   C04B11C C04AE08 C04ADC0 0&lt;br /&gt;                                                   C04AD50&lt;br /&gt;_ktrsexec+372        CALL???  00000000             C04B0D8&lt;br /&gt;_ktelwbl+770         CALLrel  _ktrsexec+0          C04B0D8&lt;br /&gt;_kdblba+168          CALLrel  _ktelwbl+0           792DE5C 1&lt;br /&gt;_kdblGetBlockDba+58  CALLrel  _kdblba+0            &lt;br /&gt;_kdblgb+26           CALLrel  _kdblGetBlockDba+0   C04B3C8 792DD9C&lt;br /&gt;_kdblailb+2101       CALLrel  _kdblgb+0            &lt;br /&gt;_kdblai+1560         CALLrel  _kdblailb+0          C04B3C8 792DC9C 792DD9C 0 1 1&lt;br /&gt;_klclil1r+187        CALLrel  _kdblai+0            &lt;br /&gt;_qerltRop+514        CALLrel  _klclil1r+0          792DBEC&lt;br /&gt;_qercbiFetch+935     CALLreg  00000000             34C4F034 7FFF&lt;br /&gt;_rwsfcd+95           CALL???  00000000             34C4F384 1C72EB4 34C4F034&lt;br /&gt;                                                   7FFF&lt;br /&gt;_qerltFetch+368      CALL???  00000000             34C4F148 1C72EB4 34C4F034&lt;br /&gt;                                                   7FFF&lt;br /&gt;_ctcdrv+7674         CALL???  00000000             34C4F034 1D28394 C04CE30 1&lt;br /&gt;_opiexe+12257        CALLrel  _ctcdrv+0            34EE5F50 C04D548 C04D510&lt;br /&gt;_opiosq0+6088        CALLrel  _opiexe+0            4 0 C04D8C0&lt;br /&gt;_kpooprx+232         CALLrel  _opiosq0+0           3 E C04D9D8 A4&lt;br /&gt;_kpoal8+775          CALLrel  _kpooprx+0           C04F6F8 C04E224 6D 1 0 A4&lt;br /&gt;_opiodr+1099         CALLreg  00000000             5E 17 C04F6F4&lt;br /&gt;_ttcpip+1273         CALLreg  00000000             5E 17 C04F6F4 0&lt;br /&gt;_opitsk+1017         CALL???  00000000             &lt;br /&gt;_opiino+1087         CALLrel  _opitsk+0            0 0&lt;br /&gt;_opiodr+1099         CALLreg  00000000             3C 4 C04FC8C&lt;br /&gt;_opidrv+819          CALLrel  _opiodr+0            3C 4 C04FC8C 0&lt;br /&gt;_sou2o+45            CALLrel  _opidrv+0            3C 4 C04FC8C&lt;br /&gt;_opimai_real+112     CALLrel  _sou2o+0             C04FC80 3C 4 C04FC8C&lt;br /&gt;_opimai+92           CALLrel  _opimai_real+0       2 C04FCB8&lt;br /&gt;_OracleThreadStart@  CALLrel  _opimai+0            &lt;br /&gt;4+708                                              &lt;br /&gt;7C80B710             CALLreg  00000000  &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ErrorStackダンプはそのレベルによっていろいろな有効な情報を提供してくれます。次のアーティクルで詳細な情報を得られます。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://tech.e2sn.com/oracle/troubleshooting/how-to-read-errorstack-output"&gt;http://tech.e2sn.com/oracle/troubleshooting/how-to-read-errorstack-output&lt;/a&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-6422339608374900684?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/6422339608374900684/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/03/errorstacksql.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/6422339608374900684'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/6422339608374900684'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/03/errorstacksql.html' title='Errorstackダンプで問題のSQLを突き止めること'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-7050504505554834214</id><published>2010-03-01T21:52:00.000-08:00</published><updated>2010-03-03T04:49:15.665-08:00</updated><title type='text'>SQL文章レベルの診断イベント</title><content type='html'>次のアーティクルを見るとOracle 11gからSQL文章レベルで診断イベントを適用する機能が追加されたのが分かります。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://oraclue.com/2009/03/24/oracle-event-sql_trace-in-11g/"&gt;http://oraclue.com/2009/03/24/oracle-event-sql_trace-in-11g/&lt;/a&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://oraclue.com/2009/03/20/oracle-diagnostic-events-part-4/"&gt;http://oraclue.com/2009/03/20/oracle-diagnostic-events-part-4/&lt;/a&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;すなわち、特定のSQL IDに該当するSQL文章に対して10046イベントや10053イベントのような診断イベントを適用できます。Oracle 10gやその以下のバージョンでは診断イベントの最低のレベルはセッションでありました。SQL文章レベルで診断イベントを適用できることになるにつれて、システムに与える影響を最小化しながら正確に特定のSQL文章だけを分析できるようになりました。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;簡単な使用例は次のようです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1106&gt; select * from t1 where c1 = 1;&lt;br /&gt;&lt;br /&gt;        C1         C2&lt;br /&gt;---------- ----------&lt;br /&gt;         1          1&lt;br /&gt;&lt;br /&gt;UKJA@ukja1106&gt; &lt;br /&gt;UKJA@ukja1106&gt; col prev_sql_id new_value sql_id&lt;br /&gt;UKJA@ukja1106&gt; select prev_sql_id from v$session where sid = userenv('sid');&lt;br /&gt;&lt;br /&gt;PREV_SQL_ID&lt;br /&gt;-------------&lt;br /&gt;3c3yp27cag5mv&lt;br /&gt;&lt;br /&gt;UKJA@ukja1106&gt; &lt;br /&gt;UKJA@ukja1106&gt; select sql_text from v$sql where sql_id = '&amp;sql_id';&lt;br /&gt;old   1: select sql_text from v$sql where sql_id = '&amp;sql_id'&lt;br /&gt;new   1: select sql_text from v$sql where sql_id = '3c3yp27cag5mv'&lt;br /&gt;&lt;br /&gt;SQL_TEXT&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;select * from t1 where c1 = 1&lt;br /&gt;&lt;br /&gt;UKJA@ukja1106&gt; &lt;br /&gt;UKJA@ukja1106&gt; alter system flush shared_pool;&lt;br /&gt;&lt;br /&gt;System altered.&lt;br /&gt;&lt;br /&gt;UKJA@ukja1106&gt; -- Enable 10046 Event&lt;br /&gt;UKJA@ukja1106&gt; alter system set events 'sql_trace[sql:&amp;sql_id] level=12';&lt;br /&gt;old   1: alter system set events 'sql_trace[sql:&amp;sql_id] level=12'&lt;br /&gt;new   1: alter system set events 'sql_trace[sql:3c3yp27cag5mv] level=12'&lt;br /&gt;&lt;br /&gt;System altered.&lt;br /&gt;&lt;br /&gt;UKJA@ukja1106&gt; -- Enable 10053 Event&lt;br /&gt;UKJA@ukja1106&gt; alter system set events 'trace[sql_costing][sql:&amp;sql_id]';&lt;br /&gt;old   1: alter system set events 'trace[sql_costing][sql:&amp;sql_id]'&lt;br /&gt;new   1: alter system set events 'trace[sql_costing][sql:3c3yp27cag5mv]'&lt;br /&gt;&lt;br /&gt;System altered.&lt;br /&gt;&lt;br /&gt;UKJA@ukja1106&gt; &lt;br /&gt;UKJA@ukja1106&gt; select * from t1 where c1 = 1;&lt;br /&gt;&lt;br /&gt;        C1         C2&lt;br /&gt;---------- ----------&lt;br /&gt;         1          1&lt;br /&gt;&lt;br /&gt;UKJA@ukja1106&gt; &lt;br /&gt;UKJA@ukja1106&gt; select * from t1 where c1 = 2;&lt;br /&gt;&lt;br /&gt;        C1         C2&lt;br /&gt;---------- ----------&lt;br /&gt;         2          2&lt;br /&gt;&lt;br /&gt;UKJA@ukja1106&gt; -- Disable 10046 Event&lt;br /&gt;UKJA@ukja1106&gt; alter system set events 'sql_trace[sql:&amp;sql_id] off';&lt;br /&gt;old   1: alter system set events 'sql_trace[sql:&amp;sql_id] off'&lt;br /&gt;new   1: alter system set events 'sql_trace[sql:3c3yp27cag5mv] off'&lt;br /&gt;&lt;br /&gt;System altered.&lt;br /&gt;&lt;br /&gt;UKJA@ukja1106&gt; -- Disable 10053 Event&lt;br /&gt;UKJA@ukja1106&gt; alter system set events 'trace[sql_costing][sql:&amp;sql_id] off';&lt;br /&gt;old   1: alter system set events 'trace[sql_costing][sql:&amp;sql_id] off'&lt;br /&gt;new   1: alter system set events 'trace[sql_costing][sql:3c3yp27cag5mv] off'&lt;br /&gt;&lt;br /&gt;System altered.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;トレースファイルを見てみたら指定されたSQL IDに当たるSQL文章だけに対して診断イベントが適用されたことが分かります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- 10053 Event&lt;br /&gt;OPTIMIZER INFORMATION&lt;br /&gt;&lt;br /&gt;******************************************&lt;br /&gt;----- Current SQL Statement for this session (sql_id=3c3yp27cag5mv) -----&lt;br /&gt;select * from t1 where c1 = 1&lt;br /&gt;*******************************************&lt;br /&gt;Legend&lt;br /&gt;The following abbreviations are used by optimizer trace.&lt;br /&gt;CBQT - cost-based query transformation&lt;br /&gt;JPPD - join predicate push-down&lt;br /&gt;OJPPD - old-style (non-cost-based) JPPD&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;-- 10046 Event&lt;br /&gt;PARSING IN CURSOR #6 len=29 dep=0 uid=88 oct=3 lid=88 tim=1619996238190 hv=3634861691 ad='355836ec' sqlid='3c3yp27cag5mv'&lt;br /&gt;select * from t1 where c1 = 1&lt;br /&gt;END OF STMT&lt;br /&gt;EXEC #6:c=0,e=60,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,tim=1619996238186&lt;br /&gt;WAIT #6: nam='SQL*Net message to client' ela= 6 driver id=1413697536 #bytes=1 p3=0 obj#=375 tim=1619996238266&lt;br /&gt;FETCH #6:c=0,e=50,p=0,cr=3,cu=0,mis=0,r=1,dep=0,og=1,tim=1619996238382&lt;br /&gt;WAIT #6: nam='SQL*Net message from client' ela= 258 driver id=1413697536 #bytes=1 p3=0 obj#=375 tim=1619996238690&lt;br /&gt;FETCH #6:c=0,e=13,p=0,cr=1,cu=0,mis=0,r=0,dep=0,og=1,tim=1619996238739&lt;br /&gt;STAT #6 id=1 cnt=1 pid=0 pos=1 obj=79656 op='TABLE ACCESS BY INDEX ROWID T1 (cr=4 pr=0 pw=0 time=0 us cost=2 size=7 card=1)'&lt;br /&gt;STAT #6 id=2 cnt=1 pid=1 pos=1 obj=79657 op='INDEX RANGE SCAN T1_N1 (cr=3 pr=0 pw=0 time=0 us cost=1 size=0 card=1)'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;まだこの機能が公式的に文書化されてはいないようです。従って本番環境で使用する前に十分な検証をしなければなりません。Oracle 11.1.0.6のテスト環境ではこの機能のせいでセッションが非正常終了する場合もたまにありました。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-7050504505554834214?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/7050504505554834214/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/03/sql.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/7050504505554834214'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/7050504505554834214'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/03/sql.html' title='SQL文章レベルの診断イベント'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-3875926806754937509</id><published>2010-02-03T23:25:00.000-08:00</published><updated>2010-02-03T23:49:51.801-08:00</updated><title type='text'>過去の統計情報を分析しましょう。</title><content type='html'>Oracle 10g以降からは統計情報が更新されるたびに以前の統計情報をディクショナリーに貯蔵します。統計情報更新によって性能問題が発生した時、以前の統計情報で復元するための目的です。&lt;br /&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;でも過去の統計情報を問い合わせる簡単な方法はなさそうです。たぶんディクショナリーを直接クエリするのが雄一な方法みたいです。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;SYS.WRI$_OPTSTAT_TAB_HISTORY:&lt;/b&gt; テーブル統計情報&lt;br /&gt;&lt;li&gt;&lt;b&gt;SYS.WRI$_OPTSTAT_HISTHEAD_HISTORY:&lt;/b&gt;列統計情報&lt;br /&gt;&lt;li&gt;&lt;b&gt;SYS.WRI$_OPTSTAT_HISTGRM_HISTORY:&lt;/b&gt;ヒストグラム統計情報&lt;br /&gt;&lt;li&gt;&lt;b&gt;SYS.WRI$_OPTSTAT_IND_HISTORY:&lt;/b&gt;索引統計情報&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;このテーブルたちを問い合わせて過去の特定の時点のテーブルの統計情報を確認する簡単なスクリプトを作ってみれば次のようです。&lt;br /&gt;&lt;br/&gt;&lt;br /&gt;(簡単なサンプルに過ぎませんので、運営システムで使用しようとすれば補完が必要です)&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;-- @name: tab_stat_hist&lt;br /&gt;-- @author: dion cho&lt;br /&gt;-- @note: show stats gathering history &lt;br /&gt;-- @usage: @tab_stat_hist &lt;schema_name&gt; &lt;table_name&gt; &lt;time&gt;&lt;br /&gt;--          @tab_stat_hist ukja t1 "2010/12/13 13:00:00"&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;define __OWNER = &amp;1;&lt;br /&gt;define __TABLE_NAME = &amp;2;&lt;br /&gt;define __TIME = "&amp;3";&lt;br /&gt;&lt;br /&gt;col obj# new_value obj_id&lt;br /&gt;col savtime new_value savtime&lt;br /&gt;&lt;br /&gt;select * from &lt;br /&gt;  (select obj#, to_char(savtime, 'yyyy/mm/dd hh24:mi:ss') as savtime&lt;br /&gt;    from sys.wri$_optstat_tab_history &lt;br /&gt;    where obj# = (select object_id from all_objects &lt;br /&gt;              where owner = upper('&amp;__OWNER') &lt;br /&gt;                    and object_name = upper('&amp;__TABLE_NAME')&lt;br /&gt;                    and subobject_name is null)&lt;br /&gt;          and savtime &lt;= to_date('&amp;__TIME', 'yyyy/mm/dd hh24:mi:ss')&lt;br /&gt;    order by savtime desc)&lt;br /&gt;where rownum = 1 &lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;set serveroutput on&lt;br /&gt;&lt;br /&gt;prompt 01. table stats&lt;br /&gt;exec print_table('-&lt;br /&gt;    select obj#, upper(''&amp;__TABLE_NAME'') as table_name, -&lt;br /&gt;      rowcnt, blkcnt, avgrln, samplesize, analyzetime, -&lt;br /&gt;      to_char(savtime, ''yyyy/mm/dd hh24:mi:ss'') as savtime -&lt;br /&gt;    from sys.wri$_optstat_tab_history -&lt;br /&gt;    where obj# = &amp;obj_id -&lt;br /&gt;          and to_char(savtime, ''yyyy/mm/dd hh24:mi:ss'') = ''&amp;savtime'' -&lt;br /&gt;');&lt;br /&gt;&lt;br /&gt;prompt 02. column stats&lt;br /&gt;exec print_table(' -&lt;br /&gt;select c.intcol#, c.name as column_name, null_cnt, minimum, maximum, -&lt;br /&gt;      distcnt, density, avgcln -&lt;br /&gt;from sys.wri$_optstat_histhead_history h, sys.col$ c -&lt;br /&gt;where h.obj# = c.obj# -&lt;br /&gt;      and h.obj# = &amp;obj_id -&lt;br /&gt;      and h.intcol# = c.intcol# -&lt;br /&gt;      and to_char(h.savtime,''yyyy/mm/dd hh24:mi:ss'') = ''&amp;savtime'' -&lt;br /&gt;order by 1 asc -&lt;br /&gt;');&lt;br /&gt;&lt;br /&gt;col col_name format a10&lt;br /&gt;col epvalue format a30&lt;br /&gt;prompt 03. histogram&lt;br /&gt;select&lt;br /&gt;  c.name as col_name,&lt;br /&gt;  h.bucket,&lt;br /&gt;  h.endpoint,&lt;br /&gt;  h.epvalue&lt;br /&gt;from &lt;br /&gt;  sys.wri$_optstat_histgrm_history h, sys.col$ c&lt;br /&gt;where&lt;br /&gt;  h.obj# = &amp;obj_id&lt;br /&gt;  and h.obj# = c.obj#&lt;br /&gt;  and h.intcol# = c.intcol#&lt;br /&gt;  and to_char(h.savtime,'yyyy/mm/dd hh24:mi:ss') = '&amp;savtime'&lt;br /&gt;order by c.intcol#, h.bucket&lt;br /&gt;;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;過去の索引統計情報やパーティション統計情報なども同じ方法で得られます。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;この方法を利用すれば統計情報更新の後、実行計画の変わった場合、統計情報の変化を追跡できます。以前のバージョンではできなっかた重要な改善だと言えます。とくにAWRのようなデータと連動してよく使えば有効な道具になるはずです。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-3875926806754937509?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/3875926806754937509/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/02/blog-post.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/3875926806754937509'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/3875926806754937509'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/02/blog-post.html' title='過去の統計情報を分析しましょう。'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-898372967482123776</id><published>2010-01-18T17:53:00.000-08:00</published><updated>2010-01-18T20:07:42.913-08:00</updated><title type='text'>クエリが現在実行段階のどこを実行しているのか分かる方法がないんでしょうか</title><content type='html'>この前、社内のエンジニアたちと話し合ううちこんな疑問が提起が提起されました。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;b&gt;クエリが現在実行段階のどこを実行しているのか分かる方法がないんでしょうか。&lt;/b&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;SQL*Traceのような一般的な方法では不可能です。たぶん唯一な方法はオラクルが実行計画によってクエリを実行しながら呼び出す関数(Function)をトレースすることでしょう。オラクルが呼び出す関数をトレースする一番簡単な方法は&lt;b&gt;oradebug short_stack&lt;/b&gt;コマンドです。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;簡単な例を通じて見ましょう。まず次のように必要なオブジェクトを作ります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;create table t1 as&lt;br /&gt;select level as c1, 'x' as c2&lt;br /&gt;from dual&lt;br /&gt;connect by level &lt;= 1000;&lt;br /&gt;&lt;br /&gt;create table t2 as&lt;br /&gt;select level as c1, 'x' as c2&lt;br /&gt;from dual&lt;br /&gt;connect by level &lt;= 1000;&lt;br /&gt;&lt;br /&gt;create index t2_n1 on t2(c1);&lt;br /&gt;&lt;br /&gt;create or replace function f_wait(p1 number, p2 number)&lt;br /&gt;return number&lt;br /&gt;is&lt;br /&gt;begin&lt;br /&gt;  dbms_lock.sleep(p2);&lt;br /&gt;  return 1;&lt;br /&gt;end;&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;次のようにNested Loops Joinを通じてデータをフェッチするクエリがあります。ただし&lt;b&gt;f_wait(t1.c1, 0.01)&lt;/b&gt;を利用して実際のデータをフェッチする時遅延が起こるようにします。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;explain plan for&lt;br /&gt;select /*+ ordered use_nl(t2) */ t1.c1, t2.c2, f_wait(t1.c1, 0.01)&lt;br /&gt;from t1, t2&lt;br /&gt;where t1.c1 = t2.c1&lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;---------------------------------------------&lt;br /&gt;| Id  | Operation                   | Name  |&lt;br /&gt;---------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT            |       |&lt;br /&gt;|   1 |  TABLE ACCESS BY INDEX ROWID| T2    |&lt;br /&gt;|   2 |   NESTED LOOPS              |       |&lt;br /&gt;|   3 |    TABLE ACCESS FULL        | T1    |&lt;br /&gt;|*  4 |    INDEX RANGE SCAN         | T2_N1 |&lt;br /&gt;---------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;クエリを実行する途中、実行計画のどんな段階にあるののかトレースしてみましょう。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; select /*+ ordered use_nl(t2) */ t1.c1, t2.c2, f_wait(t1.c1, 0.01)&lt;br /&gt;  2  from t1, t2&lt;br /&gt;  3  where t1.c1 = t2.c1&lt;br /&gt;  4  ;&lt;br /&gt;&lt;br /&gt;        C1 C F_WAIT(T1.C1,0.01)&lt;br /&gt;---------- - ------------------&lt;br /&gt;         1 x                  1&lt;br /&gt;         2 x                  1&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;クエリの特性からたぶん現在テーブルT2からデータをフェッチする段階(id=1)にある確率が高いです。下にoradebug short_stackコマンドの結果があります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;SYS@ukja1021&gt; oradebug short_stack&lt;br /&gt;_ksdxfstk+14&lt;-_ksdxcb+1481&lt;-_ssthreadsrgruncallback+428&lt;-_OracleOradebugThreadSt&lt;br /&gt;art@4+795&lt;-7C80B710&lt;-00000000&lt;-7C80253D&lt;-_skgpwwait+124&lt;-_ksliwat+843&lt;-_kslwaitn&lt;br /&gt;s+24&lt;-_kskthbwt+159&lt;-_kslwait+52&lt;-_psdwat+155&lt;-_pevm_icd_call_common+722&lt;-_pfrin&lt;br /&gt;str_ICAL+120&lt;-_pfrrun_no_tool+56&lt;-_pfrrun+781&lt;-_plsql_run+738&lt;-_peidxr_run+199&lt;-&lt;br /&gt;_peidxexe+52&lt;-_kkxdexe+278&lt;-_kkxmpexe+184&lt;-_kgmexec+295&lt;-_evapls+724&lt;-_evaopn2+4&lt;br /&gt;02&lt;-_kpofcr+1593&lt;-_qertbFetchByRowID+2083&lt;-_rwsfcd+95&lt;-_opifch2+3104&lt;-_opifch+51&lt;br /&gt;&lt;-_opiodr+1099&lt;-_ttcpip+1273&lt;-_opitsk+1017&lt;-_opiino+1087&lt;-_opiodr+1099&lt;-_opidrv+&lt;br /&gt;819&lt;-_sou2o+45&lt;-_opimai_real+112&lt;-_opimai+92&lt;-_OracleThreadStart@4+708&lt;-7C80B710&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;上のスタックトレース中実行計画と係りのあるのだけ引き出してみれば次のようです。最初の予想とぴったり合います。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;opifch2 : Fetch. SELECTに該当&lt;br /&gt;  qertbFetchByRowID : TABLE FETCH BY ROWIDに該当&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;もう少し正確な観察をために今度はWHERE句に&lt;b&gt;f_wait(t2.c1, 0.01) = 1&lt;/b&gt;条件を与えてテーブルT2をアクセスする過程で遅延が起こるようにしてみます。実行計画上でみればIndex Range Scanに該当する４番の段階で遅延が発生することが確認できます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;explain plan for&lt;br /&gt;select /*+ ordered use_nl(t2) */ t1.c1, t2.c2&lt;br /&gt;from t1, t2&lt;br /&gt;where t1.c1 = t2.c1 and f_wait(t2.c1, 0.01) = 1&lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;---------------------------------------------&lt;br /&gt;| Id  | Operation                   | Name  |&lt;br /&gt;---------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT            |       |&lt;br /&gt;|   1 |  TABLE ACCESS BY INDEX ROWID| T2    |&lt;br /&gt;|   2 |   NESTED LOOPS              |       |&lt;br /&gt;|   3 |    TABLE ACCESS FULL        | T1    |&lt;br /&gt;|*  4 |    INDEX RANGE SCAN         | T2_N1 |&lt;br /&gt;---------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;   4 - access("T1"."C1"="T2"."C1")&lt;br /&gt;       filter("F_WAIT"("T2"."C1",0.01)=1)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;クエリを実行しながらoradebug short_stackコマンドでトレースします。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; select /*+ ordered use_nl(t2) */ t1.c1, t2.c2&lt;br /&gt;  2  from t1, t2&lt;br /&gt;  3  where t1.c1 = t2.c1 and f_wait(t2.c1, 0.01) = 1&lt;br /&gt;  4  ;&lt;br /&gt;&lt;br /&gt;        C1 C&lt;br /&gt;---------- -&lt;br /&gt;         1 x&lt;br /&gt;         2 x&lt;br /&gt;         3 x&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;結果は次のようです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;SYS@ukja1021&gt; oradebug short_stack&lt;br /&gt;_ksdxfstk+14&lt;-_ksdxcb+1481&lt;-_ssthreadsrgruncallback+428&lt;-_OracleOradebugThreadSt&lt;br /&gt;art@4+795&lt;-7C80B710&lt;-00000000&lt;-7C80253D&lt;-_skgpwwait+124&lt;-_ksliwat+843&lt;-_kslwaitn&lt;br /&gt;s+24&lt;-_kskthbwt+159&lt;-_kslwait+52&lt;-_psdwat+155&lt;-_pevm_icd_call_common+722&lt;-_pfrin&lt;br /&gt;str_ICAL+120&lt;-_pfrrun_no_tool+56&lt;-_pfrrun+781&lt;-_plsql_run+738&lt;-_peidxr_run+199&lt;-&lt;br /&gt;_peidxexe+52&lt;-_kkxdexe+278&lt;-_kkxmpexe+184&lt;-_kgmexec+295&lt;-_evapls+724&lt;-_evaopn2+4&lt;br /&gt;02&lt;-_evareo+110&lt;-_expepr+73&lt;-_expeal+17&lt;-_qerixtFetch+3498&lt;-_qerjoFetch+864&lt;-_qe&lt;br /&gt;rpfFetch+251&lt;-_qertbFetchByRowID+475&lt;-_rwsfcd+95&lt;-_opifch2+3104&lt;-_opifch+51&lt;-_op&lt;br /&gt;iodr+1099&lt;-_ttcpip+1273&lt;-_opitsk+1017&lt;-_opiino+1087&lt;-_opiodr+1099&lt;-_opidrv+819&lt;-&lt;br /&gt;_sou2o+45&lt;-_opimai_real+112&lt;-_opimai+92&lt;-_OracleThreadStart@4+708&lt;-7C80B710&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;この中で実行計画と関係のある部分だけを引き出せば次のようです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;_opifch2 : Fetch. SELECT&lt;br /&gt;  _qertbFetchByRowID : TABLE FETCH BY ROWID&lt;br /&gt;     _qerpfFetch : Prefetch&lt;br /&gt;        _qerjoFetch : NESTED LOOPS JOIN&lt;br /&gt;           _qerixtFetch : INDEX RANGE SCAN&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;すなわち、現在４番の段階のIndex Range Scanを実行しているのが分かります。４番段階で遅延が発生するように操作したのがよく動作しました。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;もっと長くて複雑なクエリも上と同じ方法でトレースが可能です。もちろん安くてはないんですけど…&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;クエリの実行時間が長すぎる場合やハング(Hang)が発生するときを想像しましょう。万一SQL*Traceや待機イベント分析のような一般的な方法ではもう以上のトレースが不可能だったら?上の方法はそんな場合使える&lt;b&gt;たぶん&lt;/b&gt;唯一な方法でしょう。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-898372967482123776?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/898372967482123776/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/01/blog-post_18.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/898372967482123776'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/898372967482123776'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/01/blog-post_18.html' title='クエリが現在実行段階のどこを実行しているのか分かる方法がないんでしょうか'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-6788504772483937898</id><published>2010-01-10T18:05:00.000-08:00</published><updated>2010-01-10T19:39:14.798-08:00</updated><title type='text'>Process Explorer</title><content type='html'>この前、Ask ExemにWindows用のオラクルのメモリモニタリングの問題に対する問い合わせがありました。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;a href="http://121.254.172.39:8080/pls/apex/f?p=101:11:0::::P11_QUESTION_ID:5769800346586573"&gt;Oracleのメモリ使用量関連&lt;/a&gt;&lt;br/&gt;&lt;br /&gt;(Ask Exemは韓国のExemが運営するQ&amp;Aサイトです)&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;これはオラクルの問題というよりはOSの問題です。オラクルもOSが提供するメモリ管理記法をそのまま使用するアプリに過ぎませんからです。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;Windows環境でプロセスをモニタリングする最善のツールはは&lt;a href="http://technet.microsoft.com/ko-kr/sysinternals/bb896653%28en-us%29.aspx"&gt;Process Explorer&lt;/a&gt;です。Windowsを使用しながらこのツールを使用していないのは大きい損害だと言えます。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;Process Explorerを通じてオラクルをモニタリングする簡単な画面を見てみましょう。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;b&gt;1. ハンドラーモニタリング&lt;/b&gt; - ユニックスのlsofコマンドに該当&lt;br/&gt;&lt;br /&gt;&lt;img src="http://cfile6.uf.tistory.com/image/143565254B4A8390880EDA"&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;b&gt;2. スレッドモニタリング&lt;/b&gt; - オラクルのセッションに該当&lt;br/&gt;&lt;br /&gt;&lt;img src="http://cfile29.uf.tistory.com/image/163565254B4A83918934F6"&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;b&gt;3. CPU, Memoryなど核心性能指標&lt;/b&gt; - ユニックスのps, topなどに該当&lt;br /&gt;&lt;img src="http://cfile4.uf.tistory.com/image/143565254B4A83918A7E94"&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;より幅広い性能指標を1つの画面で見ることもできます。&lt;br /&gt;&lt;img src="http://cfile9.uf.tistory.com/image/163565254B4A83928BCA73"&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;このような機能たちをよく活用すればユニックスに劣らなく精密なプロセスモニタリングが可能です。ただし、Windows用のオラクルはマルチプロセス環境ではなくてマルチスレッド環境ですのでもう少しややこしいといえます。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-6788504772483937898?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/6788504772483937898/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/01/process-explorer.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/6788504772483937898'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/6788504772483937898'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/01/process-explorer.html' title='Process Explorer'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-6956724462331081910</id><published>2010-01-07T20:58:00.000-08:00</published><updated>2010-01-07T22:00:34.900-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='oradebug dump errorstack、dbms_system、parameter'/><title type='text'>他のセッションのパラメータを制御しよう</title><content type='html'>セッションモニタリングをしてみると次のような二つの要求事項ができるはずです。&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;特定のセッションが特定のパラメータ値を変更した場合(すなわち、システムディフォルト値と違う値を設定した場合)、これを確認できる？&lt;br /&gt;&lt;li&gt;特定のセッションの特定のパラメータ値を変えることができる？&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;まず特定のセッションのパラメータ値を確認できる一番確実な方法は&lt;b&gt;oradebug dump errorstack&lt;/b&gt;コマンドです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- Session #1&lt;br /&gt;UKJA@ukja1021&gt; @me&lt;br /&gt;&lt;br /&gt;SADDR           SID    SERIAL# PID        PADDR&lt;br /&gt;-------- ---------- ---------- ---------- --------&lt;br /&gt;34328D4C        140       5489 5924       3424C60C&lt;br /&gt;&lt;br /&gt;UKJA@ukja1021&gt; alter session set "_hash_join_enabled" = false;&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;br /&gt;-- Session #2&lt;br /&gt;SYS@ukja1021&gt; oradebug setospid 5924&lt;br /&gt;Oracle pid: 15, Windows thread id: 5924, image: ORACLE.EXE (SHAD)&lt;br /&gt;SYS@ukja1021&gt; oradebug dump errorstack 3;&lt;br /&gt;Statement processed.&lt;br /&gt;SYS@ukja1021&gt; oradebug tracefile_name&lt;br /&gt;c:\oracle\admin\ukja1021\udump\ukja1021_ora_5924.trc&lt;br /&gt;&lt;br /&gt;SYS@ukja1021&gt; ed c:\oracle\admin\ukja1021\udump\ukja1021_ora_5924.trc&lt;br /&gt;&lt;br /&gt;....&lt;br /&gt;Dump event group for SESSION&lt;br /&gt;Dump event group for SYSTEM&lt;br /&gt;DYNAMICALLY MODIFIED PARAMETERS:&lt;br /&gt;  _hash_join_enabled       = FALSE   &lt;--- 注目!&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Oracle10gからは&lt;b&gt;V$SES_OPTIMIZER_ENVビュー&lt;/b&gt;を通じて一部のパラメータ値を確認できるがオプティマイザに関したパラメータ値に限るという制約があります。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;他のセッションのパラメータ値を変更するのは&lt;b&gt;DBMS_SYSTEMパッケージ&lt;/b&gt;を通じて可能です。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- Session #2&lt;br /&gt;SYS@ukja1021&gt; exec dbms_system.set_bool_param_in_session(140,5489,'_hash_join_enabled', true);&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;-- Session #1&lt;br /&gt;UKJA@ukja1021&gt; @para hash_join_enabled&lt;br /&gt;UKJA@ukja1021&gt; set echo off&lt;br /&gt;old   9: and i.ksppinm like '%&amp;1%'&lt;br /&gt;new   9: and i.ksppinm like '%hash_join_enabled%'&lt;br /&gt;&lt;br /&gt;NAME                           VALUE                IS_DEFAUL SES_MODIFI&lt;br /&gt;------------------------------ -------------------- --------- ----------&lt;br /&gt;SYS_MODIFI&lt;br /&gt;----------&lt;br /&gt;DESCRIPTION&lt;br /&gt;------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;_hash_join_enabled             TRUE                 TRUE      true&lt;br /&gt;false&lt;br /&gt;enable/disable hash join&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;DBMS_SYTEM.SET_BOOL_PARAM_IN_SESSIONプロシージャはBoolean(True/False)タイプのパラメータ値を、DBMS_SYSTEM.SET_INT_PARAM_IN_SESSIONプロシージャはIntegerタイプのパラメータ値を変更する機能を提供します。しかし、文字列タイプのパラメータ値を変更する機能は提供されません。たぶん、過去のバージョンでどんな理由のために提供しなかったが、その後機能の追加や変更がなかったと思われます。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;OradebugやDBMS_SYSTEMのような文書化がなっていない機能を使用しなければならないのが負担になるかも知れません。こんな機能の使用でオラクルデータベースに問題ができたらサポートの領域を外れるようになると言うのも大きい負担です。でも、トラブルシューチングを少し深く入ってもすぐこんな隠し機能が必要になるのが現実でもあります。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-6956724462331081910?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/6956724462331081910/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2010/01/blog-post.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/6956724462331081910'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/6956724462331081910'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2010/01/blog-post.html' title='他のセッションのパラメータを制御しよう'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-7484745911700274396</id><published>2009-12-30T21:51:00.000-08:00</published><updated>2009-12-30T22:17:22.361-08:00</updated><title type='text'>AWRレポートを手軽に利用しよう。</title><content type='html'>AWR(Automatic Workload Reposistory)はオラクルからのプレゼントです。もちろん、ただではないけどど…&lt;br /&gt;&lt;br /&gt;私は特定な作業の性能特徴を分析する時、次のように手軽にAWRレポートを利用しています。&lt;br /&gt;&lt;pre class="brush: sql; highlight:[1,9,11];"&gt;&lt;br /&gt;@snap_begin&lt;br /&gt;&lt;br /&gt;alter session enable parallel dml;&lt;br /&gt;&lt;br /&gt;insert /*+ append parallel(t1 4) trace */ into t1&lt;br /&gt;select /*+ parallel(t2 4) */ * from t2;&lt;br /&gt;commit;&lt;br /&gt;&lt;br /&gt;@snap_end&lt;br /&gt;&lt;br /&gt;@snap_report&lt;br /&gt;&lt;br /&gt;-- AWR Report&lt;br /&gt;WORKLOAD REPOSITORY report for&lt;br /&gt;&lt;br /&gt;DB Name         DB Id    Instance     Inst Num Release     RAC Host&lt;br /&gt;------------ ----------- ------------ -------- ----------- --- ------------&lt;br /&gt;UKJA1021       738915393 ukja1021            1 10.2.0.1.0  NO  UKJAX&lt;br /&gt;&lt;br /&gt;              Snap Id      Snap Time      Sessions Curs/Sess&lt;br /&gt;            --------- ------------------- -------- ---------&lt;br /&gt;Begin Snap:      1284 29-Dec-09 15:04:38        19       3.5&lt;br /&gt;  End Snap:      1285 29-Dec-09 15:04:42        19       3.5&lt;br /&gt;   Elapsed:                0.08 (mins)&lt;br /&gt;   DB Time:                0.15 (mins)&lt;br /&gt;&lt;br /&gt;Cache Sizes&lt;br /&gt;~~~~~~~~~~~                       Begin        End&lt;br /&gt;                             ---------- ----------&lt;br /&gt;               Buffer Cache:       452M       452M  Std Block Size:         8K&lt;br /&gt;           Shared Pool Size:       116M       116M      Log Buffer:     6,968K&lt;br /&gt;&lt;br /&gt;Load Profile&lt;br /&gt;~~~~~~~~~~~~                            Per Second       Per Transaction&lt;br /&gt;                                   ---------------       ---------------&lt;br /&gt;                  Redo size:          1,201,392.36          5,725,836.00&lt;br /&gt;              Logical reads:              2,178.56             10,383.00&lt;br /&gt;              Block changes:                298.99              1,425.00&lt;br /&gt;             Physical reads:                 97.36                464.00&lt;br /&gt;            Physical writes:                142.89                681.00&lt;br /&gt;                 User calls:                 12.80                 61.00&lt;br /&gt;                     Parses:                 26.44                126.00&lt;br /&gt;                Hard parses:                  0.42                  2.00&lt;br /&gt;                      Sorts:                  8.60                 41.00&lt;br /&gt;                     Logons:                  1.68                  8.00&lt;br /&gt;                   Executes:                 27.07                129.00&lt;br /&gt;               Transactions:                  0.21&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Event                                 Waits  -outs    Time (s)    (ms)      /txn&lt;br /&gt;---------------------------- -------------- ------ ----------- ------- ---------&lt;br /&gt;db file scattered read                   42     .0           2      55      42.0&lt;br /&gt;log file parallel write                  27     .0           1      32      27.0&lt;br /&gt;rdbms ipc reply                           8     .0           0      50       8.0&lt;br /&gt;control file sequential read            674     .0           0       1     674.0&lt;br /&gt;db file sequential read                  11     .0           0      27      11.0&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;snap_begin、snap_end、snap_reportスクリプトは下記のとおりです。簡単なSQL*Plusだけで手軽にAWRレポートを作れます。&lt;br /&gt;&lt;b&gt;1. snap_begin.sql&lt;/b&gt;&lt;br /&gt;&lt;pre class="brush: sql;"&gt;&lt;br /&gt;col begin_snap new_value begin_snap;&lt;br /&gt;col db_id new_value db_id;&lt;br /&gt;col inst_num new_value inst_num;&lt;br /&gt;&lt;br /&gt;select dbid as db_id from v$database;&lt;br /&gt;select instance_number as inst_num from v$instance;&lt;br /&gt;&lt;br /&gt;select dbms_workload_repository.create_snapshot as begin_snap from dual;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;2. snap_end.sql &lt;/b&gt;&lt;br /&gt;&lt;pre class="brush: sql;"&gt;&lt;br /&gt;col end_snap new_value end_snap;&lt;br /&gt;&lt;br /&gt;select dbms_workload_repository.create_snapshot as end_snap from dual;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;3. snap_report.sql&lt;/b&gt;&lt;br /&gt;&lt;pre class="brush: sql;"&gt;&lt;br /&gt;select * from table(&lt;br /&gt;  dbms_workload_repository.awr_report_text(&lt;br /&gt;          &amp;db_id,&lt;br /&gt;          &amp;inst_num,&lt;br /&gt;          &amp;begin_snap,&lt;br /&gt;          &amp;end_snap)&lt;br /&gt;  );&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;もう一度言いますが、AWRはオラクルからの大切なプレゼントです。たとえその値段は高いかも知れませんが、よく利用すればするほど支払いの甲斐があります。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-7484745911700274396?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/7484745911700274396/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/12/awr.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/7484745911700274396'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/7484745911700274396'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/12/awr.html' title='AWRレポートを手軽に利用しよう。'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-8891382516826282453</id><published>2009-12-24T06:28:00.000-08:00</published><updated>2009-12-24T06:48:59.140-08:00</updated><title type='text'>オラクル性能に対する短い考え＃19</title><content type='html'>&lt;font size="+2"&gt;オラクルの機能は長期間にかかって完成される。&lt;/font&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;例えば、&lt;br /&gt;&lt;ul&gt;&lt;br /&gt; &lt;li&gt;プランスタビリティーはOracle 8iのStored Outlineから始まったが、実際に使えるほどの水準はOracle 11gR2で達成された。&lt;br /&gt; &lt;li&gt;CBOはOracle 7で紹介されたが、公式的にRBOを完全に代替したのはOracle 10gからだ。&lt;br /&gt; &lt;li&gt;バインドピークはOracle 9iから提供されつつあるが、現実的に使用可能にたったのはOracle 11gからだ。&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;オラクルを理解するのには結構長い時間の観察が必要だと言えますね。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-8891382516826282453?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/8891382516826282453/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/12/19.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/8891382516826282453'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/8891382516826282453'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/12/19.html' title='オラクル性能に対する短い考え＃19'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-2869730318560754317</id><published>2009-12-19T02:43:00.000-08:00</published><updated>2009-12-20T17:12:59.970-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Cardinality Feedback'/><title type='text'>Oracle 11gR2のオプティマイザのCardinaliy Feedback</title><content type='html'>Oracle 11gR2のランタイムーオプティマイザに&lt;b&gt;Cardinality Feedback機能&lt;/b&gt;が追加されたことを知るようになりました。&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://wedostreams.blogspot.com/2009/12/hidden-undocumented-and-adaptive-cursor.html"&gt;Hidden and Undocumented "Cardinality Feedback"&lt;/a&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://jonathanlewis.wordpress.com/2009/12/16/adaptive-optimisation/"&gt;Adaptive Optimisation ?&lt;/a&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://dioncho.wordpress.com/2009/12/17/trivial-research-on-the-cardinality-feedback-on-11gr2/"&gt;Trivial Research on the Cardinality Feedback on 11gR2&lt;/a&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;オラクルは10gから自動クエリチューニング(Automatic Query Tuning)という名前でCardinality Feedback機能を紹介しました。予想の行数(Estimated Cardinality)と実際の行数(Actual Cardinality)を比べてその差を減らす方式がCardinality Feedback機能の動作原理です。このためにOPT_ESTIMATEヒントが追加されました。しかし、これはあくまでもチューニングオプティマイザエンジンにすみました。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;11gR2でこの機能はチューニングエンジンにやまずにランタイムーエンジンまで適用されました。詳しい内容は上のリンクこ参照すればいいでしょう。簡単に整理すると次のとおりです。&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;クエリを行ったあと予想行数件数と実際行数件数の違いが大きすぎると判断されると該当実行計画は共有物不可の状態になって実際の行数件数をカーソルに書き込みます。しかし予想行数件数と実際行数件数の違いだけが唯一なファクターなのかは確実ではなんです。&lt;br /&gt;&lt;li&gt;あとで同一なクエリがまた実行されると最初の実行で書き込んでおいた行数をOPT_ESTIMATEヒントを通じてクエリに挿入します。すなわちCardinality Feedbackが行われます。&lt;br /&gt;&lt;li&gt;この過程はたった一度のみ行われます。すなわち最初のクエリだけがフィードバックの対象となります。&lt;br /&gt;&lt;li&gt;_OPTIMIZER_USE_FEEDBACKパラメーターを利用して制御できます。すなわちこのパラメーターの値をFALSEに変更したらCardinality Feedbackは行われません。&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;私の個人的な意見を申し上げるとCardinality Feedbackがランタイムーエンジンまで適用されるはずだとはまったく期待しませんでした。でも結局そうなってしまいました。たぶん多くのシステムで熱いイシューになるはずです。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-2869730318560754317?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/2869730318560754317/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/12/oracle-11gr2cardinaliy-feedback.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/2869730318560754317'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/2869730318560754317'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/12/oracle-11gr2cardinaliy-feedback.html' title='Oracle 11gR2のオプティマイザのCardinaliy Feedback'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-5941796450856226430</id><published>2009-12-16T00:14:00.000-08:00</published><updated>2009-12-16T00:27:11.725-08:00</updated><title type='text'>私のブログに雪が降っています。</title><content type='html'>少しだけ(数秒ぐらい)待ってください。しんしんと雪が降っているんですよ。&lt;br /&gt;&lt;table width=600px height=500px style="background-color: black"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/table&gt;&lt;br /&gt;次のようにJava Scriptをエンベッドしました。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;&lt;script type='text/javascript' src='http://s2.wordpress.com/wp-content/plugins/snow/snowstorm.js?ver=1260233238'&gt;&lt;/script&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;この雪は一月上旬までだけ降ります。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-5941796450856226430?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/5941796450856226430/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/12/blog-post.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/5941796450856226430'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/5941796450856226430'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/12/blog-post.html' title='私のブログに雪が降っています。'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-7329241337850349048</id><published>2009-12-14T22:28:00.000-08:00</published><updated>2009-12-16T04:04:21.165-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Write Consistency'/><title type='text'>書き出し一貫性 - Restartメカニズム</title><content type='html'>先日、特定のDMLが多すぎるロジカルリードを持つ問題に対する問い合わせがありました。たとえその問題に対する原因ではありませんでしたけど、おかげで書き出し一貫性(Write Consistency)問題を一度話し合う必要性を感じました。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;簡単なテストを通じて話し合ってみます。まず次のようにテーブルを一つ作ります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; select * from v$version where rownum = 1;&lt;br /&gt;&lt;br /&gt;BANNER&lt;br /&gt;----------------------------------------------------------------&lt;br /&gt;Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod&lt;br /&gt;&lt;br /&gt;UKJA@ukja1021&gt; create table t1&lt;br /&gt;  2  as&lt;br /&gt;  3  select level as c1, rpad('x',1000) as c2&lt;br /&gt;  4  from dual&lt;br /&gt;  5  connect by level &lt;= 10000&lt;br /&gt;  6  ;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;テーブルT1に対してトリガーを作って行一件が変更されるたびにpkg_temp.g_update_cnt値を１づつ増加します。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; create or replace package pkg_temp&lt;br /&gt;  2  is&lt;br /&gt;  3   g_update_cnt number;&lt;br /&gt;  4  end;&lt;br /&gt;  5  /&lt;br /&gt;&lt;br /&gt;Package created.&lt;br /&gt;&lt;br /&gt;UKJA@ukja1021&gt; -- create trigger&lt;br /&gt;UKJA@ukja1021&gt; create or replace trigger trg1&lt;br /&gt;  2  after update on t1&lt;br /&gt;  3  for each row&lt;br /&gt;  4  begin&lt;br /&gt;  5   pkg_temp.g_update_cnt := pkg_temp.g_update_cnt + 1;&lt;br /&gt;  6  end;&lt;br /&gt;  7  /&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;次のように10,000件を変更するとpkg_temp.g_update_cnt値は10,000になります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; exec pkg_temp.g_update_cnt := 0;&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;UKJA@ukja1021&gt; &lt;br /&gt;UKJA@ukja1021&gt; update t1 set c2 = rpad('y',1000)&lt;br /&gt;  2  where c1 = c1&lt;br /&gt;  3  ;&lt;br /&gt;&lt;br /&gt;10000 rows updated.&lt;br /&gt;&lt;br /&gt;UKJA@ukja1021&gt; commit;&lt;br /&gt;&lt;br /&gt;Commit complete.&lt;br /&gt;&lt;br /&gt;UKJA@ukja1021&gt; &lt;br /&gt;UKJA@ukja1021&gt; exec dbms_output.put_line('update cnt = ' || pkg_temp.g_update_cnt);&lt;br /&gt;update cnt = 10000&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;今、次のようにテストを変更します。&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;セッションAで10,000件を変更します。&lt;br /&gt;&lt;li&gt;セッションAがまだ最後の行を変更する前にセッションBが最後の行の値を変えてコミットを行います。&lt;br /&gt;&lt;li&gt;セッションAが変更を終えたあとpkg_temp.g_update_cnt値を確認します。&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;セッションAが10,000件を変更始めます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; exec pkg_temp.g_update_cnt := 0;&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;UKJA@ukja1021&gt; &lt;br /&gt;UKJA@ukja1021&gt; update t1 set c2 = rpad('y',1000)&lt;br /&gt;  2  where c1 = c1&lt;br /&gt;  3  ;&lt;br /&gt;....&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;セッションAがまだ最後の行を変更する前にセッションBが最後の行の値を変えてコミットを行います。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; update t1 set c1 = c1+1 where c1 = 10000;&lt;br /&gt;&lt;br /&gt;1 row updated.&lt;br /&gt;&lt;br /&gt;UKJA@ukja1021&gt; commit;&lt;br /&gt;&lt;br /&gt;Commit complete.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;セッションAが変更を終えた後pkg_temp.g_update_cnt値を確認します。驚いたことにpgk_temp.g_update_cntの値は19,999件です!&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;...&lt;br /&gt;10000 rows updated.&lt;br /&gt;&lt;br /&gt;UKJA@ukja1021&gt; commit;&lt;br /&gt;&lt;br /&gt;Commit complete.&lt;br /&gt;&lt;br /&gt;UKJA@ukja1021&gt; exec dbms_output.put_line('update cnt = ' || pkg_temp.g_update_cnt);&lt;br /&gt;update cnt = 19999&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;これは10,000件ではなくてその二倍に該当する20,000件ぐらいが変更されたということを意味します。これをよくRestartメカニズムと呼びます。&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;セッションAが最後の行を変更するため一応Consistent Readモードで該当ブロックを読み込みます。&lt;br /&gt;&lt;li&gt;セッションAはクエリが始まった後該当行がセッションBに変更されてコミットされたのを確認します。&lt;br /&gt;&lt;li&gt;UPDATE文の&lt;b&gt;where c1 = c1 条件&lt;/b&gt;で一貫性確認(Consistency Check)がなされます。セッションAは最後の行はUPDATEが始まった後で変更されたので該当条件を満足するかしないかを確認するのが不可能だと判断します。&lt;br /&gt;&lt;li&gt;こんな場合セッションAは今までの変更をロールバックし、UPDATE文を行い直します。これをRestartメカニズムと言います。したがって9,999＋10,000＝19,999件の行が変更されたことです。&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;Restartの副作用はSQL*Traceの結果でもよく現れます。Restartが発生した場合同じに10,000個の行を変更しますが、ずっと多い仕事をします。ロールバックをして行い直さなければならないからです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- Restartがなされない場合&lt;br /&gt;update t1 set c2 = rpad('y',1000)&lt;br /&gt;where c1 = c1&lt;br /&gt;&lt;br /&gt;call     count       cpu    elapsed       disk      query    current        rows&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;Parse        1      0.01       0.00          0          7          0           0&lt;br /&gt;Execute      1      0.34       0.52          0       1443      12992       10000&lt;br /&gt;Fetch        0      0.00       0.00          0          0          0           0&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;total        2      0.35       0.53          0       1450      12992       10000&lt;br /&gt;&lt;br /&gt;Misses in library cache during parse: 1&lt;br /&gt;Optimizer mode: ALL_ROWS&lt;br /&gt;Parsing user id: 61  &lt;br /&gt;&lt;br /&gt;Rows     Row Source Operation&lt;br /&gt;-------  ---------------------------------------------------&lt;br /&gt;      0  UPDATE  T1 (cr=1487 pr=0 pw=0 time=530716 us)&lt;br /&gt;  10000   TABLE ACCESS FULL T1 (cr=1432 pr=0 pw=0 time=74012 us)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Elapsed times include waiting on following events:&lt;br /&gt;  Event waited on                             Times   Max. Wait  Total Waited&lt;br /&gt;  ----------------------------------------   Waited  ----------  ------------&lt;br /&gt;  log buffer space                                5        0.01          0.05&lt;br /&gt;  SQL*Net message to client                       1        0.00          0.00&lt;br /&gt;  SQL*Net message from client                     1        0.00          0.00&lt;br /&gt;********************************************************************************&lt;br /&gt;&lt;br /&gt;-- Restartがなされる場合&lt;br /&gt;update t1 set c2 = rpad('y',1000)&lt;br /&gt;where c1 = c1&lt;br /&gt;&lt;br /&gt;call     count       cpu    elapsed       disk      query    current        rows&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;Parse        1      0.00       0.00          0          0          0           0&lt;br /&gt;Execute      1      1.25       6.29          0       4323      67568       10000&lt;br /&gt;Fetch        0      0.00       0.00          0          0          0           0&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;total        2      1.25       6.29          0       4323      67568       10000&lt;br /&gt;&lt;br /&gt;Misses in library cache during parse: 1&lt;br /&gt;Optimizer mode: ALL_ROWS&lt;br /&gt;Parsing user id: 61  &lt;br /&gt;&lt;br /&gt;Rows     Row Source Operation&lt;br /&gt;-------  ---------------------------------------------------&lt;br /&gt;      1  UPDATE  T1 (cr=4411 pr=0 pw=0 time=5643605 us)&lt;br /&gt;  30000   TABLE ACCESS FULL T1 (cr=4297 pr=0 pw=0 time=180259 us)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Elapsed times include waiting on following events:&lt;br /&gt;  Event waited on                             Times   Max. Wait  Total Waited&lt;br /&gt;  ----------------------------------------   Waited  ----------  ------------&lt;br /&gt;  log file switch completion                      4        0.99          2.28&lt;br /&gt;  log file switch (checkpoint incomplete)         2        0.38          0.44&lt;br /&gt;  log buffer space                               18        0.68          1.98&lt;br /&gt;  enq: TX - row lock contention                   1        0.07          0.07&lt;br /&gt;  SQL*Net message to client                       1        0.00          0.00&lt;br /&gt;  SQL*Net message from client                     1        0.00          0.00&lt;br /&gt;********************************************************************************&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;このようなRestartメカニズムはよくオラクルは読み取り一貫性(Read Consistency)のみならず書き出し一貫性(Write Consistency)を持っているとも言います。もっと正確に言えばオラクルの読み取り一貫性(Read Consistency)のメカニズムがDMLに掛ける特殊な影響だと言っても良いでしょう。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-7329241337850349048?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/7329241337850349048/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/12/restart.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/7329241337850349048'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/7329241337850349048'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/12/restart.html' title='書き出し一貫性 - Restartメカニズム'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-5166318620095639268</id><published>2009-12-12T07:15:00.000-08:00</published><updated>2009-12-12T07:37:09.369-08:00</updated><title type='text'>オラクル性能に対する短い考え＃18</title><content type='html'>&lt;font size="+2"&gt;少数の視覚を持て&lt;/font&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;例えばこのようなものであります。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;皆がSQLトレースに満足している時オラクル内部的な知識を活用して体系的な性能分析技法を研究したSteve Adamsさん。&lt;br /&gt;&lt;li&gt;皆がクエリをチューニングする方法について話をしている時オプティマィザの動作原理を元にした問題解決を話したJonathan Lewisさん。&lt;br /&gt;&lt;li&gt;皆がSQLトレースで満足している時ダイレクトメモリーアクセス方式でアクティブセッションをすべて収集して性能分析の新しい次元を提示した&lt;a href="http://www.ex-em.com"&gt;ExemとMaxgauge&lt;/a&gt;。&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;私はこのごろすこしづつ自身が少数の視覚を備えて行っていると感じます。少数の視覚が成功的に大衆化すれば、すぐ多数の視覚になり、誰かがまた新しい少数の視覚を提示するはずです。こうだから知識の世界は面白いものです。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-5166318620095639268?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/5166318620095639268/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/12/18.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/5166318620095639268'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/5166318620095639268'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/12/18.html' title='オラクル性能に対する短い考え＃18'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-4556818275244693009</id><published>2009-12-09T21:04:00.000-08:00</published><updated>2009-12-09T22:07:24.478-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='バインドピーキング'/><title type='text'>バインドピーキングとSQLトレースの出会い</title><content type='html'>バインドピーキング(Bind Peeking)が起こす一番大きい混乱の一つはバインド変数を使用する同一なSQL文章の実行計画が変わるということです。その中面白い事例一つを簡単なテストを通じて紹介します。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;次のようにオブジェクトを作ります。&lt;br /&gt;&lt;pre class="brush: sql;"&gt;&lt;br /&gt;UKJA@ukja1106&gt; create table t1(c1 int, c2 varchar2(10));&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;UKJA@ukja1106&gt; insert into t1&lt;br /&gt;  2  select 1, level from dual connect by level &lt;= 10000;&lt;br /&gt;&lt;br /&gt;10000 rows created.&lt;br /&gt;&lt;br /&gt;UKJA@ukja1106&gt; &lt;br /&gt;UKJA@ukja1106&gt; insert into t1&lt;br /&gt;  2  select level, level from dual connect by level &lt;= 10000;&lt;br /&gt;&lt;br /&gt;10000 rows created.&lt;br /&gt;&lt;br /&gt;UKJA@ukja1106&gt; &lt;br /&gt;UKJA@ukja1106&gt; create index t1_n1 on t1(c1);&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;UKJA@ukja1106&gt; &lt;br /&gt;UKJA@ukja1106&gt; exec dbms_stats.gather_table_stats(user, 't1', -&lt;br /&gt;&gt;   method_opt=&gt;'for columns c1 size skewonly');&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;コラムC1はばらつきがあってヒストグラムが存在します。統計情報はつぎのようです。Height-Balancedヒストグラムが生成されているのが分かります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1106&gt; @tab_stat t1&lt;br /&gt;UKJA@ukja1106&gt; set echo off&lt;br /&gt;01. table stats&lt;br /&gt;old   9:  table_name = upper(''&amp;T_NAME'')&lt;br /&gt;new   9:  table_name = upper(''t1'')&lt;br /&gt;TABLE_NAME                    : T1&lt;br /&gt;PARTITION_NAME                :&lt;br /&gt;NUM_ROWS                      : 20000&lt;br /&gt;BLOCKS                        : 42&lt;br /&gt;SAMPLE_SIZE                   : 20000&lt;br /&gt;LAST_ANAL                     : 2009/12/07 13:41:08&lt;br /&gt;-----------------&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;02. column stats&lt;br /&gt;old   9:  s.table_name = upper(''&amp;T_NAME'')&lt;br /&gt;new   9:  s.table_name = upper(''t1'')&lt;br /&gt;TABLE_NAME                    : T1&lt;br /&gt;COLUMN_NAME                   : C1&lt;br /&gt;NUM_DISTINCT                  : 10000&lt;br /&gt;NUM_NULLS                     : 0&lt;br /&gt;DENSITY                       : .00005&lt;br /&gt;LOW_VALUE                     : C102&lt;br /&gt;HIGH_VALUE                    : C302&lt;br /&gt;HISTOGRAM                     : HEIGHT BALANCED&lt;br /&gt;-----------------&lt;br /&gt;TABLE_NAME                    : T1&lt;br /&gt;COLUMN_NAME                   : C2&lt;br /&gt;NUM_DISTINCT                  :&lt;br /&gt;NUM_NULLS                     :&lt;br /&gt;DENSITY                       :&lt;br /&gt;LOW_VALUE                     :&lt;br /&gt;HIGH_VALUE                    :&lt;br /&gt;HISTOGRAM                     : NONE&lt;br /&gt;-----------------&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;03. histogram stats&lt;br /&gt;old   7:  table_name = upper('&amp;T_NAME')&lt;br /&gt;new   7:  table_name = upper('t1')&lt;br /&gt;&lt;br /&gt;TABLE_NAME           COLUMN_NAME          ENDPOINT_NUMBER ENDPOINT_VALUE&lt;br /&gt;-------------------- -------------------- --------------- --------------------&lt;br /&gt;T1                   C1                               126 1()&lt;br /&gt;T1                   C1                               127 33()&lt;br /&gt;T1                   C1                               128 112()&lt;br /&gt;...&lt;br /&gt;T1                   C1                               254 10000()&lt;br /&gt;&lt;br /&gt;129 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;コラムC1に”１”を代入したらバインドピーキングによって全表走査を選択するようになります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1106&gt; var b1 number;&lt;br /&gt;UKJA@ukja1106&gt; exec :b1 := 1;&lt;br /&gt;UKJA@ukja1106&gt; var b2 varchar2(10);&lt;br /&gt;UKJA@ukja1106&gt; exec :b2 := '1';&lt;br /&gt;&lt;br /&gt;UKJA@ukja1106&gt; &lt;br /&gt;UKJA@ukja1106&gt; select /*+ gather_plan_statistics */&lt;br /&gt;  2   count(*) from t1&lt;br /&gt;  3  where c1 = :b1 and c2 = :b2;&lt;br /&gt;&lt;br /&gt;  COUNT(*)&lt;br /&gt;----------&lt;br /&gt;         2&lt;br /&gt;&lt;br /&gt;--------------------------------------------------------------&lt;br /&gt;| Id  | Operation          | Name | Starts | E-Rows | A-Rows |&lt;br /&gt;--------------------------------------------------------------&lt;br /&gt;|   1 |  SORT AGGREGATE    |      |      1 |      1 |      1 |&lt;br /&gt;|*  2 |   TABLE ACCESS FULL| T1   |      1 |     99 |      2 |&lt;br /&gt;--------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   2 - filter("C2"=:B2)&lt;br /&gt;   3 - access("C1"=:B1)&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;今度は&lt;b&gt;SQL*Trace&lt;/b&gt;を活性化します。そしてコラムC1に1ではなくて2を代入します。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1106&gt; alter session set sql_trace = true;&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;br /&gt;UKJA@ukja1106&gt; var b1 number;&lt;br /&gt;UKJA@ukja1106&gt; exec :b1 := 2;&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;UKJA@ukja1106&gt; var b2 varchar2(10);&lt;br /&gt;UKJA@ukja1106&gt; exec :b2 := '1';&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;UKJA@ukja1106&gt; &lt;br /&gt;UKJA@ukja1106&gt; select /*+ gather_plan_statistics */&lt;br /&gt;  2   count(*) from t1&lt;br /&gt;  3  where c1 = :b1 and c2 = :b2;&lt;br /&gt;&lt;br /&gt;  COUNT(*)&lt;br /&gt;----------&lt;br /&gt;         0&lt;br /&gt;&lt;br /&gt;-------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                    | Name  | Starts | E-Rows | A-Rows |&lt;br /&gt;-------------------------------------------------------------------------&lt;br /&gt;|   1 |  SORT AGGREGATE              |       |      1 |      1 |      1 |&lt;br /&gt;|*  2 |   TABLE ACCESS BY INDEX ROWID| T1    |      1 |      1 |      0 |&lt;br /&gt;|*  3 |    INDEX RANGE SCAN          | T1_N1 |      1 |      1 |      1 |&lt;br /&gt;------------------------------------------------------------------------- &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;実行計画が変わりました。何かおかしいではありませんか。最初の実行でバインドピーキングがもうなされたので次の同一なテキストのクエリは同一な実行計画を見せなければなりません。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;もっと詳細な分析をしてみましょう。まず現在登録されている息子LCOたちとその実行計画を見ます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1106&gt; select * from table(dbms_xplan.display_cursor('&amp;sql_id', null, 'typical'));&lt;br /&gt;old   1: select * from table(dbms_xplan.display_cursor('&amp;sql_id', null, 'typical'))&lt;br /&gt;new   1: select * from table(dbms_xplan.display_cursor('98721ruagfx5c', null, 'typical'))&lt;br /&gt;&lt;br /&gt;PLAN_TABLE_OUTPUT&lt;br /&gt;------------------------------------------------------------------------------------------------------------------------&lt;br /&gt;SQL_ID  98721ruagfx5c, child number 0&lt;br /&gt;-------------------------------------&lt;br /&gt;select /*+ gather_plan_statistics */  count(*) from t1 where c1 = :b1&lt;br /&gt;and c2 = :b2&lt;br /&gt;&lt;br /&gt;Plan hash value: 3724264953&lt;br /&gt;&lt;br /&gt;---------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;---------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT   |      |       |       |    18 (100)|          |&lt;br /&gt;|   1 |  SORT AGGREGATE    |      |     1 |     8 |            |          |&lt;br /&gt;|*  2 |   TABLE ACCESS FULL| T1   |    99 |   792 |    18   (0)| 00:00:01 |&lt;br /&gt;---------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   2 - filter(("C2"=:B2 AND "C1"=:B1))&lt;br /&gt;&lt;br /&gt;SQL_ID  98721ruagfx5c, child number 1&lt;br /&gt;-------------------------------------&lt;br /&gt;select /*+ gather_plan_statistics */  count(*) from t1 where c1 = :b1&lt;br /&gt;and c2 = :b2&lt;br /&gt;&lt;br /&gt;Plan hash value: 359681750&lt;br /&gt;&lt;br /&gt;--------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                    | Name  | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;--------------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT             |       |       |       |     2 (100)|          |&lt;br /&gt;|   1 |  SORT AGGREGATE              |       |     1 |     8 |            |          |&lt;br /&gt;|*  2 |   TABLE ACCESS BY INDEX ROWID| T1    |     1 |     8 |     2   (0)| 00:00:01 |&lt;br /&gt;|*  3 |    INDEX RANGE SCAN          | T1_N1 |     1 |       |     1   (0)| 00:00:01 |&lt;br /&gt;--------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   2 - filter("C2"=:B2)&lt;br /&gt;   3 - access("C1"=:B1)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;確かに二つの実行計画が存在します。これが意味するのはSQLトレースが活性化されるとオラクルは同一なSQL文章だといっても他のSQL文で認識するということを意味します。その理由をV$SQL_SHARED_CURSORビューを通じて見つけられます。私が作成した&lt;a href="http://sites.google.com/site/ukja/sql-scripts-1/s-z/shared_cursor2"&gt;簡単なスクリプト&lt;/a&gt;でV$SQL_SHARED_CURSORビューを検索します。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1106&gt; @shared_cursor2 &amp;sql_id&lt;br /&gt;UKJA@ukja1106&gt; set echo off&lt;br /&gt;old  14:           and s.sql_id = ''&amp;1''',&lt;br /&gt;new  14:           and s.sql_id = ''98721ruagfx5c''',&lt;br /&gt;SQL_TEXT                       = select /*+ gather_plan_statistics */  count(*) from t1 where c1 = :b1 and c2 = :b2&lt;br /&gt;SQL_ID                         = 98721ruagfx5c&lt;br /&gt;ADDRESS                        = 29D81B30&lt;br /&gt;CHILD_ADDRESS                  = 2879E2BC&lt;br /&gt;CHILD_NUMBER                   = 0&lt;br /&gt;--------------------------------------------------&lt;br /&gt;SQL_TEXT                       = select /*+ gather_plan_statistics */  count(*) from t1 where c1 = :b1 and c2 = :b2&lt;br /&gt;SQL_ID                         = 98721ruagfx5c&lt;br /&gt;ADDRESS                        = 29D81B30&lt;br /&gt;CHILD_ADDRESS                  = 2F5E79DC&lt;br /&gt;CHILD_NUMBER                   = 1&lt;br /&gt;STATS_ROW_MISMATCH             = Y&lt;br /&gt;--------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;すなわち、SQLトレースが活性化されるとSTATS_ROW_MISMATCHによってSQL文章を共有できずに新しいチャイルドカーソルを作るようになります。その過程で再びバインドピーキングがなされます。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;SQLトレースの結果だけを見てせっかちにチューニングをするのはあぶないという恐れが生じませんか。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;Oracle 11gの基準で同一なSQL文章が共有されないにはなんと60余種類の理由があります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1106&gt; desc v$sql_shared_cursor                       &lt;br /&gt;           Name                                               &lt;br /&gt;           ---------------------------------------------------&lt;br /&gt;    1      SQL_ID                                             &lt;br /&gt;    2      ADDRESS                                            &lt;br /&gt;    3      CHILD_ADDRESS                                      &lt;br /&gt;    4      CHILD_NUMBER                                       &lt;br /&gt;    5      UNBOUND_CURSOR                                     &lt;br /&gt;    6      SQL_TYPE_MISMATCH                                  &lt;br /&gt;    7      OPTIMIZER_MISMATCH                                 &lt;br /&gt;    8      OUTLINE_MISMATCH                                   &lt;br /&gt;    9      STATS_ROW_MISMATCH                                 &lt;br /&gt;   10      LITERAL_MISMATCH                                   &lt;br /&gt;   11      FORCE_HARD_PARSE                                   &lt;br /&gt;   12      EXPLAIN_PLAN_CURSOR                                &lt;br /&gt;   13      BUFFERED_DML_MISMATCH                              &lt;br /&gt;   14      PDML_ENV_MISMATCH                                  &lt;br /&gt;   15      INST_DRTLD_MISMATCH                                &lt;br /&gt;   16      SLAVE_QC_MISMATCH                                  &lt;br /&gt;   17      TYPECHECK_MISMATCH                                 &lt;br /&gt;   18      AUTH_CHECK_MISMATCH                                &lt;br /&gt;   19      BIND_MISMATCH                                      &lt;br /&gt;   20      DESCRIBE_MISMATCH                                  &lt;br /&gt;   21      LANGUAGE_MISMATCH                                  &lt;br /&gt;   22      TRANSLATION_MISMATCH                               &lt;br /&gt;   23      ROW_LEVEL_SEC_MISMATCH                             &lt;br /&gt;   24      INSUFF_PRIVS                                       &lt;br /&gt;   25      INSUFF_PRIVS_REM                                   &lt;br /&gt;   26      REMOTE_TRANS_MISMATCH                              &lt;br /&gt;   27      LOGMINER_SESSION_MISMATCH                          &lt;br /&gt;   28      INCOMP_LTRL_MISMATCH                               &lt;br /&gt;   29      OVERLAP_TIME_MISMATCH                              &lt;br /&gt;   30      EDITION_MISMATCH                                   &lt;br /&gt;   31      MV_QUERY_GEN_MISMATCH                              &lt;br /&gt;   32      USER_BIND_PEEK_MISMATCH                            &lt;br /&gt;   33      TYPCHK_DEP_MISMATCH                                &lt;br /&gt;   34      NO_TRIGGER_MISMATCH                                &lt;br /&gt;   35      FLASHBACK_CURSOR                                   &lt;br /&gt;   36      ANYDATA_TRANSFORMATION                             &lt;br /&gt;   37      INCOMPLETE_CURSOR                                  &lt;br /&gt;   38      TOP_LEVEL_RPI_CURSOR                               &lt;br /&gt;   39      DIFFERENT_LONG_LENGTH                              &lt;br /&gt;   40      LOGICAL_STANDBY_APPLY                              &lt;br /&gt;   41      DIFF_CALL_DURN                                     &lt;br /&gt;   42      BIND_UACS_DIFF                                     &lt;br /&gt;   43      PLSQL_CMP_SWITCHS_DIFF                             &lt;br /&gt;   44      CURSOR_PARTS_MISMATCH                              &lt;br /&gt;   45      STB_OBJECT_MISMATCH                                &lt;br /&gt;   46      CROSSEDITION_TRIGGER_MISMATCH                      &lt;br /&gt;   47      PQ_SLAVE_MISMATCH                                  &lt;br /&gt;   48      TOP_LEVEL_DDL_MISMATCH                             &lt;br /&gt;   49      MULTI_PX_MISMATCH                                  &lt;br /&gt;   50      BIND_PEEKED_PQ_MISMATCH                            &lt;br /&gt;   51      MV_REWRITE_MISMATCH                                &lt;br /&gt;   52      ROLL_INVALID_MISMATCH                              &lt;br /&gt;   53      OPTIMIZER_MODE_MISMATCH                            &lt;br /&gt;   54      PX_MISMATCH                                        &lt;br /&gt;   55      MV_STALEOBJ_MISMATCH                               &lt;br /&gt;   56      FLASHBACK_TABLE_MISMATCH                           &lt;br /&gt;   57      LITREP_COMP_MISMATCH                               &lt;br /&gt;   58      PLSQL_DEBUG                                        &lt;br /&gt;   59      LOAD_OPTIMIZER_STATS                               &lt;br /&gt;   60      ACL_MISMATCH                                       &lt;br /&gt;   61      FLASHBACK_ARCHIVE_MISMATCH                         &lt;br /&gt;   62      LOCK_USER_SCHEMA_FAILED                            &lt;br /&gt;   63      REMOTE_MAPPING_MISMATCH                            &lt;br /&gt;   64      LOAD_RUNTIME_HEAP_FAILED                           &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;こんなに多い理由が存在するので同一なSQL文章が複数の実行計画を見せる時必ずこのビューを検索しなければならないんでしょうね。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-4556818275244693009?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/4556818275244693009/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/12/sql.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/4556818275244693009'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/4556818275244693009'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/12/sql.html' title='バインドピーキングとSQLトレースの出会い'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-6679328262556014504</id><published>2009-12-05T21:41:00.000-08:00</published><updated>2009-12-05T23:19:19.196-08:00</updated><title type='text'>オラクル性能に対する短い考え＃17</title><content type='html'>&lt;font size="+2"&gt;DaaSの時代が来る？&lt;/font&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;クラウドコンピューティング(Cloud Computing)という新しい流れとともにデータベースをサービスで提供する試しが本格化されています。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;私はこれを&lt;b&gt;Daas(Database As A Service. ダース)&lt;/b&gt;と呼びます。自分の勝手に作った言葉せす。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;これはオラクルのような商用ライセンスでもないし、PostgreSQLのような無料ライセンスでもない第三のライセンスです。データをどのくらい使うかによってお金を支払うとても合理的なライセンス政策が現れるようになります。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;DaaSが成功的に普遍化すると企業はメインテナンスの費用を大きく減らすことができるでしょう。中小規模の企業には魅力的に見えます。もちろん大型企業たちは保安と性能、安全性の問題でいぜんとして既存の方式を選り好みする可能性が高いです。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;エンジニアの立場から見ると大変な危機で思われるかも知れません。その間データベースのインストール、パッチ、チューニングに必要だった人力が大きく減りかねません。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;しかしデータベースへの接近費用が大きく減るによってデータベース使用の爆発的な増加を呼び起こすはずです。そこから新しい機会が開かれるかもしれません。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-6679328262556014504?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/6679328262556014504/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/12/17.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/6679328262556014504'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/6679328262556014504'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/12/17.html' title='オラクル性能に対する短い考え＃17'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-9011500768263014644</id><published>2009-12-02T21:50:00.000-08:00</published><updated>2009-12-02T22:31:33.113-08:00</updated><title type='text'>バグ5364143 - バインドピーク問題</title><content type='html'>バインドピークが活性化されているにも関わらずバインドピークが実行されないバグ5364143があります。&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;クエリが最初にハードパースされる時はバインドピークが成功的に発生します。&lt;br /&gt;&lt;li&gt;以降該当クエリがフラッシュ、メモリPressure、DDLなどの理由でInvalidationされます。&lt;br /&gt;&lt;li&gt;次回に同一なクエリがまたハードパースされる時はバインドピークを修行しません。&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;ここで核心はクエリの情報全体が消えるのではなくて実行計画情報のみ消える場合です。SQL文章に当たるLCO(Library Cache Object)はヒープ0にメタ情報を、ヒープ6に実行計画情報を持っています。万一ヒープ6が消えて再びハードパースされる時はバインドピークが起こらない場合が時々発生すると言うのがこのバグが現れる方式です。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;反面バインドピークを非活性化してもバインドピークが起こるバグもあります。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;バインド変数を使用する同じSQL文章がたまに違う実行計画を作って困らせる場合が時々あったが、とてもその理由が分からない場合があります。こんなバグたちのためか疑われますね。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-9011500768263014644?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/9011500768263014644/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/12/5364143.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/9011500768263014644'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/9011500768263014644'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/12/5364143.html' title='バグ5364143 - バインドピーク問題'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-6460283314884702511</id><published>2009-11-29T22:05:00.000-08:00</published><updated>2009-11-29T22:23:18.482-08:00</updated><title type='text'>バージョン互換性を備えたスクリプト作り</title><content type='html'>オラクルバージョンが多様になり、バージョンごと支援するオブジェクトが変わるによりバージョン互換性を持つスクリプトを作るのが少しづつ難しくなっていきます。(幸いに基本となるオブジェクトたちは大きな変化がないので問題が深刻ではないけれどね)&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;例えば、9i以下だったらV$SESSION_WAITビューから、10g以上だったらV$SESSIONビューから待機イベント情報を抽出するスクリプトが作りたいんです。PL/SQLを利用せずSQL*Plusで実行可能な一つのスクリプトファイルで作りたければどうしたらいいでしょうか。多い方法があるはずですが次のような簡単なトリックが使えます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;col is_10g new_value __is_10g&lt;br /&gt;col is_9i new_value __is_9i&lt;br /&gt;&lt;br /&gt;with v as (&lt;br /&gt; select &lt;br /&gt;  to_number(substr(banner, &lt;br /&gt;     instr(banner, 'Release ')+8,&lt;br /&gt;     instr(banner, '.') - instr(banner, 'Release ')-8)) as version&lt;br /&gt; from v$version where rownum = 1&lt;br /&gt;)&lt;br /&gt;select&lt;br /&gt; case when version &gt;= 10 then '' else '--' end as is_10g,&lt;br /&gt; case when version &lt;= 9 then '' else '--' end as is_9i&lt;br /&gt;from v&lt;br /&gt;;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;__is_19g、__is_9i置換変数を利用して次のように手軽に解決します。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;select * from (&lt;br /&gt;&amp;__is_10g select event, p1, p2, p3 from v$session&lt;br /&gt;&amp;__is_9i select event, p1, p2, p3 from v$session_wait&lt;br /&gt;)&lt;br /&gt;;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;応用したら次のようにバージョン別にコラムまで操作できます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;select &lt;br /&gt; sid&lt;br /&gt; ,serial#&lt;br /&gt;&amp;__is_10g ,event&lt;br /&gt;&amp;__is_10g ,p1&lt;br /&gt;from&lt;br /&gt; v$session&lt;br /&gt;;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;SQL*Plusの強力さが分かる良い例と言えます。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-6460283314884702511?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/6460283314884702511/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/11/blog-post.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/6460283314884702511'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/6460283314884702511'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/11/blog-post.html' title='バージョン互換性を備えたスクリプト作り'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-2178847964706282106</id><published>2009-11-26T21:35:00.000-08:00</published><updated>2009-11-26T22:00:36.499-08:00</updated><title type='text'>オラクルに対する短い考え #16</title><content type='html'>&lt;font size="+2"&gt;オラクルは長い間ほとんど変わらなかった&lt;/font&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;オラクル性能トラブルシューチングが難しいながらも易しい理由があります。オラクルがエンタープライズの姿を備えたのがバージョン6(1988年)からと言われます。Undoに基づいた細かい行レベルロックとPL/SQLを支援し始めたのがこの時からです。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;なんと20年の間オラクルの核心エンジンはほとんど変化なしに維持されて来ています。Redo、Undo、行レベルロック、Enqueue、Latch、Shared Pool、Cache Bufferなど性能を支配するすべての要素が大きい変化なしに(しかし少しづつ改善されながら)その様子をそのまま持っています。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;そのおかげで核心概念に対する理解が優れたら少ない努力だけで専門家的な水準を維持できます。しかし逆さまに言うと核心的な概念に対する理解が十分ではなければ何か勉強し続けてもいつも2%不足な状態になります。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-2178847964706282106?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/2178847964706282106/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/11/16.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/2178847964706282106'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/2178847964706282106'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/11/16.html' title='オラクルに対する短い考え #16'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-2507721145746362144</id><published>2009-11-25T20:54:00.000-08:00</published><updated>2009-11-25T21:43:00.052-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Zero-Size Unusable Index、Table Expansion、_optimizer_table_expansion'/><title type='text'>Oracle 11gR2의 Zero-Size Unusable Indexとテーブル拡張(Table Expansion)</title><content type='html'>Christian Antogniniが&lt;a href="http://antognini.ch/2009/11/zero-size-unusable-indexes-and-the-query-optimizer"&gt;ここから&lt;/a&gt;Oracle 11gR2の新機能の一つのZero-size Unusable Indexに対する簡単な紹介とオプティマィザに及ぶ影響について説明しています。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;Zero-Sized Unusable Indexというのは使用不可インデックスまたはインデックスパーティションのセグメント空間を物理的に解除することを意味しています。次に簡単な例があります。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;バージョンはOracle 11gR2です。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@UKJA1120&gt; select * from v$version where rownum &lt;= 1;&lt;br /&gt;BANNER&lt;br /&gt;----------------------------------------------------------------------&lt;br /&gt;Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;パーティションテーブルを作ってからローカルインデックスを作ります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@UKJA1120&gt; create table t1(c1 int, c2 int)&lt;br /&gt;  2  partition by range(c1) (&lt;br /&gt;  3        partition p1 values less than (10000),&lt;br /&gt;  4        partition p2 values less than (20000),&lt;br /&gt;  5        partition p3 values less than (30000),&lt;br /&gt;  6        partition p4 values less than (maxvalue)&lt;br /&gt;  7  );&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;UKJA@UKJA1120&gt; insert into t1 select level, level from dual connect by level &lt;= 30000;&lt;br /&gt;&lt;br /&gt;30000 rows created.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.14&lt;br /&gt;UKJA@UKJA1120&gt; &lt;br /&gt;UKJA@UKJA1120&gt; create index t1_n1 on t1(c1) local;&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.17&lt;br /&gt;UKJA@UKJA1120&gt; &lt;br /&gt;UKJA@UKJA1120&gt; @gather t1&lt;br /&gt;UKJA@UKJA1120&gt; exec dbms_stats.gather_table_stats(user, '&amp;1', no_invalidate=&gt;false);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;最後のパーティションP4を除いて残りの三つのパーティションを使用不可の状態に変えます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@UKJA1120&gt; &lt;br /&gt;UKJA@UKJA1120&gt; alter index t1_n1 modify partition p1 unusable;&lt;br /&gt;&lt;br /&gt;UKJA@UKJA1120&gt; alter index t1_n1 modify partition p2 unusable;&lt;br /&gt;&lt;br /&gt;UKJA@UKJA1120&gt; alter index t1_n1 modify partition p3 unusable;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;USER_SEGMENTSビューを問い合わせてみると使用不可状態のインデックスパーティション自体が存在しないのが分かります。空間節約次元で望ましい動作方式と言えます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@UKJA1120&gt; select partition_name, bytes&lt;br /&gt;  2  from user_segments where segment_name = 'T1_N1';&lt;br /&gt;&lt;br /&gt;PARTITION_NAME            BYTES&lt;br /&gt;-------------------- ----------&lt;br /&gt;P4                     10485760&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ここからもう一つの魔法のようなものが発生します。つぎのようにパーティションP3とP4をかかる条件節を持ったクエリを実行します。万一Oracle 10gだったら全表走査を選択するしかないでしょう。でもOracle 11gR2では次のようにUNION ALLに変化された完璧な実行計画が生成されます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@UKJA1120&gt; explain plan for&lt;br /&gt;  2  select count(*) from t1&lt;br /&gt;  3  where c1 between 29999 and 30001;&lt;br /&gt;&lt;br /&gt;-------------------------------------------------------------&lt;br /&gt;| Id  | Operation                 | Name    | Pstart| Pstop |&lt;br /&gt;-------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT          |         |       |       |&lt;br /&gt;|   1 |  SORT AGGREGATE           |         |       |       |&lt;br /&gt;|   2 |   VIEW                    | VW_TE_2 |       |       |&lt;br /&gt;|   3 |    UNION-ALL              |         |       |       |&lt;br /&gt;|   4 |     PARTITION RANGE SINGLE|         |     4 |     4 |&lt;br /&gt;|   5 |      INDEX RANGE SCAN     | T1_N1   |     4 |     4 |&lt;br /&gt;|   6 |     PARTITION RANGE SINGLE|         |     3 |     3 |&lt;br /&gt;|   7 |      TABLE ACCESS FULL    | T1      |     3 |     3 |&lt;br /&gt;-------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;インデックスが正常的に存在するパーティションP4に対しては索引走査を、インデックスが存在しないパーティションP3に対しては全表走査を修行します。こんな動作方式はパーティション数に関わらず良く修行されます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@UKJA1120&gt; alter index t1_n1 rebuild partition p2;&lt;br /&gt;&lt;br /&gt;UKJA@UKJA1120&gt; explain plan for&lt;br /&gt;  2  select count(*) from t1&lt;br /&gt;  3  where c1 between 19999 and 30001;&lt;br /&gt;&lt;br /&gt;--------------------------------------------------------------&lt;br /&gt;| Id  | Operation                  | Name    | Pstart| Pstop |&lt;br /&gt;--------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT           |         |       |       |&lt;br /&gt;|   1 |  SORT AGGREGATE            |         |       |       |&lt;br /&gt;|   2 |   VIEW                     | VW_TE_2 |       |       |&lt;br /&gt;|   3 |    UNION-ALL               |         |       |       |&lt;br /&gt;|   4 |     CONCATENATION          |         |       |       |&lt;br /&gt;|   5 |      PARTITION RANGE SINGLE|         |     4 |     4 |&lt;br /&gt;|   6 |       INDEX RANGE SCAN     | T1_N1   |     4 |     4 |&lt;br /&gt;|   7 |      PARTITION RANGE SINGLE|         |     2 |     2 |&lt;br /&gt;|   8 |       INDEX RANGE SCAN     | T1_N1   |     2 |     2 |&lt;br /&gt;|   9 |     PARTITION RANGE SINGLE |         |     3 |     3 |&lt;br /&gt;|  10 |      TABLE ACCESS FULL     | T1      |     3 |     3 |&lt;br /&gt;--------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;この動作方式を見てこの一語を浮かべたらオプティマィザの動作方式についてある程度理解していると言えます。&lt;b&gt;"Transformation!"&lt;/b&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;これを確認するため10053トレースを修行してみます。その結果です。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;TE: Checking validity of table expansion for query block SEL$1 (#0)&lt;br /&gt;&lt;br /&gt;***********************************&lt;br /&gt;Cost-Based Table Expansion&lt;br /&gt;***********************************&lt;br /&gt;TE: Checking validity of TE for query block SEL$1 (#1)&lt;br /&gt;TE: Checking validity of table expansion for query block SEL$1 (#1)&lt;br /&gt;&lt;br /&gt;TE: after table expansion:******* UNPARSED QUERY IS *******&lt;br /&gt;SELECT COUNT(*) "COUNT(*)" FROM  &lt;br /&gt;(&lt;br /&gt; (SELECT 0 FROM "UKJA"."T1" "T1" &lt;br /&gt;  WHERE TBL$OR$IDX$PART$NUM("UKJA"."T1",0,0,65535,"T1".ROWID)&gt;=4 AND &lt;br /&gt;  TBL$OR$IDX$PART$NUM("UKJA"."T1",0,0,65535,"T1".ROWID)&lt;=4 &lt;br /&gt;  AND "T1"."C1"&lt;= 30001 AND NULL IS NULL &lt;br /&gt;  AND ("T1"."C1"&gt;=30000 OR NULL IS NOT NULL)) &lt;br /&gt; UNION ALL  &lt;br /&gt; (SELECT 0 FROM "UKJA"."T1" "T1" &lt;br /&gt;  WHERE TBL$OR$IDX$PART$NUM("UKJA"."T1",0,0,65535,"T1".ROWID)&gt;=3 &lt;br /&gt;  AND TBL$OR$IDX$PART$NUM("UKJA"."T1",0,0,65535,"T1".ROWID)&lt;=3 &lt;br /&gt;  AND "T1"."C1"&gt;= 29999 AND "T1"."C1"&lt;30000)) &lt;br /&gt;"VW_TE_1"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;テーブル拡張(Table Expansion)という技法を通じて数個のパーティションをかかる条件節がUNION ALLに変形されたのが分かります。Oracle 11gR2で新しく追加されたこの技法は_OPTIMIZER_TABLE_EXPANSIONパラメターで制御されます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@UKJA1120&gt; alter session set "_optimizer_table_expansion" = false;&lt;br /&gt;&lt;br /&gt;UKJA@UKJA1120&gt; explain plan for&lt;br /&gt;  2  select count(*) from t1&lt;br /&gt;  3  where c1 between 19999 and 30001;&lt;br /&gt;&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;| Id  | Operation                 | Name | Pstart| Pstop |&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT          |      |       |       |&lt;br /&gt;|   1 |  SORT AGGREGATE           |      |       |       |&lt;br /&gt;|   2 |   PARTITION RANGE ITERATOR|      |     2 |     4 |&lt;br /&gt;|   3 |    TABLE ACCESS FULL      | T1   |     2 |     4 |&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;もう一つの注意するところはテーブル拡張(Table Expansion)もコストベースで定義されていることです。万一費用計算で不利な値が出たら上の例でも全表走査を選択されたかも知れません。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;Oracle 11gR2にまたどんな隠れた改善点が見つかるか楽しみですね。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-2507721145746362144?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/2507721145746362144/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/11/oracle-11gr2-zero-size-unusable.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/2507721145746362144'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/2507721145746362144'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/11/oracle-11gr2-zero-size-unusable.html' title='Oracle 11gR2의 Zero-Size Unusable Indexとテーブル拡張(Table Expansion)'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-7367710742814017943</id><published>2009-11-22T22:47:00.000-08:00</published><updated>2009-11-22T23:08:28.309-08:00</updated><title type='text'>オラクル性能に対する短い考え＃15</title><content type='html'>&lt;font size="+2"&gt;&lt;a href="http://tahiti.oracle.com"&gt;&lt;b&gt;tahiti.oracle.com&lt;/b&gt;&lt;/a&gt;を使用しましょう。&lt;/font&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;オラクルが提供する命令文やPL/SQLパッケージの情報を探すとき一番先に行ってみるべき所は？&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;答えはGoogleではなくてTahitiです。Tahitiの存在を数人に聞いてみましたが知らない人が意外に多いんでした。習慣的にGoogleを検索する前に必ずしもここを尋ねてみましょう。マニュアルも読んでみなかったと怒られたくなければ。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;なおSQL Referenceを読む方法も身につけましょう。SQL Referenceは次のような形式で文脈(Syntax)を説明しています。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_QmSegOSVHT8/Swo0gvFNAqI/AAAAAAAAACQ/Rh50pOdtT1k/s1600/create_table.jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 640px; height: 241px;" src="http://4.bp.blogspot.com/_QmSegOSVHT8/Swo0gvFNAqI/AAAAAAAAACQ/Rh50pOdtT1k/s320/create_table.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5407192039610450594" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;上の形式がまだ慣れていなかったら練習がもっと必要です。最小限オラクルマニュアルほどはどんな問題もなしに読めるべきです。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-7367710742814017943?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/7367710742814017943/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/11/15.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/7367710742814017943'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/7367710742814017943'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/11/15.html' title='オラクル性能に対する短い考え＃15'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_QmSegOSVHT8/Swo0gvFNAqI/AAAAAAAAACQ/Rh50pOdtT1k/s72-c/create_table.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-8509761918221178103</id><published>2009-11-21T03:15:00.000-08:00</published><updated>2009-11-21T04:19:14.203-08:00</updated><title type='text'>統計情報がない時CBOは何をするだろうか。</title><content type='html'>この前統計情報がない場合オプティマィザが費用(Cost)をどのように計算するかに対する問い合わせがありました。基本的な方法は次のようです。&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;動的サンプリング(Dynamic Sampling)が利用できればサンプリングにより統計情報を作った後で費用を計算する。&lt;br /&gt;&lt;li&gt;動的サンプリングが利用できなければ&lt;b&gt;内部的に決められている基本値&lt;/b&gt;を使用する。&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;ここで&lt;b&gt;内部的に決められている基本値&lt;/b&gt;ということの意味が気になります。幸いにオラクルマニュアルとWolfgang Breitlingが作成した有名な白書によく説明してあります。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://download.oracle.com/docs/cd/E11882_01/server.112/e10821/stats.htm#i41866"&gt;オラクルマニュアル&lt;/a&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.centrexcc.com/A%20Look%20under%20the%20Hood%20of%20CBO%20-%20the%2010053%20Event.pdf"&gt;Look Under the Hood of CBO - the 10053 Event&lt;/a&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;簡単なテストを通じて整理してみます。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;まず次のようにテーブルを作ります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; -- case1&lt;br /&gt;UKJA@ukja1021&gt; create table t1&lt;br /&gt;  2  as&lt;br /&gt;  3  select level as c1, 'x' as c2&lt;br /&gt;  4  from dual&lt;br /&gt;  5  connect by level &lt;= 10000;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.04&lt;br /&gt;UKJA@ukja1021&gt; &lt;br /&gt;UKJA@ukja1021&gt; create index t1_n1 on t1(c1);&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.03&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;動的サンプリングを動作しないようにする上、OPTIMIZER_MODE値をALL_ROWSにするによってオプティマィザが内部的な基本値を使用するようにします。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; alter session set optimizer_dynamic_sampling = 1;&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.00&lt;br /&gt;UKJA@ukja1021&gt; alter session set optimizer_mode = all_rows;&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;実行計画を見ましょうか。たとえ統計情報はないんですけど、予測行件数が31で結構もっともらしく予測しました。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; explain plan for&lt;br /&gt;  2  select * from t1&lt;br /&gt;  3  where c1 = 1;&lt;br /&gt;&lt;br /&gt;Explained.&lt;br /&gt;&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT            |       |    31 |   496 |     2   (0)| 00:00:01 |&lt;br /&gt;|   1 |  TABLE ACCESS BY INDEX ROWID| T1    |    31 |   496 |     2   (0)| 00:00:01 |&lt;br /&gt;|*  2 |   INDEX RANGE SCAN          | T1_N1 |    12 |       |     1   (0)| 00:00:01 |&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;31の予測行件数がどこから来たのかは10053トレースファイルによく書き込んであります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;***************************************&lt;br /&gt;BASE STATISTICAL INFORMATION&lt;br /&gt;***********************&lt;br /&gt;Table Stats::&lt;br /&gt;  Table: T1  Alias: T1  (NOT ANALYZED)&lt;br /&gt;    #Rows: 3104  #Blks:  38  AvgRowLen:  100.00&lt;br /&gt;Index Stats::&lt;br /&gt;  Index: T1_N1  Col#: 1&lt;br /&gt;    LVLS: 1  #LB: 21  #DK: 10000  LB/K: 1.00  DB/K: 1.00  CLUF: 16.00&lt;br /&gt;***************************************&lt;br /&gt;SINGLE TABLE ACCESS PATH&lt;br /&gt;  Column (#1): C1(NUMBER)  NO STATISTICS (using defaults)&lt;br /&gt;    AvgLen: 22.00 NDV: 97 Nulls: 0 Density: 0.010309&lt;br /&gt;  Table: T1  Alias: T1     &lt;br /&gt;    Card: Original: 3104  Rounded: 31  Computed: 31.04  Non Adjusted: 31.04&lt;br /&gt;  Access Path: TableScan&lt;br /&gt;    Cost:  10.15  Resp: 10.15  Degree: 0&lt;br /&gt;      Cost_io: 10.00  Cost_cpu: 892035&lt;br /&gt;      Resp_io: 10.00  Resp_cpu: 892035&lt;br /&gt;  Access Path: index (AllEqGuess)&lt;br /&gt;    Index: T1_N1&lt;br /&gt;    resc_io: 2.00  resc_cpu: 29893&lt;br /&gt;    ix_sel: 0.004  ix_sel_with_filters: 0.004&lt;br /&gt;    Cost: 2.01  Resp: 2.01  Degree: 1&lt;br /&gt;  Best:: AccessPath: IndexRange  Index: T1_N1&lt;br /&gt;         Cost: 2.01  Degree: 1  Resp: 2.01  Card: 31.04  Bytes: 0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;上の内容を分析してみると次のようです。&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;テーブルのブロック数は33である。&lt;br /&gt;&lt;li&gt;したがってテーブルの基本Cardinality = 38*(8192 - 24)/100 = 3103.84 = 3104になった。&lt;br /&gt;&lt;li&gt;したがって列C1のDensityは1 / ( 3103.84 / 32 ) = 0.01030981 = 0.010309である。&lt;br /&gt;&lt;li&gt;したがってWHERE C1 = 1に該当する予測行件数は3104 * 0.010309 = 31になる。&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;すなわち、テーブルブロック数だけ手に入ればその後は上で言った二つの文書で説明された内部的な公式によって自然に計算されます。ここで問題は&lt;b&gt;テーブルのブロック数&lt;/b&gt;をどう手に入れるのです。オラクルマニュアルによると次のように得られます。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;セグメントに存在するExtent Mapからブロック数を手に入れる。&lt;br /&gt;&lt;li&gt;万一上の方法が不可能だったら(たとえばTable Functionを使ったりExternal Tableを使用したばあい)100だと仮定する。&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;上の説明が正しいかExtent Mapを実際に確認してみます。セグメントヘッダーブロックを見ると次のようにExtent Mapが存在します。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; col header_file new_value header_file&lt;br /&gt;UKJA@ukja1021&gt; col header_block new_value header_block&lt;br /&gt;UKJA@ukja1021&gt; select header_file, header_block&lt;br /&gt;  2  from dba_segments where owner = user and segment_name = 'T1';&lt;br /&gt;&lt;br /&gt;HEADER_FILE HEADER_BLOCK&lt;br /&gt;----------- ------------&lt;br /&gt;          7        40990&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.03&lt;br /&gt;&lt;br /&gt;UKJA@ukja1021&gt; alter system dump datafile &amp;header_file block &amp;header_block;&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;  Extent Map&lt;br /&gt;  -----------------------------------------------------------------&lt;br /&gt;   0x01c0a009  length: 1280  &lt;br /&gt;&lt;br /&gt;UKJA@ukja1021&gt; select to_dec('01c0a009') from dual;&lt;br /&gt;&lt;br /&gt;TO_DEC('01C0A009')&lt;br /&gt;------------------&lt;br /&gt;          29401097&lt;br /&gt;&lt;/pre&gt;          &lt;br /&gt;Extent Mapによると一つのExtentが存在するし現在0番から37番、すなわち38個のブロックが実際に使用されているのが分かります。したがってオプティマィザは38個のブロックが存在すると認識しているのです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- dump_dba.sql : http://sites.google.com/site/ukja/sql-scripts-1/c/dump-dba&lt;br /&gt;UKJA@ukja1021&gt; @dump_dba 29401097&lt;br /&gt;...&lt;br /&gt;Dump of First Level Bitmap Block&lt;br /&gt;&lt;br /&gt;  --------------------------------------------------------&lt;br /&gt;  DBA Ranges :&lt;br /&gt;  --------------------------------------------------------&lt;br /&gt;   0x01c0a009  Length: 64     Offset: 0      &lt;br /&gt;  &lt;br /&gt;   0:Metadata   1:Metadata   2:Metadata   3:Metadata&lt;br /&gt;   4:Metadata   5:Metadata   6:Metadata   7:Metadata&lt;br /&gt;   8:Metadata   9:Metadata   10:Metadata   11:Metadata&lt;br /&gt;   12:Metadata   13:Metadata   14:Metadata   15:Metadata&lt;br /&gt;   16:Metadata   17:Metadata   18:Metadata   19:Metadata&lt;br /&gt;   20:Metadata   21:Metadata   22:FULL   23:FULL&lt;br /&gt;   24:FULL   25:FULL   26:FULL   27:FULL&lt;br /&gt;   28:FULL   29:FULL   30:FULL   31:FULL&lt;br /&gt;   32:FULL   33:FULL   34:FULL   35:FULL&lt;br /&gt;   36:FULL   37:FULL   38:unformatted   39:unformatted&lt;br /&gt;   40:unformatted   41:unformatted   42:unformatted   43:unformatted&lt;br /&gt;   44:unformatted   45:unformatted   46:unformatted   47:unformatted&lt;br /&gt;   48:unformatted   49:unformatted   50:unformatted   51:unformatted&lt;br /&gt;   52:unformatted   53:unformatted   54:unformatted   55:unformatted&lt;br /&gt;   56:unformatted   57:unformatted   58:unformatted   59:unformatted&lt;br /&gt;   60:unformatted   61:unformatted   62:unformatted   63:unformatted&lt;br /&gt;  --------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;  &lt;br /&gt;この情報は&lt;a href="http://sites.google.com/site/ukja/sql-scripts-1/s-z/show-space"&gt;dbms_space.unused_space&lt;/a&gt;プロシージャを通じて手に入れた値を一致します。したがってExtent Mapを通じてテーブルのブロック数を手に入れるというマニュアルの内容が事実であることが分かります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; exec show_space('T1');&lt;br /&gt;Free Blocks.............................&lt;br /&gt;Total Blocks............................1280&lt;br /&gt;Total Bytes.............................10485760&lt;br /&gt;Total MBytes............................10&lt;br /&gt;Unused Blocks...........................1242&lt;br /&gt;Unused Bytes............................10174464&lt;br /&gt;Last Used Ext FileId....................9&lt;br /&gt;Last Used Ext BlockId...................52489&lt;br /&gt;Last Used Block.........................38&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;一つ面白いことはテーブルのブロック数を物理的に得るのでとても正確だということです。このために統計情報が全然無い状態で(そして動的サンプリングが動作しないことにもかかわらず)CBO(RBOではなくて)が作った実行計画が以外に立派なのでびっくりする場合があります。生半な統計情報よりは統計情報が初めからないほうが良い場合がたくさんあります。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-8509761918221178103?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/8509761918221178103/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/11/cbo.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/8509761918221178103'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/8509761918221178103'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/11/cbo.html' title='統計情報がない時CBOは何をするだろうか。'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-1718370614271386471</id><published>2009-11-17T23:41:00.000-08:00</published><updated>2009-11-18T00:10:38.296-08:00</updated><title type='text'>オラクル性能に対する短い考え＃14</title><content type='html'>&lt;font size="+2"&gt;初期化パラメータのデフォルト値一つ一つには哲学が入っている。&lt;br /&gt;&lt;/font&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;オラクルほど初期化パラメータ(Initialization Parameter)を利用して多様な制御が可能なスフトも少ないんです。特に隠しパラメータと呼ばれるものたちは本当に細かいものまで制御できるようになっています。初めてはこんな多きパラメータたちが不慣れで面倒くさく感じられるかも知れませんが、オラクルを使用する時間が増えるほどありがたく感じるようになります。それだけ自由を許すと言う意味だからです。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;ここのパラメータは固有の目的を持っています。多くのパラメータが&lt;b&gt;デフォルト値(Default Value)&lt;/b&gt;を持っています。デフォルト値は当然にそのパラメータの固有の目的を一番よく果たすように設定されます。あるパラメータはデフォルト値が間違い設定されて私たちを困らせるようにします。オラクル開発チームが現実を誤解したはずです。デフォルト値が意味することを正確に理解したらそれだけオラクルの性能に対する理解が深まります。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;例をあげてみましょうか。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;なぜOPTIMIZER_MODEのデフォルト値はALL_ROWSなのか。&lt;br /&gt;&lt;li&gt;なぜ_OPTIM_PEEK_USER_BINDSのデフォルト値はTRUEなのか。&lt;br /&gt;&lt;li&gt;なぜOPTIMIZER_DYNAMIC_SAMPLINGのデフォルト値は2なのか。&lt;br /&gt;&lt;li&gt;なぜDB_BLOCK_SIZEは8KBなのか。&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-1718370614271386471?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/1718370614271386471/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/11/14.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/1718370614271386471'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/1718370614271386471'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/11/14.html' title='オラクル性能に対する短い考え＃14'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-8794201578860047697</id><published>2009-11-16T20:39:00.000-08:00</published><updated>2009-11-16T21:26:23.624-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='並列実行、V$PQ_TQSTAT'/><title type='text'>並列実行とV$PQ_TQSTATビュー</title><content type='html'>並列実行(Parallel Execution)をきちんと解析するには必ずといっても良いだけ&lt;b&gt;V$PQ_TQSTATビュー&lt;/b&gt;を確認すべき場合があります。一度もこのビューを使用したことがなかったら次の例を見てびっくりするはずです。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;オラクルバージョンは次のようです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; select * from v$version where rownum = 1&lt;br /&gt;  2  ;&lt;br /&gt;&lt;br /&gt;BANNER&lt;br /&gt;----------------------------------------------------------------&lt;br /&gt;Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;テーブルを作ります。C1列について自然に整列になっている形態です。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; create table t1&lt;br /&gt;  2  as select level as c1&lt;br /&gt;  3  from dual&lt;br /&gt;  4  connect by level &lt;= 100000&lt;br /&gt;  5  -- order by dbms_random.random&lt;br /&gt;  6  -- how the table is ordered may affect the result&lt;br /&gt;  7  ;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;テーブルの全ての行をフェッチするもっとも単純な形態の並列実行です。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; begin&lt;br /&gt;  2    for c in ( select /*+ parallel(t1,4)*/ * from t1) loop&lt;br /&gt;  3      null;&lt;br /&gt;  4    end loop;&lt;br /&gt;  5  end;&lt;br /&gt;  6  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;その結果をV$PQ_TQSTATビューを通じて観察してみます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;ed pq_stat.sql&lt;br /&gt;&lt;br /&gt;col process format a10&lt;br /&gt;col server_type format a10&lt;br /&gt;select dfo_number, tq_id, server_type,  process, &lt;br /&gt;  num_rows, bytes, avg_latency, waits, timeouts&lt;br /&gt;from v$pq_tqstat&lt;br /&gt;order by 1, 2, 3, 4, 5&lt;br /&gt;;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;その結果は次のようです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; @pq_stat&lt;br /&gt;&lt;br /&gt;DFO_NUMBER      TQ_ID SERVER_TYP PROCESS      NUM_ROWS      BYTES AVG_LATENCY&lt;br /&gt;---------- ---------- ---------- ---------- ---------- ---------- -----------&lt;br /&gt;     WAITS   TIMEOUTS&lt;br /&gt;---------- ----------&lt;br /&gt;         1          0 Consumer   QC             100000     595112           0&lt;br /&gt;       223         64&lt;br /&gt;&lt;br /&gt;         1          0 Producer   P000             5913      33710           0&lt;br /&gt;         3          0&lt;br /&gt;&lt;br /&gt;         1          0 Producer   P001            40870     243406           0&lt;br /&gt;        21          0&lt;br /&gt;&lt;br /&gt;         1          0 Producer   P002             7884      45733           0&lt;br /&gt;         4          0&lt;br /&gt;&lt;br /&gt;         1          0 Producer   P003            45333     272263           0&lt;br /&gt;        23          0&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.01&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;四つのスレーブプロセスがテーブルからデータを読み込む生産者(Producer)で動作しました。驚いたことにP001、P003二つのプロセスが大部分のデータを読み込み、残りの二つは一部のデータだけを読み込みました。スレーブプロセスの間にデータを公平に読み込んでいないと言えます。こんな現状ができたら並列実行の性能が考えより高くない結果になってしまいます。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;ORDER BY句を利用して整列を修行したらもっと深刻な結果が出ます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; begin&lt;br /&gt;  2    for c in ( select /*+ parallel(t1,4)*/ * from t1 order by c1) loop&lt;br /&gt;  3      exit;&lt;br /&gt;  4    end loop;&lt;br /&gt;  5  end;&lt;br /&gt;  6  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.07&lt;br /&gt;UKJA@ukja1021&gt; &lt;br /&gt;UKJA@ukja1021&gt; @pq_stat&lt;br /&gt;&lt;br /&gt;DFO_NUMBER      TQ_ID SERVER_TYP PROCESS      NUM_ROWS      BYTES AVG_LATENCY&lt;br /&gt;---------- ---------- ---------- ---------- ---------- ---------- -----------&lt;br /&gt;     WAITS   TIMEOUTS&lt;br /&gt;---------- ----------&lt;br /&gt;         1          0 Consumer   P000             1972       9925           0&lt;br /&gt;         9          2&lt;br /&gt;&lt;br /&gt;         1          0 Consumer   P001             1971      10018           0&lt;br /&gt;         9          3&lt;br /&gt;&lt;br /&gt;         1          0 Consumer   P002             1971      10018           0&lt;br /&gt;         8          2&lt;br /&gt;&lt;br /&gt;         1          0 Consumer   P003            94086     565403           0&lt;br /&gt;       116         47&lt;br /&gt;&lt;br /&gt;         1          0 Producer   P004            31015     183753           0&lt;br /&gt;        24          3&lt;br /&gt;&lt;br /&gt;         1          0 Producer   P005            21681     129330           0&lt;br /&gt;        17          4&lt;br /&gt;&lt;br /&gt;         1          0 Producer   P006            27594     165130           0&lt;br /&gt;        22          5&lt;br /&gt;&lt;br /&gt;         1          0 Producer   P007            19710     117279           0&lt;br /&gt;        14          2&lt;br /&gt;&lt;br /&gt;         1          0 Ranger     QC                372       3789           0&lt;br /&gt;         3          1&lt;br /&gt;&lt;br /&gt;         1          1 Consumer   QC                100       1927           0&lt;br /&gt;        67         23&lt;br /&gt;&lt;br /&gt;         1          1 Producer   P000             1166       5782           0&lt;br /&gt;        14          3&lt;br /&gt;&lt;br /&gt;         1          1 Producer   P001             1146       5780           0&lt;br /&gt;        14          3&lt;br /&gt;&lt;br /&gt;         1          1 Producer   P002             1146       5781           0&lt;br /&gt;        13          2&lt;br /&gt;&lt;br /&gt;         1          1 Producer   P003             1146       5781           0&lt;br /&gt;       122         48&lt;br /&gt;&lt;br /&gt;14 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;四つのプロセス(P004 ~ P007)が生産者(Producer)でテーブルからデータを読み込むし四つのプロセス(P000 ~ P003)が消費者(Consumer)で整列を行いますが、その分布がたいへん良くないです。四つ目のプロセスであるP003が大部分の作業(94%、10,000件中94,086件)を行うのが分かります。このままなら並列実行のメリットはほとんどないと言えます。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;こんな現状は索引を生成する時も同じです。索引の生成は内部的に整列作業を行います。次にその結果があります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; create index t1_n1 on t1(c1) parallel 4;&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;UKJA@ukja1021&gt; @pq_stat&lt;br /&gt;&lt;br /&gt;DFO_NUMBER      TQ_ID SERVER_TYP PROCESS      NUM_ROWS      BYTES AVG_LATENCY&lt;br /&gt;---------- ---------- ---------- ---------- ---------- ---------- -----------&lt;br /&gt;     WAITS   TIMEOUTS&lt;br /&gt;---------- ----------&lt;br /&gt;         1          0 Consumer   P000           100000    1807692           0&lt;br /&gt;         4          1&lt;br /&gt;&lt;br /&gt;         1          0 Consumer   P001                0         80           0&lt;br /&gt;         4          1&lt;br /&gt;&lt;br /&gt;         1          0 Consumer   P002                0         80           0&lt;br /&gt;         4          1&lt;br /&gt;&lt;br /&gt;         1          0 Consumer   P003                0         80           0&lt;br /&gt;         4          1&lt;br /&gt;&lt;br /&gt;         1          0 Producer   P004            11826     212999           0&lt;br /&gt;         8          1&lt;br /&gt;&lt;br /&gt;         1          0 Producer   P005            38899     705092           0&lt;br /&gt;        26          4&lt;br /&gt;&lt;br /&gt;         1          0 Producer   P006            11826     213097           0&lt;br /&gt;         7          0&lt;br /&gt;&lt;br /&gt;         1          0 Producer   P007            37449     676904           0&lt;br /&gt;        29          6&lt;br /&gt;&lt;br /&gt;         1          0 Ranger     QC                  0        160           0&lt;br /&gt;         5          1&lt;br /&gt;&lt;br /&gt;         1          1 Consumer   QC                  4       1272           0&lt;br /&gt;         4          1&lt;br /&gt;&lt;br /&gt;         1          1 Producer   P000                1        318           0&lt;br /&gt;         0          0&lt;br /&gt;&lt;br /&gt;         1          1 Producer   P001                1        318           0&lt;br /&gt;         0          0&lt;br /&gt;&lt;br /&gt;         1          1 Producer   P002                1        318           0&lt;br /&gt;         0          0&lt;br /&gt;&lt;br /&gt;         1          1 Producer   P003                1        318           0&lt;br /&gt;         0          0&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;14 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;今度はP000プロセスが全ての整列作業を行いました。並列作業のメリットはないと言えます。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;こんな現状はデータの整列程度とも関連があるようです。次のようにテーブルにランダムでデータが入るように変えてみます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; create table t1&lt;br /&gt;  2  as select level as c1&lt;br /&gt;  3  from dual&lt;br /&gt;  4  connect by level &lt;= 100000&lt;br /&gt;  5  order by dbms_random.random  -- !!&lt;br /&gt;  6  -- how the table is ordered may affect the result&lt;br /&gt;  7  ;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;この場合には四つのスレーブプロセスがほとんど公平に４等分して整列作業を行います。最高の理想的な場合だと言えます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; begin&lt;br /&gt;  2    for c in ( select /*+ parallel(t1,4)*/ * from t1 order by c1) loop&lt;br /&gt;  3      exit;&lt;br /&gt;  4    end loop;&lt;br /&gt;  5  end;&lt;br /&gt;  6  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.06&lt;br /&gt;UKJA@ukja1021&gt; &lt;br /&gt;UKJA@ukja1021&gt; @pq_stat&lt;br /&gt;&lt;br /&gt;DFO_NUMBER      TQ_ID SERVER_TYP PROCESS      NUM_ROWS      BYTES AVG_LATENCY&lt;br /&gt;---------- ---------- ---------- ---------- ---------- ---------- -----------&lt;br /&gt;     WAITS   TIMEOUTS&lt;br /&gt;---------- ----------&lt;br /&gt;         1          0 Consumer   P000            23570     132528           0&lt;br /&gt;        39         13&lt;br /&gt;&lt;br /&gt;         1          0 Consumer   P001            27176     164544           0&lt;br /&gt;        65         16&lt;br /&gt;&lt;br /&gt;         1          0 Consumer   P002            27655     167454           0&lt;br /&gt;        50         18&lt;br /&gt;&lt;br /&gt;         1          0 Consumer   P003            21599     130778           0&lt;br /&gt;        60         14&lt;br /&gt;&lt;br /&gt;         1          0 Producer   P004            17739     105608           0&lt;br /&gt;        15          4&lt;br /&gt;&lt;br /&gt;         1          0 Producer   P005            29044     173021           0&lt;br /&gt;        30         10&lt;br /&gt;&lt;br /&gt;         1          0 Producer   P006            21681     129097           0&lt;br /&gt;        20          4&lt;br /&gt;&lt;br /&gt;         1          0 Producer   P007            31536     187718           0&lt;br /&gt;        23          5&lt;br /&gt;&lt;br /&gt;         1          0 Ranger     QC                372       4546           0&lt;br /&gt;         1          0&lt;br /&gt;&lt;br /&gt;         1          1 Consumer   QC                100       1927           0&lt;br /&gt;        67         24&lt;br /&gt;&lt;br /&gt;         1          1 Producer   P000             1166       5782           0&lt;br /&gt;        44         14&lt;br /&gt;&lt;br /&gt;         1          1 Producer   P001              955       5782           0&lt;br /&gt;        70         17&lt;br /&gt;&lt;br /&gt;         1          1 Producer   P002              955       5780           0&lt;br /&gt;        56         20&lt;br /&gt;&lt;br /&gt;         1          1 Producer   P003              954       5777           0&lt;br /&gt;        66         16&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;14 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;하지만 인덱스를 생성할 경우에는 여전히 같은 문제가 발생합니다.&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; create index t1_n1 on t1(c1) parallel 4;&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.54&lt;br /&gt;UKJA@ukja1021&gt; &lt;br /&gt;UKJA@ukja1021&gt; @pq_stat&lt;br /&gt;&lt;br /&gt;DFO_NUMBER      TQ_ID SERVER_TYP PROCESS      NUM_ROWS      BYTES AVG_LATENCY&lt;br /&gt;---------- ---------- ---------- ---------- ---------- ---------- -----------&lt;br /&gt;     WAITS   TIMEOUTS&lt;br /&gt;---------- ----------&lt;br /&gt;         1          0 Consumer   P000           100000    1807712           0&lt;br /&gt;         4          1&lt;br /&gt;&lt;br /&gt;         1          0 Consumer   P001                0         80           0&lt;br /&gt;         4          1&lt;br /&gt;&lt;br /&gt;         1          0 Consumer   P002                0         80           0&lt;br /&gt;         4          1&lt;br /&gt;&lt;br /&gt;         1          0 Consumer   P003                0         80           0&lt;br /&gt;         4          1&lt;br /&gt;&lt;br /&gt;         1          0 Producer   P004            15768     285122           0&lt;br /&gt;        10          0&lt;br /&gt;&lt;br /&gt;         1          0 Producer   P005            32986     596372           0&lt;br /&gt;        25          0&lt;br /&gt;&lt;br /&gt;         1          0 Producer   P006            17739     320856           0&lt;br /&gt;        12          1&lt;br /&gt;&lt;br /&gt;         1          0 Producer   P007            33507     605762           0&lt;br /&gt;        22          1&lt;br /&gt;&lt;br /&gt;         1          0 Ranger     QC                  0        160           0&lt;br /&gt;         1          0&lt;br /&gt;&lt;br /&gt;         1          1 Consumer   QC                  4       1272           0&lt;br /&gt;         3          1&lt;br /&gt;&lt;br /&gt;         1          1 Producer   P000                1        318           0&lt;br /&gt;         0          0&lt;br /&gt;&lt;br /&gt;         1          1 Producer   P001                1        318           0&lt;br /&gt;         0          0&lt;br /&gt;&lt;br /&gt;         1          1 Producer   P002                1        318           0&lt;br /&gt;         0          0&lt;br /&gt;&lt;br /&gt;         1          1 Producer   P003                1        318           0&lt;br /&gt;         0          0&lt;br /&gt;&lt;br /&gt;14 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;上記の現状を見ると整列作業を並列に修行するコードの一部分にロジックの誤りがあると考えられます。V$PQ_TQSTATビューではなければこんな現状を観察するのは易しくないです。機会がある時ぜひ活用してみるのをお勧めします。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;この現状に対するもっとも詳しい説明は次のURLを参照してください。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.pythian.com/news/5379/oracle-parallel-query-sorting-performance-problems/"&gt;Oracle Parallel Query Sorting and Index Creation Performance Problems&lt;/a&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-8794201578860047697?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/8794201578860047697/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/11/vpqtqstat.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/8794201578860047697'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/8794201578860047697'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/11/vpqtqstat.html' title='並列実行とV$PQ_TQSTATビュー'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-5963451851570929367</id><published>2009-11-15T17:28:00.000-08:00</published><updated>2009-11-15T18:13:29.094-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Metalink、My Oracle Support'/><title type='text'>My Oracle Support(MOS. 旧Metalink)はなおHTMLを支援します。</title><content type='html'>何時ぞやメタリンクの次世代バージョンのMy Oracle Support(MOS)がオープンされました。その過程でログイン失敗、性能劣化などの問題かできましたが、その中でもフラッシュに対する不満が高いでした。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;幸いにフラッシュではなくてHTMLで組み合わされたMy Oracle Supportがなお支援されています。次のURLを利用してみてください。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;a href="http://supporthtml.oracle.com"&gt;HTMLバージョンのMy Oracle Support&lt;/a&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;私もここを主に利用するつもりです。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-5963451851570929367?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/5963451851570929367/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/11/my-oracle-supportmos-metalinkhtml.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/5963451851570929367'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/5963451851570929367'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/11/my-oracle-supportmos-metalinkhtml.html' title='My Oracle Support(MOS. 旧Metalink)はなおHTMLを支援します。'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-6380849297320363495</id><published>2009-11-14T06:59:00.000-08:00</published><updated>2009-11-14T07:30:49.860-08:00</updated><title type='text'>オラクル性能に対する短い考え＃13</title><content type='html'>&lt;font size="+2"&gt;プログラミング実力はオラクル性能専門家になるのに一番大きい役にたつ&lt;br /&gt;&lt;/font&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;性能問題に対してある程度知識を積んでいってみると必ずしも大きい壁に会うようになります。その壁の名前は&lt;b&gt;経験&lt;/b&gt;です。自分の経験が一番大きい限界になります。多くのエンジニアたちが一定水準を外れられない決定的な理由中の一つです。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;自分の経験だけでは解決できない問題に会ったら創造的な方式で状況を解き明かすべきです。優れたプログラミング能力はこんな時に大きい役に立ちます。万一プログラミングが苦手だとしたら問題を解決する速度は遅くなるし正確度も低くなるものです。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;すなわち、初対面の問題を速い速度で解決する最高の道具はプログラミング能力です。単純にプログラミング言語が分かるのかを言うわけではなくてそれを通じて問題を解き明かす能力を言っているのです。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;いつかあなたが今解き明かしたこの問題が全世界で自分が最初に解決した問題となる瞬間が来るはずです。オラクル性能専門家の世界へいらっしゃったのを歓迎いたします!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-6380849297320363495?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/6380849297320363495/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/11/13.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/6380849297320363495'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/6380849297320363495'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/11/13.html' title='オラクル性能に対する短い考え＃13'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-3845169400315513709</id><published>2009-11-12T20:53:00.000-08:00</published><updated>2009-11-12T22:49:49.835-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Physical Reads'/><category scheme='http://www.blogger.com/atom/ns#' term='Logical Reads'/><title type='text'>Physical Reads、Logical Readsを追い掛けよう。</title><content type='html'>安そうに見えるが、難しい問題が多いです。次の例もその代表的な場合です。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;まず次のように空いたテーブルを一つ作ります。動的サンプリングとハードパースによるI/O発生をなくすために統計情報と収集しクエリもあらかじめ一回づつ実行しておきます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;drop table t1 purge;&lt;br /&gt;&lt;br /&gt;create table t1(c1 char(1));&lt;br /&gt;&lt;br /&gt;exec dbms_stats.gather_table_stats(user, 't1');&lt;br /&gt;&lt;br /&gt;select /* no_rows */ * from t1;&lt;br /&gt;select /* 1 rows */ * from t1;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;次の結果を見てください。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;alter system flush buffer_cache;&lt;br /&gt;&lt;br /&gt;select /* no_rows */ * &lt;br /&gt;from&lt;br /&gt; t1&lt;br /&gt;&lt;br /&gt;Rows     Row Source Operation&lt;br /&gt;-------  ---------------------------------------------------&lt;br /&gt;      0  TABLE ACCESS FULL T1 (cr=3 pr=1 pw=0 time=828 us)&lt;br /&gt;      &lt;br /&gt;&lt;br /&gt;insert into t1 values('1');&lt;br /&gt;commit;&lt;br /&gt;&lt;br /&gt;alter system flush buffer_cache;&lt;br /&gt;&lt;br /&gt;select /* 1 rows */ * &lt;br /&gt;from&lt;br /&gt; t1&lt;br /&gt;&lt;br /&gt;Rows     Row Source Operation&lt;br /&gt;-------  ---------------------------------------------------&lt;br /&gt;      1  TABLE ACCESS FULL T1 (cr=21 pr=19 pw=0 time=3303 us)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;空いたテーブルに対して全表走査をする場合にはPhysical Reads=1、Logical Reads=3であります。この値はどこから来るでしょうか。行(Row)が一つのテーブルの場合は全表走査に対してPhysical Reads=19、Logical Reads=21であります。この値はまたどこから来ますか。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;この質問に答えてみたらオラクルでI/Oを追いかける簡単な技法を習うようになります。まずバージョンは次のようです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; @version&lt;br /&gt;&lt;br /&gt;BANNER&lt;br /&gt;----------------------------------------------------------------&lt;br /&gt;Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;TablespaceはASSM(Automatic Segment Space Management)で管理されていてExtentのサイズはUniform Size 10Mであります。テーブルにデータが一件もない場合のSQL*Traceに対するTkprofの結果です。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;select /* no_rows */ * &lt;br /&gt;from&lt;br /&gt; t1&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;call     count       cpu    elapsed       disk      query    current        rows&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;Parse        1      0.00       0.00          0          0          0           0&lt;br /&gt;Execute      1      0.00       0.00          0          0          0           0&lt;br /&gt;Fetch        1      0.00       0.00          1          3          0           0&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;total        3      0.00       0.00          1          3          0           0&lt;br /&gt;&lt;br /&gt;Misses in library cache during parse: 1&lt;br /&gt;Optimizer mode: ALL_ROWS&lt;br /&gt;Parsing user id: 61  &lt;br /&gt;&lt;br /&gt;Rows     Row Source Operation&lt;br /&gt;-------  ---------------------------------------------------&lt;br /&gt;      0  TABLE ACCESS FULL T1 (cr=3 pr=1 pw=0 time=828 us)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;まずPhysical Reads(1)はどこから出た値かを確認してみます。トレース原本ファイルは次のようです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;PARSING IN CURSOR #10 len=30 dep=0 uid=61 oct=3 lid=61 tim=3012155435237 hv=1069717740 ad='2836fe0c'&lt;br /&gt;select /* no_rows */ * from t1&lt;br /&gt;END OF STMT&lt;br /&gt;PARSE #10:c=0,e=877,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=1,tim=3012155435230&lt;br /&gt;EXEC #10:c=0,e=46,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,tim=3012155436544&lt;br /&gt;WAIT #10: nam='SQL*Net message to client' ela= 4 driver id=1413697536 #bytes=1 p3=0 obj#=18 tim=3012155436775&lt;br /&gt;WAIT #10: nam='db file sequential read' ela= 282 file#=7 block#=25630 blocks=1 obj#=55933 tim=3012155437320 &lt;br /&gt;FETCH #10:c=0,e=833,p=1,cr=3,cu=0,mis=0,r=0,dep=0,og=1,tim=3012155437829&lt;br /&gt;WAIT #10: nam='SQL*Net message from client' ela= 1197 driver id=1413697536 #bytes=1 p3=0 obj#=55933 tim=3012155439358&lt;br /&gt;STAT #10 id=1 cnt=0 pid=0 pos=1 obj=55933 op='TABLE ACCESS FULL T1 (cr=3 pr=1 pw=0 time=828 us)'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Db file sequential readイベントを見たらFile#=7、Block#=25630であります。ブロックダンプを通じて確認して見るとSegment Headerであるのが分かります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; @which_obj2 7 25630&lt;br /&gt;old   1: alter system dump datafile &amp;__FILE block &amp;__BLOCK&lt;br /&gt;new   1: alter system dump datafile 7 block 25630&lt;br /&gt;&lt;br /&gt;System altered.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.04&lt;br /&gt;------------------------------------------------&lt;br /&gt;dba =               7/25630&lt;br /&gt;type =              PAGETABLE SEGMENT HEADER&lt;br /&gt;------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;すなわち、Segment Headerを読み込むために一回のPhysical Readsが発生します。データが一件もないのでこれ以上のI/Oは不必要です。Physical Readsが一回しかないと言うのがその証拠です。それにもかかわらずLogical Reads(cr)が3であるのはSegment Headerをもう二度(したがって総三回)読むのを意味します。その理由は文書化されていません。たぶん空間情報を得るために内部的に確認作業をするのではないかと考えています。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;そんな理由で&lt;b&gt;全表走査による最小のLogical Readsは三回&lt;/b&gt;と認識されています。ここで一つ確認したいことは10200イベントです。10200イベントはLogical Readsを追いかける時使用されます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;10200&lt;br /&gt; "consistent read buffer status"&lt;br /&gt;// *Cause:&lt;br /&gt;// *Action:&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;でもこのイベントはデータを読み込む時のみ動作します。すなわち上の例のようにSegment Headerのようなメタブロックを読み込む時には記録されないようです。惜しい制約です。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;一件のデータをInsertした後を観察してみます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;insert into t1 values ('1');&lt;br /&gt;commit;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;SQL*TraceによるTkprofの結果は次のようです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;select /* 1 rows */ * &lt;br /&gt;from&lt;br /&gt; t1&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;call     count       cpu    elapsed       disk      query    current        rows&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;Parse        1      0.00       0.00          0          0          0           0&lt;br /&gt;Execute      1      0.00       0.00          0          0          0           0&lt;br /&gt;Fetch        2      0.00       0.00         19         21          0           1&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;total        4      0.00       0.00         19         21          0           1&lt;br /&gt;&lt;br /&gt;Misses in library cache during parse: 1&lt;br /&gt;Optimizer mode: ALL_ROWS&lt;br /&gt;Parsing user id: 61  &lt;br /&gt;&lt;br /&gt;Rows     Row Source Operation&lt;br /&gt;-------  ---------------------------------------------------&lt;br /&gt;      1  TABLE ACCESS FULL T1 (cr=21 pr=19 pw=0 time=3303 us)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;私がテストしたシステムの場合にはPhysical Reads=19、Logical Reads=21であります。この値はバージョンやSegment Space Management方式、Extentのサイズによって違えます。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;Physical Readsの19の値がどこから来たのかをトレース原本ファイルから確認してみます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;PARSING IN CURSOR #24 len=29 dep=0 uid=61 oct=3 lid=61 tim=3012155797727 hv=2697749229 ad='2825d88c'&lt;br /&gt;select /* 1 rows */ * from t1&lt;br /&gt;END OF STMT&lt;br /&gt;PARSE #24:c=0,e=713,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=1,tim=3012155797723&lt;br /&gt;EXEC #24:c=0,e=49,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,tim=3012155799033&lt;br /&gt;WAIT #24: nam='SQL*Net message to client' ela= 6 driver id=1413697536 #bytes=1 p3=0 obj#=0 tim=3012155799433&lt;br /&gt;WAIT #24: nam='db file sequential read' ela= 254 file#=7 block#=25630 blocks=1 obj#=55933 tim=3012155800000&lt;br /&gt;WAIT #24: nam='db file sequential read' ela= 243 file#=7 block#=25609 blocks=1 obj#=55933 tim=3012155800562&lt;br /&gt;WAIT #24: nam='db file sequential read' ela= 228 file#=7 block#=25629 blocks=1 obj#=55933 tim=3012155801014&lt;br /&gt;WAIT #24: nam='db file scattered read' ela= 1315 file#=7 block#=25641 blocks=16 obj#=55933 tim=3012155802707&lt;br /&gt;FETCH #24:c=0,e=3316,p=19,cr=13,cu=0,mis=0,r=1,dep=0,og=1,tim=3012155803019&lt;br /&gt;WAIT #24: nam='SQL*Net message from client' ela= 298 driver id=1413697536 #bytes=1 p3=0 obj#=55933 tim=3012155803696&lt;br /&gt;FETCH #24:c=0,e=34,p=0,cr=8,cu=0,mis=0,r=0,dep=0,og=1,tim=3012155803938&lt;br /&gt;WAIT #24: nam='SQL*Net message to client' ela= 2 driver id=1413697536 #bytes=1 p3=0 obj#=55933 tim=3012155803998&lt;br /&gt;WAIT #24: nam='SQL*Net message from client' ela= 2449 driver id=1413697536 #bytes=1 p3=0 obj#=55933 tim=3012155806478&lt;br /&gt;STAT #24 id=1 cnt=1 pid=0 pos=1 obj=55933 op='TABLE ACCESS FULL T1 (cr=21 pr=19 pw=0 time=3303 us)'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;三回のSingle Block I/O(Db file sequential read)と一回のMulti Block I/O(Db file scatteread read、Blocks=16)を通じて総19ブロックをPhysical I/Oで読み込みました。Single Block I/Oがどんなブロックを読み込んだのを確認してみたら次のようです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; @which_obj2 7 25630&lt;br /&gt;UKJA@ukja1021&gt; @which_obj2 7 25609&lt;br /&gt;UKJA@ukja1021&gt; @which_obj2 7 25629&lt;br /&gt;&lt;br /&gt;------------------------------------------------&lt;br /&gt;dba =               7/25630&lt;br /&gt;type =              PAGETABLE SEGMENT HEADER&lt;br /&gt;------------------------------------------------&lt;br /&gt;dba =               7/25609&lt;br /&gt;type =              FIRST LEVEL BITMAP BLOCK&lt;br /&gt;object_id =         55933&lt;br /&gt;object_name =       T1&lt;br /&gt;------------------------------------------------&lt;br /&gt;dba =               7/25629&lt;br /&gt;type =              SECOND LEVEL BITMAP BLOCK&lt;br /&gt;object_id =         55933&lt;br /&gt;object_name =       T1&lt;br /&gt;------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Segment HeaderとともにASSM(Automatic Segment Space Management)で使用されるFirst Level BMB、Second Level BMBを読み込んだのが分かります。（Third Level BMBはまだ生成されなかったので読み込まなかったんです）&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;それでは16個のPhysical Readsはどこから来たのでしょうか。スペース情報を見ると次のようです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; exec show_space('T1');&lt;br /&gt;Free Blocks.............................&lt;br /&gt;Total Blocks............................1280&lt;br /&gt;Total Bytes.............................10485760&lt;br /&gt;Total MBytes............................10&lt;br /&gt;Unused Blocks...........................1216&lt;br /&gt;Unused Bytes............................9961472&lt;br /&gt;Last Used Ext FileId....................7&lt;br /&gt;Last Used Ext BlockId...................25609&lt;br /&gt;Last Used Block.........................64&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;High Water Markが64番目のブロックまでを指差しています。したがってPhysical Readsは16ではなくて61(64－3)になるべきです。全表走査と言うのはデータブロックの初めてからHigh Water MarkまでをSequentialに読み込むのではありませんか。しかしオラクルはただ16個のブロックだけをPhysical Readsで読み込みました。これを説明しようとすればブロックの分布を管理するFirst Level BMBブロックの内容を見る必要があります。First Level BMB(file#=7, block#=25609)の内容は次のようです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;  --------------------------------------------------------&lt;br /&gt;  DBA Ranges :&lt;br /&gt;  --------------------------------------------------------&lt;br /&gt;   0x01c06409  Length: 64     Offset: 0      &lt;br /&gt;  &lt;br /&gt;   0:Metadata   1:Metadata   2:Metadata   3:Metadata&lt;br /&gt;   4:Metadata   5:Metadata   6:Metadata   7:Metadata&lt;br /&gt;   8:Metadata   9:Metadata   10:Metadata   11:Metadata&lt;br /&gt;   12:Metadata   13:Metadata   14:Metadata   15:Metadata&lt;br /&gt;   16:Metadata   17:Metadata   18:Metadata   19:Metadata&lt;br /&gt;   20:Metadata   21:Metadata   22:unformatted   23:unformatted&lt;br /&gt;   24:unformatted   25:unformatted   26:unformatted   27:unformatted&lt;br /&gt;   28:unformatted   29:unformatted   30:unformatted   31:unformatted&lt;br /&gt;   32:75-100% free   33:75-100% free   34:75-100% free   35:75-100% free&lt;br /&gt;   36:75-100% free   37:75-100% free   38:75-100% free   39:75-100% free&lt;br /&gt;   40:75-100% free   41:75-100% free   42:75-100% free   43:75-100% free&lt;br /&gt;   44:75-100% free   45:75-100% free   46:75-100% free   47:75-100% free&lt;br /&gt;   48:unformatted   49:unformatted   50:unformatted   51:unformatted&lt;br /&gt;   52:unformatted   53:unformatted   54:unformatted   55:unformatted&lt;br /&gt;   56:unformatted   57:unformatted   58:unformatted   59:unformatted&lt;br /&gt;   60:unformatted   61:unformatted   62:unformatted   63:unformatted&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;上の情報をみると総16個のブロック(32－47)のみが実際に使われていて残りのブロックは使わないUnformattedと言うのが分かります。オラクルはこの情報を利用して16個のブロックのみを一回のMulti Block I/Oで読み込んだのであります。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;Physical Readsが19でここで上で見たように追加的なLogical Readsが二回発生するのでLogical Readsは21だと報告されているのです。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;今頃ならなぜ安いながらも難しい質問だと言ったのか分かるんでしょう。意味のないテストのようですが(Just for Fun?)、その過程で得られる経験と知識は相当なものです。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;テストで使われたスクリプトは次のURLを参照してください。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://sites.google.com/site/ukja/sql-scripts-1/s-z/show-space"&gt;show_space&lt;/a&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://sites.google.com/site/ukja/sql-scripts-1/s-z/which_obj2"&gt;which_obj2&lt;/a&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-3845169400315513709?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/3845169400315513709/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/11/physical-readslogical-reads.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/3845169400315513709'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/3845169400315513709'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/11/physical-readslogical-reads.html' title='Physical Reads、Logical Readsを追い掛けよう。'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-968050234567324489</id><published>2009-11-10T20:47:00.000-08:00</published><updated>2009-11-10T21:05:37.602-08:00</updated><title type='text'>オラクル性能に対する短い考え＃11</title><content type='html'>&lt;font size="+2"&gt;性能問題に対する正確な理解は用語に対する正確な理解から来る。&lt;br /&gt;&lt;/font&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;人間の考える能力が言語を作ったが、逆に言語が言語が人間の考えを支配するようになるでしょう。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;オラクル性能世界でも同じです。用語に対する正確な理解がなければ問題を100％理解できないばかりでなく間違った知識を信じるようになってしまいます。例を挙げてみましょうか。&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Explain PlanとExecution Planの違いが説明できますか。&lt;br /&gt;&lt;li&gt;ScanとLookupの差異は何ですか。&lt;br /&gt;&lt;li&gt;Histogramの意味は何ですか。&lt;br /&gt;&lt;li&gt;LockとEnqueueの意味が分けられますか。&lt;br /&gt;&lt;li&gt;SQLとCursorの違いが説明できますか。&lt;br /&gt;&lt;li&gt;Eventと言う用語はいつ使われるのか説明できますか。&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;用語が100％正確に説明できたらその自体で性能問題に対する相当な洞察力を持っていると言えます。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-968050234567324489?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/968050234567324489/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/11/11_10.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/968050234567324489'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/968050234567324489'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/11/11_10.html' title='オラクル性能に対する短い考え＃11'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-2669628313137326423</id><published>2009-11-09T21:02:00.000-08:00</published><updated>2009-11-09T21:43:33.381-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PL/SQL'/><title type='text'>PL/SQLでのLoggingの使用</title><content type='html'>Programming Langaugeと言うのが開発された以降発明された最高のデバッグツールはなんでしょうか。精巧で強力なデバッグツールがたくさんありますがなお一番強力で一番よく使われるデバッグツールは&lt;b&gt;開発者が自分で記録するログ(Log)&lt;/b&gt;であります。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;しかしPL/SQLは相対的にログを残す機能がとても弱いです。この問題を話し合ってみみます。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;一番よく使用されるのが&lt;b&gt;DBMS_OUTPUT&lt;/b&gt;パッケージでしょう。でも、PL/SQLブロックの実行が終わった後で記録されるので実行時間が長いPL/SQLブロックをデバッグする場合は相当不便です。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;set serveroutput on&lt;br /&gt;&lt;br /&gt;begin&lt;br /&gt; for idx in 1 .. 10 loop&lt;br /&gt;  dbms_output.put_line(sysdate || ', ' || idx || 'th insertion');&lt;br /&gt;  dbms_lock.sleep(1);&lt;br /&gt; end loop;&lt;br /&gt;end;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;2009/11/10 13:33:18, 1th insertion&lt;br /&gt;2009/11/10 13:33:19, 2th insertion&lt;br /&gt;2009/11/10 13:33:20, 3th insertion&lt;br /&gt;2009/11/10 13:33:21, 4th insertion&lt;br /&gt;2009/11/10 13:33:22, 5th insertion&lt;br /&gt;2009/11/10 13:33:23, 6th insertion&lt;br /&gt;2009/11/10 13:33:24, 7th insertion&lt;br /&gt;2009/11/10 13:33:25, 8th insertion&lt;br /&gt;2009/11/10 13:33:26, 9th insertion&lt;br /&gt;2009/11/10 13:33:27, 10th insertion&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:10.00&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;他のトリックは次のように&lt;b&gt;DBMS_SYSTEM&lt;/b&gt;パッケージを用いてトレースファイル(1)やAlert Logファイル(2)、または両方(3)に記録することです。TAILとかテキストエディターでファイルを開けてみればいいでしょう。&lt;b&gt;UTL_FILE&lt;/b&gt;パッケージを利用して別途のファイルに記録して確認することもできます。この方法の一つデメリットはログファイルがサーバーにあると言うことです。ネットワーク接続や保安の問題によって使用しにくい場合が多いです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;begin&lt;br /&gt; for idx in 1 .. 10 loop&lt;br /&gt;  sys.dbms_system.ksdwrt(1, sysdate || ', ' || idx || 'th insertion');&lt;br /&gt;  dbms_lock.sleep(1);&lt;br /&gt; end loop;&lt;br /&gt;end;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;-- C:\ORACLE\ADMIN\UKJA1021\UDUMP\ukja1021_ora_1448.trc&lt;br /&gt;*** 2009-11-10 13:37:15.015&lt;br /&gt;*** ACTION NAME:() 2009-11-10 13:37:15.000&lt;br /&gt;*** MODULE NAME:(SQL*Plus) 2009-11-10 13:37:15.000&lt;br /&gt;*** SERVICE NAME:(UKJA1021) 2009-11-10 13:37:15.000&lt;br /&gt;*** SESSION ID:(140.1169) 2009-11-10 13:37:15.000&lt;br /&gt;2009/11/10 13:37:15, 1th insertion&lt;br /&gt;2009/11/10 13:37:16, 2th insertion&lt;br /&gt;2009/11/10 13:37:17, 3th insertion&lt;br /&gt;2009/11/10 13:37:18, 4th insertion&lt;br /&gt;2009/11/10 13:37:19, 5th insertion&lt;br /&gt;2009/11/10 13:37:20, 6th insertion&lt;br /&gt;2009/11/10 13:37:21, 7th insertion&lt;br /&gt;2009/11/10 13:37:22, 8th insertion&lt;br /&gt;2009/11/10 13:37:23, 9th insertion&lt;br /&gt;2009/11/10 13:37:24, 10th insertion&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;プログラミングに興味があったらもっと良い方法が作れます。次の例を見てください。&lt;b&gt;Pipelined Table Function&lt;/b&gt;と&lt;b&gt;DBMS_PIPE&lt;/b&gt;パッケージを利用しています。特定セッションでログを記録すれば他のセッションではSELECT文を利用してログの内容を確認できます。上で見た二つの方法をみんな乗り越えています。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;create or replace package pkg_log&lt;br /&gt;as &lt;br /&gt;  type log_array is table of varchar2(4000);&lt;br /&gt; procedure log(message in varchar2);&lt;br /&gt; procedure flush;&lt;br /&gt; function get_log return log_array pipelined;&lt;br /&gt;end;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;show err&lt;br /&gt;&lt;br /&gt;create or replace package body pkg_log&lt;br /&gt;as&lt;br /&gt; procedure log(message in varchar2) is&lt;br /&gt;  v_status number;&lt;br /&gt; begin&lt;br /&gt;  dbms_pipe.pack_message(sysdate || ', ' || message);&lt;br /&gt;    v_status := dbms_pipe.send_message('log');&lt;br /&gt; end log;&lt;br /&gt; &lt;br /&gt; procedure flush is&lt;br /&gt;  v_status number;&lt;br /&gt; begin&lt;br /&gt;  dbms_pipe.pack_message('$$END$$');&lt;br /&gt;  v_status := dbms_pipe.send_message('log');&lt;br /&gt; end;&lt;br /&gt; &lt;br /&gt; function get_log return log_array pipelined is&lt;br /&gt;  v_status  number;&lt;br /&gt;  v_message varchar2(4000);&lt;br /&gt; begin&lt;br /&gt;  while true loop&lt;br /&gt;   v_status := dbms_pipe.receive_message('log');&lt;br /&gt;     if v_status = 0 then&lt;br /&gt;       dbms_pipe.unpack_message(v_message);&lt;br /&gt;       if v_message = '$$END$$' then&lt;br /&gt;        return;&lt;br /&gt;       end if;&lt;br /&gt;       pipe row(v_message);&lt;br /&gt;       pipe row('');&lt;br /&gt;     end if;&lt;br /&gt;  end loop;&lt;br /&gt;  return;&lt;br /&gt; end get_log;&lt;br /&gt;end;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;show err&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;使用例は次のようです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;-- session #1&lt;br /&gt;begin&lt;br /&gt; for idx in 1 .. 10 loop&lt;br /&gt;  pkg_log.log(idx || 'th insertion');&lt;br /&gt;  dbms_lock.sleep(1);&lt;br /&gt; end loop;&lt;br /&gt; pkg_log.flush;&lt;br /&gt;end;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;-- session #2&lt;br /&gt;set array 2&lt;br /&gt;set pages 100&lt;br /&gt;select * from table(pkg_log.get_log);&lt;br /&gt;&lt;br /&gt;COLUMN_VALUE&lt;br /&gt;---------------------------------------&lt;br /&gt;&lt;br /&gt;2009/11/10 13:30:12, 1th insertion&lt;br /&gt;&lt;br /&gt;2009/11/10 13:30:13, 2th insertion&lt;br /&gt;&lt;br /&gt;2009/11/10 13:30:14, 3th insertion&lt;br /&gt;&lt;br /&gt;2009/11/10 13:30:15, 4th insertion&lt;br /&gt;&lt;br /&gt;2009/11/10 13:30:16, 5th insertion&lt;br /&gt;&lt;br /&gt;2009/11/10 13:30:17, 6th insertion&lt;br /&gt;&lt;br /&gt;2009/11/10 13:30:18, 7th insertion&lt;br /&gt;&lt;br /&gt;2009/11/10 13:30:19, 8th insertion&lt;br /&gt;&lt;br /&gt;2009/11/10 13:30:20, 9th insertion&lt;br /&gt;&lt;br /&gt;2009/11/10 13:30:21, 10th insertion&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;考え方に沿って無数な方法でログを管理することができます。重要なものは自分が必要なツールはたまに自分で作ることができれば良いと言うことです。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-2669628313137326423?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/2669628313137326423/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/11/plsqllogging.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/2669628313137326423'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/2669628313137326423'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/11/plsqllogging.html' title='PL/SQLでのLoggingの使用'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-1886745195547181553</id><published>2009-11-08T22:27:00.000-08:00</published><updated>2009-11-08T23:01:56.391-08:00</updated><title type='text'>オラクル性能に対する短い考え＃11</title><content type='html'>&lt;font size="+2"&gt;人間は失敗から習うようにオラクル性能問題はバーグから習う&lt;/font&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;オラクル性能に対して最高のエンジニアはオラクル出身のエンジニアである場合がたくさんあります。なぜそうでしょうか。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;オラクル内で他の人は得られない高級な情報をたくさん接したことでしょうか。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;そうかも知れませんが、一番大きい理由はオラクルの障害およびバーグを接してそれを解決した経験が多いからです。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;人間は失敗から習って、オラクルエンジニアは障害やバーグからオラクルを習うことです。障害に会ったら喜んで解決しようとすべき理由です。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-1886745195547181553?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/1886745195547181553/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/11/11.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/1886745195547181553'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/1886745195547181553'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/11/11.html' title='オラクル性能に対する短い考え＃11'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-6147635331848870308</id><published>2009-11-07T19:04:00.000-08:00</published><updated>2009-11-07T19:32:09.787-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='TKPROF'/><category scheme='http://www.blogger.com/atom/ns#' term='fetch'/><category scheme='http://www.blogger.com/atom/ns#' term='execute'/><category scheme='http://www.blogger.com/atom/ns#' term='parse'/><title type='text'>SELECT文－Parse, Execute, Fetchどの段階？</title><content type='html'>大部分のSELECT文はFetch段階で多くの時間とI/Oを使うようになります。例として、次のTkprof結果を見てください。&lt;br /&gt;&lt;pre class="brush: sql; highlight:[5]; "&gt;&lt;br /&gt;call     count       cpu    elapsed       disk      query    current        rows&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;Parse        1      0.01       0.03          0          0          0           0&lt;br /&gt;Execute      1      0.00       0.00          0          0          0           0&lt;br /&gt;Fetch        2      0.15       0.26        172        161          0           1&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;total        4      0.17       0.29        172        161          0           1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;とても一般的な結果でしょう。SQL文は次のようです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;select max(t1.c1), max(t2.c2) from t1, t2&lt;br /&gt;where rownum &lt;= 100000&lt;br /&gt;order by t2.c2, t1.c1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;今度は次の結果を見てください。いかがですか。なぜFetch段階じゃなくてParse段階で大部分の時間を使っていますか。&lt;br /&gt;&lt;pre class="brush: sql; highlight:[3];"&gt;&lt;br /&gt;call     count       cpu    elapsed       disk      query    current        rows&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;Parse        1    300.10     353.29         90       7060          0           0&lt;br /&gt;Execute      1      0.00       0.00          0          0          0           0&lt;br /&gt;Fetch        2      0.00       0.00          0          0          0           1&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;total        4    300.10     353.29         90       7060          0           1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;正答は&lt;b&gt;ハードパース&lt;/b&gt;です。ハードパースのために発生するOptimization過程はParse段階に属するのでTkprof結果でもParse段階に記録されます。SQL文は次のようです(500個のテーブルをジョインするように動的に生成されたSQL文です）。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;select count(*) from T_HEAP_DUMP, T_ALERT_ANALYZE, T_MON_TEMP, T_MON_TIME_TEMP,&lt;br /&gt;T_MON_LATCH_TEMP, T_MON_EVENT_TEMP, T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP,&lt;br /&gt;CHAINED_ROWS, TT1, TT2, T1, T_SMALL, EXT_ORAUS, T_SMALL_WITH_SMALL_ROW,&lt;br /&gt;T_TARGET2, T_ORAUS, T_TARGET1, T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG,&lt;br /&gt;T_SMALL_LOB, T_BIG_LOB, T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP,&lt;br /&gt;T_ALERT_ANALYZE, T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP,&lt;br /&gt;T_MON_EVENT_TEMP, T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2,&lt;br /&gt;T1, T_SMALL, EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP,  dual where 1 = 0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;次はどうですか。なぜ今回はParse段階でもFetch段階でもなくてExecute段階で大部分の時間を使っていますか。&lt;br /&gt;&lt;pre class="brush: sql; highlight:[4]"&gt;&lt;br /&gt;call     count       cpu    elapsed       disk      query    current        rows&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;Parse        1      0.00       0.00          0          0          0           0&lt;br /&gt;Execute      1    322.45     368.33          3       6725          0           0&lt;br /&gt;Fetch        2      0.00       0.01          0          0          0           1&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;total        4    322.45     368.34          3       6725          0           1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;正答は&lt;b&gt;Bind Peeking&lt;/b&gt;です。Bind Peekingが使われるとParse段階ではなくてExecute段階でOptimizationが行われます。従ってTkprof結果でもExecute段階に記録されます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;select count(*) from T_HEAP_DUMP, T_ALERT_ANALYZE, T_MON_TEMP, T_MON_TIME_TEMP,&lt;br /&gt;T_MON_LATCH_TEMP, T_MON_EVENT_TEMP, T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP,&lt;br /&gt;CHAINED_ROWS, TT1, TT2, T1, T_SMALL, EXT_ORAUS, T_SMALL_WITH_SMALL_ROW,&lt;br /&gt;T_TARGET2, T_ORAUS, T_TARGET1, T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG,&lt;br /&gt;T_SMALL_LOB, T_BIG_LOB, T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP,&lt;br /&gt;T_ALERT_ANALYZE, T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP,&lt;br /&gt;T_MON_EVENT_TEMP, T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2,&lt;br /&gt;T1, T_SMALL, EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP, T_MON_MUTEX_TEMP, CHAINED_ROWS, TT1, TT2, T1, T_SMALL,&lt;br /&gt;EXT_ORAUS, T_SMALL_WITH_SMALL_ROW, T_TARGET2, T_ORAUS, T_TARGET1,&lt;br /&gt;T_SMALL_WITH_BIG_ROW, T_SMALL_WITH_LOB, T_NO_LOG, T_SMALL_LOB, T_BIG_LOB,&lt;br /&gt;T_NOLOB, T3, T_LOB, T_NO_LOB, T_TARGET, T2, T_HEAP_DUMP, T_ALERT_ANALYZE,&lt;br /&gt;T_MON_TEMP, T_MON_TIME_TEMP, T_MON_LATCH_TEMP, T_MON_EVENT_TEMP,&lt;br /&gt;T_MON_ROWCACHE_TEMP,  dual where rownum = :b1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Tkprofの結果を解析する時はやっぱりいろいろなところに気にしなければならないと言う良い例の一つです。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-6147635331848870308?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/6147635331848870308/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/11/selectparse-execute-fetch.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/6147635331848870308'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/6147635331848870308'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/11/selectparse-execute-fetch.html' title='SELECT文－Parse, Execute, Fetchどの段階？'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-7044262451520708492</id><published>2009-11-03T22:22:00.000-08:00</published><updated>2009-11-08T22:28:42.819-08:00</updated><title type='text'>オラクル性能に対する短い考え＃10</title><content type='html'>&lt;font size="+2"&gt;パーティションはOLTPとはあまり関係ない&lt;/font&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;パーティションは大容量のデータを扱うDWシステムのため設計されました。OLTPシステムでのメリットはほとんどないと思います。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;単純に行数が多いという理由でパーティションの使用を考慮していたら考え直すほうが良いです。目的に会わないパーティションの使用はかえって管理の難しさと以外の性能問題を起こす可能性があります。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;もちろんOLTPとDWの中間どこかにあるハイブリッドシステムが多いので現実はこんなに単純ではありませんけど…&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-7044262451520708492?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/7044262451520708492/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/11/10.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/7044262451520708492'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/7044262451520708492'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/11/10.html' title='オラクル性能に対する短い考え＃10'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-66660404743479673</id><published>2009-11-02T22:25:00.000-08:00</published><updated>2009-11-02T23:35:46.789-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Native Full Outer Hash Join、ROWNUM'/><title type='text'>Native Full Outer Hash JoinとROWNUM</title><content type='html'>オラクル11gはNative Full Outer Hash Joinを支援しています。&lt;a href="http://oracle-randolf.blogspot.com/2008/03/native-hash-full-outer-join-in-oracle.html"&gt;ここに&lt;/a&gt;よく説明されています。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;ここで一つの疑問が生じます。Full Outer Joinを使いながらPagination QueryのためにROWNUM条件を使用すればオラクルはどんな実行計画を作るでしょうか。Native Full Outer JoinはHash Joinでだけ支援されるがPagination QueryはNested Loops Joinともっと似合います。次に簡単なテスト結果があります。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;まず次のように二つのテーブルを作ります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;create table t1 as &lt;br /&gt;select level as c1, rpad('x',100) as c2&lt;br /&gt;from dual&lt;br /&gt;connect by level &lt;= 100000&lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;create table t2 as &lt;br /&gt;select level as c1, rpad('x',100) as c2&lt;br /&gt;from dual&lt;br /&gt;connect by level &lt;= 100000&lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;alter table t1 modify c1 not null;&lt;br /&gt;alter table t2 modify c2 not null;&lt;br /&gt;&lt;br /&gt;create index t1_n1 on t1(c1);&lt;br /&gt;create index t2_n1 on t2(c1);&lt;br /&gt;&lt;br /&gt;exec dbms_stats.gather_table_stats(user, 't1', no_invalidate=&gt;false);&lt;br /&gt;exec dbms_stats.gather_table_stats(user, 't2', no_invalidate=&gt;false);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Full Outer Joinではない一般Outer Joinの場合はNested Loops Joinが選択されPaginationの目的によく合います。性能も優れるはずです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;explain plan for&lt;br /&gt;select * from (&lt;br /&gt; select rownum as r, x.* from (&lt;br /&gt;  select *&lt;br /&gt;  from t1 left join t2 on t1.c1 = t2.c1&lt;br /&gt;  ) x where rownum &lt;= 10&lt;br /&gt;) where r &gt;= 1&lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;-----------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                      | Name  | Rows  | Bytes | Cost (%CPU)|&lt;br /&gt;-----------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT               |       |    10 |  1430 |    24   (0)|&lt;br /&gt;|*  1 |  VIEW                          |       |    10 |  1430 |    24   (0)|&lt;br /&gt;|*  2 |   COUNT STOPKEY                |       |       |       |            |&lt;br /&gt;|   3 |    NESTED LOOPS OUTER          |       |    11 |  2310 |    24   (0)|&lt;br /&gt;|   4 |     TABLE ACCESS FULL          | T1    |    11 |  1155 |     2   (0)|&lt;br /&gt;|   5 |     TABLE ACCESS BY INDEX ROWID| T2    |     1 |   105 |     2   (0)|&lt;br /&gt;|*  6 |      INDEX RANGE SCAN          | T2_N1 |     1 |       |     1   (0)|&lt;br /&gt;-----------------------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;でもFull Outer Joinを使えばNative Full Outer Hash Joinが使用されます。Paginationの目的にはぜんぜん合いません。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;explain plan for&lt;br /&gt;select * from (&lt;br /&gt; select rownum as r, x.* from (&lt;br /&gt;  select *&lt;br /&gt;  from t1 full join t2 on t1.c1 = t2.c1&lt;br /&gt;  ) x where rownum &lt;= 10&lt;br /&gt;) where r &gt;= 1&lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation               | Name     | Rows  | Bytes |TempSpc| Cost (%CPU)&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT        |          |    10 |  1430 |       |  2302   (1)&lt;br /&gt;|*  1 |  VIEW                   |          |    10 |  1430 |       |  2302   (1)&lt;br /&gt;|*  2 |   COUNT STOPKEY         |          |       |       |       |            &lt;br /&gt;|   3 |    VIEW                 | VW_FOJ_0 |   100K|    12M|       |  2302   (1)&lt;br /&gt;|*  4 |     HASH JOIN FULL OUTER|          |   100K|    20M|    11M|  2302   (1)&lt;br /&gt;|   5 |      TABLE ACCESS FULL  | T1       |   100K|    10M|       |   596   (1)&lt;br /&gt;|   6 |      TABLE ACCESS FULL  | T2       |   100K|    10M|       |   596   (1)&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Native Full Outer Joinは次のパラメータで制御されます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1106&gt; @para outer_join&lt;br /&gt;NAME                           VALUE                IS_DEFAUL SES_MODIFI&lt;br /&gt;------------------------------ -------------------- --------- ----------&lt;br /&gt;SYS_MODIFI&lt;br /&gt;----------&lt;br /&gt;DESCRIPTION&lt;br /&gt;------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;_optimizer_native_full_outer_j FORCE                TRUE      true&lt;br /&gt;oin&lt;br /&gt;immediate&lt;br /&gt;execute full outer join using native implementaion&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;このパラメータをオフさせたらNative Full Outer JoinではなくてUNION ALLを利用したFull Outer Join(すなわち10gの方式)に変わるだけ、Paginationに適当なNested Loops Joinに変わることではないんです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;explain plan for&lt;br /&gt;select * from (&lt;br /&gt; select rownum as r, x.* from (&lt;br /&gt;  select /*+ opt_param('_optimizer_native_full_outer_join', 'off') */ *&lt;br /&gt;  from t1 full join t2 on t1.c1 = t2.c1&lt;br /&gt; ) x where rownum &lt;= 10&lt;br /&gt;) where r &gt;= 1&lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                 | Name  | Rows  | Bytes |TempSpc| Cost (%CPU)|&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT          |       |    10 |  1430 |       |  3620   (1)|&lt;br /&gt;|*  1 |  VIEW                     |       |    10 |  1430 |       |  3620   (1)|&lt;br /&gt;|*  2 |   COUNT STOPKEY           |       |       |       |       |            |&lt;br /&gt;|   3 |    VIEW                   |       |   199K|    24M|       |  3620   (1)|&lt;br /&gt;|   4 |     UNION-ALL             |       |       |       |       |            |&lt;br /&gt;|*  5 |      HASH JOIN OUTER      |       |   100K|    20M|    11M|  2302   (1)|&lt;br /&gt;|   6 |       TABLE ACCESS FULL   | T1    |   100K|    10M|       |   596   (1)|&lt;br /&gt;|   7 |       TABLE ACCESS FULL   | T2    |   100K|    10M|       |   596   (1)|&lt;br /&gt;|*  8 |      HASH JOIN RIGHT ANTI |       | 99999 |    10M|  1664K|  1318   (1)|&lt;br /&gt;|   9 |       INDEX FAST FULL SCAN| T1_N1 |   100K|   488K|       |    86   (2)|&lt;br /&gt;|  10 |       TABLE ACCESS FULL   | T2    |   100K|    10M|       |   596   (1)|&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;無理にFIRST_ROWS(10)ヒントを与えるとようやく欲した形態の実行計画が現れます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;explain plan for&lt;br /&gt;select * from (&lt;br /&gt; select rownum as r, x.* from (&lt;br /&gt;  select /*+ first_rows(10) &lt;br /&gt;         opt_param('_optimizer_native_full_outer_join', 'off') */ *&lt;br /&gt;  from t1 full join t2 on t1.c1 = t2.c1&lt;br /&gt; ) x where rownum &lt;= 10&lt;br /&gt;) where r &gt;= 1&lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;-------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                        | Name  | Rows  | Bytes | Cost (%CPU)|&lt;br /&gt;-------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT                 |       |    10 |  1430 |    37   (0)|&lt;br /&gt;|*  1 |  VIEW                            |       |    10 |  1430 |    37   (0)|&lt;br /&gt;|*  2 |   COUNT STOPKEY                  |       |       |       |            |&lt;br /&gt;|   3 |    VIEW                          |       |    22 |  2860 |    37   (0)|&lt;br /&gt;|   4 |     UNION-ALL                    |       |       |       |            |&lt;br /&gt;|   5 |      NESTED LOOPS OUTER          |       |    11 |  2310 |    24   (0)|&lt;br /&gt;|   6 |       TABLE ACCESS FULL          | T1    |    11 |  1155 |     2   (0)|&lt;br /&gt;|   7 |       TABLE ACCESS BY INDEX ROWID| T2    |     1 |   105 |     2   (0)|&lt;br /&gt;|*  8 |        INDEX RANGE SCAN          | T2_N1 |     1 |       |     1   (0)|&lt;br /&gt;|   9 |      NESTED LOOPS ANTI           |       |    11 |  1210 |    13   (0)|&lt;br /&gt;|  10 |       TABLE ACCESS FULL          | T2    |    11 |  1155 |     2   (0)|&lt;br /&gt;|* 11 |       INDEX RANGE SCAN           | T1_N1 |     1 |     5 |     1   (0)|&lt;br /&gt;-------------------------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;これはちょっと失望名結果です。なぜならROWNUM条件を使えばオラクルは内部的にFIRST ROWS技法を真似するオプションを持っているからです。でも願っただけきちんと動作しませんでした。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1106&gt; @para rownum_pred&lt;br /&gt;NAME                           VALUE                IS_DEFAUL SES_MODIFI&lt;br /&gt;------------------------------ -------------------- --------- ----------&lt;br /&gt;SYS_MODIFI&lt;br /&gt;----------&lt;br /&gt;DESCRIPTION&lt;br /&gt;------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;_optimizer_rownum_pred_based_f TRUE                 TRUE      true&lt;br /&gt;kr&lt;br /&gt;immediate&lt;br /&gt;enable the use of first K rows due to rownum predicate&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;次のようにヒントを使用してNative Full Outer Hash Joinを制御できます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1106&gt; @hint native&lt;br /&gt;&lt;br /&gt;NAME                           INVERSE                        VERSION&lt;br /&gt;------------------------------ ------------------------------ ----------&lt;br /&gt;NATIVE_FULL_OUTER_JOIN         NO_NATIVE_FULL_OUTER_JOIN      10.2.0.3&lt;br /&gt;NO_NATIVE_FULL_OUTER_JOIN      NATIVE_FULL_OUTER_JOIN         10.2.0.3&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;次のように使用します。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;explain plan for&lt;br /&gt;select * from (&lt;br /&gt; select rownum as r, x.* from (&lt;br /&gt;  select /*+ first_rows(10) no_native_full_outer_join */ *&lt;br /&gt;  from t1 full join t2 on t1.c1 = t2.c1&lt;br /&gt;  ) x where rownum &lt;= 10&lt;br /&gt;) where r &gt;= 1&lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;-------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                        | Name  | Rows  | Bytes | Cost (%CPU)|&lt;br /&gt;-------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT                 |       |    10 |  1430 |    37   (0)|&lt;br /&gt;|*  1 |  VIEW                            |       |    10 |  1430 |    37   (0)|&lt;br /&gt;|*  2 |   COUNT STOPKEY                  |       |       |       |            |&lt;br /&gt;|   3 |    VIEW                          |       |    22 |  2860 |    37   (0)|&lt;br /&gt;|   4 |     UNION-ALL                    |       |       |       |            |&lt;br /&gt;|   5 |      NESTED LOOPS OUTER          |       |    11 |  2310 |    24   (0)|&lt;br /&gt;|   6 |       TABLE ACCESS FULL          | T1    |    11 |  1155 |     2   (0)|&lt;br /&gt;|   7 |       TABLE ACCESS BY INDEX ROWID| T2    |     1 |   105 |     2   (0)|&lt;br /&gt;|*  8 |        INDEX RANGE SCAN          | T2_N1 |     1 |       |     1   (0)|&lt;br /&gt;|   9 |      NESTED LOOPS ANTI           |       |    11 |  1210 |    13   (0)|&lt;br /&gt;|  10 |       TABLE ACCESS FULL          | T2    |    11 |  1155 |     2   (0)|&lt;br /&gt;|* 11 |       INDEX RANGE SCAN           | T1_N1 |     1 |     5 |     1   (0)|&lt;br /&gt;-------------------------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;伝統的なUSE_NLヒントも当然動作します。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;explain plan for&lt;br /&gt;select * from (&lt;br /&gt; select rownum as r, x.* from (&lt;br /&gt;  select /*+ use_nl(t1) use_nl(t2) */ *&lt;br /&gt;  from t1 full join t2 on t1.c1 = t2.c1&lt;br /&gt;  ) x where rownum &lt;= 10&lt;br /&gt;) where r &gt;= 1&lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;--------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                        | Name  | Rows  | Bytes |TempSpc|&lt;br /&gt;--------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT                 |       |    10 |  1430 |       |&lt;br /&gt;|*  1 |  VIEW                            |       |    10 |  1430 |       |&lt;br /&gt;|*  2 |   COUNT STOPKEY                  |       |       |       |       |&lt;br /&gt;|   3 |    VIEW                          |       |   199K|    24M|       |&lt;br /&gt;|   4 |     UNION-ALL                    |       |       |       |       |&lt;br /&gt;|   5 |      NESTED LOOPS OUTER          |       |   100K|    20M|       |&lt;br /&gt;|   6 |       TABLE ACCESS FULL          | T1    |   100K|    10M|       |&lt;br /&gt;|   7 |       TABLE ACCESS BY INDEX ROWID| T2    |     1 |   105 |       |&lt;br /&gt;|*  8 |        INDEX RANGE SCAN          | T2_N1 |     1 |       |       |&lt;br /&gt;|*  9 |      HASH JOIN RIGHT ANTI        |       | 99999 |    10M|  1664K|&lt;br /&gt;|  10 |       INDEX FAST FULL SCAN       | T1_N1 |   100K|   488K|       |&lt;br /&gt;|  11 |       TABLE ACCESS FULL          | T2    |   100K|    10M|       |&lt;br /&gt;--------------------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Nested Loops Joinが選択された場合とそうではない場合はPagination Queryで相当な性能の違いが存在します。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;select /*+ gather_plan_statistics */ * from (&lt;br /&gt; select rownum as r, x.* from (&lt;br /&gt;  select *&lt;br /&gt;  from t1 full join t2 on t1.c1 = t2.c1&lt;br /&gt;  ) x where rownum &lt;= 10&lt;br /&gt;) where r &gt;= 1&lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation               | Name     | Starts | A-Rows | Buffers | Reads |&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;|*  1 |  VIEW                   |          |      1 |     10 |    1547 |    682|&lt;br /&gt;|*  2 |   COUNT STOPKEY         |          |      1 |     10 |    1547 |    682|&lt;br /&gt;|   3 |    VIEW                 | VW_FOJ_0 |      1 |     10 |    1547 |    682|&lt;br /&gt;|*  4 |     HASH JOIN FULL OUTER|          |      1 |     10 |    1547 |    682|&lt;br /&gt;|   5 |      TABLE ACCESS FULL  | T1       |      1 |    100K|    1542 |      0|&lt;br /&gt;|   6 |      TABLE ACCESS FULL  | T2       |      1 |     16 |       5 |      0|&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;select /*+ gather_plan_statistics */ * from (&lt;br /&gt; select rownum as r, x.* from (&lt;br /&gt;  select /*+ first_rows(10) &lt;br /&gt;       opt_param('_optimizer_native_full_outer_join', 'off') */ *&lt;br /&gt;  from t1 full join t2 on t1.c1 = t2.c1&lt;br /&gt; ) x where rownum &lt;= 10&lt;br /&gt;) where r &gt;= 1&lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                        | Name  | Starts | A-Rows | Buffers |&lt;br /&gt;------------------------------------------------------------------------------&lt;br /&gt;|*  1 |  VIEW                            |       |      1 |     10 |      16 |&lt;br /&gt;|*  2 |   COUNT STOPKEY                  |       |      1 |     10 |      16 |&lt;br /&gt;|   3 |    VIEW                          |       |      1 |     10 |      16 |&lt;br /&gt;|   4 |     UNION-ALL                    |       |      1 |     10 |      16 |&lt;br /&gt;|   5 |      NESTED LOOPS OUTER          |       |      1 |     10 |      16 |&lt;br /&gt;|   6 |       TABLE ACCESS FULL          | T1    |      1 |     10 |       5 |&lt;br /&gt;|   7 |       TABLE ACCESS BY INDEX ROWID| T2    |     10 |     10 |      11 |&lt;br /&gt;|*  8 |        INDEX RANGE SCAN          | T2_N1 |     10 |     10 |       9 |&lt;br /&gt;|   9 |      NESTED LOOPS ANTI           |       |      0 |      0 |       0 |&lt;br /&gt;|  10 |       TABLE ACCESS FULL          | T2    |      0 |      0 |       0 |&lt;br /&gt;|* 11 |       INDEX RANGE SCAN           | T1_N1 |      0 |      0 |       0 |&lt;br /&gt;------------------------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ここでもう一つの疑問が持てます。多くのPagination QueryがORDER BY句を使用します。万一このORDER BYをインデックスで処理できないとしたらNested Loops Joinのメリットは消えてしまいます。こんな場合はNative Full Outer Hash Joinが本然のメリットを発揮することができるはずです。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;私は個人的にFIRST_ROWS類のヒントやモードはなくなるべきだと思いますが、なお必要がありますね。:)&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;オプティマイザがだんだん賢くなっていますがたまにはとても簡単なクエリでも手動制御が必要な場合があります。われらが相変わらずヒントを身に付けなければならない理由となります。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-66660404743479673?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/66660404743479673/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/11/native-full-outer-hash-joinrownum.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/66660404743479673'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/66660404743479673'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/11/native-full-outer-hash-joinrownum.html' title='Native Full Outer Hash JoinとROWNUM'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-1266053635980234573</id><published>2009-11-01T23:14:00.000-08:00</published><updated>2009-11-01T23:55:22.657-08:00</updated><title type='text'>オラクル性能に対する短い考え＃9</title><content type='html'>&lt;font size="+2"&gt;データがなければ性能分析もない。&lt;/font&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;次の質問をよく見てください。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;昨夜にオラクルセッション数が急に増えながら性能が低下しました。あるセッションは何もできないハング状態でした。一部のセッションはlog file syncイベントを待機していました。原因は何でしょうか。&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;次の質問と比べたらどうですか。似ているんじゃないですか。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;昨夜寝ているのに急に頭が少しぼっとして手足も痛みました。右手は動かせなかったんです。左側の太股もわずかに痛みました。原因は何でしょうか。&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;個人的に病院に行って検査受けるのが大嫌いですが、患者の状態に対する細かいデータがなくては診断も治療も不可能です。相談をしたりMRIを取ったりするのがみんなこのような理由です。オラクル性能問題も同じです。性能問題の類型に応じてSystem State Dump, Process State Dump, Error Stack Dump, Heap Dump, 10046 Event Dump, 10053 Event Dumpなどのデータがなくては全てが推測に過ぎません。推測するのが面白いかも知れませんが、大事な健康を推測で危なくするのはだめでしょう。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;専門化が一番輝く瞬間は分析できるデータが十分な時です。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-1266053635980234573?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/1266053635980234573/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/11/9.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/1266053635980234573'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/1266053635980234573'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/11/9.html' title='オラクル性能に対する短い考え＃9'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-3071629505106740720</id><published>2009-10-29T01:08:00.000-07:00</published><updated>2009-10-29T01:18:54.380-07:00</updated><title type='text'>オラクル性能に対する短い考え＃8</title><content type='html'>&lt;font size="+2"&gt;ジャヴァ開発者たちはオラクルに対してもっと知らなければならない&lt;br /&gt;&lt;/font&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;ジャヴァとJDBCを利用すれば具現に一月が掛かって性能は遅いプログラムをオラクルが提供する機能を利用すれば具現には一日が掛かって性能は優れるプログラムが作れます。いつもそうだと言うことではないんですけど多くのばあいそうです。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;でもこんなノーハウたちがまだ多い開発者たちに広範囲に共有されていないみたいです。誰の責任なのかは分かりませんけど...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-3071629505106740720?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/3071629505106740720/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/10/8.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/3071629505106740720'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/3071629505106740720'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/10/8.html' title='オラクル性能に対する短い考え＃8'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-5040169856425011252</id><published>2009-10-27T18:10:00.000-07:00</published><updated>2009-10-27T18:37:10.309-07:00</updated><title type='text'>オラクル性能に対する短い考え　＃7</title><content type='html'>&lt;font size="+2"&gt;オラクル性能コンサルティングの単価は上がるか下がるか&lt;/font&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;オラクルに入門した人なら誰でもオラクル性能専門家を夢見て、究極的には高い収入が保障されるコンサルタントを目標にするようになります。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;現実は、世界的な景気沈滞と共に大型プロジェクトが失踪しながらオラクル性能コンサルタントの単価は速い速度で下がっています。当分の間はこんな傾向が加速化するはずです。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;もう一度の事実は、ソフトウエア業界が若い人たちに離れられて実力のある者の数がかえって次第に減っていることです。それなら実力を認められた人たちの単価はかえって上がらないんでしょうか。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;今の選択が向後5－10年を左右します。皆さんの選択は何ですか。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-5040169856425011252?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/5040169856425011252/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/10/7.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/5040169856425011252'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/5040169856425011252'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/10/7.html' title='オラクル性能に対する短い考え　＃7'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-315735660334257402</id><published>2009-10-26T23:18:00.000-07:00</published><updated>2009-10-26T23:59:02.504-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='診断イベント'/><category scheme='http://www.blogger.com/atom/ns#' term='SORT_AREA_SIZE'/><category scheme='http://www.blogger.com/atom/ns#' term='10033'/><category scheme='http://www.blogger.com/atom/ns#' term='10032'/><title type='text'>なぜ診断イベントを理解すべきなのか。</title><content type='html'>たとえ開発者と言ってもオラクルが提供する診断イベントを理解するのが必要だと思います。たとえば次のような質問を見てください。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;i&gt;&lt;br /&gt;整列領域大きさ(Sort Area Size)によって整列がどのように行われるかを理解するように多様なテストを修行しています。どころでSORT_AREA_SIZEパラメータ値を300バイトで低く設定してもオラクルが最小値を自分で調整するらしいです。オラクルで使用可能な整列領域大きさの最小値がいくらなのか分かる方法は何でしょうか。&lt;br /&gt;&lt;/i&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;マニュアルには下記のように記述されています。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;&lt;a href="http://download.oracle.com/docs/cd/B28359_01/server.111/b28320/initparams232.htm"&gt;http://download.oracle.com/docs/cd/B28359_01/server.111/b28320/initparams232.htm&lt;/a&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;すなわち、整列領域大きさの最小値は6*8K(データベースブロックサイズ)＝48Kバイトだというのが分かります。本当のエンジニアだったらここから進み、この最小値を目で確認する方法はないのか？と質問をするようになるはずです。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;この類の質問に一番直接的に答ができるのが&lt;b&gt;診断イベント&lt;/b&gt;です。たとえば10032イベントを活性化すれば、オラクルは整列作業の修行に対する詳細な統計情報を記録してくれます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; @oerr 10032&lt;br /&gt;10032&lt;br /&gt; "sort statistics (SOR*)"&lt;br /&gt;// *Cause:&lt;br /&gt;// *Action:&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;また10033イベントを利用すれば実際の整列を修行しながら個別Sort Runたちをどのように使用するのかまで分かります。大変有用な情報でしょう。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; @oerr 10033&lt;br /&gt;10033&lt;br /&gt; "sort run information (SRD*/SRS*)"&lt;br /&gt;// *Cause:&lt;br /&gt;// *Action:&lt;br /&gt;// *Cause:&lt;br /&gt;// *Action:&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;10032イベントを利用してマニュアルの内容を検証してみましょうか。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;drop table t1 purge;&lt;br /&gt;create table t1&lt;br /&gt;as select rpad('x',300,'x') as c1&lt;br /&gt;from dual&lt;br /&gt;connect by level &lt;= 100&lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;alter session set workarea_size_policy=manual;&lt;br /&gt;alter session set sort_area_size=300;&lt;br /&gt;&lt;br /&gt;alter session set events '10032 trace name context forever, level 1';&lt;br /&gt;&lt;br /&gt;select c1 from t1 order by c1;&lt;br /&gt;&lt;br /&gt;alter session set events '10032 trace name context off';&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;トレースファイルの内容は次のとおりです。SORT_AREA_SIZE値が正確に48Kバイトというのが確認できます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;---- Sort Parameters ------------------------------&lt;br /&gt;sort_area_size                    49152&lt;br /&gt;sort_area_retained_size           16384&lt;br /&gt;sort_multiblock_read_count        1&lt;br /&gt;max intermediate merge width      2&lt;br /&gt;---- Sort Statistics ------------------------------&lt;br /&gt;Initial runs                              1&lt;br /&gt;Input records                             100&lt;br /&gt;Output records                            100&lt;br /&gt;Disk blocks 1st pass                      4&lt;br /&gt;Total disk blocks used                    6&lt;br /&gt;Total number of comparisons performed     102&lt;br /&gt;  Comparisons performed by in-memory sort 101&lt;br /&gt;  Comparisons while searching for key in-memory 1&lt;br /&gt;Temp segments allocated                   1&lt;br /&gt;Extents allocated                         1&lt;br /&gt;Uses version 2 sort&lt;br /&gt;Uses asynchronous IO&lt;br /&gt;    ---- Run Directory Statistics ----&lt;br /&gt;Run directory block reads (buffer cache)  2&lt;br /&gt;Block pins (for run directory)            1&lt;br /&gt;Block repins (for run directory)          1&lt;br /&gt;    ---- Direct Write Statistics -----&lt;br /&gt;Write slot size                           8192&lt;br /&gt;Write slots used during in-memory sort    2&lt;br /&gt;Number of direct writes                   4&lt;br /&gt;Num blocks written (with direct write)    4&lt;br /&gt;Block pins (for sort records)             4&lt;br /&gt;Cached block repins (for sort records)    1&lt;br /&gt;Waits for async writes                    3&lt;br /&gt;    ---- Direct Read Statistics ------&lt;br /&gt;Size of read slots for output             8192&lt;br /&gt;Number of read slots for output           2&lt;br /&gt;Number of direct sync reads               2&lt;br /&gt;Number of blocks read synchronously       2&lt;br /&gt;Number of direct async reads              2&lt;br /&gt;Number of blocks read asynchronously      2&lt;br /&gt;---- End of Sort Statistics -----------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;立派な開発者ならデバッガ(Debugger)に上手なはずです。オラクルの診断イベントはオラクルデバッガと言えます。オラクルを深く理解しようとすれば必修的な知識だと言えます。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-315735660334257402?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/315735660334257402/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/10/blog-post_26.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/315735660334257402'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/315735660334257402'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/10/blog-post_26.html' title='なぜ診断イベントを理解すべきなのか。'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-8727913567609669246</id><published>2009-10-25T18:05:00.000-07:00</published><updated>2009-10-26T02:10:48.722-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='パラメータ'/><title type='text'>バージョン間パラメータの比較</title><content type='html'>多数のバージョンのオラクルを運営してみたらパラメータ値のバージョン間の違いを知るのが重要になります。次が私がよく使用する方法です。&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;比較してみたい対象DBに対してDB Linkを作ります。&lt;br /&gt;&lt;li&gt;Full Outer Joinを通じて両DB間のパラメータ値を比較します。&lt;br /&gt;&lt;li&gt;X$ビューを利用すればヒドゥンパラメータの値まで得られます。&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;このロジックが&lt;a href="http://sites.google.com/site/ukja/sql-scripts-1/o-s/para_diff"&gt;ここに&lt;/a&gt;具現されています。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;次は使用例です。まずDB Linkが成功的に作られたか確認します。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1106&gt; select * from dual@ukja1021;&lt;br /&gt;&lt;br /&gt;D&lt;br /&gt;-&lt;br /&gt;X&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Mutexに関するパラメータのバージョン間の違いは？&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1106&gt; @para_diff UKJA1021 mutex&lt;br /&gt;&lt;br /&gt;MCH NAME                      M_VALUE    Y_VALUE    DESCRIPTION&lt;br /&gt;--- ------------------------- ---------- ---------- -------------------------&lt;br /&gt;X   _kks_use_mutex_pin        TRUE       FALSE      Turning on this will make&lt;br /&gt;                                                     KKS use mutex for cursor&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;オプティマイザとインデックスに関するパラメータのバージョン間の違いは？&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1106&gt; @para_diff UKJA1021 optimizer%index&lt;br /&gt;&lt;br /&gt;MCH NAME                      M_VALUE    Y_VALUE    DESCRIPTION&lt;br /&gt;--- ------------------------- ---------- ---------- -------------------------&lt;br /&gt;O   _optimizer_compute_index_ TRUE       TRUE       force index stats collect&lt;br /&gt;    stats                                           ion on index creation/reb&lt;br /&gt;                                                    uild&lt;br /&gt;&lt;br /&gt;X   _optimizer_fkr_index_cost 10                    Optimizer index bias over&lt;br /&gt;    _bias                                            FTS/IFFS under first K r&lt;br /&gt;                                                    ows mode&lt;br /&gt;&lt;br /&gt;O   optimizer_index_caching   0          0          optimizer percent index c&lt;br /&gt;                                                    aching&lt;br /&gt;                                                    &lt;br /&gt;O   optimizer_index_cost_adj  100        100        optimizer index cost adju&lt;br /&gt;                                                    stment&lt;br /&gt;&lt;br /&gt;X   optimizer_use_invisible_i FALSE                 Usage of invisible indexe&lt;br /&gt;    ndexes                                          s (TRUE/FALSE)&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;このようにすれば全てのパラメータが比べられます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1106&gt; @para_diff UKJA1021 %&lt;br /&gt;&lt;br /&gt;MCH NAME                      M_VALUE    Y_VALUE    DESCRIPTION&lt;br /&gt;--- ------------------------- ---------- ---------- -------------------------&lt;br /&gt;O   O7_DICTIONARY_ACCESSIBILI FALSE      FALSE      Version 7 Dictionary Acce&lt;br /&gt;    TY                                              ssibility Support&lt;br /&gt;&lt;br /&gt;O   _4031_dump_bitvec         67194879   67194879   bitvec to specify dumps p&lt;br /&gt;                                                    rior to 4031 error&lt;br /&gt;&lt;br /&gt;O   _4031_dump_interval       300        300        Dump 4031 error once for&lt;br /&gt;                                                    each n-second interval&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;O   workarea_size_policy      AUTO       AUTO       policy used to size SQL w&lt;br /&gt;                                                    orking areas (MANUAL/AUTO&lt;br /&gt;                                                    )&lt;br /&gt;&lt;br /&gt;X   xml_db_events             enable                are XML DB events enabled&lt;br /&gt;&lt;br /&gt;1964 rows selected.&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:08.35&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;かなり便利じゃないですか。:)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-8727913567609669246?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/8727913567609669246/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/10/blog-post_25.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/8727913567609669246'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/8727913567609669246'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/10/blog-post_25.html' title='バージョン間パラメータの比較'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-2394065617316468538</id><published>2009-10-23T02:20:00.000-07:00</published><updated>2009-10-23T06:32:42.190-07:00</updated><title type='text'>オラクル性能に対する短い考え＃6</title><content type='html'>&lt;font size="+2"&gt;時間(Time)は一番危ない性能測定方法である&lt;/font&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;これ何のとんでもない言葉でしょうか。：)&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;もちろん時間は一番直観的で確実な性能測定方法です。これには異論の余地がありません。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;でも、時間が改善された理由を明確に打ち明けずに済んだらこれより危険なものはありません。間違った結論を下しやすいです。オラクルがどんな作業をどのようにしたので時間が改善されたのか、これが重要なものですか、時間の改善自体は重要ではありません。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;それでオラクルの性能を測る時は時間だけじゃなくてワーカロード(Logical Readsのような)、レッチの獲得、Mutexの獲得、待機イベント、必要な場合OSレベルのデータなどのバックグラウンドデータがずっと重要です。こんなデータをよく収集し分析したら時間は見る必要さえありません。不幸にも現在のオラクル性能教育ではこんなものを体系的に教える所はありません。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-2394065617316468538?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/2394065617316468538/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/10/6.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/2394065617316468538'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/2394065617316468538'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/10/6.html' title='オラクル性能に対する短い考え＃6'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-8750645017579534616</id><published>2009-10-21T21:56:00.000-07:00</published><updated>2009-10-22T00:21:12.212-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='varchar2'/><category scheme='http://www.blogger.com/atom/ns#' term='CLOB'/><category scheme='http://www.blogger.com/atom/ns#' term='dbms_sql'/><category scheme='http://www.blogger.com/atom/ns#' term='Ref Cursor'/><category scheme='http://www.blogger.com/atom/ns#' term='Execute Immediate'/><title type='text'>長い文字列(VARCHAR2)の使用</title><content type='html'>PL/SQLを使用してみたら、長い文字列、すなわち4,000バイト以上の文字列を使用する必要ができます。どころでちょっと、VARCHAR2は4,000バイトまでだけ支援していませんか。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; select * from v$version;&lt;br /&gt;&lt;br /&gt;BANNER&lt;br /&gt;----------------------------------------------------------------&lt;br /&gt;Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod&lt;br /&gt;PL/SQL Release 10.2.0.1.0 - Production&lt;br /&gt;CORE 10.2.0.1.0 Production&lt;br /&gt;TNS for 32-bit Windows: Version 10.2.0.1.0 - Production&lt;br /&gt;NLSRTL Version 10.2.0.1.0 - Production&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;次のように4,000バイトはよく表現されます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; select rpad('x',4000,'x') from dual;&lt;br /&gt;&lt;br /&gt;RPAD('X',4000,'X')&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;10,000バイトも使用できましょうか。次の結果を見ると、あら、支援されますよね。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; select rpad('x',10000,'x') from dual;&lt;br /&gt;&lt;br /&gt;RPAD('X',10000,'X')&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;でも、実際は4,000バイトで切られてしまったのが分かります。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; select length(rpad('x',10000,'x')) from dual;&lt;br /&gt;&lt;br /&gt;LENGTH(RPAD('X',10000,'X'))&lt;br /&gt;---------------------------&lt;br /&gt;                       4000&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;次のような無理な試みはやっぱり駄目でしょう。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; select rpad('x',4000,'x')||rpad('x',4000,'x') from dual;&lt;br /&gt;select rpad('x',4000,'x')||rpad('x',4000,'x') from dual&lt;br /&gt;                                                   *&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-01489: result of string concatenation is too long&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;幸いにPL/SQL内ではVARCHAR2を32Kバイトまで使用できます。もちろんPL/SQL内でだけ使用可能です。このぐらいの長い文字列だったら&lt;b&gt;大部分SQL文章を動的に生成しようとする場合&lt;/b&gt;です。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; declare&lt;br /&gt;  2        v_sql      varchar2(32767);&lt;br /&gt;  3        v_cursor      sys_refcursor;&lt;br /&gt;  4  begin&lt;br /&gt;  5        v_sql := 'select ';&lt;br /&gt;  6        for idx in 1 .. 100 loop&lt;br /&gt;  7         v_sql := v_sql || q'[']' || rpad('x',200) || q'[',]';&lt;br /&gt;  8        end loop;&lt;br /&gt;  9        v_sql := v_sql || ' 1 from dual';&lt;br /&gt; 10  &lt;br /&gt; 11        open v_cursor for v_sql;&lt;br /&gt; 12        close v_cursor;&lt;br /&gt; 13  end loop;&lt;br /&gt; 14  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;でも32Kバイトを越えるとエラーが出ます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; declare&lt;br /&gt;  2        v_sql      varchar2(32767);&lt;br /&gt;  3        v_cursor      sys_refcursor;&lt;br /&gt;  4  begin&lt;br /&gt;  5        v_sql := 'select ';&lt;br /&gt;  6        for idx in 1 .. 300 loop&lt;br /&gt;  7         v_sql := v_sql || q'[']' || rpad('x',200) || q'[',]';&lt;br /&gt;  8        end loop;&lt;br /&gt;  9        v_sql := v_sql || ' 1 from dual';&lt;br /&gt; 10  &lt;br /&gt; 11        open v_cursor for v_sql;&lt;br /&gt; 12        close v_cursor;&lt;br /&gt; 13  end loop;&lt;br /&gt; 14  /&lt;br /&gt;declare&lt;br /&gt;*&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-06502: PL/SQL: numeric or value error: character string buffer too small&lt;br /&gt;ORA-06512: at line 7&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;こんな場合にはCLOBを使用すればいいです。でもCLOBの場合にはREF CURSORとは使用できません。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; declare&lt;br /&gt;  2        v_sql      clob;&lt;br /&gt;  3        v_cursor      sys_refcursor;&lt;br /&gt;  4  begin&lt;br /&gt;  5        v_sql := 'select ';&lt;br /&gt;  6        for idx in 1 .. 300 loop&lt;br /&gt;  7         v_sql := v_sql || q'[']' || rpad('x',200) || q'[',]';&lt;br /&gt;  8        end loop;&lt;br /&gt;  9        v_sql := v_sql || ' 1 from dual';&lt;br /&gt; 10  &lt;br /&gt; 11        open v_cursor for v_sql;&lt;br /&gt; 12        close v_cursor;&lt;br /&gt; 13  end loop;&lt;br /&gt; 14  /&lt;br /&gt; open v_cursor for v_sql;&lt;br /&gt;                   *&lt;br /&gt;ERROR at line 11:&lt;br /&gt;ORA-06550: line 11, column 20:&lt;br /&gt;PLS-00382: expression is of wrong type&lt;br /&gt;ORA-06550: line 11, column 2:&lt;br /&gt;PL/SQL: Statement ignored&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;当然にEXECUTE IMMEDIATEでも願うとおり動作しません。では32Kバイトより長いSQL文章を動的に使用できる方法は何でしょうか。次のように少しは複雑に見える方法でDBMS_SQLパッケージを使用することです。CLOBをいくつかの小さなVARCHAR2文字列に割ってDBMS_SQL.PARSEパッケージに伝達します。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; declare&lt;br /&gt;  2        v_sql      clob;&lt;br /&gt;  3        v_array dbms_sql.varchar2a;&lt;br /&gt;  4        v_curno number;&lt;br /&gt;  5        v_ret      integer;&lt;br /&gt;  6        v_ub      number;&lt;br /&gt;  7  begin&lt;br /&gt;  8        v_sql := 'select ';&lt;br /&gt;  9        for idx in 1 .. 300 loop&lt;br /&gt; 10         v_sql := v_sql || q'[']' || rpad('x',200) || q'[',]';&lt;br /&gt; 11        end loop;&lt;br /&gt; 12        v_sql := v_sql || ' 1 from dual';&lt;br /&gt; 13  &lt;br /&gt; 14        v_curno := dbms_sql.open_cursor;&lt;br /&gt; 15        v_ub := ceil(dbms_lob.getlength(v_sql)/1000);&lt;br /&gt; 16        for idx in 1 .. v_ub loop&lt;br /&gt; 17         v_array(idx) := dbms_lob.substr(v_sql, 1000, (idx-1)*1000+1);&lt;br /&gt; 18        end loop;&lt;br /&gt; 19  &lt;br /&gt; 20        dbms_sql.parse(v_curno, v_array, 1, v_ub, false, dbms_sql.native);&lt;br /&gt; 21        v_ret := dbms_sql.execute(v_curno);&lt;br /&gt; 22        dbms_sql.close_cursor(v_curno);&lt;br /&gt; 23  end loop;&lt;br /&gt; 24  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;幸いにOracle11gからはCLOBをREF CURSORに直接使用できます。EXECUTE IMMEDIATEでも使用できるのは当然ですよ。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1106&gt; select * from v$version;&lt;br /&gt;&lt;br /&gt;BANNER&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 - Production&lt;br /&gt;PL/SQL Release 11.1.0.6.0 - Production&lt;br /&gt;CORE 11.1.0.6.0 Production&lt;br /&gt;TNS for 32-bit Windows: Version 11.1.0.6.0 - Production&lt;br /&gt;NLSRTL Version 11.1.0.6.0 - Production&lt;br /&gt;&lt;br /&gt;UKJA@ukja1106&gt; declare&lt;br /&gt;  2        v_sql      clob;&lt;br /&gt;  3        v_cursor      sys_refcursor;&lt;br /&gt;  4  begin&lt;br /&gt;  5        v_sql := 'select ';&lt;br /&gt;  6        for idx in 1 .. 300 loop&lt;br /&gt;  7         v_sql := v_sql || q'[']' || rpad('x',200) || q'[',]';&lt;br /&gt;  8        end loop;&lt;br /&gt;  9        v_sql := v_sql || ' 1 from dual';&lt;br /&gt; 10  &lt;br /&gt; 11        open v_cursor for v_sql;&lt;br /&gt; 12        close v_cursor;&lt;br /&gt; 13  end loop;&lt;br /&gt; 14  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;DBMS_SQLパッケージでもCLOBをSQLテキストに使用可能です。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1106&gt; declare&lt;br /&gt;  2        v_sql      clob;&lt;br /&gt;  3        v_curno number;&lt;br /&gt;  4        v_ret      integer;&lt;br /&gt;  5  begin&lt;br /&gt;  6        v_sql := 'select ';&lt;br /&gt;  7        for idx in 1 .. 300 loop&lt;br /&gt;  8         v_sql := v_sql || q'[']' || rpad('x',200) || q'[',]';&lt;br /&gt;  9        end loop;&lt;br /&gt; 10        v_sql := v_sql || ' 1 from dual';&lt;br /&gt; 11  &lt;br /&gt; 12        v_curno := dbms_sql.open_cursor;&lt;br /&gt; 13        dbms_sql.parse(v_curno, v_sql, dbms_sql.native);&lt;br /&gt; 14        v_ret := dbms_sql.execute(v_curno);&lt;br /&gt; 15        dbms_sql.close_cursor(v_curno);&lt;br /&gt; 16  end loop;&lt;br /&gt; 17  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;なお、REF CURSORとDBMS_SQLの間に変換も自由です。以前の制約がほとんどなくなりました。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1106&gt; declare&lt;br /&gt;  2        v_sql      clob;&lt;br /&gt;  3        v_curno number;&lt;br /&gt;  4        v_ret      integer;&lt;br /&gt;  5        v_cursor      sys_refcursor;&lt;br /&gt;  6  begin&lt;br /&gt;  7        v_sql := 'select ';&lt;br /&gt;  8        for idx in 1 .. 300 loop&lt;br /&gt;  9         v_sql := v_sql || q'[']' || rpad('x',200) || q'[',]';&lt;br /&gt; 10        end loop;&lt;br /&gt; 11        v_sql := v_sql || ' 1 from dual';&lt;br /&gt; 12  &lt;br /&gt; 13        v_curno := dbms_sql.open_cursor;&lt;br /&gt; 14        dbms_sql.parse(v_curno, v_sql, dbms_sql.native);&lt;br /&gt; 15        v_ret := dbms_sql.execute(v_curno);&lt;br /&gt; 16        v_cursor := dbms_sql.to_refcursor(v_curno);&lt;br /&gt; 17        close v_cursor;&lt;br /&gt; 18  &lt;br /&gt; 19  end loop;&lt;br /&gt; 20  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;やはり新しいバージョンがいいですね。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-8750645017579534616?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/8750645017579534616/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/10/varchar2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/8750645017579534616'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/8750645017579534616'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/10/varchar2.html' title='長い文字列(VARCHAR2)の使用'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-5619502104697069459</id><published>2009-10-20T23:36:00.000-07:00</published><updated>2009-10-21T00:11:02.580-07:00</updated><title type='text'>オラクル性能に対する短い考え　＃５</title><content type='html'>&lt;font size="+2"&gt;専門家の言うことを信じるな。&lt;/font&gt;&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;いわゆるオラクル性能専門家という方々が書いたこととか言ったことを反駁したら一冊の本を出せるかもと普段から考えています。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;私も三冊の本を出したが、専門家たちが自分がもうよく分かっている内容を本で出すようでしょう。とんでもないんです。分からないものを研究しながら本を出すのです。だからどんなに多くの誤りがあるでしょうか。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;皆が普通の人間なのでだんなにとんでもない失敗をするか知れません。大部分の開発者がアプリソースコードの100ラインあたり一ラインくらいでバグを作ると言われています。1％の誤謬がいつもあるのです。専門家たちの言うことや書くことも例外ではありません。私が書いた本を今見るといくら多くの誤りがあるか知れません。顔が真っ赤になります。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;誰が専門家のように何かを言えば三番だけしつこく聞いてみてください。すぐ底をつくことになってしまいますよ。その過程を繰り返してみれば自分がいつの間にか専門家の水準に発展したのを見るようになるでしょう。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-5619502104697069459?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/5619502104697069459/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dioncho.blogspot.com/2009/10/blog-post_20.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/5619502104697069459'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2194093596149383181/posts/default/5619502104697069459'/><link rel='alternate' type='text/html' href='http://dioncho.blogspot.com/2009/10/blog-post_20.html' title='オラクル性能に対する短い考え　＃５'/><author><name>Dion_Cho</name><uri>http://www.blogger.com/profile/17799718037719118540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp3.blogger.com/_QmSegOSVHT8/R1f0Skr471I/AAAAAAAAAAM/UqqQUDCCZxs/S220/Dion_Cho.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2194093596149383181.post-7315952976429270492</id><published>2009-10-19T23:13:00.000-07:00</published><updated>2009-10-20T02:53:33.504-07:00</updated><title type='text'>オラクルが知らせてくれないオラクルの小さなチップ＃1</title><content type='html'>文字列内で単一引用符号(Single Quotation Mark)を使ったら次のようにエラーが発生します。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; select 'My name is 'ukja'' from dual;&lt;br /&gt;select 'My name is 'ukja'' from dual&lt;br /&gt;                        *&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-00923: FROM keyword not found where expected&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;こんな場合には次のように単一引用符号を二つに付けて使用しなければなりません。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; select 'My name is ''ukja''' from dual;&lt;br /&gt;&lt;br /&gt;'MYNAMEIS''UKJA''&lt;br /&gt;-----------------&lt;br /&gt;My name is 'ukja'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;問題はこの場合可読性(Readibility)が著しく落ちるということです。次の簡単な文字列だけでも目が痛いぐらいです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; declare&lt;br /&gt;  2        v_sql   varchar2(10000);&lt;br /&gt;  3  begin&lt;br /&gt;  4        v_sql := 'insert into t1 values(''a'', ''b'',' || '''d''' || ', ''e'')';&lt;br /&gt;  5  end;&lt;br /&gt;  6  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;この可読性の問題は時には相当に深刻になります。私の個人的な経験談ですが、複雑な文字列を動的に生成する過程で、多い単一引用符号が使用されながらエラーを捜すにだけ数時間がかかったこともあります。&lt;br /&gt;&lt;p/&gt;&lt;br /&gt;幸いにOracle10gからはつぎのように&lt;b&gt;引用オペレーター(Quote Operator. q)&lt;/b&gt;が使えます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; select q'[My name is 'ukja']' from dual;&lt;br /&gt;&lt;br /&gt;Q'[MYNAMEIS'UKJA'&lt;br /&gt;-----------------&lt;br /&gt;My name is 'ukja'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;文字列が複雑になるほど効果は大きいです。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; declare&lt;br /&gt;  2        v_sql   varchar2(10000);&lt;br /&gt;  3  begin&lt;br /&gt;  4        v_sql := q'[insert into t1 values('a', 'b',]' || q'['d']' || q'[, 'e')]';&lt;br /&gt;  5  end;&lt;br /&gt;  6  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;次のように大部分の文字を区切り記号(Delimiter)で使えます。&lt;br /&gt;&lt;pre class="brush: sql; "&gt;&lt;br /&gt;UKJA@ukja1021&gt; select q'[My name is 'ukja']' from dual;&lt;br /&gt;&lt;br /&gt;Q'[MYNAMEIS'UKJA'&lt;br /&gt;-----------------&lt;br /&gt;My name is 'ukja'&lt;br /&gt;&lt;br /&gt;UKJA@ukja1021&gt; select q'#My name is 'ukja'#' from dual;&lt;br /&gt;&lt;br /&gt;Q'#MYNAMEIS'UKJA'&lt;br /&gt;-----------------&lt;br /&gt;My name is 'ukja'&lt;br /&gt;&lt;br /&gt;UKJA@ukja1021&gt; select q'(My name is 'ukja')' from dual;&lt;br /&gt;&lt;br /&gt;Q'(MYNAMEIS'UKJA'&lt;br /&gt;-----------------&lt;br /&gt;My name is 'ukja'&lt;br /&gt;&lt;br /&gt;UKJA@ukja1021&gt; select q'*My name is 'ukja'*' from dual;&lt;br /&gt;&lt;br /&gt;Q'*MYNAMEIS'UKJA'&lt;br /&gt;-----------------&lt;br /&gt;My name is 'ukja'&lt;br /&gt;&lt;br /&gt;UKJA@ukja1021&gt; select q'xMy name is 'ukja'x' from dual;&lt;br /&gt;&lt;br /&gt;Q'XMYNAMEIS'UKJA'&lt;br /&gt;-----------------&lt;br /&gt;My name is 'ukja'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;これ以上単一引用符号を探すのに数時間を無駄に使う必要がなくなるはずです。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2194093596149383181-7315952976429270492?l=dioncho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dioncho.blogspot.com/feeds/7315952976429270492/comments/default' title='Post Comm
