2018年5月 2日 (水)

Oracle Database Connect 2018 エキスパートはどう考えるか? 体感!パフォーマンスチューニング Ⅱ (番外編=没ネタ)

Oracle Database Connect 2018 エキスパートはどう考えるか?体感!パフォーマンスチューニング Ⅱ
~Autonomous Databaseの到来において必要となるチューニングとは~

これ、パフォーマンスチューニングネタをまとめ上げるまで、みなさんスケジュール調整し、
オンライン/オフラインミーティングを繰り返してネタを詰めていくという、かなーり面倒なことをやっています。
私なんて、ネタの候補検討だけで16時間ぐらい使ってますからねw(自分の余暇を使って、半分楽しみながら、締め切りがあるので半分苦しみながらw)


余談はこれくらいにして、今日の本題です。:)

Oracle Database Connect 2018 エキスパートはどう考えるか? 体感!パフォーマンスチューニング Ⅱ で没にしたネタがあるのですが、
そのまま捨てるのも勿体ないので、多少取得情報を追加した状態で公開しちゃおうと思います。


お時間のありますときに、頭の体操、AWRレポートの解析方法(最近はADDM、ASHレポートまで含まれています)のトレーニングにどうぞ。

遅延原因の想定と、想定原因の特定情報は、このエントリでは公開しません。おそらくGW開けの12日あたりか、その一週間後の19日ごろには原因を公開しようかと:)の後半に追記してあります:)

なお、本ページに公開した情報以外に、追加で見たい情報がある場合は、コメント欄やtwのmentionで具体的にリクエストいただければ、取得されている情報の範囲内で追加公開します。取得されてない情報はその旨を追記していきます。:)
まずは、エスパー力全開で、原因を想定してみてください。>
(かなり難易度高めだとは思いますが、過去この状況に遭遇した経験をお持ちのかたは、ここに公開した程度の情報から勘で言い当てたりしちゃうんですよね。実は、昨年末に、何年か振りでこの症状を目にしたのでネタにしよう思いました。)

以下取得された情報は以下のとおり。以下の情報を元に遅延原因を想定してみてください。



なーんでだ?!


状況説明
試験時には問題のなかった処理(30分以内に終了する状態が正常な状態です。)をリリースしました。
ところが、30分以上経過しても終了しません。遅延原因を想定、特定してください。
なお、本番環境のメンテナンス時間中に試験を行ったため、データベースインスタンス内で試験を行いリリースしています。
ちなみに、試験時の処理時間は、約21分でしたが、リリース時は約56分と大幅に遅延。


他のトランザクションが走行している状況をミックスするとより
本物っぽくなり、特定に苦労するので面白いのですが、
遅延している処理だけが走行している状況にしてあります。


資料1 試験時(正常時)AWRレポート
資料2 リリース時(遅延時)AWRレポート
資料3 試験時(正常時)とリリース時(遅延時)のAWR DIFFレポート

資料4 試験時(正常時)CPU利用率グラフ(調査による多少のノイズあり)

1

資料5 リリース時(遅延時)CPU利用率グラグ(スパイク部分調査による多少のノイズあり)

2

GWも後半スタート、天気は少々荒れそうですが、みなさん、よいGWを!



Twitterでのやり取り....
20180506_82405
20180506_82353

ここまでくるとなにかが見えてきたかな。。。原因を特定するまであと一歩な感じ。

追加資料1(遅延時)
top

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
13058 oracle 20 0 10.434g 365704 359548 R 100.0 1.4 1:05.70 oracle_13058_or
5973 oracle 20 0 1955632 253648 73812 S 4.7 1.0 1:42.62 gnome-shell
4657 oracle -2 0 10.417g 58792 55660 S 2.0 0.2 0:12.15 ora_vktm_orcl12
1285 root 20 0 483544 76000 21372 S 1.7 0.3 0:55.11 Xorg
4862 oracle 20 0 10.417g 68960 65868 S 1.3 0.3 0:01.23 ora_lg00_orcl12
223 root 20 0 0 0 0 S 0.7 0.0 0:00.49 kworker/u24:3
13059 oracle 20 0 157976 4564 3544 R 0.7 0.0 0:00.29 top
...略...

追加資料2(遅延時)
sar -P ALL

   ...略...
02:47:29 AM CPU %user %nice %system %iowait %steal %idle
02:47:34 AM all 4.78 0.00 2.36 0.07 0.00 92.80
02:47:34 AM 0 1.06 0.00 1.06 0.85 0.00 97.03
02:47:34 AM 1 0.20 0.00 0.40 0.00 0.00 99.40
02:47:34 AM 2 0.00 0.00 0.00 0.00 0.00 100.00
02:47:34 AM 3 0.00 0.00 0.00 0.00 0.00 100.00
02:47:34 AM 4 0.00 0.00 0.20 0.00 0.00 99.80
02:47:34 AM 5 0.00 0.00 0.00 0.00 0.00 100.00
02:47:34 AM 6 0.00 0.00 0.00 0.00 0.00 100.00
02:47:34 AM 7 68.08 0.00 31.92 0.00 0.00 0.00
02:47:34 AM 8 0.00 0.00 0.00 0.00 0.00 100.00
02:47:34 AM 9 0.00 0.00 0.00 0.00 0.00 100.00
02:47:34 AM 10 0.00 0.00 0.00 0.00 0.00 100.00
02:47:34 AM 11 0.00 0.00 0.20 0.20 0.00 99.60
...略...

追加資料3(遅延時)
perf top -C 7

Samples: 107K of event 'cpu-clock', Event count (approx.): 29550214790                                                              
Overhead Shared Object Symbol
4.36% libc-2.17.so [.] vfprintf
2.77% oracle [.] lxoCpStr
2.00% [kernel] [k] __radix_tree_lookup
1.94% oracle [.] dbgfcsIlcsGetNextDef
1.58% oracle [.] skgovprint
1.48% [kernel] [k] __do_softirq
1.44% [kernel] [k] selinux_file_permission
1.43% oracle [.] dbgaFmtAttrCb_int
1.36% [kernel] [k] system_call_after_swapgs
1.33% oracle [.] dbgaAttrFmtProcArg
1.26% oracle [.] dbgtfmWriteMetadata
1.13% oracle [.] dbgtfdFileWrite
1.02% libc-2.17.so [.] _IO_default_xsputn
...略...



その後のやりとり (Twitter)

20180513_80620

そして核心に迫る追加資料が...あったんです。(しばちょうさん風にはしづらかったw)



正常時(試験時)の追加情報はないようです。リリース後は常に遅いらしい。

ふむふむ。

以下、遅延時のstrace -c -pの情報を取得してもらいました。
straceで眺めるとsystem call write()がダントツで上位にきています。その次が lseek()

追加資料4
strace -c -p

[oracle@localhost ˜]$ sudo strace -c -p 13058
Process 13058 attached
^CProcess 13058 detached
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
77.24 47.883538 1 35085592 write
19.22 11.913638 1 17542850 lseek
3.53 2.191099 1 3414889 getrusage
0.01 0.006377 12 524 semop
0.00 0.000403 7 62 close
0.00 0.000210 4 54 chown
0.00 0.000126 2 63 open
0.00 0.000099 2 54 lstat
0.00 0.000050 1 54 chmod
0.00 0.000029 1 54 stat
0.00 0.000028 3 10 semtimedop
0.00 0.000015 0 54 fcntl
0.00 0.000010 1 8 read
0.00 0.000007 1 8 select
0.00 0.000000 0 3 mmap
0.00 0.000000 0 6 rt_sigprocmask
0.00 0.000000 0 1 rt_sigreturn
0.00 0.000000 0 1 readlink
------ ----------- ----------- --------- --------- ----------------
100.00 61.995629 56044287 total

そろそろ突き止めた感が。

なにかを、どこかに、書き出してますよね。

lsofで書き込みに絞って取得してもった結果は以下のとおり。

[root@localhost ˜]# lsof -p 13058 | grep -E '[0-9]+w'
oracle_13 13058 oracle 1w CHR 1,3 0t0 1038 /dev/null
oracle_13 13058 oracle 2w CHR 1,3 0t0 1038 /dev/null
oracle_13 13058 oracle 7w REG 8,17 2351882843 71176413 /u01/app/oracle/diag/rdbms/orcl12c/orcl12c/trace/orcl12c_ora_13058.trc
oracle_13 13058 oracle 8w REG 8,17 358606101 71176414 /u01/app/oracle/diag/rdbms/orcl12c/orcl12c/trace/orcl12c_ora_13058.trm

ほう。 .trcファイルへ大量の書き込みがあります。

このようになるときは、 なにが起っていると思いますか?

.trcファイルに大量の書き込みのあるケースとして、SQLトレースが有効になっている場合です。(意図したSQLトレースなら問題はないですが...意図していない場合はちょいと問題ですよね。)

ということで、確定診断に移りましょう。

SQL*Plus: Release 12.2.0.1.0 Production on Wed Apr 25 01:23:13 2018

Copyright (c) 1982, 2016, Oracle. All rights reserved.

Connected.
ORCL@SYSTEM>
ORCL@SYSTEM> select username,status,event,sql_trace from v$session where paddr = (select addr from v$process where spid = 13058)

USERNAME STATUS EVENT SQL_TRAC
------------------------------ -------- ---------------------------------------------------------------- --------
SCOTT ACTIVE log file switch completion ENABLED

見ての通り、
SQL_TRACE列がdisabled(デフォルト)からenabledに変わっていることから、.trcファイルへの大量の書き出しは、セッションレベルでSQLトレースが有効化されたため、と言えますよね。


今回の新規リリースバッチ処理遅延の原因は、

SQLトレースを有効化したままリリースしたしまったことにより、オーバーヘッドが発生し、グルグル系バッチ処理のスループットが低下したため! 

ということでした。SQLトレースを利用したあとの無効化をお忘れなく! :)


SQLトレースは便利な機能の一つですが、インスタンスレベルやグルグル系バッチ処理での利用時は大量のトレース情報の書き出し等によるオーバーヘッドが発生します。
便利なツールではありますが、スループットの低下等による影響を考慮して利用するタイプのツールでもあります。
また、利用した後は、必ず、無効化することもお忘れなく...

ミイラ取りがミイラになってしまってはいみないですから.....ね :)

| | コメント (0) | トラックバック (0)

2018年3月18日 (日)

Temp落ち #9 - 自動PGA管理で_pga_max_sizeと戯れたPGAサイズって本当に使えるのか? 12.2.0.1版

Previously on Mac De Oracle

自動PGA管理下で、_pga_max_size隠しパラメータとpga_aggregate_targetを最大値に設定するした場合、global memory boundは
最大で、約839GBまで増加することがわかりました。

ただ、これは、内部的なパラメータ上の話。


今日は、実際にソートやハッシュ結合を行なわせ、PGAのSQL Work Areaサイズがどこまで利用できるものなのか確認してみることにします。

さて、どういう結末になりますやら(w

随分前から同じようなことを試している方はいますし....:)

なぜ今そこをdiggingしちゃってるのか? って?

メモリーたっぷりあるのに、何故Temp落ち? が話題になったからに決まってるじゃないですかw

まず、デフォルト設定では最大サイズだった 1GB を超えられるか? の確認

注意)
事前にpga_aggregate_target および _pga_max_size をそれぞれ 4TB - 1に設定し、パラメータの上では、global memory boundが約839GBにしてあります。

なお、Temp落ちの確認データやスクリプトはTemp落ち #4 - 手動PGA管理で作業領域として指定可能な最大サイズ de Temp落ちの確認のエントリーを参照のこと。

ソートやハッシュ結合は2GB程度になるように調整しています。
2GBのソート

ORCL@SCOTT> @auto_sortwk2gb_optimal.sql

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
workarea_size_policy string AUTO

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
pga_aggregate_target big integer 4398046511103

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
_pga_max_size big integer 4398046511103

NAME VALUE UNIT CON_ID
---------------------------------------------------------------- ---------------- ------------ ----------
global memory bound 879609302016 bytes 0

1 SELECT
2 /*+
3 MONITOR
4 */
5 *
6 FROM
7 m1
8 WHERE
9 id <= 'C750000'
10 ORDER BY
11 id
12* ,rev#
old 1: select dbms_sqltune.report_sql_monitor(sql_id=>'&1', type=>'text') from dual
new 1: select dbms_sqltune.report_sql_monitor(sql_id=>'', type=>'text') from dual

DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>'',TYPE=>'TEXT')
------------------------------------------------------------------------------------------
SQL Monitoring Report

・・・中略・・・

SQL Plan Monitoring Details (Plan Hash Value=3534657201)
========================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Mem | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | (Max) | (%) | (# samples) |
========================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 34 | +2 | 1 | 1M | | | . | 5.26 | Cpu (1) |
| 1 | SORT ORDER BY | | 1M | 553K | 34 | +2 | 1 | 1M | | | 2GB | 26.32 | Cpu (5) |
| 2 | TABLE ACCESS FULL | M1 | 1M | 90829 | 17 | +1 | 1 | 1M | 2637 | 3GB | . | 68.42 | Cpu (3) |
| | | | | | | | | | | | | | direct path read (10) |
========================================================================================================================================================


2GBのハッシュ結合

ORCL@SCOTT> @auto_hashwk2gb_optimal.sql

・・・中略・・・

NAME VALUE UNIT CON_ID
---------------------------------------------------------------- ------------- ------------ ----------
global memory bound 879609302016 bytes 0

1 SELECT
2 /*+
3 MONITOR
4 LEADING(m1 m2)
5 USE_HASH(m1 m2)
6 */
7 *
8 FROM
9 m1
10 INNER JOIN m2
11 ON
12 m1.id = m2.id
13 AND m1.rev# = m2.rev#
14 WHERE
15* m1.id <= 'C084000'
old 1: select dbms_sqltune.report_sql_monitor(sql_id=>'&1', type=>'text') from dual
new 1: select dbms_sqltune.report_sql_monitor(sql_id=>'', type=>'text') from dual

DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>'',TYPE=>'TEXT')
------------------------------------------------------------------------------------------
SQL Monitoring Report

・・・中略・・・

SQL Plan Monitoring Details (Plan Hash Value=1822065247)
===========================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Mem | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | (Max) | (%) | (# samples) |
===========================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 37 | +2 | 1 | 840K | | | . | 4.35 | Cpu (1) |
| 1 | HASH JOIN | | 843K | 351K | 37 | +2 | 1 | 840K | | | 2GB | 26.09 | Cpu (5) |
| | | | | | | | | | | | | | PGA memory operation (1) |
| 2 | TABLE ACCESS FULL | M1 | 843K | 90828 | 15 | +1 | 1 | 840K | 2633 | 3GB | . | 52.17 | Cpu (1) |
| | | | | | | | | | | | | | direct path read (11) |
| 3 | TABLE ACCESS FULL | M2 | 846K | 90581 | 23 | +16 | 1 | 840K | 2619 | 3GB | . | 17.39 | Cpu (2) |
| | | | | | | | | | | | | | direct path read (2) |
===========================================================================================================================================================


ソートやハッシュ結合は4GB程度になるように調整しています。
見ての通り、ソートは4GBのメモリー内ソートですが、ハッシュ結合は、2GBまで使ったところで Temp落ち! (こんなもんなんですよ。実は!)
4GBのソート

ORCL@SCOTT> @auto_sortwk4gb_optimal.sql

・・・中略・・・

NAME VALUE UNIT CON_ID
---------------------------------------------------------------- ------------- ------------ ----------
global memory bound 879609302016 bytes 0

1 SELECT
2 /*+
3 MONITOR
4 */
5 *
6 FROM
7 m1
8 WHERE
9 id <= 'C085000'
10 UNION ALL
11 SELECT *
12 FROM
13 m1 m12
14 WHERE
15 id <= 'C085000'
16 ORDER BY
17* 1, 2
old 1: select dbms_sqltune.report_sql_monitor(sql_id=>'&1', type=>'text') from dual
new 1: select dbms_sqltune.report_sql_monitor(sql_id=>'', type=>'text') from dual

DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>'',TYPE=>'TEXT')
-----------------------------------------------------------------------------------------
SQL Monitoring Report

・・・中略・・・

SQL Plan Monitoring Details (Plan Hash Value=2686238998)
============================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Mem | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | (Max) | (%) | (# samples) |
============================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 40 | +2 | 1 | 2M | | | . | 27.78 | Cpu (5) |
| 1 | SORT ORDER BY | | 2M | 182K | 41 | +1 | 1 | 2M | | | 4GB | 38.89 | Cpu (6) |
| | | | | | | | | | | | | | PGA memory operation (1) |
| 2 | UNION-ALL | | | | 12 | +2 | 1 | 2M | | | . | | |
| 3 | TABLE ACCESS FULL | M1 | 855K | 90828 | 6 | +2 | 1 | 850K | 2633 | 3GB | . | 22.22 | Cpu (2) |
| | | | | | | | | | | | | | direct path read (2) |
| 4 | TABLE ACCESS FULL | M1 | 855K | 90828 | 6 | +8 | 1 | 850K | 2633 | 3GB | . | 11.11 | Cpu (2) |
============================================================================================================================================================


4GBのハッシュ結合
最大3GBのPGAが消費されていますが、Temp落ちしないサイズ、つまり、optimalで処理する場合には、最大2GBが最大サイズとなっています。以下を3GBのハッシュ結合にすると、2GBまでPGAを消費し、一時表領域が3GB利用されます。

ORCL@SCOTT> @auto_hashwk4gb_optimal.sql
old 1: select dbms_sqltune.report_sql_monitor(sql_id=>'&1', type=>'text') from dual
new 1: select dbms_sqltune.report_sql_monitor(sql_id=>'', type=>'text') from dual

DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>'',TYPE=>'TEXT')
------------------------------------------------------------------------------------------
SQL Monitoring Report

・・・中略・・・

SQL Plan Monitoring Details (Plan Hash Value=3532417599)
=========================================================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Write | Write | Mem | Temp | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | Reqs | Bytes | (Max) | (Max) | (%) | (# samples) |
=========================================================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 216 | +2 | 1 | 3M | | | | | . | . | 4.49 | Cpu (7) |
| 1 | HASH JOIN | | 2M | 670K | 217 | +1 | 1 | 3M | 18091 | 4GB | 18091 | 4GB | 3GB | 4GB | 79.49 | Cpu (87) |
| | | | | | | | | | | | | | | | | direct path read temp (19) |
| | | | | | | | | | | | | | | | | direct path write temp (18) |
| 2 | VIEW | | 2M | 182K | 9 | +2 | 1 | 2M | | | | | . | . | | |
| 3 | UNION-ALL | | | | 9 | +2 | 1 | 2M | | | | | . | . | | |
| 4 | TABLE ACCESS FULL | M1 | 752K | 90828 | 5 | +2 | 1 | 750K | 2633 | 3GB | | | . | . | 1.92 | Cpu (3) |
| 5 | TABLE ACCESS FULL | M1 | 752K | 90828 | 4 | +7 | 1 | 750K | 2633 | 3GB | | | . | . | 1.92 | Cpu (3) |
| 6 | VIEW | | 2M | 181K | 78 | +99 | 1 | 2M | | | | | . | . | 0.64 | Cpu (1) |
| 7 | UNION-ALL | | | | 78 | +99 | 1 | 2M | | | | | . | . | | |
| 8 | TABLE ACCESS FULL | M2 | 759K | 90581 | 50 | +92 | 1 | 750K | 2618 | 3GB | | | . | . | 9.62 | Cpu (2) |
| | | | | | | | | | | | | | | | | direct path read (13) |
| 9 | TABLE ACCESS FULL | M2 | 759K | 90581 | 36 | +141 | 1 | 750K | 2618 | 3GB | | | . | . | 1.92 | Cpu (2) |
| | | | | | | | | | | | | | | | | direct path read (1) |
=========================================================================================================================================================================================


では、最後に
ソートやハッシュ結合のサイズが8GB程度ならどうでしょうか?
結果は、ソートは4GBを使い切ったのち、Temp落ち、ハッシュ結合は、やはり、3GBまで利用したのちTemp落ちでした。なんと!(ちょっとわざとらしいリアクションしてすみませんw)

8GBのソート

ORCL@SCOTT> @auto_sortwk8gb_optimal.sql

・・・中略・・・

NAME VALUE UNIT CON_ID
---------------------------------------------------------------- ------------- ------------ ----------
global memory bound 879609302016 bytes 0

1 SELECT
2 /*+
3 MONITOR
4 */
5 *
6 FROM
7 m1
8 WHERE
9 id <= 'C090000'
10 UNION ALL
11 SELECT *
12 FROM
13 m1 m12
14 WHERE
15 id <= 'C090000'
16 UNION ALL
17 SELECT *
18 FROM
19 m1 m13
20 WHERE
21 id <= 'C090000'
22 UNION ALL
23 SELECT *
24 FROM
25 m1 m14
26 WHERE
27 id <= 'C090000'
28 ORDER BY
29* 1, 2
old 1: select dbms_sqltune.report_sql_monitor(sql_id=>'&1', type=>'text') from dual
new 1: select dbms_sqltune.report_sql_monitor(sql_id=>'', type=>'text') from dual

DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>'',TYPE=>'TEXT')
------------------------------------------------------------------------------------------
SQL Monitoring Report

・・・中略・・・

SQL Plan Monitoring Details (Plan Hash Value=946172832)
=========================================================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Write | Write | Mem | Temp | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | Reqs | Bytes | (Max) | (Max) | (%) | (# samples) |
=========================================================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 279 | +2 | 1 | 4M | | | | | . | . | 5.79 | Cpu (6) |
| | | | | | | | | | | | | | | | | PGA memory operation (1) |
| | | | | | | | | | | | | | | | | local write wait (7) |
| 1 | SORT ORDER BY | | 4M | 363K | 279 | +2 | 1 | 4M | 38603 | 8GB | 32072 | 8GB | 4GB | 8GB | 84.30 | Cpu (49) |
| | | | | | | | | | | | | | | | | PGA memory operation (1) |
| | | | | | | | | | | | | | | | | direct path read temp (28) |
| | | | | | | | | | | | | | | | | direct path write temp (126) |
| 2 | UNION-ALL | | | | 176 | +2 | 1 | 4M | | | | | . | . | | |
| 3 | TABLE ACCESS FULL | M1 | 907K | 90828 | 16 | +1 | 1 | 900K | 2633 | 3GB | | | . | . | 4.55 | Cpu (2) |
| | | | | | | | | | | | | | | | | direct path read (9) |
| 4 | TABLE ACCESS FULL | M1 | 907K | 90828 | 8 | +17 | 1 | 900K | 2633 | 3GB | | | . | . | 2.48 | Cpu (5) |
| | | | | | | | | | | | | | | | | direct path read (1) |
| 5 | TABLE ACCESS FULL | M1 | 907K | 90828 | 79 | +26 | 1 | 900K | 2633 | 3GB | | | . | . | 2.48 | Cpu (2) |
| | | | | | | | | | | | | | | | | direct path read (4) |
| 6 | TABLE ACCESS FULL | M1 | 907K | 90828 | 72 | +106 | 1 | 900K | 2633 | 3GB | | | . | . | 0.41 | Cpu (1) |
=========================================================================================================================================================================================

8GBのハッシュ結合

ORCL@SCOTT> @auto_hashwk8gb_optimal.sql

・・・中略・・・

NAME VALUE UNIT CON_ID
---------------------------------------------------------------- ------------- ------------ ----------
global memory bound 879609302016 bytes 0

1 SELECT
2 /*+
3 MONITOR
4 LEADING(m1 m2)
5 USE_HASH(m1 m2)
6 */
7 *
8 FROM
9 (
10 SELECT * FROM m1 m11
11 UNION ALL
12 SELECT * FROM m1 m12
13 UNION ALL
14 SELECT * FROM m1 m13
15 ) m1
16 INNER JOIN
17 (
18 SELECT * FROM m2 m21
19 UNION ALL
20 SELECT * FROM m2 m22
21 UNION ALL
22 SELECT * FROM m2 m23
23 ) m2
24 ON
25 m1.id = m2.id
26 AND m1.rev# = m2.rev#
27 WHERE
28* m1.id <= 'C075000'
old 1: select dbms_sqltune.report_sql_monitor(sql_id=>'&1', type=>'text') from dual
new 1: select dbms_sqltune.report_sql_monitor(sql_id=>'', type=>'text') from dual

DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>'',TYPE=>'TEXT')
------------------------------------------------------------------------------------------
SQL Monitoring Report

・・・中略・・・

SQL Plan Monitoring Details (Plan Hash Value=2506347387)
=========================================================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Write | Write | Mem | Temp | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | Reqs | Bytes | (Max) | (Max) | (%) | (# samples) |
=========================================================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 350 | +2 | 1 | 7M | | | | | . | . | 9.18 | Cpu (18) |
| | | | | | | | | | | | | | | | | PGA memory operation (1) |
| 1 | HASH JOIN | | 5M | 1M | 350 | +2 | 1 | 7M | 31032 | 7GB | 31032 | 7GB | 3GB | 8GB | 67.15 | Cpu (81) |
| | | | | | | | | | | | | | | | | direct path read temp (36) |
| | | | | | | | | | | | | | | | | direct path write temp (22) |
| 2 | VIEW | | 2M | 272K | 27 | +2 | 1 | 2M | | | | | . | . | | |
| 3 | UNION-ALL | | | | 27 | +2 | 1 | 2M | | | | | . | . | 0.97 | Cpu (2) |
| 4 | TABLE ACCESS FULL | M1 | 752K | 90828 | 16 | +1 | 1 | 750K | 2633 | 3GB | | | . | . | 7.25 | Cpu (3) |
| | | | | | | | | | | | | | | | | direct path read (12) |
| 5 | TABLE ACCESS FULL | M1 | 752K | 90828 | 5 | +18 | 1 | 750K | 2633 | 3GB | | | . | . | 1.45 | Cpu (2) |
| | | | | | | | | | | | | | | | | direct path read (1) |
| 6 | TABLE ACCESS FULL | M1 | 752K | 90828 | 7 | +23 | 1 | 750K | 2633 | 3GB | | | . | . | 1.93 | Cpu (2) |
| | | | | | | | | | | | | | | | | direct path read (2) |
| 7 | VIEW | | 2M | 272K | 130 | +97 | 1 | 2M | | | | | . | . | | |
| 8 | UNION-ALL | | | | 130 | +97 | 1 | 2M | | | | | . | . | 0.48 | Cpu (1) |
| 9 | TABLE ACCESS FULL | M2 | 759K | 90581 | 47 | +88 | 1 | 750K | 2618 | 3GB | | | . | . | 4.83 | Cpu (3) |
| | | | | | | | | | | | | | | | | direct path read (7) |
| 10 | TABLE ACCESS FULL | M2 | 759K | 90581 | 41 | +134 | 1 | 750K | 2618 | 3GB | | | . | . | 4.35 | Cpu (5) |
| | | | | | | | | | | | | | | | | direct path read (4) |
| 11 | TABLE ACCESS FULL | M2 | 759K | 90581 | 53 | +174 | 1 | 750K | 2618 | 3GB | | | . | . | 2.42 | Cpu (1) |
| | | | | | | | | | | | | | | | | direct path read (4) |
=========================================================================================================================================================================================


これ、Linuxのカーネルパラメータのvm.max_map_count

/proc/sys/vm/max_map_count

65530

が絡んでるよねという話と、隠しパラメータの realfree_heap関連のパラメータでも調整できそうだよね。という話はあるんですが...それはPL/SQLのはなし...で


_realfree_heap_pagesize 65536 TRUE
_use_realfree_heap TRUE TRUE

とりあえず、
vm/max_map_countを196608

にして
_realfree_heap_pagesize=65536 や
_realfree_heap_pagesize=256K

などとして戯れてみましたが

ソートやハッシュ結合で利用可能なサイズは、それらで制御できるものでもありません。いまのところ。

ORCL@SCOTT> @auto_sortwk8gb_optimal.sql

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
workarea_size_policy string AUTO

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
pga_aggregate_target big integer 4398046511103

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
_pga_max_size big integer 4398046511103

NAME VALUE UNIT CON_ID
---------------------------------------------------------------- ------------- ------------ ----------
global memory bound 879609302016 bytes 0

/proc/sys/vm/max_map_count
196608

1 SELECT
2 /*+
3 MONITOR
4 */
5 *
6 FROM
7 m1
8 WHERE
9 id <= 'C090000'
10 UNION ALL
11 SELECT *
12 FROM
13 m1 m12
14 WHERE
15 id <= 'C090000'
16 UNION ALL
17 SELECT *
18 FROM
19 m1 m13
20 WHERE
21 id <= 'C090000'
22 UNION ALL
23 SELECT *
24 FROM
25 m1 m14
26 WHERE
27 id <= 'C090000'
28 ORDER BY
29* 1, 2
old 1: select dbms_sqltune.report_sql_monitor(sql_id=>'&1', type=>'text') from dual
new 1: select dbms_sqltune.report_sql_monitor(sql_id=>'', type=>'text') from dual

DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>'',TYPE=>'TEXT')
------------------------------------------------------------------------------------------
SQL Monitoring Report

・・・中略・・・

SQL Plan Monitoring Details (Plan Hash Value=946172832)
=========================================================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Write | Write | Mem | Temp | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | Reqs | Bytes | (Max) | (Max) | (%) | (# samples) |
=========================================================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 295 | +2 | 1 | 4M | | | | | . | . | 3.38 | Cpu (8) |
| 1 | SORT ORDER BY | | 4M | 363K | 297 | +1 | 1 | 4M | 38603 | 8GB | 38600 | 8GB | 4GB | 8GB | 91.56 | Cpu (43) |
| | | | | | | | | | | | | | | | | direct path read temp (12) |
| | | | | | | | | | | | | | | | | direct path write temp (162) |
| 2 | UNION-ALL | | | | 205 | +2 | 1 | 4M | | | | | . | . | 0.84 | Cpu (2) |
| 3 | TABLE ACCESS FULL | M1 | 907K | 90828 | 6 | +2 | 1 | 900K | 2633 | 3GB | | | . | . | 2.11 | Cpu (1) |
| | | | | | | | | | | | | | | | | direct path read (4) |
| 4 | TABLE ACCESS FULL | M1 | 907K | 90828 | 7 | +8 | 1 | 900K | 2633 | 3GB | | | . | . | 0.42 | Cpu (1) |
| 5 | TABLE ACCESS FULL | M1 | 907K | 90828 | 100 | +14 | 1 | 900K | 2633 | 3GB | | | . | . | 0.84 | Cpu (2) |
| 6 | TABLE ACCESS FULL | M1 | 907K | 90828 | 94 | +113 | 1 | 900K | 2633 | 3GB | | | . | . | 0.84 | Cpu (2) |
=========================================================================================================================================================================================


ちなみに、PL/SQLで巨大なPGAメモリーを利用する場合は、しっかり9GBとか...やばい状態になったら、pga_aggregate_limitで抑えてくれると思いますが.....

[oracle@localhost ˜]$ ulimit -v -m
virtual memory (kbytes, -v) unlimited
max memory size (kbytes, -m) unlimited

orcl12c@SYS> @show_param

KSPPINM KSPPSTVL KSPPSTDF
---------------------------------------------- ------------------------------ ------------------------------
_pga_max_size 4398046511103 FALSE
_realfree_heap_pagesize 65536 TRUE
_use_realfree_heap TRUE TRUE
pga_aggregate_limit 0 FALSE
pga_aggregate_target 4398046511103 FALSE


/proc/sys/vm/max_map_count
65530

PGAを大量に消費するスクリプトは以下のブログを参考に
Max PGA Research: Check that a process can allocate a large volume of memory / Yury's Blog


ORCL@SCOTT> @plsql_pga8gb 8192
old 2: c_count number := 1024*&1;
new 2: c_count number := 1024*8192;

PL/SQL procedure successfully completed.

実際に確保されたPGAサイズなど。。。

ORCL@SCOTT> r
1 select
2 vss.value/1024/1024/1024 "GB"
3 ,vsn.name
4 ,vss.sid
5 from
6 v$sesstat vss
7 inner join v$statname vsn
8 on
9 vss.statistic# = vsn.statistic#
10 and vss.con_id = vsn.con_id
11 and (vsn.name like '%pga%' or vsn.name like '%uga%')
12 where
13 sid IN (select sid from v$session where username='SCOTT')
14 order by
15* sid,name

GB NAME SID
---------- ---------------------------------------------------------------- ----------
9.49698084 session pga memory 321
9.49698084 session pga memory max 321
1.48221001 session uga memory 321
3.99991362 session uga memory max 321

[oracle@localhost ˜]$ free -h
total used free shared buff/cache available
Mem: 22G 1.0G 9.6G 11G 11G 10G
Swap: 4.0G 8.7M 4.0G

・・・中略・・・

[oracle@localhost ˜]$ free -h
total used free shared buff/cache available
Mem: 22G 10G 128M 11G 11G 705M
Swap: 4.0G 8.8M 4.0G


ここまでくればおわかりだと思いますが、global memory bound (約839GB)からは巨大なPGAサイズになりそうに思えますが、Temp落ちせずにソート可能なサイズは、隠しパラメータを調整した場合でも最大4GB、ハッシュ結合は、2GBが上限、となっています。いまのところ。。。。

ということは、2014年の以下のプレゼンでも話題になっていましたよね。:)
Overcome Oracle PGA Memory Limits Mar.5.2014 Alex Fatkulin / Enkatic

だったら、どのようにして、Temp落ち に立ち向かえばいいんだろう.....次回へ続く :)




10gR2(64bit)のころのままなので、以下のシリーズも合わせて読んでおくといいですよ:)

Mac De Oracle なんですが、Windows(32bit)でのOracleな話
Mac De Oracle なんですが、Windows(32bit)でのOracleな話 #2
Mac De Oracle なんですが、Windows(32bit)でのOracleな話 #3
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #1
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #2
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #3
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #4
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #5
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #6
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #7
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #8
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #9
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #10
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #11
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #12
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #13
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #14
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #15
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #16
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #17
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #18
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #19
_pga_max_sizeってOracle11gではどうなったっけ? という確認。
_pga_max_sizeってOracle11gではどうなったっけ? という確認。シーズン2
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? Season2 #1
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? Season2 #2
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? Season2 #3
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? Season2 #4
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? Season2 #5
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? Season2 #6
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? Season2 #7
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? Season2 #8


Temp落ち #1 - "Temp落ち" って?
Temp落ち #2 - PGA (Program Global Area)
Temp落ち #3 - 手動PGA管理で作業領域として指定可能な最大サイズ
Temp落ち #4 - 手動PGA管理で作業領域として指定可能な最大サイズ de Temp落ちの確認
Temp落ち #5 - pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? 12.2.0.1版 (その前に少し脱線)
Temp落ち #6 - pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? 12.2.0.1版
Temp落ち #7 - 自動PGA管理で到達可能な最大サイズ de Temp落ちの確認 12.2.0.1版
Temp落ち #8 - 自動PGA管理でパラメータ上設定可能な最大サイズは?(実際に利用可能なサイズとは限りませんが。意味深) 12.2.0.1版

| | コメント (0) | トラックバック (0)

2018年3月12日 (月)

Temp落ち #8 - 自動PGA管理でパラメータ上設定可能な最大サイズは?(実際に利用可能なサイズとは限りませんが。意味深) 12.2.0.1版

Previously on Mac De Oracle

自動PGA管理下では、SQL Work Areaサイズは、pga_aggregate_targetのサイズに応じて変化し、最大1GBまで調整され、それのサイズ以上のソートやハッシュ結合は、もれなく、"Temp落ち" する。
手動PGA管理下では、最大2GBまで指定できました。。自動だと1GBまでなのか。。。と残念がる声が、昔は海だった方面から聞こえた気がしましたが、たぶん、気のせいw

というところまででした。


今日は、自動PGA管理下ではそれが最大なのか? のか? 自己責任で試してみることにしますw

まずはこれまでの復習。

自動PGA管理で、SQL Work Areaサイズの算出に関わるパラメータは以下の通り

pga_aggregate_target = 10MB 〜 4TB - 1
_pga_max_size = 200MB 〜 2GB
Oracleが動的に値を調整している_pga_max_sizeパラメータへユーザーが値を設定してしまうとOracleの自動調整が無効化され、設定した値で固定されてしまうので注意が必要です。

私の観測範囲だとおおよそ以下のように変化します。最近は大量のメモリーを搭載したサーバーが多いので、pga_aggregate_targetが10GB以上という状況も普通になってきたので、自動PGA管理下のSQL Work Areaサイズは最大1GBとざっくり丸暗記しても困ることは思いますw(ちょっと乱暴かw)

pga_aggregate_target = 10MB〜10GB - 1 :
_pga_max_size = 200MB 〜 2047MB
GREATEST(pga_aggregate_target*0.2 ,200MB)

pga_aggregate_target = 10GB以上〜4TB-1 :
_pga_max_size = 2GB
LEAST(pga_aggregate_target*0.2 ,2GB)


_pga_max_sizeはpga_aggregate_targetの値に応じて動的に変化し、それらを元に _smm_max_size が算出される
LEAST(pga_aggregate_target * 0.2, _pga_max_size * 0.5)

pga_aggregate_target = 10MB〜10GB-1
_pga_max_size = 200MB〜2047MB
_smm_max_size = 2MB〜1023MB


pga_aggregate_target = 10GB〜4TB-1
_pga_max_size = 2048MB
_smm_max_size = 1024MB
_smm_max_sizeは、v$pgastatのglobal memory boundだろうということは確からしいということまでは確認しました。

20180307_143308
20180307_143330
いままでの結果をまとめると
PGAのSQL Work Areaサイズの最大サイズを示す global memory boundは、pga_aggregate_targetが10MB〜10GB-1までは2MB〜1023MBで調整され、pga_aggregate_targetが10GB〜4TB-1では、1024MB (1GB) で固定されている、というのが、Oracle 10GR2〜12cR2まで動きであることは間違いなさそう:)

自動PGA管理下でSQL Work Areaを1GBを超えるサイズにするにはどのパラメータをどのような値に設定すればよいか。。。
その鍵を握るパラメータは

_pga_max_size

(いまいちピンとこないという方は、過去のエントリーを参照していただけるとスッキリすると思います。

ただ、冒頭にも書きましたが(昔それでハマったw)、(Oracleが動的に値を調整している_pga_max_sizeパラメータへユーザーが値を設定してしまうとOracleの自動調整が無効化され、設定した値で固定されてしまうので注意が必要です。
という点はお忘れなく。また、今後も同仕様のままである保証はなく、世界中のOracle使いの方達が調べ上げた結果、現状はこんな感じ。
という状況だと認識しておいたほうが無難だと思っています。

とはいえ、実際どこまで設定可能なのか?  1GBを以上使えるのか? 知りたいですよね...

長い前置きはこれぐらいにして、
実際にSQL Work Areaサイズに対応したメモリーが実際にどの程度割り当てられるのかということの確認は後回しですが、 パラメータの上では、どこまで設定できるのかというところを確認しました。


なお、
以下のエラーメッセージから、_pga_max_sizeに設定可能な値は、pga_aggregate_targetと同様(Big Integer)、10MB〜4TB-1までであると思われます。

orcl12c@SYS> show parameter _pga_max_size

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
_pga_max_size big integer 2G

orcl12c@SYS> alter system set "_pga_max_size" = 4T scope=memory;
alter system set "_pga_max_size" = 4T scope=memory
*
行1でエラーが発生しました。:
ORA-02097: 指定した値が無効なので、パラメータを変更できません。 ORA-00093:
_pga_max_sizeは、10Mから4096G-1の間に設定する必要があります。

ということで、
_pga_max_sizeを1GB/10GB/100GB/1TB/4T-1のそれぞれに設定したうえで、Temp落ち #6 - pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? 12.2.0.1版 で利用したスクリプトを利用し、global memory boundやその他パラメータの変化を確認しました。

ログの例

orcl12c@SYS> alter system set "_pga_max_size" = 10m scope=both;

システムが変更されました。

orcl12c@SYS> show parameter _pga_max_size

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
_pga_max_size big integer 10m


orcl12c@SYS> @dotest_auto_workarea
<< 10m >>
接続されました。
1* alter system set pga_aggregate_target=&1 scope=spfile
旧 1: alter system set pga_aggregate_target=&1 scope=spfile
新 1: alter system set pga_aggregate_target=10m scope=spfile

システムが変更されました。

データベースがクローズされました。
データベースがディスマウントされました。
ORACLEインスタンスがシャットダウンされました。
ORACLEインスタンスが起動しました。

Total System Global Area 2147483648 bytes
Fixed Size 8794848 bytes
Variable Size 603983136 bytes
Database Buffers 1526726656 bytes
Redo Buffers 7979008 bytes
データベースがマウントされました。
データベースがオープンされました。
+++ initial parameters +++

KSPPINM KSPPSTVL KSPPSTDF
---------------------------------------------- ------------------------------ ------------------------------
__pga_aggregate_target 16777216 FALSE

・・・中略・・・

pga_aggregate_limit 0 FALSE
pga_aggregate_target 10485760 FALSE

50行が選択されました。

/proc/sys/vm/max_map_count

65530

+++ v$pgastat +++

NAME VALUE UNIT CON_ID
---------------------------------------------------------------- ---------- ------------ ----------
aggregate PGA target parameter 10 MB 0
aggregate PGA auto target 4 MB 0
global memory bound 2 MB 0

・・・中略・・・


結果は以下のグラフと表にまとめたとおり。
_pga_max_size=4TB-1、pga_aggregate_target=4TB-1の時のglobal memory bound (v$pgastat) = _smm_max_size が最大となり、約839GBとなることがわかりました。
パラメータ上は839GB程度まで増加しますが、ほんとうにPGAがそんなサイズまで利用可能なのでしょうか?....(怪しいです。答え、知ってるんですけどねw。。

1g
10g
100g
1t
4t1
Globalmemorybound

ということで、

次回は実際に、そんなに使えんのかよ! という実験をしてみたいと思います :)

つづく。




10gR2(64bit)のころのままなので、以下のシリーズも合わせて読んでおくといいですよ:)
Mac De Oracle なんですが、Windows(32bit)でのOracleな話
Mac De Oracle なんですが、Windows(32bit)でのOracleな話 #2
Mac De Oracle なんですが、Windows(32bit)でのOracleな話 #3
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #1
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #2
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #3
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #4
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #5
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #6
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #7
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #8
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #9
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #10
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #11
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #12
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #13
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #14
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #15
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #16
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #17
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #18
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? #19
_pga_max_sizeってOracle11gではどうなったっけ? という確認。
_pga_max_sizeってOracle11gではどうなったっけ? という確認。シーズン2
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? Season2 #1
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? Season2 #2
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? Season2 #3
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? Season2 #4
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? Season2 #5
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? Season2 #6
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? Season2 #7
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? Season2 #8


Temp落ち #1 - "Temp落ち" って?
Temp落ち #2 - PGA (Program Global Area)
Temp落ち #3 - 手動PGA管理で作業領域として指定可能な最大サイズ
Temp落ち #4 - 手動PGA管理で作業領域として指定可能な最大サイズ de Temp落ちの確認
Temp落ち #5 - pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? 12.2.0.1版 (その前に少し脱線)
Temp落ち #6 - pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? 12.2.0.1版
Temp落ち #7 - 自動PGA管理で到達可能な最大サイズ de Temp落ちの確認 12.2.0.1版


| | コメント (0) | トラックバック (0)

2018年3月 8日 (木)

Temp落ち #7 - 自動PGA管理で到達可能な最大サイズ de Temp落ちの確認 12.2.0.1版

Previously on Mac De Oracle

自動PGA管理で利用可能なSQL Work Areaサイズはいくつなのか?の確認でした。
これまで同様、隠しパラメータ等の変更をしないデフォルトの設定では最大1GBまで到達することを確認しました。
また、手動PGA管理で利用可能な最大サイズより小さいことも再確認しました。


今日は自動PGA管理下ではオンメモリーで処理可能なサイズは1GBまでなのか、それを超えた場合はもれなく "Temp落ち" なのか確認することにします。

確認用データおよび方法は前々回のエントリーを参照ください。


自動PGA管理でGlobal Memory Bound = 1GB(自動PGA管理での最大 Sort作業領域サイズ)


メモリーソートで1GBぐらいまで利用していることが確認できます。(ソートされるデータ量は1GB以下程度に制限しています。)
ORCL@SCOTT> @auto_sortwk1gb_optimal

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
workarea_size_policy string AUTO

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
pga_aggregate_target big integer 4398046511103

NAME VALUE UNIT CON_ID
---------------------------------------------------------------- ---------- ------------ ----------
global memory bound 1073741824 bytes 0

1 SELECT
2 /*+
3 MONITOR
4 */
5 *
6 FROM
7 m1
8 WHERE
9 id <= 'C045000'
10 ORDER BY
11 id
12* ,rev#
old 1: select dbms_sqltune.report_sql_monitor(sql_id=>'&1', type=>'text') from dual
new 1: select dbms_sqltune.report_sql_monitor(sql_id=>'', type=>'text') from dual

DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>'',TYPE=>'TEXT')
------------------------------------------------------------------------------------------
SQL Monitoring Report

・・・中略・・・

Global Stats
===========================================================================
| Elapsed | Cpu | IO | Other | Fetch | Buffer | Read | Read |
| Time(s) | Time(s) | Waits(s) | Waits(s) | Calls | Gets | Reqs | Bytes |
===========================================================================
| 4.78 | 3.85 | 0.34 | 0.59 | 30001 | 334K | 2633 | 3GB |
===========================================================================

SQL Plan Monitoring Details (Plan Hash Value=3534657201)
==================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Mem | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | (Max) | (%) | (# samples) |
==================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 10 | +2 | 1 | 450K | | | . | 33.33 | Cpu (2) |
| 1 | SORT ORDER BY | | 456K | 302K | 11 | +1 | 1 | 450K | | | 1GB | 33.33 | Cpu (2) |
| 2 | TABLE ACCESS FULL | M1 | 456K | 90827 | 3 | +2 | 1 | 450K | 2633 | 3GB | . | 33.33 | Cpu (2) |
==================================================================================================================================================

Temp落ちする程度のデータ量でソートさせた場合も、PGAの作業領域は最大1GBまで利用されていたことが確認できます。

ORCL@SCOTT> @auto_sortwk1gb_mpass    

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
workarea_size_policy string AUTO

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
pga_aggregate_target big integer 4398046511103

NAME VALUE UNIT CON_ID
---------------------------------------------------------------- ---------- ------------ ----------
global memory bound 1073741824 bytes 0

1 SELECT
2 /*+
3 MONITOR
4 */
5 *
6 FROM
7 m1
8 WHERE
9 id <= 'C050000'
10 ORDER BY
11 id
12* ,rev#
old 1: select dbms_sqltune.report_sql_monitor(sql_id=>'&1', type=>'text') from dual
new 1: select dbms_sqltune.report_sql_monitor(sql_id=>'', type=>'text') from dual

DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>'',TYPE=>'TEXT')
------------------------------------------------------------------------------------------
SQL Monitoring Report

・・・中略・・・

Global Stats
===========================================================================================
| Elapsed | Cpu | IO | Other | Fetch | Buffer | Read | Read | Write | Write |
| Time(s) | Time(s) | Waits(s) | Waits(s) | Calls | Gets | Reqs | Bytes | Reqs | Bytes |
===========================================================================================
| 16 | 6.91 | 7.91 | 0.71 | 33335 | 334K | 7996 | 4GB | 4431 | 1GB |
===========================================================================================

SQL Plan Monitoring Details (Plan Hash Value=3534657201)
=====================================================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Write | Write | Mem | Temp | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | Reqs | Bytes | (Max) | (Max) | (%) | (# samples) |
=====================================================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 22 | +2 | 1 | 500K | | | | | . | . | 6.25 | Cpu (1) |
| 1 | SORT ORDER BY | | 507K | 326K | 22 | +2 | 1 | 500K | 5363 | 1GB | 4431 | 1GB | 1GB | 1GB | 68.75 | Cpu (4) |
| | | | | | | | | | | | | | | | | direct path read temp (1) |
| | | | | | | | | | | | | | | | | direct path write temp (6) |
| 2 | TABLE ACCESS FULL | M1 | 507K | 90828 | 13 | +1 | 1 | 500K | 2633 | 3GB | | | . | . | 25.00 | Cpu (3) |
| | | | | | | | | | | | | | | | | direct path read (1) |
=====================================================================================================================================================================================

自動PGA管理でGlobal Memory Bound = 1GB(自動PGA管理での最大 Hash作業領域サイズ)


Hash Joinの場合も、自動PGA管理の最大サイズ程度まで利用されていることが確認できます。(Temp落ちしない程度のデータ量にしています。)
ORCL@SCOTT> @auto_hashwk1gb_optimal

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
workarea_size_policy string AUTO

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
pga_aggregate_target big integer 4398046511103

NAME VALUE UNIT CON_ID
---------------------------------------------------------------- ---------- ------------ ----------
global memory bound 1073741824 bytes 0

1 SELECT
2 /*+
3 MONITOR
4 LEADING(m1 m2)
5 USE_HASH(m1 m2)
6 */
7 *
8 FROM
9 m1
10 INNER JOIN m2
11 ON
12 m1.id = m2.id
13 AND m1.rev# = m2.rev#
14 WHERE
15* m1.id <= 'C042000'
old 1: select dbms_sqltune.report_sql_monitor(sql_id=>'&1', type=>'text') from dual
new 1: select dbms_sqltune.report_sql_monitor(sql_id=>'', type=>'text') from dual

DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>'',TYPE=>'TEXT')
------------------------------------------------------------------------------------------
SQL Monitoring Report

・・・中略・・・

Global Stats
===========================================================================
| Elapsed | Cpu | IO | Other | Fetch | Buffer | Read | Read |
| Time(s) | Time(s) | Waits(s) | Waits(s) | Calls | Gets | Reqs | Bytes |
===========================================================================
| 7.29 | 5.05 | 0.68 | 1.55 | 28001 | 695K | 5251 | 5GB |
===========================================================================

SQL Plan Monitoring Details (Plan Hash Value=1822065247)
==================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Mem | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | (Max) | (%) | (# samples) |
==================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 14 | +2 | 1 | 420K | | | . | 22.22 | Cpu (2) |
| 1 | HASH JOIN | | 423K | 268K | 15 | +1 | 1 | 420K | | | 1GB | 22.22 | Cpu (2) |
| 2 | TABLE ACCESS FULL | M1 | 432K | 90827 | 2 | +2 | 1 | 420K | 2633 | 3GB | . | 11.11 | Cpu (1) |
| 3 | TABLE ACCESS FULL | M2 | 423K | 90580 | 13 | +3 | 1 | 420K | 2618 | 3GB | . | 44.44 | Cpu (4) |
==================================================================================================================================================


Hash Joinの場合もSort時と同じように、一時表領域も利用が必要となるデータ量になると、一旦、自動PGA管理の最大サイズ程度まで利用したうえでTemp落ちしていることが確認できます。

ORCL@SCOTT> @auto_hashwk1gb_mpass

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
workarea_size_policy string AUTO

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
pga_aggregate_target big integer 4398046511103

NAME VALUE UNIT CON_ID
---------------------------------------------------------------- ---------- ------------ ----------
global memory bound 1073741824 bytes 0

1 SELECT
2 /*+
3 MONITOR
4 LEADING(m1 m2)
5 USE_HASH(m1 m2)
6 */
7 *
8 FROM
9 m1
10 INNER JOIN m2
11 ON
12 m1.id = m2.id
13 AND m1.rev# = m2.rev#
14 WHERE
15* m1.id <= 'C055000'
old 1: select dbms_sqltune.report_sql_monitor(sql_id=>'&1', type=>>'text') from dual
new 1: select dbms_sqltune.report_sql_monitor(sql_id=>'', type=>'text') from dual

DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>'',TYPE=>'TEXT')
------------------------------------------------------------------------------------------
SQL Monitoring Report

・・・中略・・・

Global Stats
============================================================================================
| Elapsed | Cpu | IO | Other | Fetch | Buffer | Read | Read | Write | Write |
| Time(s) | Time(s) | Waits(s) | Waits(s) | Calls | Gets | Reqs | Bytes | Reqs | Bytes |
============================================================================================
| 24 | 7.18 | 15 | 1.99 | 36668 | 687K | 10829 | 6GB | 5578 | 1GB |
============================================================================================

SQL Plan Monitoring Details (Plan Hash Value=1822065247)
======================================================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Write | Write | Mem | Temp | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | Reqs | Bytes | (Max) | (Max) | (%) | (# samples) |
======================================================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 33 | +2 | 1 | 550K | | | | | . | . | 14.29 | Cpu (4) |
| 1 | HASH JOIN | | 551K | 293K | 33 | +2 | 1 | 550K | 5578 | 1GB | 5578 | 1GB | 1GB | 1GB | 67.86 | Cpu (8) |
| | | | | | | | | | | | | | | | | direct path read temp (1) |
| | | | | | | | | | | | | | | | | direct path write temp (10) |
| 2 | TABLE ACCESS FULL | M1 | 551K | 90828 | 16 | +1 | 1 | 550K | 2633 | 3GB | | | . | . | 7.14 | Cpu (1) |
| | | | | | | | | | | | | | | | | direct path read (1) |
| 3 | TABLE ACCESS FULL | M2 | 557K | 90580 | 16 | +16 | 1 | 550K | 2618 | 3GB | | | . | . | 10.71 | Cpu (3) |
======================================================================================================================================================================================

PGAの最大サイズは異なりますが、自動PGA管理、手動PGA管理いずれでの場合でも制限値を超えた場合は、もれなく "Temp落ち" するということはご理解いただけたのではないかと思います。


では、次回は恒例?w の隠しパラメータと戯れた場合、自動PGA管理下ではどこまで増加させることができるのか?。。。。確認してみたいと思います。


Temp落ち #1 - "Temp落ち" って?
Temp落ち #2 - PGA (Program Global Area)
Temp落ち #3 - 手動PGA管理で作業領域として指定可能な最大サイズ
Temp落ち #4 - 手動PGA管理で作業領域として指定可能な最大サイズ de Temp落ちの確認
Temp落ち #5 - pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? 12.2.0.1版 (その前に少し脱線)
Temp落ち #6 - pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? 12.2.0.1版

| | コメント (0) | トラックバック (0)

2018年3月 7日 (水)

Temp落ち #6 - pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? 12.2.0.1版

Previously on Mac De Oracle
pga_aggregate_target scope=memory or bothでそれまでとはことなる動きとエラーは発生するところの話でした。


きょうは、自動PGA管理下ではどうなるか確認しておきます。
以前、簡単に確認した範囲結果から、これまでの仕様と大きな違いはないとみています。(変わっていないと思っていたのでブログでも書いていなかったのですが、"Temp落ち"ネタの準備運動も兼ね現状を確認しながら進めてみたいと思います)


まず環境情報から、
確認に利用するインスタンスのPGA以外のパラメータは以下の通りです。
(隠しパラメータは必要がある場合には適宜変更します。また、いくつかのパラメータは確認の都合上物理メモリーサイズ以上に設定することがあります。それらの変更が必要な場合には事前に解説する予定です。)

OS等の情報は以前のエントリーを参照のこと。

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
sga_max_size big integer 12G
sga_min_size big integer 0
sga_target big integer 12G

今回の主役である自動PGA管理のパラメータの初期設定は以下の通り(隠しパラメータを除く)
pga_aggregate_limit = 0 に設定し、pga_aggregate_targetの上限値制限を仕様上のサイズ4096GB - 1まで利用できるようにしておきます。(以前と同様の手順で確認するために、pga_aggregate_limitによる制限を無効化しています。なお、pga_aggregate_limitを0以外に設定して行う場合には、pga_aggregate_targetを4TB - 1まで設定することを考慮すると、pga_aggregate_limitは、8TB以上に設定する必要があります。)

orcl12c@SYS> show parameter pga_aggregate

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
pga_aggregate_limit big integer 0
pga_aggregate_target big integer 10M

上記設定からスタートして、pga_aggregate_targetを10MB/50MB/100MB/500MB/1GB/5GB/10GB/50GB/100GB/500GB/1T/4TB - 1と増加させながら、pgaサイズおよび、関連する隠しパラメータ(_pga_max_sizeや_smm_*など)がどのように変化するか、pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? Season2 #8の手順で確認します。

昔のように実行ログをペタペタ貼っていると長くなるので、確認結果を表とグラフにしました。
想定通り以前と変わっていませんでした:)

デフォルト設定のままであれば、pga_aggregate_targetを限界値まで大きく増やしたとしても、10GB以降、個々のソート操作やハッシュ結合操作で利用できるPGAのSQL work areaサイズはv$pgastatのglobal memory boundの示す通り、1GB(シリアル実行時)が最大であることが確認できます。 えーーー。手動PGA管理より最大サイズ小さいじゃんと、今更驚かないようにしましょうねw (以前からそうなのですからw)
つまり、自動PGA管理の場合、特定の隠しパラメータを変更しない限り、1GBを超えるソート処理やハッシュ結合操作は全て、"Temp落ち" する宿命にあるわけです。12cR2であっても。
え"〜〜〜〜〜っ! と一応、驚いておきましょうw。 メモリーを沢山積んでるマシンは最近多いわけですが、このあたりはなぜか変わってません。
20180307_143101

ちなみに、v$pgastatの示すglobal memory boundは、どこから? という確認がしたくて、 _smm_max_sizeの変化と比較しているグラグも作成しておきました。
_smm_max_sizeパラメータはpga_aggregate_targetのサイズ変化に伴い変化する隠しパラメータですが、このパラメータがglobal memory boundの元になっているのは確からしいですね。
20180307_143308

最後のグラフは、pga_aggregate_targetの値とv$pgastatのglobal memory boundの変化をまとめたグラフです。
global memory boundで示されるPGAのSQL Work Areaサイズの最大サイズには上限があることがわかります:)
20180307_143330

次回は、自動PGA管理では、1GBを超えるソートやハッシュ結合はもれなく"Temp落ち"するのか確認してみることにします。


参考:このエントリーで利用したスクリプト
(今後の確認も兼ねてw このエントリーでネタにしている以外で関連しそうなパラメータも取得して変化を確認できるようにしてあります。)

orcl12c@SYS> !cat show_param.sql
set linesize 200
col ksppinm for a46
col ksppstvl for a30
col ksppstdf for a30
select
a.ksppinm
,b.ksppstvl
,b.ksppstdf
from
x$ksppi a join x$ksppcv b
on a.indx = b.indx
where
a.ksppinm in (
'pga_aggregate_target'
,'pga_aggregate_limit'
,'_use_realfree_heap'
,'_realfree_heap_pagesize'
)
or a.ksppinm like '%\_smm%' escape '\'
or a.ksppinm like '%\_pga%' escape '\'
order by
a.ksppinm
/

!echo /proc/sys/vm/max_map_count
!more /proc/sys/vm/max_map_count

orcl12c@SYS> !cat show_pgstat.sql
select
name
,case
when unit='bytes' then
value/1024/1024
else value
end "VALUE"
,case
when unit='bytes' then
'MB'
else unit
end "UNIT"
,con_id
from
v$pgastat
where
name in (
'aggregate PGA target parameter'
,'aggregate PGA auto target'
,'global memory bound'
,'total PGA used for auto workareas'
,'maximum PGA used for auto workareas'
,'total PGA used for manual workareas'
,'maximum PGA used for manual workareas'
)
;
orcl12c@SYS> !cat auto_workarea.sql
conn sys/oracle@orcl12c as sysdba
alter system set pga_aggregate_target=&1 scope=spfile
.
r
shutdown immediate
startup

prompt +++ initial parameters +++
@show_param

prompt +++ v$pgastat +++
@show_pgstatå
orcl12c@SYS> !cat doTest_auto_workarea.sql
prompt << 10m >>
@auto_workarea 10m

prompt << 50m >>
@auto_workarea 50m

prompt << 100m >>
@auto_workarea 100m

prompt << 500m >>
@auto_workarea 500m

prompt << 1g >>
@auto_workarea 1g

prompt << 5g >>
@auto_workarea 5g

prompt << 10g >>
@auto_workarea 10g

prompt << 50g >>
@auto_workarea 50g

prompt << 100g >>
@auto_workarea 100g

prompt << 500g >>
@auto_workarea 500g

prompt << 1t >>
@auto_workarea 1t

prompt << 4t - 1 >>
@auto_workarea 4398046511103


実行例

orcl12c@SYS> @doTest_auto_workarea.sql
<< 10m >>
Connected.
1* alter system set pga_aggregate_target=&1 scope=spfile
old 1: alter system set pga_aggregate_target=&1 scope=spfile
new 1: alter system set pga_aggregate_target=10m scope=spfile

System altered.

Database closed.
Database dismounted.
ORACLE instance shut down.
ORACLE instance started.

Total System Global Area 1.2885E+10 bytes
Fixed Size 8807168 bytes
Variable Size 1375735040 bytes
Database Buffers 5033164800 bytes
Redo Buffers 24743936 bytes
In-Memory Area 6442450944 bytes
Database mounted.
Database opened.
+++ initial parameters +++

KSPPINM KSPPSTVL KSPPSTDF
---------------------------------------------- ------------------------------ ------------------------------
__pga_aggregate_target 33554432 FALSE

・・・略・・・

pga_aggregate_limit 0 FALSE
pga_aggregate_target 10485760 FALSE

50 rows selected.

/proc/sys/vm/max_map_count

65530

+++ v$pgastat +++

NAME VALUE UNIT CON_ID
---------------------------------------------------------------- ---------- ------------ ----------
aggregate PGA target parameter 10 MB 0
aggregate PGA auto target 4 MB 0
global memory bound 2 MB 0
total PGA used for auto workareas .106445313 MB 0
maximum PGA used for auto workareas .106445313 MB 0
total PGA used for manual workareas 0 MB 0
maximum PGA used for manual workareas 0 MB 0

7 rows selected.

・・・略・・・


Temp落ち #1 - "Temp落ち" って?
Temp落ち #2 - PGA (Program Global Area)
Temp落ち #3 - 手動PGA管理で作業領域として指定可能な最大サイズ
Temp落ち #4 - 手動PGA管理で作業領域として指定可能な最大サイズ de Temp落ちの確認
Temp落ち #5 - pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? 12.2.0.1版 (その前に少し脱線)

| | コメント (0) | トラックバック (0)

2018年2月24日 (土)

Temp落ち #5 - pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? 12.2.0.1版 (その前に少し脱線)


Previously on Mac De Oracle

手動PGA管理下で利用される4つのパラメータ(HASH_AREA_SIZE / SORT_AREA_SIZE / BITMAP_MERGE_AREA_SIZE / CREATE_BITMAP_AREA_SIZE)は、Integer型であり2,147,483,647バイト(つまり2GB - 1)までは指定可能、かつ、個々の作業領域は同程度確保されること。そのサイズを超えるデータ量の場合は一時表領域が利用される。つまり"Temp落ち"が発生するというところまでした。

SQL> show parameter _area_size

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
bitmap_merge_area_size integer 2147483647
create_bitmap_area_size integer 2147483647
hash_area_size integer 2147483647
sort_area_size integer 2147483647

64bit環境でInteger型なので、そんなもんでしょうね。 仕様ですからね、こればっかりはどうにもならない。


で、自動PGA管理で確認しようとパタパタ準備していたところ、12.1.0.2まではなかった12.2.0.1での変更点に気付いたので、そちらを先に。
(少し脱線)

pga_aggregate_targetをscope=memoryで設定する際、物理メモリーの空きサイズ以上に設定しようとするとORA-00855エラーなり設定できないよう動作が変更されたようです。

また、spfileにのみ設定する場合には動作が異なり、設定時には、ORA-00855は発生しません。
アラートログファイルには、起動時のワーニングとして記録され、アラートログにもなにも記録されれず、正常起動しpga_aggregate_targetには指定したサイズで設定されるという、
少々分かりづらい仕様に変わったようです。 (今頃気づくとは、遅い!w

(18cではどうなるんだろう。。なんとなく、Autonomousを意識してるようなアトモスフィアを感じます..が、どうなんでしょうw?)

以下、動作確認の記録。
Guest OSのメモリーサイズは以下の通り。空いているメモリーサイズは500MB〜600MB程度です。

Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production
に接続されました。
orcl12c@SYS> !cat /proc/meminfo | grep Mem
MemTotal: 3781732 kB
MemFree: 43300 kB
MemAvailable: 559076 kB


orcl12c@SYS> show parameter sga

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
allow_group_access_to_sga boolean FALSE
lock_sga boolean FALSE
pre_page_sga boolean TRUE
sga_max_size big integer 2G
sga_min_size big integer 0
sga_target big integer 2G
unified_audit_sga_queue_size integer 1048576

orcl12c@SYS> show parameter pga_agg

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
pga_aggregate_limit big integer 2G
pga_aggregate_target big integer 200M


この状態で、pga_aggregate_limitの50%のサイズでpga_aggregate_limitの制限内、pga_aggregate_target=1g としてメモリー上でのみ変更してみると

orcl12c@SYS> alter system set pga_aggregate_target=1g scope=memory;
alter system set pga_aggregate_target=1g scope=memory
*
行1でエラーが発生しました。:
ORA-02097: 指定した値が無効なので、パラメータを変更できません。
ORA-00855: PGA_AGGREGATE_TARGET cannot be set because of insufficient physical memory.


空きメモリーサイズ以下ならどうなるか試して見ます。

orcl12c@SYS> alter system set pga_aggregate_target=500m scope=memory;

システムが変更されました。


空きメモリーサイズを多少上回るサイズでは。。。(やはり、ORA-00855となります)

orcl12c@SYS> alter system set pga_aggregate_target=700m scope=memory;
alter system set pga_aggregate_target=700m scope=memory
*
行1でエラーが発生しました。:
ORA-02097: 指定した値が無効なので、パラメータを変更できません。
ORA-00855: PGA_AGGREGATE_TARGET cannot be set because of insufficient physical memory.


scope=bothとなる状態で、空きメモリー以上のサイズを設定しようとしても同様のエラーになりますが.....

orcl12c@SYS> alter system set pga_aggregate_target=1g;
alter system set pga_aggregate_target=1g
*
行1でエラーが発生しました。:
ORA-02097: 指定した値が無効なので、パラメータを変更できません。
ORA-00855: PGA_AGGREGATE_TARGET cannot be set because of insufficient physical memory.


scope=spfileとした場合には、エラーにならず変更可能です。このあたりの動きは、他のパラメータと同様、起動時にエラーで起動しないという動きになるのかな??? と思いきや.....
なんと、正常起動し、pga_aggregate_targetにはしっかり、1GBが設定されています。!!!!! (動きが違う!!!)

orcl12c@SYS> alter system set pga_aggregate_target=1g scope=spfile;

システムが変更されました。

orcl12c@SYS> shutdown immediate
データベースがクローズされました。
データベースがディスマウントされました。
ORACLEインスタンスがシャットダウンされました。
orcl12c@SYS> startup
ORACLEインスタンスが起動しました。

Total System Global Area 2147483648 bytes
Fixed Size 8794848 bytes
Variable Size 603983136 bytes
Database Buffers 1526726656 bytes
Redo Buffers 7979008 bytes
データベースがマウントされました。
データベースがオープンされました。
orcl12c@SYS>
orcl12c@SYS> show parameter pga_agg

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
pga_aggregate_limit big integer 2G
pga_aggregate_target big integer 1G

なぜ、動作が違うんだろう........


2018/2/25訂正
以下のメッセージよくよく見たら、pga_aggregate_limitパラメータへの警告でpga_aggregate_targetのORA-00855とは無関係なワーニングでした。orz.

pga_aggregate_limitの下限値は2G、sea_max_size=2Gなのに、Guest OSへのメモリー割り当て4Gなのでそりゃでるわね。というこでした。

とはいえ、 pga_aggreagte_target scope=memoryとspfileではORA-00855エラーの有無の違いはあるようで、spfileに設定した場合にはORA-00855は発生せず、アラートログファイルには何も記録されていない(謎


なお、起動時のアラートログファイルには以下の"WARNING"メッセージが記録されます。しかも、メッセージを見る限り、pga_aggregate_limit へのワーニングと読めるが....

2018-02-24T22:16:38.907017+09:00
WARNING: pga_aggregate_limit value is too high for the
amount of physical memory on the system
PGA_AGGREGATE_LIMIT is 2048 MB
PGA_AGGREGATE_TARGET is 1024 MB.
physical memory size is 3693 MB
limit based on physical memory and SGA usage is 1275 MB
SGA_TARGET is 2048 MB

おまけ


12.1.0.2までは該当するメッセージはありません。
[oracle@vbgeneric ˜]$ sqlplus -version

SQL*Plus: Release 12.1.0.2.0 Production

[oracle@vbgeneric ˜]$ oerr ORA 855
[oracle@vbgeneric ˜]$



12.2.0.1で実装されたメッセージと機能だということがわかります。
[oracle@localhost ˜]$ sqlplus -version

SQL*Plus: Release 12.2.0.1.0 Production

[oracle@localhost ˜]$ oerr ORA 855
00855, 00000, "PGA_AGGREGATE_TARGET cannot be set because of insufficient physical memory."
// *Cause: PGA_AGGREGATE_TARGET value was too high for the current system global area (SGA) size and amount of physical memory available.
// *Action: Reduce the SGA size or increase the physical memory size.

ということで、次回こそw 自動PGA管理下での確認へ

続く。


Temp落ち #1 - "Temp落ち" って?
Temp落ち #2 - PGA (Program Global Area)
Temp落ち #3 - 手動PGA管理で作業領域として指定可能な最大サイズ
Temp落ち #4 - 手動PGA管理で作業領域として指定可能な最大サイズ de Temp落ちの確認

| | コメント (0) | トラックバック (0)

2018年2月22日 (木)

Temp落ち #4 - 手動PGA管理で作業領域として指定可能な最大サイズ de Temp落ちの確認


Previously on Mac De Oracle
手動PGA管理で作業領域として指定可能な最大サイズは、2GB - 1までということの確認でした。
本当に、その程度のサイズまでPGAの作業領域が利用されるのでしょうか?。 念のために確認しておきましょう。
実は、それより少ないサイズで頭打ちなんてことは、ないかな〜 と わざとらしく言ってみたりして(意味深w

その前に、指定した作業領域を使い切れるぐらい(つまり、Temp落ちさせられる程度)のデータ量の表を準備しておきます。
今回は、Nested Loop Join(NLJ)やソート回避などのための索引は作成しません。Temp落ちのネタなので。

M1とM2の2表を作成し、それぞれ、2.5GB程度のセグメントサイズにしておきます。
なお、以下の無名PL/SQLブロックでは、FORALLを利用して1000行単位でバルク処理しています。配列を利用するのでメモリー使用量にはそれなりに配慮が必要ですが。:)
単純なぐるぐる系INSERTにしてしまうとデータ量が多い場合、性能的に辛くなってしまうので、ここ重要!

ORCL@SCOTT> l
1 CREATE TABLE m1
2 (
3 id CHAR(7) NOT NULL
4 ,rev# NUMBER NOT NULL
5 ,value NUMBER NOT NULL
6 ,description VARCHAR2(4000)
7 ,additional_info CHAR(200)
8* ) NOLOGGING
ORCL@SCOTT> /

Table created.

ORCL@SCOTT> l
1 DECLARE
2 TYPE IDS_t IS TABLE OF m1.id%TYPE INDEX BY PLS_INTEGER;
3 TYPE REV#S_t IS TABLE OF m1.rev#%TYPE INDEX BY PLS_INTEGER;
4 TYPE VALS_t IS TABLE OF m1.value%TYPE INDEX BY PLS_INTEGER;
5 IDs IDS_t;
6 REV#s REV#S_t;
7 VALs VALS_t;
8 k PLS_INTEGER := 1;
9 BEGIN
10 FOR i IN 1..100000 LOOP
11 FOR j IN 1..10 LOOP
12 IDs(k) := 'C' || TO_CHAR(i, 'FM000000');
13 REV#s(k) := j;
14 VALs(k) := i + j;
15 k := k + 1;
16 END LOOP;
17 IF MOD(i, 100) = 0 THEN
18 FORALL l in 1..1000 EXECUTE IMMEDIATE
19 'INSERT /*+ APPEND_VALUE NO_GATHER_OPTIMIZER_STATISTICS */ INTO m1 '
20 || 'VALUES(:1, :2, :3, LPAD(''X'',2000, ''X''), LPAD(''9'',200,''9''))'
21 USING IDs(l), REV#s(l), VALs(l);
22 COMMIT;
23 k := 1;
24 END IF;
25 END LOOP;
26* END;
ORCL@SCOTT> /

PL/SQL procedure successfully completed.

Elapsed: 00:01:22.45
ORCL@SCOTT> select count(1) from m1;

COUNT(1)
----------
1000000

ORCL@SCOTT> select segment_name,sum(bytes)/1024/1024/1024 "GB" from user_segments where segment_name='M1' group by segment_name;

SEGMENT_NAME GB
------------------------------ ----------
M1 2.5625

ORCL@SCOTT> l
1 CREATE TABLE m2
2 (
3 id CHAR(7) NOT NULL
4 ,rev# NUMBER NOT NULL
5 ,value NUMBER NOT NULL
6 ,description VARCHAR2(4000)
7* ) NOLOGGING
ORCL@SCOTT> /

Table created.

ORCL@SCOTT> l
1 INSERT /*+ APPEND NO_GATHER_OPTIMIZER_STATISTICS */ INTO m2
2* SELECT id,rev#,value,description FROM m1
ORCL@SCOTT> /

1000000 rows created.

Elapsed: 00:00:40.22
ORCL@SCOTT> commit;

Commit complete.

ORCL@SCOTT> exec dbms_stats.gather_table_stats(ownname=>'SCOTT',tabname=>'M1',no_invalidate=>false,method_opt=>'FOR ALL COLUMNS SIZE SKEWONLY');

PL/SQL procedure successfully completed.

ORCL@SCOTT> exec dbms_stats.gather_table_stats(ownname=>'SCOTT',tabname=>'M2',no_invalidate=>false,method_opt=>'FOR ALL COLUMNS SIZE SKEWONLY');

PL/SQL procedure successfully completed.

これで、準備完了。
ちなみに、NO_GATHER_OPTIMIZER_STATISTICSヒントを利用していますが、データ登録後に、ヒストグラムも含めて取得したかったため、バルクロード時のオンラインオプティマイザ統計収集を行わないようにするためのヒントです。
データ登録後オプティマイザ統計を取得するため、データ登録時のオンラインオプティマイザ統計のオーバーヘッドは無駄となるためです。利用できるなら利用したほうがよいとは思いますが、制限もあるのでご一読を(バルク・ロードのためのオンライン統計収集



手動PGA管理で2GB - 1(手動PGA管理での最大 Sort作業領域サイズ)


メモリーソートで2GBぐらいまで利用していることが確認できます。(ソートされるデータ量が2GB以下程度に制限しています。)
ORCL@SCOTT> @manual_sortwk2gb_optimal    
1* alter session set workarea_size_policy=manual

Session altered.

1* alter session set sort_area_size = 2147483647

Session altered.

1 SELECT
2 /*+
3 MONITOR
4 */
5 *
6 FROM
7 m1
8 WHERE
9 id <= 'C075000'
10 ORDER BY
11 id
12* ,rev#
old 1: select dbms_sqltune.report_sql_monitor(sql_id=>'&1', type=>'text') from dual
new 1: select dbms_sqltune.report_sql_monitor(sql_id=>'', type=>'text') from dual

DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>'',TYPE=>'TEXT')
------------------------------------------------------------------------------------------
SQL Monitoring Report

・・・中略・・・

Global Stats
=================================================
| Elapsed | Cpu | Other | Fetch | Buffer |
| Time(s) | Time(s) | Waits(s) | Calls | Gets |
=================================================
| 5.42 | 4.13 | 1.29 | 50001 | 334K |
=================================================

SQL Plan Monitoring Details (Plan Hash Value=3534657201)
===================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Mem | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | (Max) | (%) | (# samples) |
===================================================================================================================================
| 0 | SELECT STATEMENT | | | | 15 | +2 | 1 | 750K | . | 20.00 | Cpu (1) |
| 1 | SORT ORDER BY | | 752K | 439K | 16 | +1 | 1 | 750K | 2GB | 60.00 | Cpu (3) |
| 2 | TABLE ACCESS FULL | M1 | 752K | 90828 | 3 | +2 | 1 | 750K | . | 20.00 | Cpu (1) |
===================================================================================================================================

Temp落ちする程度のデータ量でソートさせた場合も、PGAの作業領域は一旦、2GBまで利用されていることが確認できます。

ORCL@SCOTT> @manual_sortwk2gb
1* alter session set workarea_size_policy=manual

Session altered.

1* alter session set sort_area_size = 2147483647

Session altered.

1 SELECT
2 /*+
3 MONITOR
4 */
5 *
6 FROM
7 m1
8 ORDER BY
9 id
10* ,rev#
old 1: select dbms_sqltune.report_sql_monitor(sql_id=>'&1', type=>'text') from dual
new 1: select dbms_sqltune.report_sql_monitor(sql_id=>'', type=>'text') from dual

DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>'',TYPE=>'TEXT')
-------------------------------------------------------------------------------------------
SQL Monitoring Report

・・・中略・・・

Global Stats
===========================================================================================
| Elapsed | Cpu | IO | Other | Fetch | Buffer | Read | Read | Write | Write |
| Time(s) | Time(s) | Waits(s) | Waits(s) | Calls | Gets | Reqs | Bytes | Reqs | Bytes |
===========================================================================================
| 46 | 11 | 15 | 20 | 66668 | 334K | 2148 | 2GB | 2146 | 2GB |
===========================================================================================

SQL Plan Monitoring Details (Plan Hash Value=3534657201)
=====================================================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Write | Write | Mem | Temp | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | Reqs | Bytes | (Max) | (Max) | (%) | (# samples) |
=====================================================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 60 | +2 | 1 | 1M | | | | | . | . | 6.38 | Cpu (2) |
| | | | | | | | | | | | | | | | | PGA memory operation (1) |
| 1 | SORT ORDER BY | | 1M | 553K | 61 | +1 | 1 | 1M | 2148 | 2GB | 2146 | 2GB | 2GB | 2GB | 91.49 | Cpu (32) |
| | | | | | | | | | | | | | | | | direct path read temp (10) |
| | | | | | | | | | | | | | | | | direct path write temp (1) |
| 2 | TABLE ACCESS FULL | M1 | 1M | 90822 | 27 | +2 | 1 | 1M | | | | | . | . | 2.13 | Cpu (1) |
=====================================================================================================================================================================================

手動PGA管理で2GB - 1(手動PGA管理での最大 Hash作業領域サイズ)
Hash Joinの場合も、手動PGA管理で設定可能な最大サイズ程度まで利用されていることが確認できます。(Temp落ちしない程度のデータ量にしています。)

ORCL@SCOTT> @manual_hashwk2gb_optimal
1* alter session set workarea_size_policy = manual

Session altered.

1* alter session set hash_area_size = 2147483647

Session altered.

1 SELECT
2 /*+
3 MONITOR
4 LEADING(m1 m2)
5 USE_HASH(m1 m2)
6 */
7 *
8 FROM
9 m1
10 INNER JOIN m2
11 ON
12 m1.id = m2.id
13 AND m1.rev# = m2.rev#
14 WHERE
15* m1.id <= 'C075000'
old 1: select dbms_sqltune.report_sql_monitor(sql_id=>'&1', type=>'text') from dual
new 1: select dbms_sqltune.report_sql_monitor(sql_id=>'', type=>'text') from dual

DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>'',TYPE=>'TEXT')
------------------------------------------------------------------------------------------
SQL Monitoring Report

・・・中略・・・

Global Stats
===========================================================================
| Elapsed | Cpu | IO | Other | Fetch | Buffer | Read | Read |
| Time(s) | Time(s) | Waits(s) | Waits(s) | Calls | Gets | Reqs | Bytes |
===========================================================================
| 8.05 | 4.66 | 0.16 | 3.23 | 50001 | 717K | 2618 | 3GB |
===========================================================================

SQL Plan Monitoring Details (Plan Hash Value=1822065247)
==================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Mem | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | (Max) | (%) | (# samples) |
==================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 21 | +2 | 1 | 750K | | | . | 12.50 | Cpu (1) |
| 1 | HASH JOIN | | 752K | 181K | 22 | +1 | 1 | 750K | | | 2GB | 25.00 | Cpu (2) |
| 2 | TABLE ACCESS FULL | M1 | 752K | 90828 | 2 | +2 | 1 | 750K | | | . | 25.00 | Cpu (2) |
| 3 | TABLE ACCESS FULL | M2 | 759K | 90581 | 20 | +3 | 1 | 750K | 2618 | 3GB | . | 37.50 | Cpu (3) |
==================================================================================================================================================

Hash Joinの場合もSort時と同じように、一時表領域も利用させる程度のデータ量になると、一旦、手動PGA管理で設定可能な最大サイズ程度まで利用したうえでTemp落ちしていることが確認できます。

ORCL@SCOTT> @manual_hashwk2gb
1* alter session set workarea_size_policy = manual

Session altered.

1* alter session set hash_area_size = 2147483647

Session altered.

1 SELECT
2 /*+
3 MONITOR
4 LEADING(m1 m2)
5 USE_HASH(m1 m2)
6 */
7 *
8 FROM
9 m1
10 INNER JOIN m2
11 ON
12 m1.id = m2.id
13* AND m1.rev# = m2.rev#
old 1: select dbms_sqltune.report_sql_monitor(sql_id=>'&1', type=>'text') from dual
new 1: select dbms_sqltune.report_sql_monitor(sql_id=>'', type=>'text') from dual

DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>'',TYPE=>'TEXT')
------------------------------------------------------------------------------------------
SQL Monitoring Report

・・・中略・・・

Global Stats
===========================================================================================
| Elapsed | Cpu | IO | Other | Fetch | Buffer | Read | Read | Write | Write |
| Time(s) | Time(s) | Waits(s) | Waits(s) | Calls | Gets | Reqs | Bytes | Reqs | Bytes |
===========================================================================================
| 12 | 5.68 | 2.17 | 4.59 | 66668 | 706K | 3133 | 3GB | 515 | 511MB |
===========================================================================================

SQL Plan Monitoring Details (Plan Hash Value=1822065247)
=====================================================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Write | Write | Mem | Temp | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | Reqs | Bytes | (Max) | (Max) | (%) | (# samples) |
=====================================================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 30 | +2 | 1 | 1M | | | | | . | . | 25.00 | Cpu (4) |
| 1 | HASH JOIN | | 1M | 189K | 31 | +1 | 1 | 1M | 515 | 511MB | 515 | 511MB | 2GB | 515MB | 43.75 | Cpu (5) |
| | | | | | | | | | | | | | | | | direct path read temp (1) |
| | | | | | | | | | | | | | | | | direct path write temp (1) |
| 2 | TABLE ACCESS FULL | M1 | 1M | 90822 | 6 | +0 | 1 | 1M | | | | | . | . | 12.50 | Cpu (2) |
| 3 | TABLE ACCESS FULL | M2 | 1M | 90574 | 24 | +5 | 1 | 1M | 2618 | 3GB | | | . | . | 18.75 | Cpu (2) |
| | | | | | | | | | | | | | | | | direct path read (1) |
=====================================================================================================================================================================================

おまけ
1つのSQLの実行で利用されるPGAの作業領域は1つだけではないということも確認しておきましょうかね。(知ってる方はスルーしてよいですよ:)
以下の例では、MERGE JOINさせていますが、表M1と表M2それぞれのSort作業領域は、Merge Join終了時まで保持されることになるため、最大3GBのSort作業領域が利用されています。(SQLモニターの結果ではわかりにくいのですが。。。)
なお、手動PGA管理、自動PGA管理に関係なく、実行される操作により複数の作業領域が同時に確保されることがあります。
(ちなみに、自動PGA管理で利用されるPGA_AGGREGATE_LIMITがPGA_AGGREGATE_TARGETの2倍とされるのも、このような動きが考慮された結果だと知っていると、納得感はあるかもしれません。)

前回使った図で朱色で示したSQL Work Areaが複数ありますが、Hash/Sort/Bitmap系など複数のタイプおよび同一タイプの作業領域が同時に確保されることも意図した図になっているのは、これが理由なんです。


Structure_of_pga

ORCL@SCOTT> @manual_sortwk2gb2_optimal
1* alter session set workarea_size_policy = manual

Session altered.

1* alter session set sort_area_size = 2147483647

Session altered.

1 SELECT
2 /*+
3 MONITOR
4 USE_MERGE(m1 m2)
5 */
6 *
7 FROM
8 m1
9 INNER JOIN m2
10 ON
11 m1.id = m2.id
12 AND m1.rev# = m2.rev#
13 WHERE
14* m1.id <= 'C075000'
old 1: select dbms_sqltune.report_sql_monitor(sql_id=>'&1', type=>'text') from dual
new 1: select dbms_sqltune.report_sql_monitor(sql_id=>'', type=>'text') from dual

DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>'',TYPE=>'TEXT')
-------------------------------------------------------------------------------------------
SQL Monitoring Report

・・・中略・・・

Global Stats
===========================================================================
| Elapsed | Cpu | IO | Other | Fetch | Buffer | Read | Read |
| Time(s) | Time(s) | Waits(s) | Waits(s) | Calls | Gets | Reqs | Bytes |
===========================================================================
| 17 | 10 | 4.75 | 2.19 | 50001 | 667K | 5251 | 5GB |
===========================================================================

SQL Plan Monitoring Details (Plan Hash Value=1391069689)
========================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Mem | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | (Max) | (%) | (# samples) |
========================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 23 | +10 | 1 | 750K | | | . | 14.29 | Cpu (2) |
| 1 | MERGE JOIN | | 752K | 849K | 23 | +10 | 1 | 750K | | | . | | |
| 2 | SORT JOIN | | 752K | 439K | 32 | +1 | 1 | 750K | | | 2GB | 21.43 | Cpu (3) |
| 3 | TABLE ACCESS FULL | M1 | 752K | 90828 | 8 | +2 | 1 | 750K | 2633 | 3GB | . | 35.71 | Cpu (3) |
| | | | | | | | | | | | | | direct path read (2) |
| 4 | SORT JOIN | | 759K | 410K | 23 | +10 | 750K | 750K | | | 1GB | 14.29 | Cpu (2) |
| 5 | TABLE ACCESS FULL | M2 | 759K | 90581 | 5 | +10 | 1 | 750K | 2618 | 3GB | . | 14.29 | Cpu (2) |
========================================================================================================================================================


SQLモニターの結果ではわかりにくいわけですが、v$sesstatをから眺めれば状況がよくわかります!
2つのソート作業領域が同時に確保されたことで、2GB + 1GB = 3GB程度のサイズにまで達していることが確認できます。

ORCL@SYSTEM> r
1 select
2 vss.value/1024/1024/1024 "GB"
3 ,vsn.name
4 ,vss.sid
5 from
6 v$sesstat vss
7 inner join v$statname vsn
8 on
9 vss.statistic# = vsn.statistic#
10 and vss.con_id = vsn.con_id
11 and vsn.name like '%pga%'
12 where
13 sid IN (select sid from v$session where username='SCOTT')
14 order by
15* sid,name

GB NAME SID
---------- ---------------------------------------------------------------- ----------
3.04611414 session pga memory 203
3.04611414 session pga memory max 203

では、次回はやっとw、真打、自動PGA管理下での確認。(引っ張り過ぎかもしれないw)
つづく。


Temp落ち #1 - "Temp落ち" って?
Temp落ち #2 - PGA (Program Global Area)
Temp落ち #3 - 手動PGA管理で作業領域として指定可能な最大サイズ

| | コメント (0) | トラックバック (0)

2018年2月20日 (火)

Temp落ち #3 - 手動PGA管理で作業領域として指定可能な最大サイズ

自動PGA管理は12cでどうなんだっけ?という確認の前に、
いままで何度か質問されたことがあり、FAQだと思っているので

手動PGA管理で利用する以下パラメータの最大サイズはいくつ? 

HASH_AREA_SIZE
SORT_AREA_SIZE
BITMAP_MERGE_AREA_SIZE
CREATE_BITMAP_AREA_SIZE

ということを書いておきたいと思います。

これからしばらく続く Temp落ち ネタで利用する環境で固定部分は以下のとおり
(初期化パラメータ等は必要に応じて載せるつもりです。)


環境は以下のとおり。
host osとguest osのバージョンやメモリーサイズなど

discus:˜ oracle$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.13.3
BuildVersion: 17D47

discus:˜ oracle$ system_profiler SPHardwareDataType | grep -E 'Processor Name|Cores|Memory'
Processor Name: 6-Core Intel Xeon
Total Number of Cores: 12
Memory: 32 GB

discus:˜ oracle$ VBoxManage -v
5.2.6r120293

discus:˜ oracle$ VBoxManage showvminfo e3d4f948-b2e6-4db3-a89d-df637d87a372 | grep -E 'Memory size|OS type|Number of CPUs'
Memory size: 23569MB
Number of CPUs: 12
OS type: Linux26_64


orcl12c@SYS> select * from v$version;

BANNER CON_ID
-------------------------------------------------------------------------------- ----------
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production 0
PL/SQL Release 12.2.0.1.0 - Production 0
CORE 12.2.0.1.0 Production 0
TNS for Linux: Version 12.2.0.1.0 - Production 0
NLSRTL Version 12.2.0.1.0 - Production 0


orcl12c@SYS> show pdbs

CON_ID CON_NAME OPEN MODE RESTRICTED
---------- ------------------------------ ---------- ----------
2 PDB$SEED READ ONLY NO
3 ORCL READ WRITE NO

さて、今日の本題

手動PGA管理で各SQL Work Area Sizeを決定する以下の初期化パラメータの最大サイズは? いくつでしょう? 
すでにご存知のかたはスキップしていいですよ:)

マニュアルを読んでみるときづくのですが、手動PGA管理で各SQL Work Area Sizeを決定する4パラメータとも、「0以上、上限はOS依存」のような記述になっています。

そう、マニュアルを読んだだけではまったく参考にならないわけです。(え〜〜っ!)

そこで、みなさんサポートを頼るなり、ご自分でMOSを検索するなり、そこそこの苦労をして入手することになります。
私みたいな性格だと、どのマニュアルでもいいからまとめて載せてよーめんどくさいから。と、めんどくさい病の発作がでたりしますw

なので、環境があるのなら、you 試しちゃいなよー。が手っ取り早いかなーと(最終的にMOSとかサポートを頼るにしてもw)

上限はOS依存とだけしか記載されていませんが、私の観測範囲では、2GB - 1 が上限 となっています。
以下、Linux/Solaris/Windowsでの検証ログ。

Oracle® Databaseリファレンス 12cリリース2 (12.2) E72905-03より

HASH_AREA_SIZE
https://docs.oracle.com/cd/E82638_01/REFRN/HASH_AREA_SIZE.htm

SORT_AREA_SIZE
https://docs.oracle.com/cd/E82638_01/REFRN/SORT_AREA_SIZE.htm

BITMAP_MERGE_AREA_SIZE
https://docs.oracle.com/cd/E82638_01/REFRN/BITMAP_MERGE_AREA_SIZE.htm

CREATE_BITMAP_AREA_SIZE
https://docs.oracle.com/cd/E82638_01/REFRN/CREATE_BITMAP_AREA_SIZE.htm


Linux

orcl12c@SYS> !uname -r; cat /etc/redhat-release /etc/oracle-release
4.1.12-94.3.6.el7uek.x86_64
Red Hat Enterprise Linux Server release 7.3 (Maipo)
Oracle Linux Server release 7.3

orcl12c@SYS> create pfile from spfile;

File created.

orcl12c@SYS> --2GB
orcl12c@SYS> select 2*1024*1024*1024 from dual;

2*1024*1024*1024
----------------
2147483648

orcl12c@SYS> alter system set hash_area_size = 2147483648 scope=spfile;
alter system set hash_area_size = 2147483648 scope=spfile
*
ERROR at line 1:
ORA-02017: integer value required

orcl12c@SYS> alter system set sort_area_size = 2147483648 scope=spfile;
alter system set sort_area_size = 2147483648 scope=spfile
*
ERROR at line 1:
ORA-02017: integer value required

orcl12c@SYS> alter system set bitmap_merge_area_size = 2147483648 scope=spfile;
alter system set bitmap_merge_area_size = 2147483648 scope=spfile
*
ERROR at line 1:
ORA-02017: integer value required

orcl12c@SYS> alter system set create_bitmap_area_size = 2147483648 scope=spfile;
alter system set create_bitmap_area_size = 2147483648 scope=spfile
*
ERROR at line 1:
ORA-02017: integer value required

orcl12c@SYS> --2GB - 1
orcl12c@SYS> select 2*1024*1024*1024-1 from dual;

2*1024*1024*1024-1
------------------
2147483647

orcl12c@SYS> alter system set hash_area_size = 2147483647 scope=spfile;

System altered.

orcl12c@SYS> alter system set sort_area_size = 2147483647 scope=spfile;

System altered.

orcl12c@SYS> alter system set bitmap_merge_area_size = 2147483647 scope=spfile;

System altered.

orcl12c@SYS> alter system set create_bitmap_area_size = 2147483647 scope=spfile;

System altered.

orcl12c@SYS> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
orcl12c@SYS> create spfile from pfile;

File created.

orcl12c@SYS>


Solaris 11 (x86)

SQL> !uname -r; cat /etc/release
5.11
Oracle Solaris 11.3 X86
Copyright (c) 1983, 2015, Oracle and/or its affiliates. All rights reserved.
Assembled 06 October 2015

SQL>
SQL> create pfile from spfile;

ファイルが作成されました。

SQL> --2GB
SQL> select 2*1024*1024*1024 from dual;

2*1024*1024*1024
----------------
2147483648

SQL> alter system set hash_area_size = 2147483648 scope=spfile;
alter system set hash_area_size = 2147483648 scope=spfile
*
行1でエラーが発生しました。:
ORA-02017: 整数値が必要です。


SQL> alter system set sort_area_size = 2147483648 scope=spfile;
alter system set sort_area_size = 2147483648 scope=spfile
*
行1でエラーが発生しました。:
ORA-02017: 整数値が必要です。


SQL> alter system set bitmap_merge_area_size = 2147483648 scope=spfile;
alter system set bitmap_merge_area_size = 2147483648 scope=spfile
*
行1でエラーが発生しました。:
ORA-02017: 整数値が必要です。


SQL> alter system set create_bitmap_area_size = 2147483648 scope=spfile;
alter system set create_bitmap_area_size = 2147483648 scope=spfile
*
行1でエラーが発生しました。:
ORA-02017: 整数値が必要です。


SQL> --2GB - 1
SQL> select 2*1024*1024*1024-1 from dual;

2*1024*1024*1024-1
------------------
2147483647

SQL> alter system set hash_area_size = 2147483647 scope=spfile;

システムが変更されました。

SQL> alter system set sort_area_size = 2147483647 scope=spfile;

システムが変更されました。

SQL> alter system set bitmap_merge_area_size = 2147483647 scope=spfile;

システムが変更されました。

SQL> alter system set create_bitmap_area_size = 2147483647 scope=spfile;

システムが変更されました。

SQL> shutdown immediate
データベースがクローズされました。
データベースがディスマウントされました。
ORACLEインスタンスがシャットダウンされました。
ERROR:
ORA-12514: TNS:
リスナーは接続記述子でリクエストされたサービスを現在認識していません


警告: Oracleにはもう接続されていません。
SQL> conn sys/oracle@orcl122 as sysdba
ERROR:
ORA-12514: TNS:
リスナーは接続記述子でリクエストされたサービスを現在認識していません


SQL> exit
bash-4.1$ export ORACLE_SID=orcl122
bash-4.1$ sqlplus / as sysdba

SQL*Plus: Release 12.2.0.1.0 Production on 月 2月 19 22:44:47 2018

Copyright (c) 1982, 2016, Oracle. All rights reserved.

アイドル・インスタンスに接続しました。

SQL> startup
ORACLEインスタンスが起動しました。

Total System Global Area 838860800 bytes
Fixed Size 8790120 bytes
Variable Size 356519832 bytes
Database Buffers 465567744 bytes
Redo Buffers 7983104 bytes
データベースがマウントされました。
データベースがオープンされました。
SQL> show parameter _area_size

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
bitmap_merge_area_size integer 2147483647
create_bitmap_area_size integer 2147483647
hash_area_size integer 2147483647
sort_area_size integer 2147483647
workarea_size_policy string AUTO
SQL>
SQL> shutdown immediate
データベースがクローズされました。
データベースがディスマウントされました。
ORACLEインスタンスがシャットダウンされました。
SQL>
SQL> create spfile from pfile;

File created.


Microsoft Windows

Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production
に接続されました。
SQL>
SQL> $ver

Microsoft Windows [Version 10.0.16299.125]

SQL> create pfile from spfile;

ファイルが作成されました。

SQL> --2GB
SQL> select 2*1024*1024*1024 from dual;

2*1024*1024*1024
----------------
2147483648

SQL> alter system set hash_area_size = 2147483648 scope=spfile;
alter system set hash_area_size = 2147483648 scope=spfile
*
行1でエラーが発生しました。:
ORA-02017: 整数値が必要です。

SQL> alter system set sort_area_size = 2147483648 scope=spfile;
alter system set sort_area_size = 2147483648 scope=spfile
*
行1でエラーが発生しました。:
ORA-02017: 整数値が必要です。

SQL> alter system set bitmap_merge_area_size = 2147483648 scope=spfile;
alter system set bitmap_merge_area_size = 2147483648 scope=spfile
*
行1でエラーが発生しました。:
ORA-02017: 整数値が必要です。

SQL> alter system set create_bitmap_area_size = 2147483648 scope=spfile;
alter system set create_bitmap_area_size = 2147483648 scope=spfile
*
行1でエラーが発生しました。:
ORA-02017: 整数値が必要です。


SQL> --2GB - 1
SQL> select 2*1024*1024*1024-1 from dual;

2*1024*1024*1024-1
------------------
2147483647

SQL> alter system set hash_area_size = 2147483647 scope=spfile;

システムが変更されました。

SQL> alter system set sort_area_size = 2147483647 scope=spfile;

システムが変更されました。

SQL> alter system set bitmap_merge_area_size = 2147483647 scope=spfile;

システムが変更されました。

SQL> alter system set create_bitmap_area_size = 2147483647 scope=spfile;

システムが変更されました。

SQL> shutdown immediate
データベースがクローズされました。
データベースがディスマウントされました。
ORACLEインスタンスがシャットダウンされました。
ERROR:ORA-12514: TNSリスナーは接続記述子でリクエストされたサービスを現在認識していません
SQL> 警告: Oracleにはもう接続されていません。
SQL> exit
Disconnected
C:\Users\discus>set ORACLE_SID=orcl122SQL>
C:\Users\discus>sqlplus / as sysdba
SQL*Plus: Release 12.2.0.1.0 Production on 月 2月 19 22:28:45 2018
Copyright (c) 1982, 2016, Oracle. All rights reserved.
SQL>
アイドル・インスタンスに接続しました。
SQL> create spfile from pfile;
SQL>
File created.
SQL> exit
Disconnected

C:\Users\discus>


ということで、

手動PGA管理でSQL Work Area Sizeを制御する初期化パラメータで指定可能な最大サイズは

2GB -1

ということになります。(HP-UXやAIXは未確認なので情報いただけたら、ここに追記しまーす。:)

次回へつづく。




Temp落ち #1 - "Temp落ち" って?
Temp落ち #2 - PGA (Program Global Area)

| | コメント (0) | トラックバック (0)

2018年2月18日 (日)

Temp落ち #2 - PGA (Program Global Area)

Previously on Mac De Oracle
”Temp落ち”ってなに? 
という話と、それを確認できるツールをいくつか紹介したところまででした。


余談
このネタを書きながら、"Temp落ち"とは異なる理由で今回ネタにするメモリー領域の事を調べていた事を思い出しました。(懐かしい)
当時、自動PGA管理に関する情報があまりなく、新・ソートに関する検証 その1 ペンネーム グリーンペペを隅から隅まで読み試していました:)。その何年か後に、グリーンペペさんが、だれなのかを知ることに。。業界狭いですw
むか〜し、Windows(32bit)環境のOracleで、」ORA-12518が頻発していたトラブルの原因は今回のネタで取り上げるメモリー領域だった!なんてこともあるので、構造等、知ってて損はないですよ:)

では、今回のお話。

"Hash JoinやSortなどの処理をできる限りメモリー上で行う"という、そのメモリー領域とは???
PGA (Program Global Area)と呼ばれるメモリー領域のSQL Work Areaに確保されます。
また、それらのサイズを制御する初期化パラメータがあります。(後述)
PGAは、おおよそ以下のような構造で、サーバープロセスやバックグランドプロセス毎に確保されます。”ここ大切"
Pga

Pgas_processes2_2

Structure_of_pga




参考)
PGAのメモリーサイズを制御するパラメータは、以下の通り(隠しパラメータもありますが、とりあえずデフォルト前提で:)

以下の4パラメータは、手動PGA管理で利用されるパラメータで、自動PGA管理が主流となった今ではあまり利用されることはありません。(たま〜〜に使いたくなるときはありますが、結局使わなかったり、それは別の機会にでも書くことにします)

HASH_AREA_SIZE
https://docs.oracle.com/cd/E82638_01/REFRN/HASH_AREA_SIZE.htm

SORT_AREA_SIZE
https://docs.oracle.com/cd/E82638_01/REFRN/SORT_AREA_SIZE.htm

BITMAP_MERGE_AREA_SIZE
https://docs.oracle.com/cd/E82638_01/REFRN/BITMAP_MERGE_AREA_SIZE.htm

CREATE_BITMAP_AREA_SIZE
https://docs.oracle.com/cd/E82638_01/REFRN/CREATE_BITMAP_AREA_SIZE.htm

Oracle database 9.2.0以降、自動PGA管理が登場し、上記4パラメータで個別にサイズを管理する必要がなくなりました。
PGA_AGGREGATE_TARGET/PGA_AGGREATE_LIMITの2つのパラメータで自動PGA管理で行う方法に慣れておいた方が良いと思います。

余談
ちなみに、10gR1〜11gR2までは、PGA_AGGREGATE_TARGETパラメータだけだったのですが、その頃、自動PGA管理で割り当てられるサイズを検証していたネタは以下:) pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? Season2 #8 http://discus-hamburg.cocolog-nifty.com/mac_de_oracle/2010/09/pga_aggregate-7.html

ところで、ひさびさに以下マニュアルを読んで見たのですが、何度見てもモヤモヤは消えない感じのままなのがつらいです。もう少しなんとかならんかなー。
と思わなくもないです...w 作業領域の割り当てルールを明記しないのは、何故なんだろうと、ズーーーーっと考えてる。書いてくれてれば楽なのにな〜と。

Oracle® Databaseパフォーマンス・チューニング・ガイド 12cリリース2 (12.2) E72901-03
16 プログラム・グローバル領域のチューニング
https://docs.oracle.com/cd/E82638_01/TGDBA/tuning-program-global-area.htm


Oracle® Databaseリファレンス 12cリリース2 (12.2) E72905-03
PGA_AGGREGATE_LIMIT
https://docs.oracle.com/cd/E82638_01/REFRN/PGA_AGGREGATE_LIMIT.htm

12.2からResource Managerを利用して特定のコンシューマ・グループ内の各セッションに割り当てられるようになったようで、 PGAメモリー使用量に絶対制限を設定し、超過した場合にはORA-10260エラーとさせることができるようになったようですね。(まだ試したことはないのですが)

16.3.2 リソース・マネージャを使用したプログラム・グローバル領域のサイズ設定
https://docs.oracle.com/cd/E82638_01/TGDBA/tuning-program-global-area.htm

PGA_AGGREGATE_TARGET
https://docs.oracle.com/cd/E82638_01/REFRN/PGA_AGGREGATE_TARGET.htm


12cの自動PGA管理について軽く確認した感触だと、12cR2でPDB毎の制御ができるようになった事以外、大きな変化ないんじゃないかと思っているのですが、そういえばちゃんと確認したことないな。。。
次回は、昔試した方法で、12cでもPGA割り当てルールに変化はないのか確認しておきますか。。。18cはどうなるんだろ:)


続く。


Temp落ち #1 - "Temp落ち" って?

| | コメント (0) | トラックバック (0)

2018年2月15日 (木)

Temp落ち #1 - "Temp落ち" って?

ぐるぐる系に並び、一部で人気?w のある俗称、

Temp_ochi


この、”Temp落ち”とはなんなのでしょうか?(ご存知の方も多いと思いますが)

こんなイメージです。
Optimal

Multipass
ざっくり、言ってしまうと、
Hash JoinやSort処理などを”メモリー上”で行おうとします。その際、利用可能なメモリー不足が発生すると一時表領域(HDDやSSDなど)を作業域としてして処理を行います。
この一時表領域も利用しながらHash JoinやSort処理などを行う動きが、"Temp落ち" の正体です。


なぜ可能な限りメモリー上で完結させようとするのか? 
たとえば、HDDを利用したソートとメモリー内のみで同量のソート行ったらどちらが早く終わると思いますか?
ということを考えれば、理由はわかりますよね。

ただ、最近はメモリーも安くなり、大容量のメモリーのサーバーもあり、ぜーんぶオンメモリーでできるじゃん?
と思わなくも無いのですが、

無限にメモリーを利用できるわけでもないく(仕様などにもよる)、データ量の爆発とともに、"Temp落ち”と戦わなければならない場面も多くなってきているような気はします。
また、"Temp落ち"の辛さが認識されていない場合は、大人の事情も絡まって予想以上にめんどくさい状況になることもしばしば。。。


そういえば、某性能問題専門チームへ本格的に参画しはじめたころの最初の戦いが、
”性能試験でTemp落ちして試験にならないというプロジェクトをなんとかする”というやつでしたw (もう7年ぐらい前ですがw)

その時期に調べていた内容をまとめていたのがエントリーが以下のシリーズでした。
pga_aggregate_targetでPGA?、_pga_max_sizeでPGA? Season2 #8
http://discus-hamburg.cocolog-nifty.com/mac_de_oracle/2010/09/pga_aggregate-7.html


以下、津島博士のパフォーマンス講座も読んでおくとよいと思います。:)
参考)
津島博士のパフォーマンス講座 - 第36回 遅くなるSQLについて
http://www.oracle.com/technetwork/jp/database/articles/tsushima/tsushima-hakushi-36-2184118-ja.html

津島博士のパフォーマンス講座 - 第45回 Temp領域について

http://www.oracle.com/technetwork/jp/database/articles/tsushima/tsushima-hakushi-45-2491038-ja.html


ちなみに、AWRレポートやstatpackレポートなど、一時表領域が利用された"Temp落ち"の有無を確認する方法はいろいろあるのですが、代表的なものをいくつか載せておきますね。

ここで紹介する機能で利用したdatabase versionは以下の通り

BANNER                                                                               CON_ID
-------------------------------------------------------------------------------- ----------
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production 0
PL/SQL Release 12.2.0.1.0 - Production 0
CORE 12.2.0.1.0 Production 0
TNS for Linux: Version 12.2.0.1.0 - Production 0
NLSRTL Version 12.2.0.1.0 - Production 0

12cR2のAWRレポートのTop 10 Foreground Eventsセクションより
インスタンス全体の状態を確認できます。
上位の待機イベントから、一時表領域が多く利用されていたことを知ることができます。
direct path write temp / direct path read tempという待機イベントが上位にきています。この2つの待機イベントは一時表領域への書き込みと読み込みを示す待機イベントです。
一時表領域に書き込まれたデータは、”どのようなタイプのデータ”であるか、この情報からでは判断できませんが、一時表領域を利用する操作が全体の6割近くを占めていることは確認できます。

Top 10 Foreground Events by Total Wait Time
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Total Wait Avg % DB Wait
Event Waits Time (sec) Wait time Class
------------------------------ ----------- ---------- --------- ------ --------
direct path write temp 8,688 36.9 4.24ms 50.3 User I/O
DB CPU 26 35.4
direct path read temp 8,403 6.2 738.19us 8.5 User I/O
direct path read 400 4.1 10.36ms 5.6 User I/O
db file sequential read 1,082 1.8 1.66ms 2.4 User I/O
control file sequential read 456 1.3 2.80ms 1.7 System I
SQL*Net message to client 66,674 .2 2.30us .2 Network
PGA memory operation 4,944 .1 25.30us .2 Other
log file sync 8 .1 10.55ms .1 Commit
db file scattered read 33 .1 1.69ms .1 User I/O

おなじく、12cR2のAWRレポートのPGA Aggr Target Histogramセクションより
インスタンス全体の状態を確認できます。1-Pass/M-Passの部分で一時表領域の利用した操作があったことが確認できます。
GB単位の1-Pass操作(一時表領域を利用した操作)があったことが確認できます。

PGA Aggr Target Histogram             DB/Inst: ORCL12C/orcl12c  Snaps: 214-216
-> Optimal Executions are purely in-memory operations

Low High
Optimal Optimal Total Execs Optimal Execs 1-Pass Execs M-Pass Execs
------- ------- -------------- -------------- ------------ ------------
2K 4K 879 879 0 0
64K 128K 12 12 0 0
128K 256K 14 14 0 0
256K 512K 8 8 0 0
512K 1024K 60 60 0 0
1M 2M 40 40 0 0
2M 4M 1 1 0 0
32M 64M 1 1 0 0
1G 2G 1 0 1 0
------------------------------------------------------

おなくじ、12cR2のAWRレポートのTop SQL with Top Row Sourcesより
以下の例では、SQLID=g95385r59bm47というSQL文の実行時間の内、50%以上がHash join操作でのdirect path write temp待機イベント(一時表領域への書き込み)であることがレポートされています。
このSQL文のHash Joinでメモリー内では処理しきれないほどの結合操作が行われたことがわかります。

Top SQL with Top Row Sources          DB/Inst: ORCL12C/orcl12c  Snaps: 214-216
-> Top SQL statements by DB Time along with the top row sources by DB Time
for those SQLs.
-> % Activity is the percentage of DB Time due to the SQL.
-> % Row Source is the percentage of DB Time spent on the row source by
that SQL.
-> % Event is the percentage of DB Time spent on the event by the
SQL executing the row source.
-> Executions is the number of executions of the SQL that were sampled in ASH.

SQL ID Plan Hash Executions % Activity
----------------------- -------------------- -------------------- --------------
% Row
Row Source Source Top Event % Event
---------------------------------------- ------- ----------------------- -------
Container Name
-------------------------------------------
g95385r59bm47 1822065247 1 100.00
HASH JOIN 66.67 direct path write temp 50.00
SELECT /*+ MONITOR LEADING(m1 m2) USE_HASH(m1 m2)
*/ * FROM m1 INNER JOIN m2 ON m1.id = m2.id AN
D m1.rev# = m2.rev#

AWRレポート以外では、
v$tempseg_usageを問い合わせることで一時表領域の利用状況をセッション、SQL毎に確認することもできます。
この例では、ブロックサイズは、8KBとしているので、Hash一時セグメントで、3GB程の一時表領域が利用されたことがわかります。

orcl12c@SYS> select con_id,session_num,username,sql_id,segtype,contents,blocks from v$Tempseg_usage order by session_num;

CON_ID SESSION_NUM USERNAME SQL_ID SEGTYPE CONTENTS BLOCKS
---------- ----------- ------------------------------ ------------- --------- --------- ----------
3 26123 SCOTT g95385r59bm47 HASH TEMPORARY 427520

SQLモニター - REPORT_SQL_MONITORファンクション
SQLモニターのMem(Max)/Temp(Max)列でHash joinやSort処理で利用された最大メモリーサイズと最大一時表領域のサイズを確認することができます。
この例ではId=1のHash JoinでTemp(Max) = 3GB より、一時表領域がHash Join操作の作業領域として3GB利用されたことを確認できます。
また、Activity Details列で、direct path write temp / direct path read temp待機イベントの割合も高いため一時表領域へのI/O量をどうするかが性能改善へのポイントであることも確認できます。

SQL Plan Monitoring Details (Plan Hash Value=1822065247)
=======================================================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Write | Write | Mem | Temp | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | Reqs | Bytes | (Max) | (Max) | (%) | (# samples) |
=======================================================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 86 | +2 | 1 | 1M | | | | | . | . | 9.52 | Cpu (6) |
| 1 | HASH JOIN | | 1M | 382K | 86 | +2 | 1 | 1M | 13302 | 3GB | 13302 | 3GB | 1GB | 3GB | 76.19 | Cpu (10) |
| | | | | | | | | | | | | | | | | direct path read temp (4) |
| | | | | | | | | | | | | | | | | direct path write temp (34) |
| 2 | TABLE ACCESS FULL | M1 | 1M | 90575 | 45 | +2 | 1 | 1M | 2619 | 3GB | | | . | . | 6.35 | Cpu (3) |
| | | | | | | | | | | | | | | | | direct path read (1) |
| 3 | TABLE ACCESS FULL | M2 | 1M | 90574 | 24 | +46 | 1 | 1M | 2619 | 3GB | | | . | . | 7.94 | Cpu (1) |
| | | | | | | | | | | | | | | | | direct path read (4) |
=======================================================================================================================================================================================

最後に、SQLモニターはTuning pack と Diagnostic packの両方が必要なのですが、追加パックなしで利用できる機能として、DBMS_XPLAN.DISPLAY_CURSORでActual Planを取得して確認する方法もあります。
DBMS_XPLAN.DISPLAY_CURSOR(format=>'ALL ALLSTATS LAST')でActual Planで確認することもできます。
Used-Mem/Used-Tmpから利用されたメモリーサイズと一時表領域のサイズが確認できます。
ただ、User_Tmpのサイズ単位は、KじゃなくてMなんじゃないか?(バグ? 誰か調べてw)
前述したv$tempseg_usageとSQLモニターで実行したSQL文なのですが....ほかにも単位が変なところがあるみたい。。。まあいいか。(いいわけ無い!w)

Plan hash value: 1822065247

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows |E-Bytes|E-Temp | Cost (%CPU)| E-Time | A-Rows | A-Time | Buffers | Reads | Writes | OMem | 1Mem | Used-Mem | Used-Tmp|
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | | 382K(100)| | 1000K|00:00:56.65 | 727K| 1079K| 412K| | | | |
|* 1 | HASH JOIN | | 1 | 1000K| 4038M| 2126M| 382K (1)| 00:00:15 | 1000K|00:00:56.65 | 727K| 1079K| 412K| 2047M| 28M| 1049M (1)| 3340K|
| 2 | TABLE ACCESS FULL| M1 | 1 | 1000K| 2115M| | 90575 (1)| 00:00:04 | 1000K|00:00:06.79 | 374K| 333K| 0 | | | | |
| 3 | TABLE ACCESS FULL| M2 | 1 | 1000K| 1923M| | 90574 (1)| 00:00:04 | 1000K|00:00:01.68 | 352K| 333K| 0 | | | | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

SQLモニター関連と、Actual Planを取得するサンプルスクリプト(私がよく利用してるやっつけスクリプト)
どちらを利用するかは、必要としている情報次第なのですが、利用できる環境であれば(主にライセンス)、SQLモニターとActual Planを併用することが多いです。(経験上)
SQLモニターは5秒以上の処理時間やパラレル処理を自動的にモニターできるので、利用可能な環境であれば、本番環境で今まさに終わらないSQLとなっているようなSQL文を分析するには便利です。
SQLIDは判明していても、終わらないSQL文の場合は、DBMS_XPLAN.DISPLAY_CORSORでActual Planを取得することは難しい(Actual Planの場合、SQLが正常終了しないと実行統計が取得できない)
ツール毎に、取得できる内容に多少違いがあるので、複数のツールを組み合わせて使うことが多い(ここ重要)


なお、SQLモニターは、対象とするSQL文に MONITORヒント付加することで強制的にモニタリングすることを前提にしてあります。
自動的にSQLモニターの対象となる条件は、5秒以上実行されているか、パラレル実行されている場合のみ

MONITORヒント付加を前提としており、パラメータを空で渡した場合は、直前に実行されたSQLモニター対象のSQLのモニター結果をテキスト形式でレポートします。(sql_idをパラメータとして渡した場合には対象のSQL文がSQLモニターの対象となっている必要があります)
SQLモニタースクリプト

[oracle@localhost ˜]$ cat show_realmon.sql
set linesize 1000
set long 1000000
set longchunksize 1000000
select dbms_sqltune.report_sql_monitor(sql_id=>'&1', type=>'text') from dual;

SQLモニタースクリプトの実行例

ORCL@SCOTT> select /*+ monitor */ * from dual;

D
-
X

Elapsed: 00:00:00.00
ORCL@SCOTT> @show_realmon ''
old 1: select dbms_sqltune.report_sql_monitor(sql_id=>'&1', type=>'text') from dual
new 1: select dbms_sqltune.report_sql_monitor(sql_id=>'', type=¥>'text') from dual

DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>'',TYPE=>'TEXT')
------------------------------------------------------------------------------------------------------------------------
SQL Monitoring Report

SQL Text
------------------------------
select /*+ monitor */ * from dual

Global Information
------------------------------
Status : DONE (ALL ROWS)
Instance ID : 1
Session : SCOTT (123:34510)
SQL ID : 2w4nk70tv7w1d
SQL Execution ID : 16777216

・・・中略・・・

Fetch Calls : 1

Global Stats
=======================================
| Elapsed | Other | Fetch | Buffer |
| Time(s) | Waits(s) | Calls | Gets |
=======================================
| 0.00 | 0.00 | 1 | 3 |
=======================================

SQL Plan Monitoring Details (Plan Hash Value=272002086)
=========================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | (%) | (# samples) |
=========================================================================================================================
| 0 | SELECT STATEMENT | | | | 1 | +0 | 1 | 1 | | |
| 1 | TABLE ACCESS FULL | DUAL | 1 | 2 | 1 | +0 | 1 | 1 | | |
=========================================================================================================================

Actual Plan取得スクリプト


ORCL@SCOTT> !cat show_actualplan.sql
set linesize 1000
set long 1000000
set longchunksize 1000000
select * from table(dbms_xplan.display_cursor(format=>'ALL ALLSTATS LAST'));

Actual Plan取得スクリプトの実行例

ORCL@SCOTT> alter session set statistics_level=all;

Session altered.

Elapsed: 00:00:00.01
ORCL@SCOTT> select * from dual;

D
-
X

Elapsed: 00:00:00.01
ORCL@SCOTT> @show_actualplan

PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------------------------
SQL_ID a5ks9fhw2v9s1, child number 0
-------------------------------------
select * from dual

Plan hash value: 272002086

--------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time | A-Rows | A-Time | Buffers |
--------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | 2 (100)| | 1 |00:00:00.01 | 3 |
| 1 | TABLE ACCESS FULL| DUAL | 1 | 1 | 2 | 2 (0)| 00:00:01 | 1 |00:00:00.01 | 3 |
--------------------------------------------------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

1 - SEL$1 / DUAL@SEL$1

Column Projection Information (identified by operation id):
-----------------------------------------------------------

1 - "DUAL"."DUMMY"[VARCHAR2,1]


23 rows selected.

では、つづく。(どういうながれにするかまだ、考えてないけど、ブログ書かないとw)

| | コメント (0) | トラックバック (0)

2017年12月 4日 (月)

”utl_file I/O” - この症状はあれの可能性が高いですね。

JPOUG Advent Calendar 2017の4日目のエントリーです。

さて、最近あまりお目にかかってなかったUTL_FILEパッケージで表データをcsvに書きだすネタにしました。

先日、UTL_FILEパッケージを利用した処理が想定以上に遅いという相談をうけました。
AWRレポートをみると、上位の待機イベントは、”utl_file I/O"。

!!!!おーーーこれは、珍しいというか、久々にみた病気w

UTL_FILEパッケージを利用したI/Oをグルグルしているとか、でかいファイル読み書きしているかの、どちらかですよねw
ということで、この症状の治療法を考えてみます。


<参考 - 環境>

MacBook:˜ system_profiler SPHardwareDataType | grep -E 'Model|Processor|Cores|Memory'
Model Name: MacBook
Model Identifier: MacBook9,1
Processor Name: Intel Core m5
Processor Speed: 1.2 GHz
Number of Processors: 1
Total Number of Cores: 2
Memory: 8 GB

ホストOS
MacBook:˜ discus$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.13.1
BuildVersion: 17B1003

MacBook:˜ discus$ VBoxManage -version
5.1.30r118389


ゲストOSとOracle Database
orcl@SYS> !uname -a
Linux localhost.localdomain 4.1.12-94.3.6.el7uek.x86_64 #2 SMP Tue May 30 19:25:15 PDT 2017 x86_64 x86_64 x86_64 GNU/Linux

orcl@SYS> !cat /etc/oracle-release
Oracle Linux Server release 7.3

orcl@SYS>
orcl@SYS> select * from v$version;

BANNER CON_ID
-------------------------------------------------------------------------------- ----------
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production 0
PL/SQL Release 12.2.0.1.0 - Production 0
CORE 12.2.0.1.0 Production 0
TNS for Linux: Version 12.2.0.1.0 - Production 0
NLSRTL Version 12.2.0.1.0 - Production 0

UTL_FILEでファイル出力する際、ちょっとした手順の漏れが性能差として現れてしまうことがあります。
表の行長は数百バイト程度ですが、行数は数千万〜数億行なんていう状況だと、性能差が顕著に現れてしまうので注意が必要です。

以下の表、セグメントサイズは1GB程度ですが、行数は1千万行以上あります。
この表データをUTL_FILEパッケージを利用してcsvに出力してみます。

ORCL@SCOTT> select segment_name,sum(bytes)/1024/1024/1024 "GB" from user_segments group by segment_name;

SEGMENT_NAME GB
------------------------------ ----------
ABOUT_100BYTES_ROWS .9140625
PK_ABOUT_100BYTES_ROWS .171875


ORCL@SCOTT> select count(1) from about_100bytes_rows;

COUNT(1)
----------
10737420

表の1行は100bytes(改行コード含)です。

ORCL@SCOTT%gt; r
1 SELECT
2 LENGTH(
3 TO_CHAR(id,'FM000000000000000000000000000009')
4 ||','||FOO
5 ) row_length
6 FROM
7 about_100bytes_rows
8 WHERE
9* rownum <= 1

ROW_LENGTH
----------
99

次の2つのコードの赤太文字部分に着目してください。
その部分が異なるだけで処理時間に大きな差が出ます。

DECLARE
cDIR_NAME CONSTANT VARCHAR2(30) := 'FILES_DIR';
cFILE_NAME CONSTANT VARCHAR2(128) := 'no_writebuffering_'||TO_CHAR(systimestamp, 'rrmmddhh24miss.ff')||'.txt';
cBufferSize CONSTANT BINARY_INTEGER := 32767;
cOpenMode CONSTANT VARCHAR2(2) := 'w';
fileHandle UTL_FILE.FILE_TYPE;

cBulkReadLimit CONSTANT PLS_INTEGER := 324;
TYPE tBulkReadArray IS TABLE OF VARCHAR2(8192) INDEX BY BINARY_INTEGER;
bulkReadArray tBulkReadArray;
CURSOR cur_about100bytesRow IS
SELECT
TO_CHAR(id,'FM000000000000000000000000000009')
||','||FOO
AS csvrow
FROM
about_100bytes_rows
;
BEGIN
OPEN cur_about100bytesRow;
fileHandle := UTL_FILE.FOPEN(cDIR_NAME, cFILE_NAME, cOpenMode, cBufferSize);
LOOP
FETCH cur_about100bytesRow
BULK COLLECT INTO bulkReadArray
LIMIT cBulkReadLimit;

EXIT WHEN bulkReadArray.COUNT = 0;

FOR i IN bulkReadArray.FIRST..bulkReadArray.LAST LOOP
UTL_FILE.PUT_LINE(
file => fileHandle
, buffer => bulkReadArray(i)
, autoflush => true
);
END LOOP;
END LOOP;
UTL_FILE.FFLUSH(fileHandle);
UTL_FILE.FCLOSE(fileHandle);
CLOSE cur_about100bytesRow;
EXCEPTION
WHEN OTHERS THEN
IF UTL_FILE.IS_OPEN(fileHandle) THEN
UTL_FILE.FCLOSE(fileHandle);
END IF;

IF cur_about100bytesRow%ISOPEN THEN
CLOSE cur_about100bytesRow;
END IF;
RAISE;
END;
/


PL/SQLプロシージャが正常に完了しました。

経過: 00:02:27.05


DECLARE
cDIR_NAME CONSTANT VARCHAR2(30) := 'FILES_DIR';
cFILE_NAME CONSTANT VARCHAR2(128) := 'writebuffering_'||TO_CHAR(systimestamp, 'rrmmddhh24miss.ff')||'.txt';
cBufferSize CONSTANT BINARY_INTEGER := 32767;
cOpenMode CONSTANT VARCHAR2(2) := 'w';
fileHandle UTL_FILE.FILE_TYPE;
buffer VARCHAR2(32767);

cBulkReadLimit CONSTANT PLS_INTEGER := 324;
TYPE tBulkReadArray IS TABLE OF VARCHAR2(8192) INDEX BY BINARY_INTEGER;
bulkReadArray tBulkReadArray;
CURSOR cur_about100bytesRow IS
SELECT
TO_CHAR(id,'FM000000000000000000000000000009')
||','||FOO
AS csvrow
FROM
about_100bytes_rows
;
BEGIN
OPEN cur_about100bytesRow;
fileHandle := UTL_FILE.FOPEN(cDIR_NAME, cFILE_NAME, cOpenMode, cBufferSize);
LOOP
FETCH cur_about100bytesRow
BULK COLLECT INTO bulkReadArray
LIMIT cBulkReadLimit;

EXIT WHEN bulkReadArray.COUNT = 0;

buffer := NULL;
FOR i IN bulkReadArray.FIRST..bulkReadArray.LAST LOOP
buffer := buffer || bulkReadArray(i) || UTL_TCP.CRLF;
END LOOP;
UTL_FILE.PUT(fileHandle, buffer);
UTL_FILE.FFLUSH(fileHandle);
END LOOP;
UTL_FILE.FFLUSH(fileHandle);
UTL_FILE.FCLOSE(fileHandle);
CLOSE cur_about100bytesRow;
EXCEPTION
WHEN OTHERS THEN
IF UTL_FILE.IS_OPEN(fileHandle) THEN
UTL_FILE.FCLOSE(fileHandle);
END IF;

IF cur_about100bytesRow%ISOPEN THEN
CLOSE cur_about100bytesRow;
END IF;
RAISE;
END;
/


PL/SQLプロシージャが正常に完了しました。

経過: 00:00:43.96


何が違うかお分ですよね!
UTL_FILE.PUT_LINE/UTL_FILE.PUTが違う!!w
その部分は重要ではなく、100Bytes単位に書き込んでいるか、約32KB単位で書き込んでいるかが重要なんです。

UTL_FILEは最大32767バイトのバッファを利用できますが、前者はバッファを有効利用せず、100Bytes毎に書き出しています。後者は約32KB単位で書き出しています。

その差はAWRレポートからも見えてきます。


AWRレポート(一部抜粋)
UTL_FILEパッケージによるI/Oの待機は、”utl_file I/O”という待機イベントで現れます。

Avg Waitは短いですが、理由は100バイト単位の小さいなサイズの書き込みを繰り返しているわけなので、そんなとこでしょう。

Top 10 Foreground Events by Total Wait Time

Total Wait Avg % DB Wait
Event Waits Time (sec) Wait time Class
------------------------------ ----------- ---------- --------- ------ --------
DB CPU 147.4 99.3
utl_file I/O 32,212,266 49.5 1.54us 33.3 User I/O
direct path read 1,881 1.2 620.61us .8 User I/O
resmgr:cpu quantum 1 .1 85.85ms .1 Schedule
db file sequential read 17 0 .98ms .0 User I/O
PGA memory operation 90 0 35.13us .0 Other
latch: shared pool 1 0 1.05ms .0 Concurre
control file sequential read 80 0 12.83us .0 System I
Disk file operations I/O 6 0 73.67us .0 User I/O
SQL*Net message to client 2 0 9.00us .0 Network


一方、約32KB単位でバッファリングして書き出している場合、Waits回数が激減しています。
Avg Waitsは大きくなっていますが、書き出しサイズにほぼ比例しているので想定通りというところ。

Top 10 Foreground Events by Total Wait Time

Total Wait Avg % DB Wait
Event Waits Time (sec) Wait time Class
------------------------------ ----------- ---------- --------- ------ --------
DB CPU 45 98.5
utl_file I/O 66,288 27.4 413.54us 60.1 User I/O
direct path read 1,881 1 550.71us 2.3 User I/O
db file sequential read 88 0 443.38us .1 User I/O
direct path write 2 0 4.69ms .0 User I/O
control file sequential read 80 0 33.25us .0 System I
latch: shared pool 1 0 2.38ms .0 Concurre
Disk file operations I/O 4 0 323.50us .0 User I/O
PGA memory operation 79 0 15.73us .0 Other
SQL*Net message to client 4 0 20.25us .0 Network

utl_file.put/put_lineでcsvを出力しているdeviceのiostat(util%)
まだまだ余裕があるのでI/Oで詰まっているのではなく、UTL_FILE.PUT/PUT_LINEの使い方の影響が大きいということですね。
20171203_202342


最後に、
UTL_FILEパッケージの入出力時にはバッファの有効利用をお忘れなく。(つい忘れちゃうこともあるので、急いでるときとかw)
扱うデータが多い場合は得に。

そして、みなさま、
メリークリスマス(まだエントリーを書くかもしれませんがw)
#JPOUG

| | コメント (0) | トラックバック (0)

2017年11月11日 (土)

SDCLI Error Out "Failed to create the processor for command format"

昨日 Windowsの SQL Developer 17.2のsdcliコマンドで整形しようと思ったらハマったので、備忘録


SDCLI Error Out "Failed to create the processor for command format" (Doc ID 2297353.1)

Oracle SQL Developer - Version 17.2 and later
と記載されているようですが、Version4.2で発生していたのでそのまま新版でも引き続き発生してるという感じですね。
改善されるまでの間、面倒くさいので、Version 4.0.2〜4.1.5あたりをインストールしてSQLを整形したほうがイライラしなくてよいと思います。

discus-mother:bin oracle$ cat version.properties
COMPANY=Oracle
PRODUCT=SQL Developer
VERSION=4.2000170891709f
VER=4.2.0
VER_FULL=4.2.0.17.089.1709
BUILD_LABEL=17.089.1709
BUILD_NUM=17.089.1709
EDITION=
discus-mother:bin oracle$
discus-mother:bin oracle$ sdcli format intput=sample.sql output=sample_fmt.sql

Oracle SQL Developer
Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.

Failed to create the processor for command format

私の場合、SQL Developer 4.0.3をインストールしてSQL文整形オンリー使いで回避しました。


参考 SQL Developer 4の素敵なコマンドライン de SQL整形 :)
Note: ディレクトリ指定の一括変換も動作が怪しいことがあるので、単一のSQL文整形を繰り返すようなshell作ったほうがいいかもしれません。

| | コメント (0) | トラックバック (0)

2017年11月 7日 (火)

DBMS_ADVISOR.CREATE_FILE()プロシージャは、Diagnostic/Tuning Pack不要らしいということの確認

Previously on Mac De Oracle



余談

DBMS_ADVISOR.CREATE_FILEとなっているがライセンス上、diagnostic/tuning packライセンスはなくてもアクセスしてできるように読み取れるのだけと、もしかすると、この影響で、DBMS_ADVISOR.CREATE_FILEの存在は知っていても使ってないのかあるのかな??

以下のマニュアルを読む限り、Diagnostic/Tuning Packでは、DBMS_ADVISORパッケージ全体ではなく、Diagnostic/Tuning Packに関連するパラメータを与える必要のある機能についての制限であることしか記載されていない。
つまり、DBMS_ADVISOR.CREATE_FILE()に関して言えば、Diagnostic/Tuning Pack特有の機能ではないから利用可能なはず。 ←間違ってたらツッコミ希望w

Oracle® Databaseライセンス情報 12cリリース1 (12.1) 2 オプションおよびパック
https://docs.oracle.com/cd/E49329_01/license.121/b71334/options.htm



なんてことを書いたところ、

control_management_pack_access=none

でも使えるとなら間違いなくないんじゃないの?
というコメントがあったので試してみました。 マニュアルの通りだと思われます。ということですね。FAQ!

orcl@SYS> show parameter control_management_pack_access

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
control_management_pack_access string NONE
orcl@SYS>
orcl@SYS> l
1 DECLARE
2 reportClob CLOB := EMPTY_CLOB();
3 BEGIN
4 reportClob := 'test test test';
5 DBMS_ADVISOR.CREATE_FILE(
6 buffer => reportClob
7 ,location => 'REP_DIR'
8 ,filename => 'test.txt'
9 );
10* END;
orcl@SYS> /

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:00.23
orcl@SYS> !ls report
test.txt

orcl@SYS> !cat report/test.txt
test test test


DBMS_SQLTUNE.PACK_STGTAB_SQLSETって、例外投げんのかよw
SQL Tuning Setのキャプチャから退避までのスクリプト(やっつけ)
SQL Tuning Set (STS)のフィルタリング
DBMS_SQLTUNE.REPORT_ANALYSIS_TASK()ファンクションで生成されるCLOB型のレポートをファイルに保存する簡単な方法

| | コメント (0) | トラックバック (0)

2017年11月 3日 (金)

DBMS_SQLTUNE.REPORT_ANALYSIS_TASK()ファンクションで生成されるCLOB型のレポートをファイルに保存する簡単な方法

Previously on Mac De Oracle
STSとの格闘というかSPAとの格闘に疲れてきたところw ですが、
SQL Tuning Set (STS)のフィルタリング
などで、SQLSETを程よい大きさに分割するところまでたどり着きましたw

今回は、いくつかある細かい分析方法については、一旦、置いといて、
分析レポートを保存する簡単な方法についての備忘録

分析などにこの記事も参考になると思いますが、紹介している記事でも分析レポートをファイルに保存する方法はSQL*Plusのコマンドを駆使して行われています。
個人的には少々やぼったい方法かなと感じている方法なんですが、以前はこんなやりかたが多かったようにも思います。

var rep CLOB
...略...
EXEC :rep := DBMS_SQLSPA.REPORT_ANALYSIS_TASK('STSNAM','TEXT',...
spo hoge_report.txt
print :rep
spo off

SQL*PlusとPL/SQLが行ったり来たりするところや、ループ制御しにくいので、この方法は避けたい。。。。

かといって、DBMS_OUTPUT.PUT_LINEで行おうとするとset linesize等の制御が面倒くさいし
結局、SQL*Plusの機能にも依存してしまうので、いまひとつ。

PL/SQLだけで行う方法として、UTL_FILEパッケージを利用してファイル出力する方法もありますが、
UTL_FILEパッケージで提供されているAPIはlow levelなものばかりなので、急ぎの時にはめんどくさくて、やだw

もっと簡単なやつないの?????

あります!w

DBMS_ADVISOR.CREATE_FILE(buffer, location, filename)

なぜ、UTL_FILEパッケージに入れてくれないんだw と思いたくなる方もいるかと思いますが、 理由はよくわかりませんw
(UTL_FILEパッケージはlow levelなAPIだけだから入れたくないのかも、かも、かも。と思ってますが、DBMS_ADVISORパッケージに入れるとは。どういうつもりだ!w)

UTL_FILE.CREATE_FILE()ってあったほうが直感的に探しやすくないかw UTL_FILEみて無い!!
となると、他を探すこともあまりなくなって、結局、コード書いてる場面を多く見てる>< 


余談
DBMS_ADVISOR.CREATE_FILEとなっているがライセンス上、diagnostic/tuning packライセンスはなくてもアクセスしてできるように読み取れるのだけと、もしかすると、この影響で、DBMS_ADVISOR.CREATE_FILEの存在は知っていても使ってないのかあるのかな??

以下のマニュアルを読む限り、Diagnostic/Tuning Packでは、DBMS_ADVISORパッケージ全体ではなく、Diagnostic/Tuning Packに関連するパラメータを与える必要のある機能についての制限であることしか記載されていない。
つまり、DBMS_ADVISOR.CREATE_FILE()に関して言えば、Diagnostic/Tuning Pack特有の機能ではないから利用可能なはず。 ←間違ってたらツッコミ希望w

Oracle® Databaseライセンス情報 12cリリース1 (12.1) 2 オプションおよびパック
https://docs.oracle.com/cd/E49329_01/license.121/b71334/options.htm


とりあえず、SQLSETの分析レポートをDBMS_ADVISOR.CREATE_FILE()を利用して保存するやっつけスクリプトを作ってみました
これを元に育てていけるかなw


実行例)

注)STS20171102というSQLSETを事前に作成してあります。


orcl@SYS> select * from v$version;

BANNER CON_ID
-------------------------------------------------------------------------------- ----------
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production 0
PL/SQL Release 12.2.0.1.0 - Production 0
CORE 12.2.0.1.0 Production 0
TNS for Linux: Version 12.2.0.1.0 - Production 0
NLSRTL Version 12.2.0.1.0 - Production 0


orcl@SYS> select owner,name,statement_count,created from dba_sqlset order by name;

OWNER NAME STATEMENT_COUNT CREATED
---------- ------------------------------ --------------- ---------
SPAUSER STS20171102 1003 02-NOV-17


spa.sqlのパラメータは、SQLSETと分析レポートの出力ディレクトリオブジェクトとSQLを実行するスキーマ向けのDBリンクです。

スクリプトの概要
・初回の分析(SQLSETで元のSQLを分析)
・2回目の分析(新環境でSQLを実行して分析)
・初回と2回目の比較レポート作成/保存
・レポートを出力後、分析に利用したタスクを削除



orcl@SYS> @spa STS20171102 rep_dir link_4_target

task name:STS20171102 has been droped.
***END***

PL/SQL procedure successfully completed.

分析レポートは、ディレクトリオブジェクトに対応づけられたパスに保存されます

orcl@SYS> !ls -l report
-rw-r--r-- 1 oracle oinstall 13688 11月 3 02:44 DIFF_STS20171102171103024433.txt

orcl@SYS> !cat report/DIFF_STS20171102171103024433.txt

General Information
---------------------------------------------------------------------------------------------

Task Information: Workload Information:
--------------------------------------------- ---------------------------------------------
Task Name : STS20171102 SQL Tuning Set Name : STS20171102
Task Owner : SYS SQL Tuning Set Owner : SPAUSER
Description : Total SQL Statement Count : 1003

Execution Information:
---------------------------------------------------------------------------------------------
Execution Name : DIFF_STS20171102 Started : 11/03/2017 02:44:26
Execution Type : COMPARE PERFORMANCE Last Updated : 11/03/2017 02:44:29
Description : Global Time Limit : UNLIMITED
Scope : COMPREHENSIVE Per-SQL Time Limit : UNUSED
Status : COMPLETED Number of Errors : 0
Number of Unsupported SQL : 4

Analysis Information:
---------------------------------------------------------------------------------------------
Before Change Execution: After Change Execution:
--------------------------------------------- ---------------------------------------------
Execution Name : SOURCE_STS20171102 Execution Name : TARGET_STS20171102
Execution Type : CONVERT SQLSET Execution Type : TEST EXECUTE REMOTE
Scope : COMPREHENSIVE Database Link : LINK_4_TARGET
Status : COMPLETED Scope : COMPREHENSIVE
Started : 11/03/2017 02:44:04 Status : COMPLETED
Last Updated : 11/03/2017 02:44:04 Started : 11/03/2017 02:44:04
Global Time Limit : UNLIMITED Last Updated : 11/03/2017 02:44:18
Per-SQL Time Limit : UNUSED Global Time Limit : UNLIMITED
Per-SQL Time Limit : UNUSED
Number of Errors : 0

---------------------------------------------
Comparison Metric: ELAPSED_TIME
------------------
Workload Impact Threshold: 1%
--------------------------
SQL Impact Threshold: 1%
----------------------

Report Summary
---------------------------------------------------------------------------------------------

Projected Workload Change Impact:
-------------------------------------------
Overall Impact : 0%
Improvement Impact : 0%
Regression Impact : 0%

SQL Statement Count
-------------------------------------------
SQL Category SQL Count Plan Change Count
Overall 999 490
Unchanged 995 490
Unsupported 4 0

Top 100 SQL Sorted by Absolute Value of Change Impact on the Workload
---------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------
| | | Impact on | Execution | Metric | Metric | Impact | Plan |
| object_id | sql_id | Workload | Frequency | Before | After | on SQL | Change |
-----------------------------------------------------------------------------------------
| 1477 | 7hys3h7ysgf9m | .1% | 1 | 14630 | 33 | 99.77% | n |
| 1811 | cw6vxf0kbz3v1 | .03% | 1 | 3602 | 24 | 99.33% | n |
| 1175 | 2s9mmb6g8kbqb | .01% | 1 | 1537 | 20 | 98.7% | n |

...中略...

| 1691 | az25yp5qunj77 | .01% | 1 | 882 | 20 | 97.73% | n |
| 1896 | faun6bjrjqr17 | .01% | 1 | 881 | 19 | 97.84% | n |
| 1956 | g7a2tmw1nrxbh | .01% | 1 | 881 | 19 | 97.84% | n |
-----------------------------------------------------------------------------------------
Note: time statistics are displayed in microseconds
---------------------------------------------------------------------------------------------


---------------------------------------------------------------------------------------------

スクリプトは以下のとおり、無名PL/SQLブロックにしてあります。
レポートのタイプなどは固定にしてますが、それらも含めてパラメータにしてもいいかも。

ちなみに、このエントリの本題である、DBMS_ADVISOR.CREATE_FILE()は赤字部分です!

orcl@SYS> !cat spa.sql

set serveroutput on
DECLARE
report_clob CLOB;
cSQLSET_OWNER VARCHAR2(30) := 'SPAUSER';
cSQLSET_NAME VARCHAR2(20) := UPPER('&1');
cEXECUTION_NAME_PREFIX_SOURCE VARCHAR2(7) := 'SOURCE_';
cEXECUTION_NAME_PREFIX_TARGET VARCHAR2(7) := 'TARGET_';
cEXECUTION_NAME_PREFIX_DIFF VARCHAR2(5) := 'DIFF_';
cREPORT_TYPE CONSTANT VARCHAR2(4) := 'text';
cFILE_EXTENTION CONSTANT VARCHAR2(5) := '.txt';
cDIRECTORY_NAME CONSTANT VARCHAR2(30) := UPPER('&2');
cDB_LINK_4_PARSING_SCHEMA CONSTANT VARCHAR2(128) := UPPER('&3');
stmt_task VARCHAR2(64);

PROCEDURE drop_task (iTASK_NAME VARCHAR2)
IS
BEGIN
FOR task_rec IN (
SELECT
task_name
FROM
dba_advisor_tasks
WHERE
task_name = iTASK_NAME
AND advisor_name = 'SQL Performance Analyzer'
) LOOP
DBMS_OUTPUT.PUT_LINE(
'task name:'||task_rec.task_name||' has been droped.'
);
DBMS_SQLPA.DROP_ANALYSIS_TASK(
task_name => task_rec.task_name
);
END LOOP;
END drop_task;
BEGIN
--Create analysis task
stmt_task := DBMS_SQLPA.CREATE_ANALYSIS_TASK(
sqlset_name => cSQLSET_NAME
,basic_filter => 'parsing_schema_name in (''SCOTT'')'
,sqlset_owner => cSQLSET_OWNER
,task_name => cSQLSET_NAME
);

--SQL Analysis on Source DB
DBMS_SQLPA.EXECUTE_ANALYSIS_TASK(
task_name => cSQLSET_NAME
,execution_type => 'CONVERT SQLSET'
,execution_name =>
cEXECUTION_NAME_PREFIX_SOURCE
|| cSQLSET_NAME
,execution_params =>
DBMS_ADVISOR.ARGLIST(
'sqlset_name', cSQLSET_NAME
,'sqlset_owner', cSQLSET_OWNER
)
);

--SQL Analysis on Target DB
DBMS_SQLPA.EXECUTE_ANALYSIS_TASK(
task_name => cSQLSET_NAME
,execution_type => 'EXECUTE'
,execution_name =>
cEXECUTION_NAME_PREFIX_TARGET
|| cSQLSET_NAME
,execution_params =>
DBMS_ADVISOR.ARGLIST(
'DATABASE_LINK', cDB_LINK_4_PARSING_SCHEMA
,'EXECUTE_FULLDML', 'TRUE'
)
);

--Comparison Source and Target Analysys
DBMS_SQLPA.EXECUTE_ANALYSIS_TASK(
task_name => cSQLSET_NAME
,execution_type => 'COMPARE'
,execution_name =>
cEXECUTION_NAME_PREFIX_DIFF
|| cSQLSET_NAME
,execution_params =>
DBMS_ADVISOR.ARGLIST(
'PLAN_LINES_COMPARISON','ALWAYS'
)
);

--Make comparison report
report_clob := DBMS_SQLPA.REPORT_ANALYSIS_TASK(
task_name => cSQLSET_NAME
,execution_name =>
cEXECUTION_NAME_PREFIX_DIFF
|| cSQLSET_NAME
,type => cREPORT_TYPE
,level => 'typical'
,section => 'summary'
);

--Save comparison report
DBMS_ADVISOR.CREATE_FILE(
buffer => report_clob
,location => cDIRECTORY_NAME
,filename =>
cEXECUTION_NAME_PREFIX_DIFF
||cSQLSET_NAME
||TO_CHAR(systimestamp, 'RRMMDDHH24MISS')
||cFILE_EXTENTION
);


--Drop Task
drop_task(cSQLSET_NAME);
DBMS_OUTPUT.PUT_LINE('***END***');
EXCEPTION
WHEN OTHERS THEN
drop_task(cSQLSET_NAME);
RAISE;
END;
/


DBMS_SQLTUNE.PACK_STGTAB_SQLSETって、例外投げんのかよw
SQL Tuning Setのキャプチャから退避までのスクリプト(やっつけ)
SQL Tuning Set (STS)のフィルタリング

| | コメント (0) | トラックバック (0)

2017年10月30日 (月)

SQL Tuning Set (STS)のフィルタリング

Previously on Mac De Oracle
前回は、やっつけすくりぷとを書いたところまででした。

ということで、今回のやっつけスクリプトの準備というかアイデア

SQLSET大量のSQL文キャプチャされていて、SQLSETにキャプチャされている大量のSQL文を一括でSPAでまわしたりすることが辛い場合があります。
そんなときはどうすればよいか。。なやみますよね。。。ほんと。。
SQL Performance AnalyzerによるSQLパフォーマンス比較実行例(API)

SQLSETから、より細かなSQLSETにほぼ当分に分割し、新たなSQLSETを作成したいような場合もあるかもしれません。
そんな時はどうするか?

手取りばやくやるなら、ORA_HASH()が便利ですよね。(SQLSETの全表走査はさけられないのですが、Exaならw)

たとえば、以下のSQLSETがあったとして、300件程度に均等に分割したいという場合。
ora_hash()のバケット数はだいたい12ぐらいでできそうですね。

orcl@SYS> select name,statement_count,created from dba_sqlset order by name;

NAME STATEMENT_COUNT CREATED
------------------------------ --------------- ---------
STS20171029 3819 29-OCT-17

orcl@SYS> select statement_count/300 from dba_sqlset where name='STS20171029';

STATEMENT_COUNT/300
-------------------
12.73

いい感じに分割できそうです。

orcl@SYS> r
1 select
2 ora_hash(sql_id,12) as hash#
3 ,count(sql_id)
4 from
5 table(dbms_sqltune.select_sqlset(
6 sqlset_name=>'STS20171029'
7 ,basic_filter=>null
8 ))
9 group by
10 ora_hash(sql_id,12)
11* order by 1

HASH# COUNT(SQL_ID)
---------- -------------
0 289
1 293
2 278
3 296
4 297
5 281
6 282
7 298
8 288
9 304
10 305
11 298
12 310

13 rows selected.


実際に利用するには、basic_filterでwhere句の条件文そのものを書いてあげます。サブクエリも書けますが、データ量が多い場合は性能面に注意しながらいろいろ試してみるといいと思います。
basic_filterでは、SQLSET_ROWオブジェクト・タイプの属性を利用したフィルタリングができるのですが、インジェクションですよね。構文を見ている限り;)
Oracle® Database PL/SQLパッケージおよびタイプ・リファレンス 12c リリース1 (12.1) SQLSET_ROWオブジェクト・タイプ

orcl@SYS> set serveroutput on
orcl@SYS> r
1 begin
2 for i in 0..12 loop
3 for sqlset_rec in
4 (
5 select count(sql_id) as num_sql
6 from
7 table(dbms_sqltune.select_sqlset(
8 sqlset_name=>'STS20171029'
9 ,basic_filter=>'ora_hash(sql_id,12)='||i
10 ))
11 )
12 loop
13 dbms_output.put_line('hash#='||to_char(i,'fm99')||':'||sqlset_rec.num_sql);
14 end loop;
15 end loop;
16* end;
hash#=0:289
hash#=1:293
hash#=2:278
hash#=3:296
hash#=4:297
hash#=5:281
hash#=6:282
hash#=7:298
hash#=8:288
hash#=9:304
hash#=10:305
hash#=11:298
hash#=12:310

PL/SQL procedure successfully completed.

ハッシュ値10でsql_id数をカウント!
うまくできそうですよね。

orcl@SYS> r
1 select
2 count(sql_id)
3 from
4 table(dbms_sqltune.select_sqlset(
5 sqlset_name=>'STS20171029'
6 ,basic_filter=>'ora_hash(sql_id,12) = 10'
7* ))

COUNT(SQL_ID)
-------------
305

参考
Oracle® Database PL/SQLパッケージおよびタイプ・リファレンス 12c リリース1 (12.1) DBMS_SQLTUNE
Oracle® Database SQL言語リファレンス 12cリリース1 (12.1) ORA_HASH


DBMS_SQLTUNE.PACK_STGTAB_SQLSETって、例外投げんのかよw
SQL Tuning Setのキャプチャから退避までのスクリプト(やっつけ)

| | コメント (0) | トラックバック (0)

2017年10月 7日 (土)

SQL Tuning Setのキャプチャから退避までのスクリプト(やっつけ)

Previously on Mac De Oracleは
DBMS_SQLTUNE.PACK_STGTAB_SQLSETって、例外投げんのかよwという備忘録でした、

今回は
以下のURLで紹介されているSTS (SQL Tuning Set)へSQLの性能統計や実行計画をキャプチャしちゃおう!
Oracle DatabaseのSTS(SQL Tuning Set) を活用して、SQLの性能統計や実行計画をキャプチャする。 / ora_gonsuke777

というSTS機能を利用した応用編w (という名のやっつけスクリプト) を書いたので、備忘録

STSでSQLの実行計画や性能統計をキャプチャするのはいいのですが、キャプチャするデータが多い場合、SYSAUX表領域を圧迫したり、拡張したりしてしまうことがあります。
本番環境で表領域サイズにドキドキする日々を送るのも嫌なので、一定期間STSヘキャプチャしたあとSTSを退避、削除したいよね。という方向の話が湧いてきたりしますw

で、書いたやっつけスクリプトが以下。(11g2と12cR1でテスト済み)
STSをキャプチャする時間、インターバル、そして、STSを退避するためのステージング表を作成するスキーマ名と、エクスポートに必要なディレクトリオブジェクト名を
パラメータに取ります。キャプチャする時間とインターバルは秒を指定します。エクスポートは、DBMS_DATAPUMPパッケージを利用しています。
今回利用したパッケージの詳細は以下を参照のこと:)

Oracle® Database PL/SQLパッケージおよびタイプ・リファレンス 11g リリース2(11.2) B56262-06
- DBMS_SQLTUNEサブプログラムの要約
- 46 DBMS_DATAPUMP

なお、本スクリプト実施前に、DataPump Export向けディレクトリオブジェクトを、ステージング表を作成するユーザを作成しておく必要があります。
(後半の実行例は、STS_EXP_DIRディレクトリオブジェクト、STSUSRユーザを事前に作成)
sts_capture.sql

SET SERVEROUTPUT ON
/*
Arguments :
&1 - Schema name for sts staging table
&2 - Directory object name for Data Pump Export
&3 - Duration for SQLSET capturing (sec)
&4 - Sampling interval for SQLSET (sec)
*/
DECLARE
-- for STS capturing and packing
vStsName VARCHAR2(30) := 'STS' || TO_CHAR(SYSDATE, 'YYYYMMDDHH24MISS');
cTimeLimit CONSTANT POSITIVE := &3;
cInterval CONSTANT POSITIVE := &4;
cCaptureOpt CONSTANT VARCHAR2(20) := 'MERGE';
cStagingSchema CONSTANT VARCHAR2(30) := UPPER('&1');
v4Debug VARCHAR2(50);

-- for DataPump Export Job
cDirectory CONSTANT VARCHAR2(20) := UPPER('&2');
IsSkipExport BOOLEAN := false;
i NUMBER;
vDataPumpJobHandle NUMBER;
vProgress_ratio NUMBER;
vJobState VARCHAR2(30);
oLogEntry ku$_LogEntry;
oStatus ku$_Status;
BEGIN
DBMS_OUTPUT.ENABLE;

-- STS名にInstance#を付加
-- (RAC環境での複数ノードでの実行を考慮してインスタンス番号を付加)
v4Debug := 'Build STSNAME';
FOR instance_rec IN (SELECT TO_CHAR(instance_number) AS inum FROM v$instance) LOOP
vStsName := vStsName || instance_rec.inum;
END LOOP;

-- SQLSETの作成
v4Debug := 'CREATE_SQLSET';
DBMS_SQLTUNE.CREATE_SQLSET (
sqlset_name => vStsName
,sqlset_owner => NULL
);

-- カーソルキャッシュからSTSへ定期キャプチャ
v4Debug := 'CAPTURE_CURSOR_CACHE_SQLSET';
DBMS_SQLTUNE.CAPTURE_CURSOR_CACHE_SQLSET (
sqlset_name => vStsName
,time_limit => cTimeLimit
,repeat_interval => cInterval
,capture_option => cCaptureOpt
,capture_mode => DBMS_SQLTUNE.MODE_REPLACE_OLD_STATS
,basic_filter =>
'parsing_schema_name NOT IN (
''SYS'', ''SYSTEM'', ''APEX_050000'', ''APEX_040000'', ''SYSMAN''
)'
,sqlset_owner => NULL
);

-- STSエクスポート向けステージング表の作成
v4Debug := 'CREATE_STGTAB_SQLSET';
DBMS_SQLTUNE.CREATE_STGTAB_SQLSET (
table_name => vStsName
,schema_name => cStagingSchema
,db_version => NULL
);

-- STSをステージング表へパック
v4Debug := 'PACK_STGTAB_SQLSET';
BEGIN
DBMS_SQLTUNE.PACK_STGTAB_SQLSET (
sqlset_name => vStsName
,sqlset_owner => NULL
,staging_table_name => vStsName
,staging_schema_owner => cStagingSchema
,db_version => NULL
);
EXCEPTION
WHEN OTHERS THEN
IF sqlcode = -15701 THEN
IsSkipExport := true;
DBMS_OUTPUT.PUT_LINE('*** Info - No data packed from SQLSET ***');
ELSE
RAISE;
END IF;
END;

IF IsSkipExport = false
THEN
-- ステージング表を表モードでエクスポート
-- エクスポートのモード等の設定
v4Debug := 'OPEN';
vDataPumpJobHandle
:= DBMS_DATAPUMP.OPEN (
operation => 'EXPORT'
,job_mode => 'TABLE'
,remote_link => NULL
,job_name => vStsName
,version => 'LATEST'
);

-- エスポートダンプファイルとログファイルの設定
v4Debug := 'ADD_FILE - dumpfile';
DBMS_DATAPUMP.ADD_FILE (
handle => vDataPumpJobHandle
,filename => vStsName || '.dmp'
,directory => cDirectory
,filetype => DBMS_DATAPUMP.KU$_FILE_TYPE_DUMP_FILE
);

v4Debug := 'ADD_FILE - logfile';
DBMS_DATAPUMP.ADD_FILE (
handle => vDataPumpJobHandle
,filename => vStsName || '.log'
,directory => cDirectory
,filetype => DBMS_DATAPUMP.KU$_FILE_TYPE_LOG_FILE
);

-- メターデータフィルタの設定
-- エクスポート対象表のスキーマ
v4Debug := 'METADATA_FILTER - schema name';
DBMS_DATAPUMP.METADATA_FILTER (
handle => vDataPumpJobHandle
,name => 'SCHEMA_LIST'
,value => '''' || cStagingSchema || ''''
);

-- エクスポート対象表
v4Debug := 'METADATA_FILTER - table name';
DBMS_DATAPUMP.METADATA_FILTER (
handle => vDataPumpJobHandle
,name => 'NAME_LIST'
,value => '''' || vStsName || ''''
);

-- DataPump Exportジョブの実行
v4Debug := 'START_JOB';
DBMS_DATAPUMP.START_JOB (
handle => vDataPumpJobHandle
);

-- DataPump Exportジョブ状況監視と終了判定
-- ジョブ終了または停止されるまでループして待機
v4Debug := 'JOB_STATE';
vProgress_ratio := 0;
vJobState := 'UNDEFINED';
WHILE (vJobState != 'COMPLETED') AND (vJobState != 'STOPPED') LOOP
DBMS_DATAPUMP.GET_STATUS (
vDataPumpJobHandle
,DBMS_DATAPUMP.KU$_STATUS_JOB_ERROR
+ DBMS_DATAPUMP.KU$_STATUS_JOB_STATUS
+ DBMS_DATAPUMP.KU$_STATUS_WIP
,-1
,vJobState
,oStatus
);

-- 処理中(Work-In-Progress : WIP)または、
-- エラーのいずれかのメッセージを受け取ったら表示
IF (BITAND(oStatus.mask, DBMS_DATAPUMP.KU$_STATUS_WIP) != 0)
THEN
oLogEntry := oStatus.wip;
ELSE
IF (BITAND(oStatus.mask, DBMS_DATAPUMP.KU$_STATUS_JOB_ERROR) != 0)
THEN
oLogEntry := oStatus.error;
ELSE
oLogEntry := NULL;
END IF;
END IF;
IF oLogEntry IS NOT NULL
THEN
i := oLogEntry.FIRST;
WHILE i IS NOT NULL LOOP
DBMS_OUTPUT.PUT_LINE(oLogEntry(i).LogText);
i := oLogEntry.NEXT(i);
END LOOP;
END IF;
END LOOP;

-- Data Pump Exportジョブ終了
DBMS_DATAPUMP.DETACH(vDataPumpJobHandle);
END IF;

-- ステージング表の削除
v4Debug := 'Drop staging table';
EXECUTE IMMEDIATE 'DROP TABLE ' || cStagingSchema || '.' || vStsName || ' PURGE';

-- SQLSETの削除
v4Debug := 'DROP_SQLSET';
DBMS_SQLTUNE.DROP_SQLSET (
sqlset_name => vStsName
);

EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(sqlerrm());
DBMS_OUTPUT.PUT_LINE(v4Debug);
RAISE;
END;
/
EXIT


STSが空ではない場合の実行例
キャプチャ処理は600秒実行しキャプチャ間隔は120秒、キャプチャ終了後、STSUSRスキーマにステージング表を作成、STSをステージング表へパック、ステージング表を指定したディレクトリオブジェクト以下にDataPump Exportしています。

[oracle@guppy ˜]$ sqlplus system@orcl @sts_capture stsusr sts_exp_dir 10*60 2*60

SQL*Plus: Release 12.1.0.2.0 Production on Fri Oct 6 15:13:53 2017

Copyright (c) 1982, 2014, Oracle. All rights reserved.

Enter password:

Connected to:
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
With the Partitioning, OLAP, Advanced Analytics and Real Application Testing options

old 4: cTimeLimit CONSTANT POSITIVE := &3;
new 4: cTimeLimit CONSTANT POSITIVE := 10*60;
old 5: cInterval CONSTANT POSITIVE := &4;
new 5: cInterval CONSTANT POSITIVE := 2*60;
old 7: cStagingSchema CONSTANT VARCHAR2(30) := UPPER('&1');
new 7: cStagingSchema CONSTANT VARCHAR2(30) := UPPER('stsusr');
old 11: cDirectory CONSTANT VARCHAR2(20) := UPPER('&2');
new 11: cDirectory CONSTANT VARCHAR2(20) := UPPER('sts_exp_dir');
Starting "SYS"."STS201710061513571":
Estimate in progress using BLOCKS method...
Processing object type TABLE_EXPORT/TABLE/TABLE_DATA
Total estimation using BLOCKS method: 1.062 MB
Processing object type TABLE_EXPORT/TABLE/TABLE
Processing object type TABLE_EXPORT/TABLE/INDEX/STATISTICS/INDEX_STATISTICS
Processing object type TABLE_EXPORT/TABLE/STATISTICS/TABLE_STATISTICS
Processing object type TABLE_EXPORT/TABLE/STATISTICS/MARKER
. . exported "STSUSR"."STS201710061513571" 51.55 KB 1 rows
Master table "SYS"."STS201710061513571" successfully loaded/unloaded
******************************************************************************
Dump file set for SYS.STS201710061513571 is:
/home/oracle/exp/STS201710061513571.dmp
Job "SYS"."STS201710061513571" successfully completed at Fri Oct 6 15:26:50 2017 elapsed 0 00:00:47

PL/SQL procedure successfully completed.

STSが空の場合の例
ステージング表へのSTSのパックやDataPump Exportの実行をパイパス。STSが空であることを表示して終了します。
なお、以下の例ではキャプチャ時間と間隔は意図的に短くしてあります。

[oracle@guppy  ˜]$ sqlplus system@orcl @sts_capture stsusr sts_exp_dir 2*60 60

SQL*Plus: Release 12.1.0.2.0 Production on Fri Oct 6 16:04:55 2017

Copyright (c) 1982, 2014, Oracle. All rights reserved.

Enter password:

Connected to:
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
With the Partitioning, OLAP, Advanced Analytics and Real Application Testing options

old 4: cTimeLimit CONSTANT POSITIVE := &3;
new 4: cTimeLimit CONSTANT POSITIVE := 2*60;
old 5: cInterval CONSTANT POSITIVE := &4;
new 5: cInterval CONSTANT POSITIVE := 60;
old 7: cStagingSchema CONSTANT VARCHAR2(30) := UPPER('&1');
new 7: cStagingSchema CONSTANT VARCHAR2(30) := UPPER('stsusr');
old 11: cDirectory CONSTANT VARCHAR2(20) := UPPER('&2');
new 11: cDirectory CONSTANT VARCHAR2(20) := UPPER('sts_exp_dir');
*** Info - No data packed from SQLSET ***

PL/SQL procedure successfully completed.


このスクリプトをshellに組み込んでcronでも定期実行したりDBMS_SCHEDULERで定期実行するのも良いかな。
オレオレサンプルなので、より便利に改造して使ってね。v

| | コメント (0) | トラックバック (0)

2017年10月 6日 (金)

DBMS_SQLTUNE.PACK_STGTAB_SQLSETって、例外投げんのかよw

ということで、タイトル通り
マニュアル上、例外を投げるとは記載されてないのですが、テストしてたら”例外投げる”PL/SQLプロシージャ、意外と多いんですw
意図的に例外投げるよーというのは大抵マニュアルに記載されているんですが。人が書いてますからね、記載漏れも仕方ないっすねぇw 

マニュアルバグ、忘れちゃうので、自分向けFAQ and 備忘録 


Oracle® Database PL/SQLパッケージおよびタイプ・リファレンス 12c リリース1 (12.1) B71281-05
DBMS_SQLTUNE.PACK_STGTAB_SQLSETプロシージャ

Oracle Database PL/SQL Packages and Types Reference (12.2)のマニュアルには DBMS_SQLTUNE.PACK_STGTAB_SQLSET Procedure は例外を投げるよ!という記載はないが...

DBMS_SQLTUNE.PACK_STGTAB_SQLSET Procedure
DBMS_SQLTUNE.PACK_STGTAB_SQLSET Procedure

orcl@SYS> r
1 SELECT
2 ID
3 ,NAME
4 ,OWNER
5 ,CREATED
6 ,LAST_MODIFIED
7 ,STATEMENT_COUNT
8 FROM
9 dba_sqlset
10 ORDER BY
11 OWNER
12 ,CREATED
13*


ID NAME OWNER CREATED LAST_MODI STATEMENT_COUNT
---------- ------------------------------ ------------------------------ --------- --------- ---------------
1 STS201710052340431 SYS 05-OCT-17 05-OCT-17 0
2 STS201710061431151 SYS 06-OCT-17 06-OCT-17 2

statement_count=0と2の2つのSTS(SQL Tuning Set)があります。
STS(SQL Tuning Set)については、ここでは記載しませんが、何それ? 気になる、気になる〜という方は、
Oracle Database の STS(SQL Tuning Set) を活用して、SQL の 性能統計 や 実行計画 を キャプチャする。を覗いて見てください。

(次回のネタは思いっきりそこなので、予習にもなりますよ :)

例外を投げるとはマニュアルに記載されてないプロシージャが実は例外投げるじゃん! 
というケースは、dbms_sqltune.pack_stgtab_sqlset以外にもあるんですが、
急ぎのやっつけ仕事で遭遇すると、ここで見つかってよかった!!
という安堵感とともに、
ムカ〜〜〜〜っという憎悪も湧いてくるわけですw 
(何れにしてもテストはしっかりやりましょうw

以下、例外投げるとは書かれてないけど、投げるじゃん、dbms_sqltune.pack_stgtab_sqlset の簡易テストの記録

準備として、dbms_sqltune.pack_stgtab_sqlsetは、STSを退避する為に必要なステージング表を作成します。

orcl@SYS> exec dbms_sqltune.create_stgtab_sqlset(table_name=>'TEST',schema_name=>'STSUSR');

PL/SQL procedure successfully completed.

statement_count=0つまり、STSに記録されたSQLの情報がない場合です。
ワーニングとしての意味が強いと思われますが、ORA-15701が投げつけられて、STSが空だということを教えてくれます! :)
マニュアルには記載されてなかったので焦りますw 実際のところざっくり書いたコードではこの例外をハンドリングなんてしてませんでした。

orcl@SYS> l
1 begin
2 dbms_sqltune.pack_stgtab_sqlset(
3 sqlset_name=>'STS201710052340431'
4 ,sqlset_owner=>null
5 ,staging_table_name=>'TEST'
6 ,staging_schema_owner=>'STSUSR'
7 );
8* end;
orcl@SYS> /
begin
*
ERROR at line 1:
ORA-15701: All "SQL Tuning Set(s)" with name like "STS201710052340431" and owner like "SYS" are empty
ORA-06512: at "SYS.DBMS_SQLTUNE", line 5398
ORA-06512: at "SYS.DBMS_SQLTUNE", line 7928
ORA-06512: at line 2


一方、statement_count=2でSTSに記録されたSQLがある場合、つまり、STSは空じゃない場合は正常終了します。

orcl@SYS> l
1 begin
2 dbms_sqltune.pack_stgtab_sqlset(
3 sqlset_name=>'STS201710061431151'
4 ,sqlset_owner=>null
5 ,staging_table_name=>'TEST'
6 ,staging_schema_owner=>'STSUSR'
7 );
8* end;
orcl@SYS> /

PL/SQL procedure successfully completed.

STSが空だという例外を拾って、後続処理にあるであろう、ステージング表のエクスポートとかをバイパスしたりすることはできますね:)

dbms_sqltune.pack_stgtab_sqlsetは、STSが空だと例外投げるよ というお話でした。


To be continued...

| | コメント (0) | トラックバック (0)

2017年7月12日 (水)

SQLチューニング祭りの記録-随時更新(祭りじゃないのもあるけど)

Oracle DatabaseのSQLチューナーとしてのデビューは2005年ぐらいで、ちょうど12年:)
Twitterで結果報告?w するようになったのは2011年ごろから。

なんだか当時のプロジェクトを思い出して、あんな人いたっけなー、などと思い出しながら、ニヤニヤしてましたw


SQLチューニング祭りにも色々ありますが、お祭りと化す状況だと、ほとんどの場合、

SQL文書き換えたくない

ビームが放たれることが多いんですが、
最終的に必要最低限ということで、ヒントは使えることは多いんですよね。
(外科的手術が必要なSQLの遅延もあります。。。けどね)

SPMという手もありますが、そもそもハードパースが遅いとSPMどころじゃなかったりw

疲れすぎて、twするのも忘れっちゃったりするので、twしたやつは随時更新予定:)

年月 ネン Before Tuning(ms) After Tuning(ms) 改善 カイゼン倍) バイ
2011/10 1 6,000 10 600
2011/10 2 5,780 10 578
2011/10 3 170 60 3
2011/11 4 30 10 3
2011/11 5 485 1 485
2011/11 6 890 10 89
2011/11 7 900,500 2,500 360
2011/11 8 170 10 17
2011/11 9 1,560 1 1,560
2011/11 10 1,890 33 57
2011/12 11 10,800,000 3,000 3,600
2012/01 12 58,000 1,000 58
2012/06 13 1,080,000 1,000 1,080
2012/08 14 1,800,000 50 36,000
2012/12 15 18,000,000 7,000 2,571
2012/12 16 7,300,000 17,000 429
2013/01 17 21,600,000 180,000 120
2013/01 18 4,000 10 400
2013/03 19 1,100 10 110
2013/06 20 234,000,000 120,000 1,950
2013/08 21 5,000 400 13
2013/08 22 50,400,000 1,620,000 31
2013/08 23 6,000,000 70,000 86
2013/09 24 32,000 420 76
2014/03 25 191 2 96
2014/05 26 14,400,000 90,000 160
2014/05 27 40,000 2,000 20
2014/05 28 320,000 8,000 40
2014/06 29 17,000 3,000 6
2016/02 30 2,000,000 10 200,000
2016/04 31 24,000 400 60
2016/05 32 10,000 40 250
合計ゴウケ 368,808,766 2,125,987 173


番外編

| | コメント (0) | トラックバック (0)

2017年7月 1日 (土)

リソースマネージャ:MTA環境のインスタンスケージングが効いているかざっくり確認するスクリプト

元になるビューは1分間隔で更新されているようで秒単位の粒度では確認できないもののざっくりでもいいからリアルタムに確認したい場合には便利

むかーしやった検証はnon CDBかつ、秒単位で見たいという要望だったため他のビューから算出したこともあり、ここで利用しているv$rsrcmetricの類は使わなかったことを思い出した。
完全に忘れてた(@@)。
2年も触らなきゃ忘れるさ。人間だものw orz.

ということで、 Oracle Database 12c 12.1.0.2.0 向けのメモ

注)以下、CDB$ROOTに接続してSQL文を実行しています


利用環境は以下の通りのMTA

orcl12c@SYS> select * from v$version;

BANNER CON_ID
-------------------------------------------------------------------------------- ----------
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production 0
PL/SQL Release 12.1.0.2.0 - Production 0
CORE 12.1.0.2.0 Production 0
TNS for Linux: Version 12.1.0.2.0 - Production 0
NLSRTL Version 12.1.0.2.0 - Production 0


CPUは4 (VMですが)

orcl12c@SYS> show parameter cpu_count

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
cpu_count integer 4


ORCLとORCL2という2つのPDB対してCPUのUTILIZATION_LIMITでCPU利用率を制限しています。
12.2以降はPDBのインスタンスケージングもnon-CDB環境同様に、CPU_COUNT設定+リソースマネージャで制御できちゃうらしい。わかりやすくて良い!(時間があればいずれ)

orcl12c@SYS> select name from v$containers;

NAME
------------------------------
CDB$ROOT
PDB$SEED
ORCL
ORCL2


orcl12c@SYS> r
1 SELECT
2 vc.name
3 , vp.utilization_limit
4 FROM
5 v$rsrc_plan vp
6 INNER JOIN v$containers vc
7 ON vp.con_id=vc.con_id
8 ORDER BY
9* vc.con_id

NAME UTILIZATION_LIMIT
------------------------------ -----------------
CDB$ROOT
PDB$SEED
ORCL 25
ORCL2 50

それぞれのPDBでCPU数以上のCPUバウンドな処理を実行して負荷かけ中

resmgr:cpu quantumとう待機イベントはresource managerがCPUの利用率を制御していることを示す待機イベント!
リソース制御が効いていることを示してます。

orcl12c@SYS> select username,event from v$session where username is not null order by username;

USERNAME EVENT
-------------- ----------------------------------------------------------------
SYS Streams AQ: waiting for messages in the queue
SYS Streams AQ: waiting for messages in the queue
SYS SQL*Net message from client
SYS Streams AQ: waiting for messages in the queue
SYS Streams AQ: waiting for messages in the queue
SYS resmgr:cpu quantum
SYS SQL*Net message to client
SYS SQL*Net message from client
TOTHER01 resmgr:cpu quantum
TOTHER01 resmgr:cpu quantum
TOTHER01 resmgr:cpu quantum
TOTHER01 resmgr:cpu quantum
TOTHER01 resmgr:cpu quantum
TOTHER01 resmgr:cpu quantum
TOTHER01 resmgr:cpu quantum
TOTHER01 resmgr:cpu quantum
TOTHER01 resmgr:cpu quantum
TOTHER01 resmgr:cpu quantum
TOTHER01 resmgr:cpu quantum
TOTHER01 resmgr:cpu quantum
TOTHER01 resmgr:cpu quantum
TOTHER01 resmgr:cpu quantum


vmstatでは全体のCPU利用率は見えますが複数のPDBが想定通りケージングされているかは見えません。

$ vmstat -t 5 1000
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- -----timestamp-----
r b swpd free buff cache si so bi bo in cs us sy id wa st JST
12 0 0 1487628 55660 4727120 0 0 125 13 452 262 21 1 78 0 0 2017-06-29 23:48:16
11 0 0 1480868 55668 4727176 0 0 0 28 3817 1459 69 2 29 0 0 2017-06-29 23:48:21
11 0 0 1480900 55676 4727068 0 0 0 16 3982 1480 73 2 25 0 0 2017-06-29 23:48:26
13 0 0 1480948 55684 4727008 0 0 0 17 3722 1383 68 2 30 0 0 2017-06-29 23:48:31
1 0 0 1480956 55684 4726980 0 0 0 10 3765 1410 69 2 29 0 0 2017-06-29 23:48:36
3 0 0 1480956 55692 4726984 0 0 0 18 3886 1417 76 2 22 0 0 2017-06-29 23:48:41
10 0 0 1480336 55716 4727044 0 0 0 35 3830 1441 69 2 29 0 0 2017-06-29 23:48:46
0 0 0 1480336 55724 4727036 0 0 0 24 4033 1536 76 2 22 0 0 2017-06-29 23:48:51
14 0 0 1469888 55724 4727100 0 0 0 14 3722 1508 67 2 31 0 0 2017-06-29 23:48:56
11 0 0 1459988 55732 4727384 0 0 0 17 4049 1685 73 3 24 0 0 2017-06-29 23:49:01
13 0 0 1419652 55740 4728940 0 0 134 38 4299 1901 80 3 16 0 0 2017-06-29 23:49:06
10 0 0 1419676 55748 4728860 0 0 0 21 3861 1491 69 2 29 0 0 2017-06-29 23:49:11
10 0 0 1419768 55756 4729044 0 0 0 28 3914 1471 71 2 27 0 0 2017-06-29 23:49:16
12 0 0 1421380 55764 4729064 0 0 0 31 3697 1445 68 2 31 0 0 2017-06-29 23:49:21
11 0 0 1421412 55764 4729016 0 0 0 13 3735 1412 68 2 30 0 0 2017-06-29 23:49:26
10 0 0 1421544 55772 4728980 0 0 0 18 3986 1521 73 2 25 0 0 2017-06-29 23:49:31
11 0 0 1464824 55780 4728524 0 0 0 11 3702 1372 68 2 30 0 0 2017-06-29 23:49:36
10 0 0 1458468 55788 4728572 0 0 1 20 4030 1495 74 2 24 0 0 2017-06-29 23:49:41
10 0 0 1458624 55796 4728452 0 0 0 17 3836 1520 68 2 29 0 0 2017-06-29 23:49:46
0 0 0 1458880 55804 4728400 0 0 0 20 3908 1522 70 2 28 0 0 2017-06-29 23:49:51
0 0 0 1458864 55804 4728424 0 0 0 13 4039 1453 76 2 21 0 0 2017-06-29 23:49:56
0 0 0 1458864 55812 4728420 0 0 0 18 3870 1471 70 2 28 0 0 2017-06-29 23:50:01
1 0 0 1454680 55820 4728804 0 0 2 16 4541 2070 76 4 20 0 0 2017-06-29 23:50:06
1 0 0 1454728 55828 4728688 0 0 0 29 3829 1453 70 2 28 0 0 2017-06-29 23:50:11
9 0 0 1454048 55836 4728596 0 0 0 18 3751 1415 69 2 29 0 0 2017-06-29 23:50:16
10 0 0 1454056 55836 4728548 0 0 0 15 3867 1472 72 2 26 0 0 2017-06-29 23:50:21
11 0 0 1454088 55844 4728480 0 0 0 21 3704 1390 67 2 31 0 0 2017-06-29 23:50:26
10 0 0 1454252 55852 4728396 0 0 0 17 3774 1391 70 2 28 0 0 2017-06-29 23:50:31
10 0 0 1465692 55860 4728416 0 0 0 24 3914 1484 71 2 26 0 0 2017-06-29 23:50:36
10 0 0 1465468 55868 4728380 0 0 0 18 3794 1435 68 2 30 0 0 2017-06-29 23:50:41


最初に書いたように、1分間隔で少々粒度は粗めですがざっくり各PDBのCPU利用率を確認する場合には以下のようなスクリプトが便利!!
以下クエリをshellで定期的に実行するなり、随時実行するなりして確認すると便利、なお、gv$rsrcmgrmetricの代わりに、gv$rsrcmgrmetric_historyを
利用すれば過去1時間分だけですが、遡って確認することもできます。

orcl12c@SYS> !cat show_con_cpu.sql
SELECT
to_char(begin_time, 'RR/MM/DD HH24:MI:SS') AS time
, vc.con_id
, vc.name
, (SELECT value FROM v$osstat WHERE stat_name = 'NUM_CPUS') AS "CPUs"
, round((sum(cpu_consumed_time) / 1000) / (60 * (SELECT value FROM v$parameter WHERE name = 'cpu_count'))*100,2) AS "%cpu"
FROM
gv$rsrcmgrmetric gvr
INNER JOIN v$containers vc
ON
vc.con_id = gvr.con_id
GROUP BY
vc.con_id
,vc.name
,begin_time
ORDER BY
begin_time
,vc.con_id
/

各PDBが25%、50%で制限されていることがわかります!!

orcl12c@SYS> @show_con_cpu.sql

TIME CON_ID NAME CPUs %cpu
----------------- ---------- ------------------------------ ---------- ----------
17/06/29 23:48:30 1 CDB$ROOT 4 .32
17/06/29 23:48:30 2 PDB$SEED 4 0
17/06/29 23:48:30 3 ORCL 4 25.87
17/06/29 23:48:30 5 ORCL2 4 51.3

orcl12c@SYS> @show_con_cpu.sql

TIME CON_ID NAME CPUs %cpu
----------------- ---------- ------------------------------ ---------- ----------
17/06/29 23:49:31 1 CDB$ROOT 4 .03
17/06/29 23:49:31 2 PDB$SEED 4 0
17/06/29 23:49:31 3 ORCL 4 25.31
17/06/29 23:49:31 5 ORCL2 4 50.74


しかし、おとといの無茶振りというか、俺、何スレッドで動作すればいいの的な、パススルーな振りに、疲れて
こんなネタにしてみましたよ。と。w

| | コメント (0) | トラックバック (0)

2017年6月17日 (土)

SQL Developer de Real time SQL monitoring / FAQ

Oracle Database 12c EEかつ、Diagnostic Pack / Tuning Packもあるのに
SQL real time monitoringしないのはもったいないという話から、
http://www.oracle.com/technetwork/jp/ondemand/db-basic/d-16-ssqltuning-1448439-ja.pdf

EMなくても簡単にできないのか? 

と聞かれたので

おすすめなのが、SQL Developer

SQL Developer 4.2
http://www.oracle.com/technetwork/jp/developer-tools/sql-developer/downloads/index.html

テキストよりGUIでという方にはおすすめ。

細けーはなしはいつか書くつもりですが、とりあえず、こんなことを確認できるんでっす。

20170617_110017

20170617_110411


20170617_110436


!!!!!ここ重要!!!!!
以下のダイアログには、Tuning Packが必要とありますが、Tuning Packを利用するにはDiagnostic Packが必要なので、 Real time SQL monitoringを行うには、Diagnostic PackとTuning Packの両方が必要というこなので、間違わないようにしてくださいませ。マニュアルにはしっかり書かれてますが、折角ダイアログで警告してるのにコメントが残念な。
https://docs.oracle.com/cd/E57425_01/121/DBLIC/options.htm#CIHFIHFG

20170617_110454

20170617_121606


20170617_121624


20170617_121629


20170617_121637


| | コメント (0) | トラックバック (0)

2017年4月 3日 (月)

CDBとPDBの間で迷子になりそう PART3 - containers clause - その3

Previously on Mac De Oracle.

AWRでなんとか発行したSQL文以外に、containers句を元とする再帰SQL文を捕まえられたのはいいが、その再帰SQL文と元となるcontainers句を紐づけることが難しそう。
また、ヒントでチューニングするのも難しそう(これはそれっぽいのがあるけどまだ調べてないのでどこまでできるのか未知の領域)。
できるとすれば、SPMによるチューニングかな?、というところまではなんとか見えてきました。


AWRではcontainers句で生成される再帰SQL文を特定するのに難ありというところまでは見えてきたので、
最後の希望w SQLトレースはどうなのか、気になっていたので試した見た。
(利用している表とSQL文は多少変えていますが、大きな差はありません)

SQLトレースをセッションレベルで有効化して(トレースファイルを特定しやすくしておくことをお忘れなく)、containers句を含むSQL文を実行して、SQLトレースを無効化。

orcl12c@C##HOGE> alter session set tracefile_identifier=containers;
orcl12c@C##HOGE> exec dbms_monitor.session_trace_enable(waits=>true);

PL/SQL procedure successfully completed.

orcl12c@C##HOGE> select * from containers(hoge) where id in (1,3);

ID DATA CON_ID
---------- -------------- ----------
3 test3 4
1 test1 3

orcl12c@C##HOGE> exec dbms_monitor.session_trace_disable;

ここで、前々回、SQL監視のParallel Execution Detailsセクションをみてみると、スレーブプロセスがどうなっていたかわかりやすいですね!

Parallel Execution Details (DOP=4 , Servers Allocated=4)
==========================================================================================
| Name | Type | Server# | Elapsed | Cpu | Other | Buffer | Wait Events |
| | | | Time(s) | Time(s) | Waits(s) | Gets | (sample #) |
==========================================================================================
| PX Coordinator | QC | | 0.00 | 0.00 | 0.00 | | |
| p000 | Set 1 | 1 | 0.00 | 0.00 | | | |
| p001 | Set 1 | 2 | 0.00 | | 0.00 | | |
| p002 | Set 1 | 3 | 0.00 | | 0.00 | 3 | |
| p003 | Set 1 | 4 | 0.00 | 0.00 | | 3 | |
==========================================================================================


SQLトレースファイルも各プロセスごとの取得されていることがわかります!!!
contrainers句から生成される再帰SQLはAWRレポートより見つけやすいかもしれない(個人差はあると思われるw) :)

Cp000〜p003と前述のSQL監視の情報と合わせて見ると多少は見やすいですよね。(AWRレポートのみで捉えることと比べたら遥かに面倒だとは思いますが)

[oracle@vbgeneric admin]$ cd $ORACLE_BASE/diag/rdbms/orcl12c/orcl12c/trace/
[oracle@vbgeneric trace]$ ls -l *CONTAINERS*
-rw-r----- 1 oracle oinstall 95231 4月 1 11:36 orcl12c_ora_4609_CONTAINERS.trc
-rw-r----- 1 oracle oinstall 875 4月 1 11:36 orcl12c_ora_4609_CONTAINERS.trm
-rw-r----- 1 oracle oinstall 23140 4月 1 11:36 orcl12c_p000_3375_CONTAINERS.trc
-rw-r----- 1 oracle oinstall 197 4月 1 11:36 orcl12c_p000_3375_CONTAINERS.trm
-rw-r----- 1 oracle oinstall 2942 4月 1 11:36 orcl12c_p001_3377_CONTAINERS.trc
-rw-r----- 1 oracle oinstall 149 4月 1 11:36 orcl12c_p001_3377_CONTAINERS.trm
-rw-r----- 1 oracle oinstall 98455 4月 1 11:35 orcl12c_p002_3379_CONTAINERS.trc
-rw-r----- 1 oracle oinstall 705 4月 1 11:35 orcl12c_p002_3379_CONTAINERS.trm
-rw-r----- 1 oracle oinstall 168368 4月 1 11:35 orcl12c_p003_3381_CONTAINERS.trc
-rw-r----- 1 oracle oinstall 1138 4月 1 11:35 orcl12c_p003_3381_CONTAINERS.trm
[oracle@vbgeneric trace]$

とりあえず、のぞいて見ましょう。


[oracle@vbgeneric trace]$ tkprof orcl12c_ora_4609_CONTAINERS.trc orcl12c_ora_4609_container.txt explain=system/oracle@orcl12c sys=yes waits=yes

TKPROF: Release 12.1.0.2.0 - Development on Sat Apr 1 11:47:26 2017

Copyright (c) 1982, 2014, Oracle and/or its affiliates. All rights reserved.

[oracle@vbgeneric trace]$ tkprof orcl12c_p000_3375_CONTAINERS.trc container_p000.txt explain=system/oracle@orcl12c sys=yes waits=yes

・・・中略・・・

[oracle@vbgeneric trace]$ tkprof orcl12c_p003_3381_CONTAINERS.trc container_p003.txt explain=system/oracle@orcl12c sys=yes waits=yes

TKPROF: Release 12.1.0.2.0 - Development on Sat Apr 1 11:47:26 2017

Copyright (c) 1982, 2014, Oracle and/or its affiliates. All rights reserved.

[oracle@vbgeneric trace]$ ls -l *container*
-rw-r--r-- 1 oracle oinstall 47736 4月 1 11:47 orcl12c_ora_4609_container.txt
-rw-r--r-- 1 oracle oinstall 7104 4月 1 11:47 container_p000.txt
-rw-r--r-- 1 oracle oinstall 5228 4月 1 11:47 container_p001.txt
-rw-r--r-- 1 oracle oinstall 34869 4月 1 11:47 container_p002.txt
-rw-r--r-- 1 oracle oinstall 38321 4月 1 11:48 container_p003.txt
[oracle@vbgeneric trace]$


最初にorcl12c_ora_4609_container.txtから見てみますか。
SQLトレースを有効化して、containers句を含むSQL文を発行、それに伴いいくつかの再帰SQL文、最後にSQLトレースを無効化してる流れは確認できます。
途中、再帰SQLとしてhoge表を問い合わせるために利用すると思われるSQL文をパースだけしているとみられる箇所。興味深いです:)



・・・中略・・・

SQL ID: g2nujq01pmynd Plan Hash: 0

SELECT /* */ *
FROM
"C##HOGE"."HOGE"


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

Misses in library cache during parse: 1
Optimizer mode: CHOOSE
Parsing user id: SYS (recursive depth: 1)
********************************************************************************
・・・中略・・・

この部分実際にPDBで実行される想定の再帰SQL文に近いけど、件数カウントしてるだけ..なんですね。なんで件数が必要なんだろ

SELECT count(*) 
FROM
C##HOGE."HOGE" "HOGE" WHERE "HOGE"."ID"=1 OR "HOGE"."ID"=3


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

Misses in library cache during parse: 1
Optimizer mode: CHOOSE
Parsing user id: SYS (recursive depth: 1)
Number of plan statistics captured: 1

Rows (1st) Rows (avg) Rows (max) Row Source Operation
---------- ---------- ---------- ---------------------------------------------------
0 0 0 SORT AGGREGATE (cr=0 pr=0 pw=0 time=14 us)
0 0 0 CONCATENATION (cr=0 pr=0 pw=0 time=7 us)
0 0 0 INDEX UNIQUE SCAN SYS_C0010012 (cr=0 pr=0 pw=0 time=4 us)(object id 93050)
0 0 0 INDEX UNIQUE SCAN SYS_C0010012 (cr=0 pr=0 pw=0 time=0 us)(object id 93050)

********************************************************************************
・・・中略・・・


そして実際にタイプしたSQL文の登場
実行計画は実行しているSQL文からは想像できない変わり果てた姿となっていて、実際に参照する表やオペレーションではなく、hoge表をアクセスしてる実行計画ではないのは見ての通り。

SQL ID: 25a37y74xrat4 Plan Hash: 1439328272

select *
from
containers(hoge) where id in (1,3)


call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 3 1 0
Execute 1 0.00 0.01 0 0 0 0
Fetch 2 0.00 0.12 0 0 0 2
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.01 0.14 0 3 1 2

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 103 (C##HOGE)
Number of plan statistics captured: 1

Rows (1st) Rows (avg) Rows (max) Row Source Operation
---------- ---------- ---------- ---------------------------------------------------
2 2 2 PX COORDINATOR (cr=0 pr=0 pw=0 time=133547 us)
0 0 0 PX SEND QC (RANDOM) :TQ10000 (cr=0 pr=0 pw=0 time=0 us cost=-9223372036854775808 size=78 card=1)
0 0 0 PX PARTITION LIST ALL PARTITION: 1 254 (cr=0 pr=0 pw=0 time=0 us cost=-9223372036854775808 size=78 card=1)
0 0 0 FIXED TABLE FULL X$CDBVW$ (cr=0 pr=0 pw=0 time=0 us cost=-9223372036854775808 size=78 card=1)


パラレルクエリーの各スレーブプロセスはどうかというと...

p000のトレース(抜粋)

rowsが0、Fetch countも0、ふーん、なるほど。

SQL ID: 25a37y74xrat4 Plan Hash: 1439328272

select *
from
containers(hoge) where id in (1,3)


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

Misses in library cache during parse: 0
Optimizer mode: ALL_ROWS
Parsing user id: 103 (C##HOGE) (recursive depth: 1)
Number of plan statistics captured: 1

Rows (1st) Rows (avg) Rows (max) Row Source Operation
---------- ---------- ---------- ---------------------------------------------------
0 0 0 PX COORDINATOR (cr=0 pr=0 pw=0 time=0 us)
0 0 0 PX SEND QC (RANDOM) :TQ10000 (cr=0 pr=0 pw=0 time=0 us cost=-9223372036854775808 size=78 card=1)
0 0 0 PX PARTITION LIST ALL PARTITION: 1 254 (cr=2 pr=0 pw=0 time=56179 us cost=-9223372036854775808 size=78 card=1)
0 0 0 FIXED TABLE FULL X$CDBVW$ (cr=2 pr=0 pw=0 time=53500 us cost=-9223372036854775808 size=78 card=1)

AWR SQLレポートで捕まえた再帰SQL文、これで実際に表をアクセスしてデータを取得していると考えられる。
OR句は、index unique scanをUNION、ヒントだと USE_CONCATヒントを利用したのと同じ実行計画になってる。

Fetch=1で、rows=0、ふふふーん。 0件ということは、これはデータなし!、どちらのPDBにもヒットするデータが1件ある。
データがないのは、SEEDとCDBのみなので、それのいずれかということ。

そして、HOGEという表は、CDB$ROOTと残る2つのPDBにある。また、CDB$ROOTのHOGE表はcontainers句を動作させるための前提条件なので空の表!

なので、p000はROOT$CDBのHOGE表を問い合わせていることになる!!!のか?!

SQL ID: 1u03tbqvywyf8 Plan Hash: 3444470255

SELECT /*+ RESULT_CACHE (SYSOBJ=TRUE SHELFLIFE=30) */ ID,DATA
FROM
"C##HOGE"."HOGE" "HOGE" WHERE "HOGE"."ID"=1 OR "HOGE"."ID"=3


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

Misses in library cache during parse: 1
Optimizer mode: CHOOSE
Parsing user id: SYS (recursive depth: 2)
Number of plan statistics captured: 1

Rows (1st) Rows (avg) Rows (max) Row Source Operation
---------- ---------- ---------- ---------------------------------------------------
0 0 0 RESULT CACHE 2fmgs0vhj7brb4c44nkh7shu6f (cr=2 pr=0 pw=0 time=76 us)
0 0 0 CONCATENATION (cr=2 pr=0 pw=0 time=38 us)
0 0 0 TABLE ACCESS BY INDEX ROWID HOGE (cr=1 pr=0 pw=0 time=19 us)
0 0 0 INDEX UNIQUE SCAN SYS_C0010012 (cr=1 pr=0 pw=0 time=11 us)(object id 93050)
0 0 0 TABLE ACCESS BY INDEX ROWID HOGE (cr=1 pr=0 pw=0 time=10 us)
0 0 0 INDEX UNIQUE SCAN SYS_C0010012 (cr=1 pr=0 pw=0 time=2 us)(object id 93050)

p001のトレース(抜粋)

不思議ですが、これは、元のクエリーがあるけど、データを取得する SQLID=1u03tbqvywyf8(前述したRESULT_CACHEヒントのついてるクエリ)が生成されていない!!
containers句を含むクエリのみあり、rowsも0、HOGEをといあわせる再帰SQL文を生成する必要のない表。HOGE表が存在しないのは今のところSEEDのみ。
HOGE表が存在しないので、 C##HOGE.HOGE表をアクセスするSQL文を実行したらエラーですからねぇ。無駄なことはしないはず。

ふむふむふむ。

・・・中略・・・

SQL ID: 25a37y74xrat4 Plan Hash: 1439328272

select *
from
containers(hoge) where id in (1,3)


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

Misses in library cache during parse: 0
Optimizer mode: ALL_ROWS
Parsing user id: 103 (C##HOGE) (recursive depth: 1)
Number of plan statistics captured: 1

Rows (1st) Rows (avg) Rows (max) Row Source Operation
---------- ---------- ---------- ---------------------------------------------------
0 0 0 PX COORDINATOR (cr=0 pr=0 pw=0 time=0 us)
0 0 0 PX SEND QC (RANDOM) :TQ10000 (cr=0 pr=0 pw=0 time=0 us cost=-9223372036854775808 size=78 card=1)
0 0 0 PX PARTITION LIST ALL PARTITION: 1 254 (cr=0 pr=0 pw=0 time=189 us cost=-9223372036854775808 size=78 card=1)
0 0 0 FIXED TABLE FULL X$CDBVW$ (cr=0 pr=0 pw=0 time=12 us cost=-9223372036854775808 size=78 card=1)

p002のトレースファイル(抜粋)


・・・中略・・・

SQL ID: 25a37y74xrat4 Plan Hash: 1439328272

select *
from
containers(hoge) where id in (1,3)


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

Misses in library cache during parse: 0
Optimizer mode: ALL_ROWS
Parsing user id: 103 (C##HOGE) (recursive depth: 1)
Number of plan statistics captured: 1

Rows (1st) Rows (avg) Rows (max) Row Source Operation
---------- ---------- ---------- ---------------------------------------------------
0 0 0 PX COORDINATOR (cr=0 pr=0 pw=0 time=0 us)
0 0 0 PX SEND QC (RANDOM) :TQ10000 (cr=0 pr=0 pw=0 time=0 us cost=-9223372036854775808 size=78 card=1)
1 1 1 PX PARTITION LIST ALL PARTITION: 1 254 (cr=961 pr=0 pw=0 time=121936 us cost=-9223372036854775808 size=78 card=1)
1 1 1 FIXED TABLE FULL X$CDBVW$ (cr=961 pr=0 pw=0 time=121610 us cost=-9223372036854775808 size=78 card=1)

・・・中略・・・

お! Fetch=1で、rows=1 HOGE表から1行フェッチしているので2つのPDBのいずれかということになりますね!!
ORのUNION ALLへの書き換えがOR条件の順序通りであれば、以下の赤字部分の結果からみればたぶん、ID=3のでデータが存在している方のPDBであるはず。

SQL ID: 1u03tbqvywyf8 Plan Hash: 1725204971

SELECT /*+ RESULT_CACHE (SYSOBJ=TRUE SHELFLIFE=30) */ ID,DATA
FROM
"C##HOGE"."HOGE" "HOGE" WHERE "HOGE"."ID"=1 OR "HOGE"."ID"=3


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

Misses in library cache during parse: 1
Optimizer mode: CHOOSE
Parsing user id: SYS (recursive depth: 2)
Number of plan statistics captured: 1

Rows (1st) Rows (avg) Rows (max) Row Source Operation
---------- ---------- ---------- ---------------------------------------------------
1 1 1 RESULT CACHE 6158j5h7rmww7g9fxb6fwawgvy (cr=3 pr=0 pw=0 time=120 us)
1 1 1 CONCATENATION (cr=3 pr=0 pw=0 time=44 us)
0 0 0 TABLE ACCESS BY INDEX ROWID HOGE (cr=1 pr=0 pw=0 time=15 us)
0 0 0 INDEX UNIQUE SCAN SYS_C0014566 (cr=1 pr=0 pw=0 time=9 us)(object id 99927)
1 1 1 TABLE ACCESS BY INDEX ROWID HOGE (cr=2 pr=0 pw=0 time=18 us)
1 1 1 INDEX UNIQUE SCAN SYS_C0014566 (cr=1 pr=0 pw=0 time=5 us)(object id 99927)


p003もp002のトレース同様 Fetch=1で、rows=1 HOGE表から1行フェッチしているので2つのPDBのいずれかということになりますね!!
p003のSQLトレースファイル


・・・中略・・・


SQL ID: 25a37y74xrat4 Plan Hash: 1439328272

select *
from
containers(hoge) where id in (1,3)


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

Misses in library cache during parse: 0
Optimizer mode: ALL_ROWS
Parsing user id: 103 (C##HOGE) (recursive depth: 1)
Number of plan statistics captured: 1

Rows (1st) Rows (avg) Rows (max) Row Source Operation
---------- ---------- ---------- ---------------------------------------------------
0 0 0 PX COORDINATOR (cr=0 pr=0 pw=0 time=0 us)
0 0 0 PX SEND QC (RANDOM) :TQ10000 (cr=0 pr=0 pw=0 time=0 us cost=-9223372036854775808 size=78 card=1)
1 1 1 PX PARTITION LIST ALL PARTITION: 1 254 (cr=1793 pr=0 pw=0 time=119492 us cost=-9223372036854775808 size=78 card=1)
1 1 1 FIXED TABLE FULL X$CDBVW$ (cr=1793 pr=0 pw=0 time=119279 us cost=-9223372036854775808 size=78 card=1)

・・・中略・・・


plan hash = 0 でParse = 1なのでやはりパースだけ。

SQL ID: g2nujq01pmynd Plan Hash: 0

SELECT /* */ *
FROM
"C##HOGE"."HOGE"


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

Misses in library cache during parse: 1
Optimizer mode: CHOOSE
Parsing user id: SYS (recursive depth: 2)
********************************************************************************

・・・中略・・・

やはり、rows=1 これも1行フェッチしているのでデータ登録済みの2PDBのいずれかで実行されているクエリだという点は間違いないらしい
そして、赤字部分のrowsからORの順番からして、ID=1のデータのあるPDBか。

SQL ID: 1u03tbqvywyf8 Plan Hash: 1990946707

SELECT /*+ RESULT_CACHE (SYSOBJ=TRUE SHELFLIFE=30) */ ID,DATA
FROM
"C##HOGE"."HOGE" "HOGE" WHERE "HOGE"."ID"=1 OR "HOGE"."ID"=3


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

Misses in library cache during parse: 1
Optimizer mode: CHOOSE
Parsing user id: SYS (recursive depth: 2)
Number of plan statistics captured: 1

Rows (1st) Rows (avg) Rows (max) Row Source Operation
---------- ---------- ---------- ---------------------------------------------------
1 1 1 RESULT CACHE 24q4fuaf71g7jf1upwg76y3a3q (cr=3 pr=0 pw=0 time=94 us)
1 1 1 CONCATENATION (cr=3 pr=0 pw=0 time=49 us)
1 1 1 TABLE ACCESS BY INDEX ROWID HOGE (cr=2 pr=0 pw=0 time=23 us)
1 1 1 INDEX UNIQUE SCAN SYS_C0010048 (cr=1 pr=0 pw=0 time=11 us)(object id 92602)
0 0 0 TABLE ACCESS BY INDEX ROWID HOGE (cr=1 pr=0 pw=0 time=11 us)
0 0 0 INDEX UNIQUE SCAN SYS_C0010048 (cr=1 pr=0 pw=0 time=5 us)(object id 92602)


SQLトレースで見たのが一番追いやすかったのですが(個人的に)、実践でこれをやるのは、少々辛いのでcontainers句を業務APで使っててチューニングという状況には遭遇したくないなw)

containers句の場合、実際にユーザ表にアクセスするために生成される再起SQL部分をいかにして特定するかが、
チューニング前の課題になってくるのは間違いなさそう。(今の所は)
SQLレポートやSQL監視が進化して、containsers句の場合は該当再起SQL文と実行計画も表示してくれたら楽かもしれないですけどね(それはAWRレポートも同様)

ということで、今日はここまで。

CDBとPDBの間で迷子になりそう PART3 - SQL*PlusのAutotrace編
CDBとPDBの間で迷子になりそう PART3 - SQL*PlusのAutotrace編 - その2
CDBとPDBの間で迷子になりそう PART3 - containers clause
CDBとPDBの間で迷子になりそう PART3 - containers clause - その2

| | コメント (0) | トラックバック (0)

2017年3月24日 (金)

CDBとPDBの間で迷子になりそう PART3 - containers clause - その2

Previously on Mac De Oracle.
CDBとPDBの間で迷子になりそう PART3 - containers clause

昨日は、AWRで再帰SQL文として各PDBで実行されるSQL文を捉えた!ところまででした。


今日はそのAWRレポートを見てみようと思います。

それらしきSQL文は、AWRレポートのSQL ordered by User I/O Wait Timeなど幾つかのセクションで見つかった!!!(テキストフォーマットで出力しています)

元のSQL文で付加したコメントは再帰SQL文にはまったく引きづがれないので、多くのSQL文をcontainers句で実行していたら紐づけるの大変そうだろうという予感とともに、ヒントによるチューニングってほぼ無理なんじゃないかって思います。
(RDF Graph系の似たような句というか関数だと幾つかのヒントを渡せる仕組みもあるんですがねぇ、これは無理っぽい)

使えるとすればSPMかな。

で、recult cacheも使うようなので、そのあたりの待機イベントも多少気にかける必要もでてくるかもしれない。
と、とーくを見ながら...

どう使うか次第でしょうけど、もし、チューニングが必要な状態になったら辛くなりそうだ!、という気配だけは強く感じたところで、今日は時間切れ。

SQL ordered by User I/O Wait Time       DB/Inst: ORCL12C/orcl12c  Snaps: 71-72
-> Resources reported for PL/SQL code includes the resources used by all SQL
statements called by the code.
-> %Total - User I/O Time as a percentage of Total User I/O Wait time
-> %CPU - CPU Time as a percentage of Elapsed Time
-> %IO - User I/O Time as a percentage of Elapsed Time
-> Captured SQL account for 44.2% of Total User I/O Wait Time (s):
-> Captured PL/SQL account for 39.8% of Total User I/O Wait Time (s):

User I/O UIO per Elapsed
Time (s) Executions Exec (s) %Total Time (s) %CPU %IO SQL Id
---------- ------------ ---------- ------ ---------- ------ ------ -------------

・・・中略・・・

0.0 1 0.00 2.6 0.0 92.9 6.9 fj6g3jvma49dq
Module: SQL*Plus
select /* test01 */ * from containers(emp) where empno=7369

・・・中略・・・

0.0 1 0.00 1.1 0.0 88.2 11.4 0n3ta15r2qc98
Module: SQL*Plus
PDB: PDBORCL12C
SELECT /*+ RESULT_CACHE (SYSOBJ=TRUE SHELFLIFE=30) */ EMPNO,ENAME,JOB,MGR,HIREDA
TE,SAL,COMM,DEPTNO FROM "C##HOGE"."EMP" "EMP" WHERE "EMP"."EMPNO"=7369

0.0 1 0.00 1.1 0.0 88.2 11.4 0n3ta15r2qc98
Module: SQL*Plus
PDB: PDBORCL12C
SELECT /*+ RESULT_CACHE (SYSOBJ=TRUE SHELFLIFE=30) */ EMPNO,ENAME,JOB,MGR,HIREDA
TE,SAL,COMM,DEPTNO FROM "C##HOGE"."EMP" "EMP" WHERE "EMP"."EMPNO"=7369

0.0 1 0.00 1.0 0.0 89.8 12.0 0n3ta15r2qc98
Module: SQL*Plus
PDB: PDBORCL12CLONED
SELECT /*+ RESULT_CACHE (SYSOBJ=TRUE SHELFLIFE=30) */ EMPNO,ENAME,JOB,MGR,HIREDA
TE,SAL,COMM,DEPTNO FROM "C##HOGE"."EMP" "EMP" WHERE "EMP"."EMPNO"=7369

0.0 1 0.00 1.0 0.0 89.8 12.0 0n3ta15r2qc98
Module: SQL*Plus
PDB: PDBORCL12CLONED
SELECT /*+ RESULT_CACHE (SYSOBJ=TRUE SHELFLIFE=30) */ EMPNO,ENAME,JOB,MGR,HIREDA
TE,SAL,COMM,DEPTNO FROM "C##HOGE"."EMP" "EMP" WHERE "EMP"."EMPNO"=7369

参考までに、各SQLの実行計画を(AWR SQLレポートより)

Execution Plan
-----------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Pstart| Pstop | TQ |IN-OUT| PQ Distrib |
-----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 1 | | | | | |
| 1 | PX COORDINATOR | | | | | | | | | |
| 2 | PX SEND QC (RANDOM) | :TQ10000 | 1 | 100 | | | | Q1,00 | P->S | QC (RAND) |
| 3 | PX PARTITION LIST ALL| | 1 | 100 | | 1 | 254 | Q1,00 | PCWC | |
| 4 | FIXED TABLE FULL | X$CDBVW$ | 1 | 100 | | | | Q1,00 | PCWP | |
-----------------------------------------------------------------------------------------------------------------

Note
-----
- cpu costing is off (consider enabling it)

Full SQL Text

SQL ID SQL Text
------------ -----------------------------------------------------------------
fj6g3jvma49d select /* test01 */ * from containers(emp) where empno=7369



Execution Plan
------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 1 (100)|
| 1 | RESULT CACHE | bxqp7vj85m72tbusfcmqynsj1t | | | |
| 2 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 87 | 0 (0)|
| 3 | INDEX UNIQUE SCAN | PK_EMP | 1 | | 0 (0)|
------------------------------------------------------------------------------------------------

Result Cache Information (identified by operation id):
------------------------------------------------------

1 -

Full SQL Text

SQL ID SQL Text
------------ -----------------------------------------------------------------
0n3ta15r2qc9 SELECT /*+ RESULT_CACHE (SYSOBJ=TRUE SHELFLIFE=30) */ EMPNO, ENAM
E, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO FROM "C##HOGE"."EMP" "EM
P" WHERE "EMP"."EMPNO"=7369

次回でこのシリーズは最終回にするかも。。もう1つ2つ追加するかも。。。

To be continued.


CDBとPDBの間で迷子になりそう PART3 - SQL*PlusのAutotrace編
CDBとPDBの間で迷子になりそう PART3 - SQL*PlusのAutotrace編 - その2
CDBとPDBの間で迷子になりそう PART3 - containers clause

| | コメント (0) | トラックバック (0)

2016年12月17日 (土)

スタースキーマを扱う実行計画の特徴

JPOUG Advent Calendar 2016の17日目のエントリです。
昨日は、id:kenken08さんのMySQLのsql_modeにあるORACLEとは - kenken0807_DBメモでした。

第三の柴田さんのネタを見て、急遽内容を変更しました。:)
SQLチューニングと対戦格闘ゲームの類似性について語る。- JPOUG Advent Calendar 2016 Day 15 - - ねら~ITエンジニア雑記


DWH系のスタースキーマを扱う実行計画の特徴を簡単にまとめておきたいと思います。(個人的には、in-memory aggregationが今年のハイライトだったのでw)

※サンプルスキーマ:SHスキーマを利用しています。
Installing Sample Schemas

まず、ハッシュ結合とBloom Filterを利用した実行計画です。面倒な準備もなく、癖も少ないので力技でなんとかする系ではよく見かける実行計画です。
Right-Deep Join + Bloom Filter
Right-Deep Joinが可能なのはHash Joinのみです。 意図的に行う場合は、LEADING/USE_HASH/SWAP_JOIN_INPUTSを利用します。
Right-Deep Join Trees and Star Schema Queries
津島博士のパフォーマンス講座 - 第46回 パーティション・プルーニングとハッシュ結合について

スタースキーマでない結合や、NLJではLeft-Deep Joinとなるのが一般的なので見慣れない実行計画だと思う方もいると思いますが、巨大なファクト表よりサイズの小さいディメンジョン表が常にハッシュ結合のビルド表(外部表)になるように結合順序が入れ替えられています。

ハッシュ結合の実行計画としては理にかなっているのですが、超巨大なファクト表との結合がある場合、Exadataをもってしても倒すことができない敵に出会うこともありますw
弱点といえば弱点ですが、方式上難しいところでもあります。
パラレル度を増加させたとしても太刀打ちできないケースもね。。。。とほほ。

Execution Plan
----------------------------------------------------------
Plan hash value: 2503647845

------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop | TQ |IN-OUT| PQ Distrib |
------------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1546 | 137K| 3879 (1)| 00:00:01 | | | | | |
| 1 | PX COORDINATOR | | | | | | | | | | |
| 2 | PX SEND QC (ORDER) | :TQ10003 | 1546 | 137K| 3879 (1)| 00:00:01 | | | Q1,03 | P->S | QC (ORDER) |
| 3 | SORT GROUP BY | | 1546 | 137K| 3879 (1)| 00:00:01 | | | Q1,03 | PCWP | |
| 4 | PX RECEIVE | | 1546 | 137K| 3879 (1)| 00:00:01 | | | Q1,03 | PCWP | |
| 5 | PX SEND RANGE | :TQ10002 | 1546 | 137K| 3879 (1)| 00:00:01 | | | Q1,02 | P->P | RANGE |
| 6 | HASH GROUP BY | | 1546 | 137K| 3879 (1)| 00:00:01 | | | Q1,02 | PCWP | |
|* 7 | HASH JOIN | | 580K| 50M| 3875 (1)| 00:00:01 | | | Q1,02 | PCWP | |
| 8 | PX RECEIVE | | 23 | 621 | 2 (0)| 00:00:01 | | | Q1,02 | PCWP | |
| 9 | PX SEND BROADCAST | :TQ10000 | 23 | 621 | 2 (0)| 00:00:01 | | | Q1,00 | P->P | BROADCAST |
| 10 | PX BLOCK ITERATOR | | 23 | 621 | 2 (0)| 00:00:01 | | | Q1,00 | PCWC | |
| 11 | TABLE ACCESS INMEMORY FULL | COUNTRIES | 23 | 621 | 2 (0)| 00:00:01 | | | Q1,00 | PCWP | |
|* 12 | HASH JOIN | | 580K| 35M| 3872 (1)| 00:00:01 | | | Q1,02 | PCWP | |
| 13 | PX RECEIVE | | 55500 | 541K| 8 (13)| 00:00:01 | | | Q1,02 | PCWP | |
| 14 | PX SEND BROADCAST | :TQ10001 | 55500 | 541K| 8 (13)| 00:00:01 | | | Q1,01 | P->P | BROADCAST |
| 15 | PX BLOCK ITERATOR | | 55500 | 541K| 8 (13)| 00:00:01 | | | Q1,01 | PCWC | |
| 16 | TABLE ACCESS INMEMORY FULL| CUSTOMERS | 55500 | 541K| 8 (13)| 00:00:01 | | | Q1,01 | PCWP | |
|* 17 | HASH JOIN | | 580K| 29M| 3864 (1)| 00:00:01 | | | Q1,02 | PCWP | |
|* 18 | TABLE ACCESS INMEMORY FULL | CHANNELS | 2 | 42 | 2 (0)| 00:00:01 | | | Q1,02 | PCWP | |
|* 19 | HASH JOIN | | 1161K| 36M| 3862 (1)| 00:00:01 | | | Q1,02 | PCWP | |
| 20 | PART JOIN FILTER CREATE | :BF0000 | 1845 | 22140 | 2 (0)| 00:00:01 | | | Q1,02 | PCWP | |
|* 21 | TABLE ACCESS INMEMORY FULL| TIMES | 1845 | 22140 | 2 (0)| 00:00:01 | | | Q1,02 | PCWP | |
| 22 | PX BLOCK ITERATOR | | 3673K| 73M| 3857 (1)| 00:00:01 |:BF0000|:BF0000| Q1,02 | PCWC | |
| 23 | TABLE ACCESS FULL | SALES | 3673K| 73M| 3857 (1)| 00:00:01 |:BF0000|:BF0000| Q1,02 | PCWP | |
------------------------------------------------------------------------------------------------------------------------------------------------

Note
-----
- dynamic statistics used: dynamic sampling (level=2)
- Degree of Parallelism is 4 because of session
- 1 Sql Plan Directive used for this statement

Star Transformation
巨大なファクト表とディメンジョン表の結合が辛いなら、昔からあるスター変換だ! 
とも思うのですが、スター変換は頑固ものかつ、曲者なのが難点。

頑固さ
ディメンジョン表やファクト表にビットマップ索引や参照整合性制約作成等、利用できるようにするお膳立てができてないと、ピクリとも動きませんw
巨大なファクト表の外部キー列にビットマップ索引を1つ作成するのに、数時間w 複数作成して、さらに、ディメンジョン表との参照整合性制約まで必要なのでまともにやっていると、1日では終わらないことも><
(俺を信じろ、 RELYが利用できるデータの状態であれば楽ではありますが

スター変換の最大の弱点は、ビットマップ索引を利用したROWIDアクセス(以下の実行計画ではId=42の部分)による読み込み件数が多すぎるケースです。性能が伸びなかったり、または、悪化することもあります。
スター変換を利用するかどうかは、ディメンジョン表との結合でファクト表が十分に絞り込めるかにかかっています。

ディメンジョン表との結合キーがビットマップ索引中にあるため、ディメンジョン表とファクト表を結合することなく、ROWIDでファクト表をアクセスして集計することができます。
ファクト表のアクセス量が少ない場合はROWIDアクセスがメリットとなるわけですが、その逆のケースでは、ファクト表はROWIDで1行ごとにアクセスされることになるため、アクセスするファクト表の行数が多くなればなるほど不利になります。

ROWIDによるシングルブロックリードが数十億回繰り返されるとしたら、待機イベントのほとんどが、db file sequential readやcell single block physical read(Exadata)になってしまうことになります。

使いどころを見誤らないようにしたいものです。

Execution Plan
----------------------------------------------------------
Plan hash value: 2513598833

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Pstart| Pstop | TQ |IN-OUT| PQ Distrib |
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 6490 | 627K| | 5584 (1)| 00:00:01 | | | | | |
| 1 | TEMP TABLE TRANSFORMATION | | | | | | | | | | | |
| 2 | PX COORDINATOR | | | | | | | | | | | |
| 3 | PX SEND QC (RANDOM) | :TQ10000 | 1845 | 22140 | | 2 (0)| 00:00:01 | | | Q1,00 | P->S | QC (RAND) |
| 4 | LOAD AS SELECT (TEMP SEGMENT MERGE) | SYS_TEMP_0FD9D662C_336236 | | | | | | | | Q1,00 | PCWP | |
| 5 | PX BLOCK ITERATOR | | 1845 | 22140 | | 2 (0)| 00:00:01 | | | Q1,00 | PCWC | |
|* 6 | TABLE ACCESS INMEMORY FULL | TIMES | 1845 | 22140 | | 2 (0)| 00:00:01 | | | Q1,00 | PCWP | |
| 7 | PX COORDINATOR | | | | | | | | | | | |
| 8 | PX SEND QC (ORDER) | :TQ20003 | 6490 | 627K| | 5582 (1)| 00:00:01 | | | Q2,03 | P->S | QC (ORDER) |
| 9 | SORT GROUP BY | | 6490 | 627K| 60M| 5582 (1)| 00:00:01 | | | Q2,03 | PCWP | |
| 10 | PX RECEIVE | | 6490 | 627K| | 5582 (1)| 00:00:01 | | | Q2,03 | PCWP | |
| 11 | PX SEND RANGE | :TQ20002 | 6490 | 627K| | 5582 (1)| 00:00:01 | | | Q2,02 | P->P | RANGE |
| 12 | HASH GROUP BY | | 6490 | 627K| 60M| 5582 (1)| 00:00:01 | | | Q2,02 | PCWP | |
|* 13 | HASH JOIN | | 580K| 54M| | 4334 (1)| 00:00:01 | | | Q2,02 | PCWP | |
| 14 | PX RECEIVE | | 1845 | 22140 | | 2 (0)| 00:00:01 | | | Q2,02 | PCWP | |
| 15 | PX SEND BROADCAST | :TQ20000 | 1845 | 22140 | | 2 (0)| 00:00:01 | | | Q2,00 | P->P | BROADCAST |
| 16 | PX BLOCK ITERATOR | | 1845 | 22140 | | 2 (0)| 00:00:01 | | | Q2,00 | PCWC | |
| 17 | TABLE ACCESS FULL | SYS_TEMP_0FD9D662C_336236 | 1845 | 22140 | | 2 (0)| 00:00:01 | | | Q2,00 | PCWP | |
|* 18 | HASH JOIN | | 580K| 48M| | 4332 (1)| 00:00:01 | | | Q2,02 | PCWP | |
|* 19 | TABLE ACCESS INMEMORY FULL | CHANNELS | 2 | 42 | | 2 (0)| 00:00:01 | | | Q2,02 | PCWP | |
|* 20 | HASH JOIN | | 580K| 36M| | 4329 (1)| 00:00:01 | | | Q2,02 | PCWP | |
| 21 | PX RECEIVE | | 55500 | 2005K| | 10 (10)| 00:00:01 | | | Q2,02 | PCWP | |
| 22 | PX SEND BROADCAST | :TQ20001 | 55500 | 2005K| | 10 (10)| 00:00:01 | | | Q2,01 | P->P | BROADCAST |
|* 23 | HASH JOIN | | 55500 | 2005K| | 10 (10)| 00:00:01 | | | Q2,01 | PCWP | |
| 24 | TABLE ACCESS INMEMORY FULL | COUNTRIES | 23 | 621 | | 2 (0)| 00:00:01 | | | Q2,01 | PCWP | |
| 25 | PX BLOCK ITERATOR | | 55500 | 541K| | 8 (13)| 00:00:01 | | | Q2,01 | PCWC | |
| 26 | TABLE ACCESS INMEMORY FULL | CUSTOMERS | 55500 | 541K| | 8 (13)| 00:00:01 | | | Q2,01 | PCWP | |
| 27 | VIEW | VW_ST_A44449E3 | 580K| 16M| | 4319 (1)| 00:00:01 | | | Q2,02 | PCWP | |
| 28 | NESTED LOOPS | | 580K| 28M| | 4315 (1)| 00:00:01 | | | Q2,02 | PCWP | |
| 29 | PX PARTITION RANGE SUBQUERY | | 580K| 12M| | 15 (14)| 00:00:01 |KEY(SQ)|KEY(SQ)| Q2,02 | PCWC | |
| 30 | BITMAP CONVERSION TO ROWIDS | | 580K| 12M| | 15 (14)| 00:00:01 | | | Q2,02 | PCWP | |
| 31 | BITMAP AND | | | | | | | | | Q2,02 | PCWP | |
| 32 | BITMAP MERGE | | | | | | | | | Q2,02 | PCWP | |
| 33 | BITMAP KEY ITERATION | | | | | | | | | Q2,02 | PCWP | |
| 34 | BUFFER SORT | | | | | | | | | Q2,02 | PCWP | |
|* 35 | TABLE ACCESS INMEMORY FULL| CHANNELS | 2 | 26 | | 2 (0)| 00:00:01 | | | Q2,02 | PCWP | |
|* 36 | BITMAP INDEX RANGE SCAN | SALES_CHANNEL_BIX | | | | | |KEY(SQ)|KEY(SQ)| Q2,02 | PCWP | |
| 37 | BITMAP MERGE | | | | | | | | | Q2,02 | PCWP | |
| 38 | BITMAP KEY ITERATION | | | | | | | | | Q2,02 | PCWP | |
| 39 | BUFFER SORT | | | | | | | | | Q2,02 | PCWP | |
| 40 | TABLE ACCESS FULL | SYS_TEMP_0FD9D662C_336236 | 1845 | 14760 | | 2 (0)| 00:00:01 | | | Q2,02 | PCWP | |
|* 41 | BITMAP INDEX RANGE SCAN | SALES_TIME_BIX | | | | | |KEY(SQ)|KEY(SQ)| Q2,02 | PCWP | |
| 42 | TABLE ACCESS BY USER ROWID | SALES | 1 | 29 | | 4304 (1)| 00:00:01 | ROWID | ROWID | Q2,02 | PCWP | |
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Note
-----
- dynamic statistics used: dynamic sampling (level=2)
- Degree of Parallelism is 4 because of session
- star transformation used for this statement
- 1 Sql Plan Directive used for this statement

長い前置きでしたが、やっと真打の登場です!
in-memory aggregationとして解説されていることが多いのですが、実行計画を眺めている人間からすると機能名よりvector transformationの方がイメージしやすいので、以下、Vector Tranformation/ベクター変換とします。

Vector Transformation
スター変換同様に、巨大なファクト表とディメンジョン表を直接結合しない点や、パラレル実行時も他の実行計画ではみられない(誤解をおそれずにいうと、全力投球に近いかもw)特徴があります。
索引や参照整合性制約などの作成は不要。
in-memory database関連の機能ではあるのですが、全ての表がinmemory化されていなくても発動させることができます。(inmemory_sizeパラメータの設定は必要となる模様。後述)
最強の力を発揮するのは、全てがinmemoryで動作した場合であることは間違いないわけですが、巨大過ぎるファクト表がinmemory化できるほどメモリが潤沢にあるかというと、そうじゃなかっりしますし。


ベクター変換の動きを簡単に説明すると以下のような感じです。
ディメンジョン表を元に集計結果相当の構造体(in-memory accumulatorと呼ばれる多次元構造体)をメモリ上に構築後、ファクト表を読みながらin-memory accumulator上で集計します!!!(画期的!)
ハッシュ結合が全くなくなるわけではないですが、集計終了後に読み替え目的で少量(この部分が少量じゃないと辛くなるはずなのでよーく確認しておくことをおすすめします)はのハッシュ結合が行われるだけなので、冒頭で紹介した巨大なファクト表とディメンジョン表の結合によるCPUネック部分を華麗に回避していることがわかります。
Right-Deep Join+Bloom Filterで苦しい状況になったら、in-memory aggregationのことを思い出してあげてください。

助けてくれるかもしれません。



Oracle Database In-Memory: In-Memory Aggregation - Oracle White Paper JANUARY 2015


Execution Plan
----------------------------------------------------------
Plan hash value: 3211261687

------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Pstart| Pstop | TQ |IN-OUT| PQ Distrib |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 483 | 60858 | | 3840 (1)| 00:00:01 | | | | | |
| 1 | TEMP TABLE TRANSFORMATION | | | | | | | | | | | |
| 2 | LOAD AS SELECT | SYS_TEMP_0FD9D6630_336236 | | | | | | | | | | |
| 3 | PX COORDINATOR | | | | | | | | | | | |
| 4 | PX SEND QC (RANDOM) | :TQ10001 | 21 | 336 | | 3 (34)| 00:00:01 | | | Q1,01 | P->S | QC (RAND) |
| 5 | BUFFER SORT | | 21 | 336 | | 3 (34)| 00:00:01 | | | Q1,01 | PCWP | |
| 6 | VECTOR GROUP BY | | 21 | 336 | | 3 (34)| 00:00:01 | | | Q1,01 | PCWP | |
| 7 | KEY VECTOR CREATE BUFFERED | :KV0000 | 1845 | 29520 | | 3 (34)| 00:00:01 | | | Q1,01 | PCWP | |
| 8 | PX RECEIVE | | 1845 | 22140 | | 2 (0)| 00:00:01 | | | Q1,01 | PCWP | |
| 9 | PX SEND HASH | :TQ10000 | 1845 | 22140 | | 2 (0)| 00:00:01 | | | Q1,00 | P->P | HASH |
| 10 | PX BLOCK ITERATOR | | 1845 | 22140 | | 2 (0)| 00:00:01 | | | Q1,00 | PCWC | |
|* 11 | TABLE ACCESS INMEMORY FULL | TIMES | 1845 | 22140 | | 2 (0)| 00:00:01 | | | Q1,00 | PCWP | |
| 12 | LOAD AS SELECT | SYS_TEMP_0FD9D6631_336236 | | | | | | | | | | |
| 13 | PX COORDINATOR | | | | | | | | | | | |
| 14 | PX SEND QC (RANDOM) | :TQ20001 | 23 | 851 | | 12 (25)| 00:00:01 | | | Q2,01 | P->S | QC (RAND) |
| 15 | HASH GROUP BY | | 23 | 851 | 2624K| 12 (25)| 00:00:01 | | | Q2,01 | PCWP | |
| 16 | KEY VECTOR CREATE BUFFERED | :KV0001 | | | | | | | | Q2,01 | PCWP | |
| 17 | PX RECEIVE | | 55500 | 2005K| | 10 (10)| 00:00:01 | | | Q2,01 | PCWP | |
| 18 | PX SEND HASH | :TQ20000 | 55500 | 2005K| | 10 (10)| 00:00:01 | | | Q2,00 | P->P | HASH |
|* 19 | HASH JOIN | | 55500 | 2005K| | 10 (10)| 00:00:01 | | | Q2,00 | PCWP | |
| 20 | TABLE ACCESS INMEMORY FULL | COUNTRIES | 23 | 621 | | 2 (0)| 00:00:01 | | | Q2,00 | PCWP | |
| 21 | PX BLOCK ITERATOR | | 55500 | 541K| | 8 (13)| 00:00:01 | | | Q2,00 | PCWC | |
| 22 | TABLE ACCESS INMEMORY FULL | CUSTOMERS | 55500 | 541K| | 8 (13)| 00:00:01 | | | Q2,00 | PCWP | |
| 23 | LOAD AS SELECT | SYS_TEMP_0FD9D6632_336236 | | | | | | | | | | |
| 24 | PX COORDINATOR | | | | | | | | | | | |
| 25 | PX SEND QC (RANDOM) | :TQ30001 | 2 | 50 | | 3 (34)| 00:00:01 | | | Q3,01 | P->S | QC (RAND) |
| 26 | BUFFER SORT | | 2 | 50 | | 3 (34)| 00:00:01 | | | Q3,01 | PCWP | |
| 27 | VECTOR GROUP BY | | 2 | 50 | | 3 (34)| 00:00:01 | | | Q3,01 | PCWP | |
| 28 | KEY VECTOR CREATE BUFFERED | :KV0002 | 2 | 50 | | 3 (34)| 00:00:01 | | | Q3,01 | PCWP | |
| 29 | PX RECEIVE | | 2 | 42 | | 2 (0)| 00:00:01 | | | Q3,01 | PCWP | |
| 30 | PX SEND HASH | :TQ30000 | 2 | 42 | | 2 (0)| 00:00:01 | | | Q3,00 | P->P | HASH |
| 31 | PX BLOCK ITERATOR | | 2 | 42 | | 2 (0)| 00:00:01 | | | Q3,00 | PCWC | |
|* 32 | TABLE ACCESS INMEMORY FULL | CHANNELS | 2 | 42 | | 2 (0)| 00:00:01 | | | Q3,00 | PCWP | |
| 33 | PX COORDINATOR | | | | | | | | | | | |
| 34 | PX SEND QC (ORDER) | :TQ40003 | 483 | 60858 | | 3821 (1)| 00:00:01 | | | Q4,03 | P->S | QC (ORDER) |
| 35 | SORT GROUP BY | | 483 | 60858 | | 3821 (1)| 00:00:01 | | | Q4,03 | PCWP | |
| 36 | PX RECEIVE | | 483 | 60858 | | 3821 (1)| 00:00:01 | | | Q4,03 | PCWP | |
| 37 | PX SEND RANGE | :TQ40002 | 483 | 60858 | | 3821 (1)| 00:00:01 | | | Q4,02 | P->P | RANGE |
| 38 | HASH GROUP BY | | 483 | 60858 | | 3821 (1)| 00:00:01 | | | Q4,02 | PCWP | |
|* 39 | HASH JOIN | | 483 | 60858 | | 3820 (1)| 00:00:01 | | | Q4,02 | PCWP | |
| 40 | PX RECEIVE | | 23 | 851 | | 2 (0)| 00:00:01 | | | Q4,02 | PCWP | |
| 41 | PX SEND BROADCAST | :TQ40000 | 23 | 851 | | 2 (0)| 00:00:01 | | | Q4,00 | P->P | BROADCAST |
| 42 | PX BLOCK ITERATOR | | 23 | 851 | | 2 (0)| 00:00:01 | | | Q4,00 | PCWC | |
| 43 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6631_336236 | 23 | 851 | | 2 (0)| 00:00:01 | | | Q4,00 | PCWP | |
|* 44 | HASH JOIN | | 483 | 42987 | | 3818 (1)| 00:00:01 | | | Q4,02 | PCWP | |
| 45 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6630_336236 | 21 | 252 | | 2 (0)| 00:00:01 | | | Q4,02 | PCWP | |
|* 46 | HASH JOIN | | 483 | 37191 | | 3816 (1)| 00:00:01 | | | Q4,02 | PCWP | |
| 47 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6632_336236 | 2 | 42 | | 2 (0)| 00:00:01 | | | Q4,02 | PCWP | |
| 48 | VIEW | VW_VT_AF0F4755 | 483 | 27048 | | 3814 (1)| 00:00:01 | | | Q4,02 | PCWP | |
| 49 | HASH GROUP BY | | 483 | 15939 | | 3814 (1)| 00:00:01 | | | Q4,02 | PCWP | |
| 50 | PX RECEIVE | | 483 | 15939 | | 3814 (1)| 00:00:01 | | | Q4,02 | PCWP | |
| 51 | PX SEND HASH | :TQ40001 | 483 | 15939 | | 3814 (1)| 00:00:01 | | | Q4,01 | P->P | HASH |
| 52 | VECTOR GROUP BY | | 483 | 15939 | | 3814 (1)| 00:00:01 | | | Q4,01 | PCWP | |
| 53 | HASH GROUP BY | | 483 | 15939 | | 3814 (1)| 00:00:01 | | | Q4,01 | PCWP | |
| 54 | KEY VECTOR USE | :KV0001 | 580K| 18M| | 3811 (1)| 00:00:01 | | | Q4,01 | PCWC | |
| 55 | KEY VECTOR USE | :KV0002 | 580K| 16M| | 3811 (1)| 00:00:01 | | | Q4,01 | PCWC | |
| 56 | KEY VECTOR USE | :KV0000 | 1161K| 27M| | 3811 (1)| 00:00:01 | | | Q4,01 | PCWC | |
| 57 | PX BLOCK ITERATOR | | 3673K| 73M| | 3811 (1)| 00:00:01 |KEY(SQ)|KEY(SQ)| Q4,01 | PCWC | |
|* 58 | TABLE ACCESS FULL| SALES | 3673K| 73M| | 3811 (1)| 00:00:01 |KEY(SQ)|KEY(SQ)| Q4,01 | PCWP | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Note
-----
- dynamic statistics used: dynamic sampling (level=2)
- Degree of Parallelism is 4 because of session
- 2 Sql Plan Directives used for this statement
- vector transformation used for this statement

超絶必殺技にも思えるベクター変換ですが、癖がないわけではありません。(大技につきものの反動というか、なんというかw)

癖 その1)
パラレルクエリー時に割り当てられるサーバープロセスが多く、割り当てられるサーバー数は、KEY VECTORの作成数により大きく変化します。
以下、SQL MONITORのParallel Execution Detailsセクション(抜粋)の比較

並列度は同じでも割り当てるサーバー数はこんなに違う!
VECTOR TRANSFORMATION
KEY VECTORが3つ作成されるベクター変換の場合
(SQL MONITORのParallel Execution Detailsセクションより抜粋)
Parallel Execution Details (DOP=4 , Servers Allocated=32)

KEY VECTORが2つ作成されるベクター変換の場合
Parallel Execution Details (DOP=4 , Servers Allocated=24)

STAR TRANSFORMATION
Parallel Execution Details (DOP=4 , Servers Allocated=12)

Hash Joinのみ
Parallel Execution Details (DOP=4 , Servers Allocated=8)


癖 その2)
スター変換を発動させるための索引作成や、制約作成の煩雑さは無く、全ての表がinmemoryになっていなくても発動させることはできるのですが、発動させるためは、最低限設定しなればならない(ほんと? 不具合?)パラメータが存在します。(

inmemory化する表は無くとも、inmemory_size=100m(設定可能な最小サイズ)に設定しないとVECTOR_TRANSFORMヒントが無視されるという点です。
この制限?を記載しているマニュアルなどは探し出せていないのですが、どこかに記載されているのでしょうか?(いまのところ見つけることができず。。。教えていただけるとうれしいです)


では、最後に、inmemory_size初期化パラメータの設定有無による変化をみてみましょう。

inmemory_size初期化パラメータが設定されている場合にはVECTOR_TRANSFORMヒントでベクター変換を強制できています。

10:36:23 orcl12c@SYSTEM> show parameter inmemory

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
inmemory_clause_default string
inmemory_force string DEFAULT
inmemory_max_populate_servers integer 3
inmemory_query string ENABLE
inmemory_size big integer 512M
inmemory_trickle_repopulate_servers_ integer 1
percent
optimizer_inmemory_aware boolean TRUE


10:37:04 ORCL@SH> set autot trace exp stat
10:56:18 ORCL@SH> @sample3_2

135 rows selected.

Execution Plan
----------------------------------------------------------
Plan hash value: 3211261687

------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Pstart| Pstop | TQ |IN-OUT| PQ Distrib |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 483 | 60858 | | 3967 (1)| 00:00:01 | | | | | |
| 1 | TEMP TABLE TRANSFORMATION | | | | | | | | | | | |
| 2 | LOAD AS SELECT | SYS_TEMP_0FD9D6609_33B383 | | | | | | | | | | |
| 3 | PX COORDINATOR | | | | | | | | | | | |
| 4 | PX SEND QC (RANDOM) | :TQ10001 | 21 | 336 | | 20 (5)| 00:00:01 | | | Q1,01 | P->S | QC (RAND) |
| 5 | BUFFER SORT | | 21 | 336 | | 20 (5)| 00:00:01 | | | Q1,01 | PCWP | |
| 6 | VECTOR GROUP BY | | 21 | 336 | | 20 (5)| 00:00:01 | | | Q1,01 | PCWP | |
| 7 | KEY VECTOR CREATE BUFFERED | :KV0000 | 1845 | 29520 | | 20 (5)| 00:00:01 | | | Q1,01 | PCWP | |
| 8 | PX RECEIVE | | 1845 | 22140 | | 19 (0)| 00:00:01 | | | Q1,01 | PCWP | |
| 9 | PX SEND HASH | :TQ10000 | 1845 | 22140 | | 19 (0)| 00:00:01 | | | Q1,00 | P->P | HASH |
| 10 | PX BLOCK ITERATOR | | 1845 | 22140 | | 19 (0)| 00:00:01 | | | Q1,00 | PCWC | |
|* 11 | TABLE ACCESS FULL | TIMES | 1845 | 22140 | | 19 (0)| 00:00:01 | | | Q1,00 | PCWP | |
| 12 | LOAD AS SELECT | SYS_TEMP_0FD9D660A_33B383 | | | | | | | | | | |
| 13 | PX COORDINATOR | | | | | | | | | | | |
| 14 | PX SEND QC (RANDOM) | :TQ20001 | 23 | 851 | | 122 (3)| 00:00:01 | | | Q2,01 | P->S | QC (RAND) |
| 15 | HASH GROUP BY | | 23 | 851 | 2624K| 122 (3)| 00:00:01 | | | Q2,01 | PCWP | |
| 16 | KEY VECTOR CREATE BUFFERED | :KV0001 | | | | | | | | Q2,01 | PCWP | |
| 17 | PX RECEIVE | | 55500 | 2005K| | 119 (0)| 00:00:01 | | | Q2,01 | PCWP | |
| 18 | PX SEND HASH | :TQ20000 | 55500 | 2005K| | 119 (0)| 00:00:01 | | | Q2,00 | P->P | HASH |
|* 19 | HASH JOIN | | 55500 | 2005K| | 119 (0)| 00:00:01 | | | Q2,00 | PCWP | |
| 20 | TABLE ACCESS FULL | COUNTRIES | 23 | 621 | | 2 (0)| 00:00:01 | | | Q2,00 | PCWP | |
| 21 | PX BLOCK ITERATOR | | 55500 | 541K| | 117 (0)| 00:00:01 | | | Q2,00 | PCWC | |
| 22 | TABLE ACCESS FULL | CUSTOMERS | 55500 | 541K| | 117 (0)| 00:00:01 | | | Q2,00 | PCWP | |
| 23 | LOAD AS SELECT | SYS_TEMP_0FD9D660B_33B383 | | | | | | | | | | |
| 24 | PX COORDINATOR | | | | | | | | | | | |
| 25 | PX SEND QC (RANDOM) | :TQ30001 | 2 | 50 | | 3 (34)| 00:00:01 | | | Q3,01 | P->S | QC (RAND) |
| 26 | BUFFER SORT | | 2 | 50 | | 3 (34)| 00:00:01 | | | Q3,01 | PCWP | |
| 27 | VECTOR GROUP BY | | 2 | 50 | | 3 (34)| 00:00:01 | | | Q3,01 | PCWP | |
| 28 | KEY VECTOR CREATE BUFFERED | :KV0002 | 2 | 50 | | 3 (34)| 00:00:01 | | | Q3,01 | PCWP | |
| 29 | PX RECEIVE | | 2 | 42 | | 2 (0)| 00:00:01 | | | Q3,01 | PCWP | |
| 30 | PX SEND HASH | :TQ30000 | 2 | 42 | | 2 (0)| 00:00:01 | | | Q3,00 | P->P | HASH |
| 31 | PX BLOCK ITERATOR | | 2 | 42 | | 2 (0)| 00:00:01 | | | Q3,00 | PCWC | |
|* 32 | TABLE ACCESS FULL | CHANNELS | 2 | 42 | | 2 (0)| 00:00:01 | | | Q3,00 | PCWP | |
| 33 | PX COORDINATOR | | | | | | | | | | | |
| 34 | PX SEND QC (ORDER) | :TQ40003 | 483 | 60858 | | 3821 (1)| 00:00:01 | | | Q4,03 | P->S | QC (ORDER) |
| 35 | SORT GROUP BY | | 483 | 60858 | | 3821 (1)| 00:00:01 | | | Q4,03 | PCWP | |
| 36 | PX RECEIVE | | 483 | 60858 | | 3821 (1)| 00:00:01 | | | Q4,03 | PCWP | |
| 37 | PX SEND RANGE | :TQ40002 | 483 | 60858 | | 3821 (1)| 00:00:01 | | | Q4,02 | P->P | RANGE |
| 38 | HASH GROUP BY | | 483 | 60858 | | 3821 (1)| 00:00:01 | | | Q4,02 | PCWP | |
|* 39 | HASH JOIN | | 483 | 60858 | | 3820 (1)| 00:00:01 | | | Q4,02 | PCWP | |
| 40 | PX RECEIVE | | 23 | 851 | | 2 (0)| 00:00:01 | | | Q4,02 | PCWP | |
| 41 | PX SEND BROADCAST | :TQ40000 | 23 | 851 | | 2 (0)| 00:00:01 | | | Q4,00 | P->P | BROADCAST |
| 42 | PX BLOCK ITERATOR | | 23 | 851 | | 2 (0)| 00:00:01 | | | Q4,00 | PCWC | |
| 43 | TABLE ACCESS FULL | SYS_TEMP_0FD9D660A_33B383 | 23 | 851 | | 2 (0)| 00:00:01 | | | Q4,00 | PCWP | |
|* 44 | HASH JOIN | | 483 | 42987 | | 3818 (1)| 00:00:01 | | | Q4,02 | PCWP | |
| 45 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6609_33B383 | 21 | 252 | | 2 (0)| 00:00:01 | | | Q4,02 | PCWP | |
|* 46 | HASH JOIN | | 483 | 37191 | | 3816 (1)| 00:00:01 | | | Q4,02 | PCWP | |
| 47 | TABLE ACCESS FULL | SYS_TEMP_0FD9D660B_33B383 | 2 | 42 | | 2 (0)| 00:00:01 | | | Q4,02 | PCWP | |
| 48 | VIEW | VW_VT_AF0F4755 | 483 | 27048 | | 3814 (1)| 00:00:01 | | | Q4,02 | PCWP | |
| 49 | HASH GROUP BY | | 483 | 15939 | | 3814 (1)| 00:00:01 | | | Q4,02 | PCWP | |
| 50 | PX RECEIVE | | 483 | 15939 | | 3814 (1)| 00:00:01 | | | Q4,02 | PCWP | |
| 51 | PX SEND HASH | :TQ40001 | 483 | 15939 | | 3814 (1)| 00:00:01 | | | Q4,01 | P->P | HASH |
| 52 | VECTOR GROUP BY | | 483 | 15939 | | 3814 (1)| 00:00:01 | | | Q4,01 | PCWP | |
| 53 | HASH GROUP BY | | 483 | 15939 | | 3814 (1)| 00:00:01 | | | Q4,01 | PCWP | |
| 54 | KEY VECTOR USE | :KV0001 | 580K| 18M| | 3811 (1)| 00:00:01 | | | Q4,01 | PCWC | |
| 55 | KEY VECTOR USE | :KV0002 | 580K| 16M| | 3811 (1)| 00:00:01 | | | Q4,01 | PCWC | |
| 56 | KEY VECTOR USE | :KV0000 | 1161K| 27M| | 3811 (1)| 00:00:01 | | | Q4,01 | PCWC | |
| 57 | PX BLOCK ITERATOR | | 3673K| 73M| | 3811 (1)| 00:00:01 |KEY(SQ)|KEY(SQ)| Q4,01 | PCWC | |
|* 58 | TABLE ACCESS FULL| SALES | 3673K| 73M| | 3811 (1)| 00:00:01 |KEY(SQ)|KEY(SQ)| Q4,01 | PCWP | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

11 - filter("T"."FISCAL_YEAR"=2000 OR "T"."FISCAL_YEAR"=2005 OR "T"."FISCAL_YEAR"=2010 OR "T"."FISCAL_YEAR"=2015 OR "T"."FISCAL_YEAR"=2016)
19 - access("C"."COUNTRY_ID"="R"."COUNTRY_ID")
32 - filter("CH"."CHANNEL_DESC"='Internet' OR "CH"."CHANNEL_DESC"='Partners')
39 - access("ITEM_13"=INTERNAL_FUNCTION("C0") AND "ITEM_14"="C4")
44 - access("ITEM_17"=INTERNAL_FUNCTION("C0") AND "ITEM_18"="C2")
46 - access("ITEM_15"=INTERNAL_FUNCTION("C0") AND "ITEM_16"="C2")
58 - filter(SYS_OP_KEY_VECTOR_FILTER("S"."TIME_ID",:KV0000) AND SYS_OP_KEY_VECTOR_FILTER("S"."CHANNEL_ID",:KV0002))

Note
-----
- dynamic statistics used: dynamic sampling (level=2)
- Degree of Parallelism is 4 because of session
- 2 Sql Plan Directives used for this statement
- vector transformation used for this statement

inmemory_size初期化パラメータを0にし、同一SQL文を実行すると。。。。なんということでしょう。ベクター変換は発動しません!

10:46:22 orcl12c@SYSTEM> show parameter inmemory

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
inmemory_clause_default string
inmemory_force string DEFAULT
inmemory_max_populate_servers integer 0
inmemory_query string ENABLE
inmemory_size big integer 0
inmemory_trickle_repopulate_servers_ integer 1
percent
optimizer_inmemory_aware boolean TRUE


10:47:56 ORCL@SH> @sample3_2

135 rows selected.

Execution Plan
----------------------------------------------------------
Plan hash value: 2503647845

--------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop | TQ |IN-OUT| PQ Distrib |
--------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1546 | 137K| 4006 (1)| 00:00:01 | | | | | |
| 1 | PX COORDINATOR | | | | | | | | | | |
| 2 | PX SEND QC (ORDER) | :TQ10003 | 1546 | 137K| 4006 (1)| 00:00:01 | | | Q1,03 | P->S | QC (ORDER) |
| 3 | SORT GROUP BY | | 1546 | 137K| 4006 (1)| 00:00:01 | | | Q1,03 | PCWP | |
| 4 | PX RECEIVE | | 1546 | 137K| 4006 (1)| 00:00:01 | | | Q1,03 | PCWP | |
| 5 | PX SEND RANGE | :TQ10002 | 1546 | 137K| 4006 (1)| 00:00:01 | | | Q1,02 | P->P | RANGE |
| 6 | HASH GROUP BY | | 1546 | 137K| 4006 (1)| 00:00:01 | | | Q1,02 | PCWP | |
|* 7 | HASH JOIN | | 580K| 50M| 4002 (1)| 00:00:01 | | | Q1,02 | PCWP | |
| 8 | PX RECEIVE | | 23 | 621 | 2 (0)| 00:00:01 | | | Q1,02 | PCWP | |
| 9 | PX SEND BROADCAST | :TQ10000 | 23 | 621 | 2 (0)| 00:00:01 | | | Q1,00 | P->P | BROADCAST |
| 10 | PX BLOCK ITERATOR | | 23 | 621 | 2 (0)| 00:00:01 | | | Q1,00 | PCWC | |
| 11 | TABLE ACCESS FULL | COUNTRIES | 23 | 621 | 2 (0)| 00:00:01 | | | Q1,00 | PCWP | |
|* 12 | HASH JOIN | | 580K| 35M| 3999 (1)| 00:00:01 | | | Q1,02 | PCWP | |
| 13 | PX RECEIVE | | 55500 | 541K| 117 (0)| 00:00:01 | | | Q1,02 | PCWP | |
| 14 | PX SEND BROADCAST | :TQ10001 | 55500 | 541K| 117 (0)| 00:00:01 | | | Q1,01 | P->P | BROADCAST |
| 15 | PX BLOCK ITERATOR | | 55500 | 541K| 117 (0)| 00:00:01 | | | Q1,01 | PCWC | |
| 16 | TABLE ACCESS FULL | CUSTOMERS | 55500 | 541K| 117 (0)| 00:00:01 | | | Q1,01 | PCWP | |
|* 17 | HASH JOIN | | 580K| 29M| 3882 (1)| 00:00:01 | | | Q1,02 | PCWP | |
|* 18 | TABLE ACCESS FULL | CHANNELS | 2 | 42 | 2 (0)| 00:00:01 | | | Q1,02 | PCWP | |
|* 19 | HASH JOIN | | 1161K| 36M| 3879 (1)| 00:00:01 | | | Q1,02 | PCWP | |
| 20 | PART JOIN FILTER CREATE| :BF0000 | 1845 | 22140 | 19 (0)| 00:00:01 | | | Q1,02 | PCWP | |
|* 21 | TABLE ACCESS FULL | TIMES | 1845 | 22140 | 19 (0)| 00:00:01 | | | Q1,02 | PCWP | |
| 22 | PX BLOCK ITERATOR | | 3673K| 73M| 3857 (1)| 00:00:01 |:BF0000|:BF0000| Q1,02 | PCWC | |
| 23 | TABLE ACCESS FULL | SALES | 3673K| 73M| 3857 (1)| 00:00:01 |:BF0000|:BF0000| Q1,02 | PCWP | |
--------------------------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

7 - access("C"."COUNTRY_ID"="R"."COUNTRY_ID")
12 - access("S"."CUST_ID"="C"."CUST_ID")
17 - access("S"."CHANNEL_ID"="CH"."CHANNEL_ID")
18 - filter("CH"."CHANNEL_DESC"='Internet' OR "CH"."CHANNEL_DESC"='Partners')
19 - access("S"."TIME_ID"="T"."TIME_ID")
21 - filter("T"."FISCAL_YEAR"=2000 OR "T"."FISCAL_YEAR"=2005 OR "T"."FISCAL_YEAR"=2010 OR "T"."FISCAL_YEAR"=2015 OR
"T"."FISCAL_YEAR"=2016)

Note
-----
- dynamic statistics used: dynamic sampling (level=2)
- Degree of Parallelism is 4 because of session
- 1 Sql Plan Directive used for this statement

参考
Database Virtual Box Appliance / Virtual Machine
Installing Sample Schemas
Getting started with Oracle Database In-Memory Part V - Aggregation
津島博士のパフォーマンス講座 - 第54回 Oracle Database In-Memoryについて(2)

利用したSQL
sample1_2.sql
Right-Deep Join (Right-Deep Treeにならない場合には SWAP_JOIN_INPUTSで制御する必要がありますが、SHスキーマでオプティマイザ統計が取得されているのであれば不要.今回の例ではSWAP_JOIN_INPUTSは利用していませんが、オプティマイザは判断を誤るようであれば利用したほうがよいと思います。)

SELECT	
/*+
MONITOR
LEADING(r sum)
USE_HASH(r sum)
*/
sum.fiscal_year
, r.country_name
, sum.channel_class
, sum.sales_amount
FROM
(
SELECT
t.fiscal_year
, c.country_id
, ch.channel_class
, SUM(s.amount_sold) sales_amount
FROM
sales s
, times t
, customers c
, channels ch
WHERE
s.time_id = t.time_id
AND s.cust_id = c.cust_id
AND s.channel_id = ch.channel_id
AND ch.channel_desc in ('Internet','Partners')
AND t.fiscal_year IN (
2000, 2005, 2010, 2015, 2016
)
GROUP BY
ch.channel_class
, c.country_id
, t.fiscal_year
) sum
, countries r
WHERE
sum.country_id = r.country_id
ORDER BY
sum.fiscal_year
, r.country_name
, sum.channel_class
/

sample2_2.sql
スター変換ヒントが必要です。スター変換はデフォルトでOFFに設定されています。
SHスキーマはスタースキーマかつ、スター変換をすぐに試せる環境(ファクト表の外部キーのビットマップ索引やデョメンジョン表への参照整合性制約等)になっています。

SELECT	
/*+
MONITOR
LEADING(r sum)
USE_HASH(r sum)
*/
sum.fiscal_year
, r.country_name
, sum.channel_class
, sum.sales_amount
FROM
(
SELECT
/*+
STAR_TRANSFORMATION
*/
t.fiscal_year
, c.country_id
, ch.channel_class
, SUM(s.amount_sold) sales_amount
FROM
sales s
, times t
, customers c
, channels ch
WHERE
s.time_id = t.time_id
AND s.cust_id = c.cust_id
AND s.channel_id = ch.channel_id
AND ch.channel_desc in ('Internet','Partners')
AND t.fiscal_year IN (
2000, 2005, 2010, 2015, 2016
)
GROUP BY
ch.channel_class
, c.country_id
, t.fiscal_year
) sum
, countries r
WHERE
sum.country_id = r.country_id
ORDER BY
sum.fiscal_year
, r.country_name
, sum.channel_class
/

sample3_2.sql
ベクター変換の例です。ベクター変換のヒントは数種類(ファクト表を記述場合、ディメンジョン表を記述場合)あります。(詳細はv$sql_hintを参照のこと)

SELECT	
/*+
MONITOR
LEADING(r sum)
USE_HASH(r sum)
*/
sum.fiscal_year
, r.country_name
, sum.channel_class
, sum.sales_amount
FROM
(
SELECT
/*+
VECTOR_TRANSFORM
*/
t.fiscal_year
, c.country_id
, ch.channel_class
, SUM(s.amount_sold) sales_amount
FROM
sales s
, times t
, customers c
, channels ch
WHERE
s.time_id = t.time_id
AND s.cust_id = c.cust_id
AND s.channel_id = ch.channel_id
AND ch.channel_desc in ('Internet','Partners')
AND t.fiscal_year IN (
2000, 2005, 2010, 2015, 2016
)
GROUP BY
ch.channel_class
, c.country_id
, t.fiscal_year
) sum
, countries r
WHERE
sum.country_id = r.country_id
ORDER BY
sum.fiscal_year
, r.country_name
, sum.channel_class
/


明日は、@yoshikawさんです! お楽しみに!


俺のターンおわたー!:)

| | コメント (0) | トラックバック (0)

2016年9月22日 (木)

メモ:SIMDあれこれ

SIMDあれこれ自分メモ

primitive: blog / introdunction to SIMD programming
http://i-saint.hatenablog.com/entry/2015/05/26/212441

wikipedia / Streaming SIMD Extensions
https://ja.wikipedia.org/wiki/Streaming_SIMD_Extensions

IA Software User Society / コンパイラー最適化入門: 第1回 SIMD 命令とプロセッサーの関係
http://www.isus.jp/products/c-compilers/compiler_part1/

IA Software User Society / コンパイラー最適化入門: 第2回 SIMD 命令と伝統的な IA 命令
http://www.isus.jp/products/c-compilers/compiler_part2/

IA Software User Society / コンパイラー最適化入門: 第3回 インテル® コンパイラーのベクトル化レポートを活用する
http://www.isus.jp/products/c-compilers/compiler_part3/

IA Software User Society / コンパイラー最適化入門: 第4回 自動ベクトル化はどんな時に行われるか
http://www.isus.jp/products/c-compilers/compiler_part4/

IA Software User Society / コンパイラー最適化入門: 第5回 明示的にベクトル化されたコードを記述する
http://www.isus.jp/products/c-compilers/compiler_part5/

IA Software User Society / コンパイラー最適化入門: 第6回 ベクトル化の裏技集
http://www.isus.jp/products/c-compilers/compiler_part6/

Oracle In-Memory Column Store Internals – Part 1 – Which SIMD extensions are getting used?
http://blog.tanelpoder.com/2014/10/05/oracle-in-memory-column-store-internals-part-1-which-simd-extensions-are-getting-used/

SIMD-Scan: Ultra Fast in-Memory Table Scan using on- Chip Vector Processing Units
http://www.vldb.org/pvldb/2/vldb09-327.pdf

Rethinking SIMD Vectorization for In-Memory Databases
http://www.cs.columbia.edu/~orestis/sigmod15.pdf

Oracle … as usual / SIMD Extensions in and out Oracle 12.1.0.2
https://laurent-leturgez.com/2015/04/22/simd-extensions-in-and-out-oracle-12-1-0-2/

Laurent Leturgez, / Ukoug15 SIMD outside and inside Oracle 12c (12.1.0.2)
http://www.slideshare.net/lolo115/ukoug15-simd-outside-and-inside-oracle-12c-12102

Cellプログラミングチュートリアル Version 0.2c
http://cell.fixstars.com/ps3linux/tutorial/index.html

| | コメント (0) | トラックバック (0)

2016年7月19日 (火)

DB Tech Showcase 2016 Tokyo - E35 - SQLチューニング総合診療所的予防医学のセッション資料

DB Tech Showcase 2016 Tokyo - E35 - SQLチューニング総合診療所的予防医学のセッション資料を公開しました。

ぼくとつと、性能試験データの質などのことを言い続ける感じになっていたでしょうか?w

ところで、次回のJPOUG主催のイベントの開催が決まりました。セッション内容は現在準備中ですが、確定次第随時更新します。
お申し込みは以下のイベントベージよりお願いします。

JPOUG in 15 minutes #1

| | コメント (0) | トラックバック (0)

2016年3月21日 (月)

OracleのB*Tree索引にはNULLが含まれる場合があるんです! - その性質を使ってチューニングすることもあるよ:) その2

OracleのB*Tree索引にはNULLが含まれる場合があるんです! - その性質を使ってチューニングすることもあるよ:)
の続きです。

念のために、もう一つの主役NULL登場してもらいましょう。

IX1_TAB01索引はNullableなFOO列だけの索引なので、このタイプの索引ではNULLは索引に格納されることはありません。
いくらヒントで索引を指定しても索引にはNULLは格納されていないので全表走査になるはず。

注):索引が多いので意図した索引を利用するようにヒントで固定しています
例2)FOO IS NULL で検索

orcl@SCOTT> r
1 SELECT
2 /*+
3 gather_plan_statistics
4 index(tab01 ix1_tab01)
5 no_index(tab01 ix3_tab01)
6 no_index(tab01 ix4_tab01)
7 */
8 *
9 FROM
10 tab01
11 WHERE
12* foo IS NULL


FOO BAR HO ID
---------- ---------- -- ----------
[NULL] [NULL] ** 1
[NULL] 1 ** 3


ーーー中略ーーー
Plan hash value: 2044041692

-------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 2 |00:00:00.01 | 8 |
|* 1 | TABLE ACCESS FULL| TAB01 | 1 | 2 | 2 |00:00:00.01 | 8 |
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter("FOO" IS NULL)

想定通り。NULLは格納されていないので索引が利用できず全表走査になっています。
索引からROWIDを取得できないので全表走査してフィルタリングしている箇所がポイントです。

OracleのB*Tree索引にはNULLが入らない都市伝説の始まりはここだったんじゃないか? とい言っている方がいたのですが、ここだけの話が広まってしまい

OracleのB*Tree索引にはNULLは絶対格納されない。

という都市伝説になってしまったのだろうと。。確かに入ってないですからねー。 (^^;;;

では、NULLが索引に含まれる一例を見てみましょう。

例3)ID列(NOT NULL)とFOO列(Nullable)で作成した複合索引を ID=1 AND FOO IS NULLで検索
注):索引が多いので意図した索引を利用するようにヒントで固定しています

Predicate Informationセクションのaccess predicateを見るとわかると思いますが、ID=1 AND FOO IS NULLで索引をアクセスしています。
NULLが索引に含まれていない場合はこのような状況にはなりません。 
複合索引では1列でもNOT NULL制約がり他の列がNullableである場合、NULLは格納されます。

NULLは格納されることもあるんです。マニュアル上の表現は少々わかりにくいとは思いますが、このような状況を指しています。

orcl@SCOTT> r
1 SELECT
2 /*+
3 gather_plan_statistics
4 no_index(tab01 ix1_tab01)
5 index(tab01 ix3_tab01)
6 no_index(tab01 ix4_tab01)
7 */
8 *
9 FROM
10 tab01
11 WHERE
12 id = 1
13* AND foo IS NULL

FOO BAR HO ID
---------- ---------- -- ----------
[NULL] [NULL] ** 1

ーーー中略ーーー
Plan hash value: 2558346564

-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 3 |
| 1 | TABLE ACCESS BY INDEX ROWID BATCHED| TAB01 | 1 | 1 | 1 |00:00:00.01 | 3 |
|* 2 | INDEX RANGE SCAN | IX3_TAB01 | 1 | 1 | 1 |00:00:00.01 | 2 |
-----------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - access("ID"=1 AND "FOO" IS NULL)


少し寄り道。

そういえば、偶になんですが、IS NOT NULLだと索引使うことはないという都市伝説も聞いたことがあります。
それも都市伝説なんですよね。
次の例は、その都市伝説を覆す一例


例4)FOO IS NOT NULL で検索

IX1_TAB01索引は、NullableなFOO列だけの索引です。これまでの検証でこのタイプの索引にはNULLが格納されていないことは確認できると思います。
ようするに、NULLじゃないものは、まるっととズバッと索引に含まれているはずでっす!
なので、FOO IS NOT NULLという検索条件だと索引を使ってくれます。

このような性質を知っているとチューニングに役立つんですよ。
(この例のようなチューニング方法を使ったことは過去数度あるんですよ、苦肉の策でしたけどw NULLの数が非常に多い場合、この手の索引のセグメントサイズは非常に小さくなります(索引に格納されるエントリそのものがNULLを除くと非常に少ない場合)。その性質を利用したチューニング法もあるんです。)

余談NOTE:
INDEX FAST FULL SCANにならない理由:索引にNOT NULL制約の列が少なくとも1列あること、という前提条件を満たせていないからです。チューニングや設計時に必要な知識なので覚えておくと何かの時に役に立つと思います:)

注):索引が多いので意図した索引を利用するようにヒントで固定しています

orcl@SCOTT> r
1 SELECT
2 /*+
3 gather_plan_statistics
4 index(tab01 ix1_tab01)
5 no_index(tab01 ix3_tab01)
6 no_index(tab01 ix4_tab01)
7 */
8 *
9 FROM
10 tab01
11 WHERE
12* foo IS NOT NULL


FOO BAR HO ID
---------- ---------- -- ----------
1 [NULL] ** 2
1 1 ** 4

ーーー中略ーーー
Plan hash value: 2840602802

-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 2 |00:00:00.01 | 4 |
| 1 | TABLE ACCESS BY INDEX ROWID BATCHED| TAB01 | 1 | 2 | 2 |00:00:00.01 | 4 |
|* 2 | INDEX FULL SCAN | IX1_TAB01 | 1 | 2 | 2 |00:00:00.01 | 2 |
-----------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - filter("FOO" IS NOT NULL)


恐竜博とか**博ってやつは、最後のショップがメインで博のほうはおまけなんじゃないかと最近思ってるw 子供達の目が活き活きしているのはショップに入ってからだ!w

次回へつづく。

| | コメント (0) | トラックバック (0)

2016年3月20日 (日)

OracleのB*Tree索引にはNULLが含まれる場合があるんです! - その性質を使ってチューニングすることもあるよ:)

先日、OracleのB*Tree索引には絶対にNULLが含まれないって思い込んでる人が意外にいるよね〜とか。
OracleのB*Tree索引には絶対NULLが含まれないって都市伝説があるのはなんでだろう。
Oracle® Database概要12cリリース1 (12.1) - 一意索引と非一意索引

みたいなことが話題になって、
ある一人が、「だよね〜、ダンプ見れば含まれてるのわかります。」って言ってて、それ、ふつ〜の人は見ないからw

と思いつつ、私の周りには、やはり、変態が多いことに改めて気づいた次第です。:) はい。


で、
変態じゃない、ごく一般的なエンジニアの方々(ブロックダンプを華麗かつ自然にキメちゃわない方々)向けに、

OracleのB*Tree索引にNULLが含まれているか、NULLが含まれていないかの簡単な確認方法をお伝えしなければ!w

ということで、数回にわけて書いておこうかと思ってます。(予定は未定w)

(実は、このネタとほぼ同じことを1年前ぐらい前に、ローカルかつクローズドな勉強会?、でも使ってました。 最近、やってないみたいですけど)

環境は最近の定番 Oracle Database 12c R1 EE

orcl@SCOTT> select banner from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
PL/SQL Release 12.1.0.2.0 - Production
CORE 12.1.0.2.0 Production
TNS for Linux: Version 12.1.0.2.0 - Production
NLSRTL Version 12.1.0.2.0 - Production


SCOTTスキーマに以下のような表と索引を作成し少ないですが、データを登録
今回の主役は索引とNULLなのでデータ量少なくても必要なパターンが登録できていれば十分です。
(データ量を増やせばチューニングのお題の元ネタにもなると思います。)

orcl@SCOTT> create table tab01 (foo number, bar number, hoge char(2) not null,id number not null);

Table created.

orcl@SCOTT> insert into tab01 values(null,null,'**',1);

1 row created.

orcl@SCOTT> insert into tab01 values(1,null,'**',2);

1 row created.

orcl@SCOTT> insert into tab01 values(null,1,'**',3);

1 row created.

orcl@SCOTT> insert into tab01 values(1,1,'**',4);

1 row created.

orcl@SCOTT> commit;

Commit complete.

orcl@SCOTT> alter table tab01 add constraint pk_tab01 primary key(id) using index;

Table altered.

orcl@SCOTT> exec dbms_stats.gather_table_stats(ownname=>'SCOTT',tabname=>'TAB01',cascade=>true,no_invalidate=>false);

PL/SQL procedure successfully completed.

orcl@SCOTT> create index ix1_tab01 on tab01 (foo);

Index created.

orcl@SCOTT> create index ix2_tab01 on tab01 (bar,foo);

Index created.

orcl@SCOTT> create index ix3_tab01 on tab01(id,foo);

Index created.

orcl@SCOTT> create index ix4_tab01 on tab01(id,bar,foo);

Index created.


FOOとBAR列はNullableにしています

orcl@SCOTT> desc tab01
Name Null? Type
----------------------------------------- -------- ----------------------------
FOO NUMBER
BAR NUMBER
HOGE NOT NULL CHAR(2)
ID NOT NULL NUMBER

主演の索引たち

NULLが索引でどう扱われるかを確認するため、事前作成の索引をたくさん用意してしました。後から作るの面倒なのでw
これだけ類似索引も含めて多数の索引があると確認時に意図した索引が利用されない可能性も高いため、検証では内容に合わせて利用する索引をヒントで指定することにします。

ちなみに、
普通の環境でこんなに索引があったら、アンチパターン:インデックスショットガンですからね。ご注意ください:) たまにこのような環境に遭遇することはありますが。。。

IX1_TAB01はnullableなFOO列だけの単一列索引
IX2_TAB01はnullableなBAR列とFOO列からなる複合索引
IX3_TAB01とTX4_TAB01は上記の列に加え主キー列のID列を第1キーとする複合索引

としてあります。

orcl@SCOTT> break on table_name on index_name skip 1
orcl@SCOTT> select table_name,index_name,column_name from user_ind_columns where table_name='TAB01' order by table_name,index_name,column_position;

TABLE_NAME INDEX_NAME COLUMN_NAME
------------------------------ ------------------------------ ------------------------------
TAB01 IX1_TAB01 FOO

IX2_TAB01 BAR
FOO

IX3_TAB01 ID
FOO

IX4_TAB01 ID
BAR
FOO

PK_TAB01 ID

登録したデータは以下の通り
NULLは索引とともに今回の主役なので、どこがNULLになっているかメモしておいてくださいませ。

orcl@SCOTT> set null [NULL]
orcl@SCOTT> select * from tab01 order by id;

FOO BAR HO ID
---------- ---------- -- ----------
[NULL] [NULL] ** 1
1 [NULL] ** 2
[NULL] 1 ** 3
1 1 ** 4

さて、索引にNULLが含まれているか、いないか、どうやって確認すると思います? ブロックダンプを華麗にキメきめる以外の方法でw

SQLチューニングをしたことがある方なら一般的に利用してい(と思ってる)機能で簡単に確認できちゃうんですよ。これが。

SQL*Plusのautotraceや、explain plan それに、DBMS_XPLAN.DISPLAY* なファンクションでも確認できます。
SQLclはautotraceにはまだ未対応となっているようですが、それ以外の方法ならはできるようです。
(今回はSQL*Plusを使い、dbms_xplan.display_cursor(format=>'ALLSTATS LAST'))で確認します)


どうやって、どの部分で確認するのか? 
autottraceやDBMS_XPLAN.DISPLAY*などでリストされるpredicate information部分のaccess/filter predicate部分で確認できるんです!


例1)FOO = 1 で検索した例

注):索引が多いので意図した索引を利用するようにヒントで固定しています

おわかりでしょうか?
Predicate Infomation部分から、Id=2のINDEX RANGE SCANで IX1_TAB01索引を FOO=1でアクセスしていることが読み取れますよね?
つまり、1 という値が索引に含まれている(含まれる)からaccess pathとしてIX1_TAB01索引を参照しているわけです。

orcl@SCOTT> r
1 SELECT
2 /*+
3 gather_plan_statistics
4 index(tab01 ix1_tab01)
5 */
6 *
7 FROM
8 tab01
9 WHERE
10* foo = 1

FOO BAR HO ID
---------- ---------- -- ----------
1 [NULL] ** 2
1 1 ** 4

orcl@SCOTT> select * from table(dbms_xplan.display_cursor(format=>'ALLSTATS LAST'));

ーーー中略ーーー

Plan hash value: 3795960549

-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 2 |00:00:00.01 | 4 |
| 1 | TABLE ACCESS BY INDEX ROWID BATCHED| TAB01 | 1 | 2 | 2 |00:00:00.01 | 4 |
|* 2 | INDEX RANGE SCAN | IX1_TAB01 | 1 | 2 | 2 |00:00:00.01 | 2 |
-----------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - access("FOO"=1)


ということで、今日は馬事公苑の散歩は気持ちいいよ〜。

次回へつづく。

| | コメント (0) | トラックバック (0)

2016年3月19日 (土)

Relational Database Index Design and the Optimizers [Kindle版]とUSE THE INDEX, LUKE

時代は繰り返すw のか? という感じで何周目かのIndex only scanとかのネタを元にした資料とか作ることが多くて。。。

索引の設計って、基本的に、機械的だと思っているのですが、皆さんはどう思っているのか気になる今日この頃。
初心者ならともかく、そうでもない感じの方が、腕組みして、眉間にしわ寄せてるのをみるとちょっと心配になっちゃったり。


と昨日のtwで見かけたやつとかも含めて参考になりそうな書籍を載せておきますね。

最初は私のお気に入りの一冊(ハードカバー本でしたが、最近kindle版がでてました。そして私が買った時より高いw
この書籍は2011 db tech showcaseでTom Kyteさんが紹介していたもので、速攻でポチった記憶があります。
そしてその頃は、8,000円台でしたw(円高だったからねぇ)

この書籍ではCovering Index → FAT Index / Semi Covering Index → Semi FAT Indexと記載されています。

Web上でも読めるんですが、書籍化されPDF版もでてます。
SQLパフォーマンス詳解(原文タイトルSQL Performance Explained)


USE THE INDEX, LUKE - 開発者のためのSQLパフォーマンスの全て

| | コメント (0) | トラックバック (0)

2015年12月23日 (水)

Edgeも出たし、JetStreamも出たので久々にブラウザのベンチマーク

Windows10とEdgeも世に出たし、SunSpider1.0.2Octane2.0が合体した感じのJetStreamが良さげなので久々にブラウザのJavaScriptベンチマーク。
これやっておくと大体の傾向が見えるので面倒だけど記録していくのが重要かと。

遅いんだけど〜と、データベース屋さんが呼ばれ、現場で見た結果、
遅いのは、
データベースでもなく、
アプリケーションサーバーでもなく、
ブラウザ上で動く、JavaScriptなんてこともあるわけで。

IEの一人負けの時代からモダンプラウザのJavaScript速くなったから、
詰め込み過ぎて遅いとか、
ただ単に作りが悪いとか、
ということも多くなってくるんだろ〜な〜と遠くを見てる。


環境
Windows 10 Pro - Core i7-980X - 3.33Ghz
MacPro OS X 10.11.2 - Intel Xeon E5645 * 2 - 2.4Ghz

CPU比較結果から
Core i7-980X 3.33Ghz/Intel Xeon E5645 2.4Ghzを約1.34
Intel E5645 2.4Ghz/Core i7-980X 3.33Ghzを約0.75
としてあります。

http://www.cpubenchmark.net/compare.php?cmp[]=1252&cmp[]=866
Cpu比較

これからはJetStreamだけでいいと思うんですが、Octane2.0とSunSpider1.0.2の結果も載せています。
(V8とかOctane1.0の計測したのですがそれほど傾向に違いは見られないこともあり略)

各試験共通:10回実行して、Max()/Min()を除いた結果をAvgとしています。

JetStream


Windows 10
Win_jetstream
Win_jetstream_detail


OS X

Osx_jetstream
Osx_jetstream_detail


Core i7-980X 3.33Ghzの結果をIntel E5645 2.4Ghz換算(0.75倍)したグラフ
OSの違いなどもありますが、同一ブラウザはほぼ同じ傾向がありますね。
それにしてもEdge頑張ってますね〜。IEはもういいんじゃないかなw

Osx_winconvert_jetstream


Octane 2.0


Windows 10
Win_octane2
Win_octane2_detail


OS X

Osx_octane2
Osx_octane2_detail


Core i7-980X 3.33Ghzの結果をIntel E5645 2.4Ghz換算(0.75倍)したグラフ
OSの違いなどもありますが、同一ブラウザはほぼ同じ傾向なのはJetStreamの結果と同じ傾向。
単一プラットフォームのブラウザの方が有利といえば有利だとは思いますが、Chrome/Opera/FireFoxって頑張ってると思います!!

Osx_winconvert_octane2

SunSpider 1.0.2


Windows 10
Win_sunspider102


OS X


Osx_sunspider102


Core i7-980X 3.33Ghzの結果をIntel E5645 2.4Ghz換算(1.34倍)したグラフ
SunSpiderはIE11の頃から(なぜか?)ダンドツだったわけですがEdgeになってもその傾向はあるようですね。
Octane系では平均点な感じなので合わせるとJetStreamの結果近づくと思われます。

Osx_winconvert_sunspider


そして、最後は個人的な趣味w 
document.write()を10,000回したらどうなるかw シンプルな繰り返しです
Edgeはかなり早くなってきてますが、他のブラウザと比べるとやはり何か違う傾向があるんですよね:)

Windows 10
20151223_163726


OS X

20151223_163806


Core i7-980X 3.33Ghzの結果をIntel E5645 2.4Ghz換算(1.34倍)したグラフ
document.write()*10000回、Edge速くなってますが、他のブラウザには...
私の趣味なのでご勘弁を。これ比較しないと落ち着かなくてw

20151223_163911

ではでは、メリークリスマス



あれから1年.....あの恐怖が再びw
http://discus-hamburg.cocolog-nifty.com/mac_de_oracle/2014/02/w-2053.html

まだ、レガシーブラウザと戦うのですか...
http://discus-hamburg.cocolog-nifty.com/mac_de_oracle/2013/01/post-bc4e.html

ネタとして古すぎるので、思い出話ですけどw
IEって遅いんだよー。ってことをあまり気にしてない方へ。
http://discus-hamburg.cocolog-nifty.com/mac_de_oracle/2010/05/ie-a910.html


| | コメント (0) | トラックバック (0)

2015年12月 4日 (金)

OTHER_XMLの中身

JPOUG Advent Calendar 2015 - 4日目のエントリーです。

役に立たないから、絶対最後まで読まないでね!!! (^^)

さて、
みなさん、マニュアル読んでますか? 
最近は量が多いので、必要になってから、なりそうだから読むことが多いです。
Oracle® Databaseリファレンス 12cリリース1 (12.1) なんて特にそうです。
ただ、一度読み始めると、いろいろと気づくところもあるわけです...こんな情報が取れるのか! とか。


あ〜、これは、禁断のw マニュアルに記載されてないパラメータを指定すると見れるやつだ!!! とか気づくこともあったり。

例えば、V$SQL_PLANDBA_HIST_SQL_PLANのOTHER_XML列。

この列の説明には実に興味深いことが書かれています。以下の説明、読んでてワクワクしますw 
何が格納されているのでしょう???(もう、みんな知ってるくせに〜)

”アウトライン・データ(同じ計画の再作成に使用できる一連のオプティマイザ・ヒント)”

見たくないですか? 中身。

見たいですよね。私もそうです!


ただ、OTHER_XML列、列名の通り、CLOB型で中身はXMLです!!!
SQL*Plusで普通に表示しようとすると長すぎて読みにくいってのが難点w


ならばXMLにも対応しているSQL文で何とかしてみたいと思います。

ゴニョゴニョ、パタパタ。

できました。

dba_hist_sql_plan向けですが。(v$sql_planビューでも同様のことができます!)
(注:dba_hist_*ビューを参照するにはOracle Diagnostics Packが必要でっす。ですが、v$sql_planを参照するのならオプションはいらないですよね.)

こんな感じです

SYSTEM> !cat report_outline_hints.sql
col outline for a200
set linesize 300
set pagesize 10000
set veri off

SELECT '/*+' AS outline FROM DUAL
UNION ALL
SELECT ' '||'BEGIN_OUTLINE_DATA' FROM DUAL
UNION ALL
SELECT
' '||o.outlinehint
FROM
(
SELECT
EXTRACTVALUE(
VALUE(x)
, '/hint/text()'
) AS outlinehint
FROM
XMLTABLE(
'/*/outline_data/hint'
PASSING(
SELECT
XMLTYPE(other_xml)
FROM
dba_hist_sql_plan
WHERE
other_xml IS NOT NULL
AND sql_id = '&1'
AND plan_hash_value = &2
)
) x
) o
UNION ALL
SELECT ' '||'END_OUTLINE_DATA' FROM DUAL
UNION ALL
SELECT '*/' FROM DUAL
/
set veri on

実行すると以下のような、:) 情報を取り出すことができます!!

SYSTEM> @report_outline_hints gdtyuqcyk8x1c 2236229349

OUTLINE
------------------------------------------------------------------------------------------
/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('12.1.0.2')
DB_VERSION('12.1.0.2')
ALL_ROWS
OUTLINE_LEAF(@"SEL$2")
OUTLINE_LEAF(@"SEL$1")
INDEX(@"SEL$1" "B"@"SEL$1" ("ILM_EXECUTION$"."EXECUTION_ID"))
NO_ACCESS(@"SEL$1" "A"@"SEL$1")
LEADING(@"SEL$1" "B"@"SEL$1" "A"@"SEL$1")
USE_MERGE(@"SEL$1" "A"@"SEL$1")
PQ_FILTER(@"SEL$1" SERIAL)
INDEX(@"SEL$2" "A"@"SEL$2" ("ILM_EXECUTION$"."EXECUTION_ID"))
FULL(@"SEL$2" "B"@"SEL$2")
LEADING(@"SEL$2" "A"@"SEL$2" "B"@"SEL$2")
USE_MERGE(@"SEL$2" "B"@"SEL$2")
USE_HASH_AGGREGATION(@"SEL$2")
END_OUTLINE_DATA
*/

表示された内容に、身に覚え、
いや、見覚えのある方も多いことと思います :) あれでね。そう、あれです。
マニュアルに記載されいないパラメータを利用しなくても取り出せるんです。

ついでなので、マニュアルに記載のないパラメータを使ったDBMS_XPLAN.DISPLAY_AWRで同じ情報をリストしてみました。DBMS_XPLAN.DISPLAY_AWR以外のDISPLAY*関数のformatパラメータでもできます:)
(注: DISPLAY_AWRでAWRを参照するのでOracle Diagnostics Packが必要でっす。 ですが、DISPLAY_CURSOR(),DISPLAY()とDISPLAY_PLAN()ならAWRは参照しないのでオプションはいらないですよね。)

PROMPT
PROMPT ****** display_awr with outline option *********
SELECT
plan_table_output as outline
FROM
TABLE(DBMS_XPLAN.DISPLAY_AWR(sql_id=>'&1',plan_hash_value=>&2,format=>'OUTLINE'))
/


****** DBMS_XPLAN.DISPLAY_AWR with OUTLINE option *******

OUTLINE
------------------------------------------------------------------------------------------
SQL_ID gdtyuqcyk8x1c
--------------------
SELECT B.EXECUTION_ID, NVL(A.N_COUNT,0), A.COMP_TIME FROM ( SELECT
A.EXECUTION_ID, COUNT(*) N_COUNT, NVL(MAX(B.COMPLETION_TIME), SYSDATE)
COMP_TIME FROM SYS.ILM_EXECUTION$ A, SYS.ILM_RESULTS$ B WHERE
EXECUTION_STATE = :B7 AND A.EXECUTION_ID = B.EXECUTION_ID AND
B.JOB_STATUS NOT IN (:B6 , :B5 , :B4 , :B3 , :B2 , :B1 ) GROUP BY
A.EXECUTION_ID )A, ILM_EXECUTION$ B WHERE B.EXECUTION_ID =
A.EXECUTION_ID (+) AND EXECUTION_STATE = :B7 AND (ROWNUM <= :B9 OR :B9
= :B8 )

Plan hash value: 2236229349

-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 4 (100)| |
| 1 | COUNT | | | | | |
| 2 | FILTER | | | | | |
| 3 | MERGE JOIN OUTER | | 1 | 65 | 4 (50)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID | ILM_EXECUTION$ | 1 | 26 | 0 (0)| |
| 5 | INDEX FULL SCAN | PK_TASKID | 1 | | 0 (0)| |
| 6 | SORT JOIN | | 1 | 39 | 4 (50)| 00:00:01 |
| 7 | VIEW | | 1 | 39 | 3 (34)| 00:00:01 |
| 8 | HASH GROUP BY | | 1 | 65 | 3 (34)| 00:00:01 |
| 9 | MERGE JOIN | | 1 | 65 | 3 (34)| 00:00:01 |
| 10 | TABLE ACCESS BY INDEX ROWID| ILM_EXECUTION$ | 1 | 26 | 0 (0)| |
| 11 | INDEX FULL SCAN | PK_TASKID | 1 | | 0 (0)| |
| 12 | SORT JOIN | | 1 | 39 | 3 (34)| 00:00:01 |
| 13 | TABLE ACCESS FULL | ILM_RESULTS$ | 1 | 39 | 2 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------

Outline Data
-------------

/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('12.1.0.2')
DB_VERSION('12.1.0.2')
ALL_ROWS
OUTLINE_LEAF(@"SEL$2")
OUTLINE_LEAF(@"SEL$1")
INDEX(@"SEL$1" "B"@"SEL$1" ("ILM_EXECUTION$"."EXECUTION_ID"))
NO_ACCESS(@"SEL$1" "A"@"SEL$1")
LEADING(@"SEL$1" "B"@"SEL$1" "A"@"SEL$1")
USE_MERGE(@"SEL$1" "A"@"SEL$1")
PQ_FILTER(@"SEL$1" SERIAL)
INDEX(@"SEL$2" "A"@"SEL$2" ("ILM_EXECUTION$"."EXECUTION_ID"))
FULL(@"SEL$2" "B"@"SEL$2")
LEADING(@"SEL$2" "A"@"SEL$2" "B"@"SEL$2")
USE_MERGE(@"SEL$2" "B"@"SEL$2")
USE_HASH_AGGREGATION(@"SEL$2")
END_OUTLINE_DATA
*/

ちなみに、11g R2でも動作します!


どうでしたか?役に立たなかったですよね?
以上、OUTLINE HINTSより愛を込めてお送りしました!


参考: (日本語で書かれたエントリーは見当たらない。初か、もしかして!w)

Oracle SQL Plan Stability
http://blog.tanelpoder.com/oracle/performance/sql/oracle-sql-plan-stability/


Plan stability in 10g - using existing cursors to create Stored Outlines and SQL profiles
http://oracle-randolf.blogspot.jp/2009/03/plan-stability-in-10g-using-existing.html


FORCE_MATCH for Stored Outlines and/or SQL Baselines????? – follow up
https://tonyhasler.wordpress.com/2011/12/

How to hint – 1
https://jonathanlewis.wordpress.com/2011/06/08/how-to-hint-1/

dbms_xplan(3)
https://jonathanlewis.wordpress.com/2008/03/06/dbms_xplan3/



追記:

@yoshikawさんに指摘され、EXTRACTVALUE()が11.2で非推奨になっていたとことに気づく orz.

@yoshikawさん、指摘ありがとうございます。 

XMLTABLE().... COLUMNSを使えばEXTRACTVALUE()はいらなかった!!!! ということでEXTRACTVALUEなし版も作りました!!

ただ、CON_IDも見ないとマルチテナントに対応できず、エラーになると気づいたが、
11gにも対応しようとすると、もう一工夫必要なことに気づき、再び、orz.

つづきは、...いずれ...

変更したSQL文は以下のとおり。

SYSTEM> !cat report_outline_hints.sql
col outline for a200
set linesize 300
set pagesize 10000
set veri off

SELECT '/*+' AS outline FROM dual
UNION ALL
SELECT ' '||'BEGIN_OUTLINE_DATA' FROM DUAL
UNION ALL
SELECT
' '||x.outline_hint
FROM
XMLTABLE(
'/*/outline_data/hint/text()'
PASSING(
SELECT
XMLTYPE(other_xml)
FROM
dba_hist_sql_plan
WHERE
other_xml IS NOT NULL
AND sql_id = '&1'
AND plan_hash_value = &2
)
COLUMNS outline_hint PATH '/text()'
) x
UNION ALL
SELECT ' '||'END_OUTLINE_DATA' FROM DUAL
UNION ALL
SELECT '*/' FROM DUAL
/

SYSTEM> @report_outline_hints gdtyuqcyk8x1c 2236229349

OUTLINE
--------------------------------------------------------------------------------
/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('12.1.0.2')
DB_VERSION('12.1.0.2')
ALL_ROWS
OUTLINE_LEAF(@"SEL$2")
OUTLINE_LEAF(@"SEL$1")
INDEX(@"SEL$1" "B"@"SEL$1" ("ILM_EXECUTION$"."EXECUTION_ID"))
NO_ACCESS(@"SEL$1" "A"@"SEL$1")
LEADING(@"SEL$1" "B"@"SEL$1" "A"@"SEL$1")
USE_MERGE(@"SEL$1" "A"@"SEL$1")
PQ_FILTER(@"SEL$1" SERIAL)
INDEX(@"SEL$2" "A"@"SEL$2" ("ILM_EXECUTION$"."EXECUTION_ID"))
FULL(@"SEL$2" "B"@"SEL$2")
LEADING(@"SEL$2" "A"@"SEL$2" "B"@"SEL$2")
USE_MERGE(@"SEL$2" "B"@"SEL$2")
USE_HASH_AGGREGATION(@"SEL$2")
END_OUTLINE_DATA
*/

JPOUG Advent Calendar 2015、12/5は、Yohei Azekatsu さんです。どんな変態ネタが飛び出すのか、乞うご期待!!

| | コメント (0) | トラックバック (0)

2015年11月21日 (土)

WITH clause in-line PL/SQL functionって....

12cの新機能でオイタをしてみたw

with句に関数仕込んで内部でSQL文を実行してオイタしてみました。

実際にこのような使い方するんでしょうか?
これをやるなら素直にスカラー副問合せの方がいいと思うんですね.....
(スカラー副問合せのように使われたら、同じく12cの新機能、Scalar subquery unnestingもできなし。)

なんでこんな機能必要なんだろう?
どのような場面で、有効なんだろう?

今の所、使いどころが思い浮かばないので、誰か教えて〜〜〜〜〜! 
とモヤモヤしてたんですが、今日はお留守番で時間だけはあったので、ごにょごにょとw


SCOTT> set serveroutput on
SCOTT> set feed off
SCOTT> r
1 with
2 function hoge return varchar2
3 deterministic
4 as
5 begin
6 for rec in (select empno,ename from emp order by empno) loop
7 dbms_output.put_line('**** '||rec.empno||':'||rec.ename||' ****');
8 end loop;
9 return null;
10 end;
11 select
12 hoge
13 from
14 dual
15 where
16* hoge is not null

**** 7369:SMITH ****
**** 7499:ALLEN ****
**** 7521:WARD ****
**** 7566:JONES ****
**** 7654:MARTIN ****
**** 7698:BLAKE ****
**** 7782:CLARK ****
**** 7839:KING ****
**** 7844:TURNER ****
**** 7900:JAMES ****
**** 7902:FORD ****
**** 7934:MILLER ****

経過: 00:00:00.01

こんなことをやられると....
実行計画には現れないしw (ちなみにスカラー副問合せは実行計画を見ただけでわかりますです。はい)

ただのチューナーいじめじゃ >< w
SQLトレースなら補足できるけど、なんぎじゃのう。げほげほ

(WITH句のインラインPL/SQLファンクション(名称あってるのかな?)内部でSQL文は実行しない方がいいと思うけど。。。。???)


SCOTT> 
SCOTT> set autot trace exp stat
SCOTT> r
1 with
2 function hoge return varchar2
3 deterministic
4 as
5 begin
6 for rec in (select empno,ename from emp order by empno) loop
7 dbms_output.put_line('**** '||rec.empno||':'||rec.ename||' ****');
8 end loop;
9 return null;
10 end;
11 select
12 hoge
13 from
14 dual
15 where
16* hoge is not null

**** 7369:SMITH ****
**** 7499:ALLEN ****
**** 7521:WARD ****
**** 7566:JONES ****
**** 7654:MARTIN ****
**** 7698:BLAKE ****
**** 7782:CLARK ****
**** 7839:KING ****
**** 7844:TURNER ****
**** 7900:JAMES ****
**** 7902:FORD ****
**** 7934:MILLER ****

経過: 00:00:00.03

実行計画
----------------------------------------------------------
Plan hash value: 4034615273

-----------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-----------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 2 (0)| 00:00:01 |
|* 1 | FILTER | | | | |
| 2 | FAST DUAL | | 1 | 2 (0)| 00:00:01 |
-----------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter("HOGE"() IS NOT NULL)


統計
----------------------------------------------------------
1 recursive calls
0 db block gets
2 consistent gets
0 physical reads
0 redo size
363 bytes sent via SQL*Net to client
541 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed




WITH句のインラインPL/SQLファンクション内でのSQL文の実行はダークサイドな香りしかしないので、使うならデータの加工等、計算中心目的かねぇ〜。とマニュアル読んでたら見つけたw
 
(マニュアルのサンプルも加工のみ! )

SCOTT> r
1 with
2 function hoge(eno number) return varchar2
3 deterministic
4 as
5 begin
6 dbms_output.put_line('Called');
7 return '**** '||eno||' ****';
8 end;
9 select
10 hoge(empno)
11 from
12* emp

HOGE(EMPNO)
---------------------------------------------------------------------
**** 7369 ****
**** 7499 ****
**** 7521 ****
**** 7566 ****
**** 7654 ****
**** 7698 ****
**** 7782 ****
**** 7839 ****
**** 7844 ****
**** 7900 ****
**** 7902 ****
**** 7934 ****
Called
Called
Called
Called
Called
Called
Called
Called
Called
Called
Called
Called
経過: 00:00:00.02

Database SQL Language Reference (12.1)
Database SQL Language Reference (12.1) - SELECT


みなさん、ダークサイドに落ちないように注意しましょうね! :)

| | コメント (0) | トラックバック (0)

2015年5月17日 (日)

Meetup! JPOUG @ Oracle CloudWorld 2015

意外に心?w 時間? に余裕がないのですが、途切れ途切れでも更新していこうかなと50%いや、60%
ぐらい思ってます。

久々の投稿ですが、一ヶ月前のネタです m(_ _)m

Meetup! JPOUG 開催報告

ショートセッション

スペシャル企画 「な〜んでだ?」


ショートセッションのSQLチューニング総合診療Oracle CloudWorld出張所は、db tech showcase 2014 Tokyoの続編のようなw (ような、じゃなくて、続編ですね、キッパリ

いろいろな性能病は無駄な物理I/Oや論理I/Oを削減していくことが、治療の第一歩!。

な〜んでだ? #3は、時間が押してしまい残り5分程度で駆け足で終わらせてしまったので、「な〜んでだ?」は?な雰囲気にできなかったのが心残りでした (笑

AWRを眺めていると、あるような、ないような時間が見えたりしてモヤモヤすることもあると思うんですよね。
そんなときの参考にならないかな? と思ったネタでした。


Oracle Databaseが雲の向こうに行っちゃっても、人工知能型オプティマイザでも登場してこないかぎり、まだ、まだ、人間のサポートが必要だと思うんだよねw いつまで必要なのかは、わからないけど。:)

賢い人工知能型オプティマイザがうまれたとして、各インスタンスごとに性格の違うオプティマイザが成長しちゃったら面白い世界になるかもしれない。
と妄想していると、眠れなくなるので、今日はこのへんで。

| | コメント (0) | トラックバック (0)

2015年1月 2日 (金)

机上SQLチューニング、クイズ! 駆動表(外部表)はどれだ!!!! (実解答編)

JPOUG Advent Calendar 2014 17日目のエントリーで公開したクイズを実解答編です。
実際にOracle Database 12c 12.1.0.2.0を使ってどの表を駆動表(外部表)にするか確認してみます。


予想はあたるか、外れるか...どのような結果が待っているのか..ワクワクします。 :-)
Oracle® Database SQLチューニング・ガイド 12cリリース1(12.1) B71277-02 - 7 結合


前回の予想も参考に....机上SQLチューニング、クイズ! 駆動表(外部表)はどれだ!!!! (予想解答編)

問題1
次に示すSQL文の駆動表(外部表)はどれでしょうか? 

表と索引
create table a1 (
id number primary key
, data varchar2(1000)
) nologging
/
create table a2 (
id number primary key
, data varchar2(1000)
) nologging
/

統計情報
TABLE_NAME INDEX_NAME NUM_ROWS DISTINCT_KEYS CLUSTERING_FACTOR
-------------- -------------- ---------- ------------- -----------------
A1 SYS_C0010377 10000 10000 295
A2 SYS_C0010378 2000 2000 59

SELECT
/* SQL01 */
/*+
MONITOR
USE_NL(a1 a2)
*/
*
FROM
a1
INNER JOIN a2
ON
a1.id = a2.id
WHERE
a1.id BETWEEN 1 AND 100
/


私の答え(予想)
Nested Loop結合かつ、INNER JOINですから駆動表(外部表)はデータセットの小さい方。
(Nested Loop結合でOUTER JOINだと結合順は固定されるので駆動表は見つけやすいですが...INNER JOINの場合はそうはいかないですよね。ちなみに、最近のOracleだと、OUTER JOINでHash結合だと外部表を入れれかえて最適化するケースもあります。いずれどこかでそのネタをやると思います。)

ただ、結合条件キーはどちらも主キーですし、WHERE a1.id BETWEEN 1 AND 100は、a2表にも適用されることになるでしょうから、a1、a2どちらも最大で100行ヒットする可能性はあります。
どちらでも正解になる可能性はありますが...w (最初から引っ掛けかよw

細かい条件は提示していないので、提示した情報だけで机上で駆動表(外部表)を決めるとすれば、全体のサイズが小さい、a2でいいんじゃないでしょうか?(私ならそうします。机上ですから)
(当初、もう少し詳細な情報を提示しようとしていたのですが忘れてました...なのでa1だと思った方も可能性は五分五分ですね。前提条件不足でした。...ごめんなさい。ごめんなさい。)

Oracle Database 12c 12.1.0.2.0の動き
以下、SQLモニタリングレポートをみると、駆動表は、 a2表でした。 :)
ちなみに、a1を駆動表にした場合は、若干Buffer Getsが増加していました。誤差の範囲ですが...

SQL Monitoring Report

Global Stats
=======================================
| Elapsed | Other | Fetch | Buffer |
| Time(s) | Waits(s) | Calls | Gets |
=======================================
| 0.00 | 0.00 | 3 | 37 |
=======================================

SQL Plan Monitoring Details (Plan Hash Value=3695212685)
=====================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | (%) | (# samples) |
=====================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 1 | +0 | 1 | 20 | | |
| 1 | NESTED LOOPS | | 20 | 23 | 1 | +0 | 1 | 20 | | |
| 2 | NESTED LOOPS | | 20 | 23 | 1 | +0 | 1 | 20 | | |
| 3 | TABLE ACCESS BY INDEX ROWID BATCHED | A2 | 20 | 3 | 1 | +0 | 1 | 20 | | |
| 4 | INDEX RANGE SCAN | SYS_C0010378 | 20 | 2 | 1 | +0 | 1 | 20 | | |
| 5 | INDEX UNIQUE SCAN | SYS_C0010377 | 1 | | 1 | +0 | 20 | 20 | | |
| 6 | TABLE ACCESS BY INDEX ROWID | A1 | 1 | 1 | 1 | +0 | 20 | 20 | | |
=====================================================================================================================================================

問題2
次に示すSQL文の駆動表(外部表)はどれでしょうか? 
(表と索引、および、統計情報は問題1と同じです。)

SELECT
/* SQL02 */
/*+
MONITOR
USE_HASH(a1 a2)
*/
*
FROM
a1
INNER JOIN a2
ON
a1.id = a2.id
/

私の答え(予想)
Hash結合でINNER JOINかつ、WHERE句はないのでこの問題は簡単ですよね! (これは迷わないはず!!!)

Hash結合も外部表はデータセットの小さい方ですから...この場合だと、a2が外部表になるはず。

Oracle Database 12c 12.1.0.2.0の動き
以下、SQLモニタリングレポートから....外部表は、予想通りのa2

SQL Monitoring Report

Global Stats
=================================================
| Elapsed | Cpu | Other | Fetch | Buffer |
| Time(s) | Time(s) | Waits(s) | Calls | Gets |
=================================================
| 0.01 | 0.01 | 0.00 | 135 | 497 |
=================================================

SQL Plan Monitoring Details (Plan Hash Value=1713954154)
==================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Mem | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | (Max) | (%) | (# samples) |
==================================================================================================================================
| 0 | SELECT STATEMENT | | | | 1 | +0 | 1 | 2000 | | | |
| 1 | HASH JOIN | | 2000 | 121 | 1 | +0 | 1 | 2000 | 1M | | |
| 2 | TABLE ACCESS FULL | A2 | 2000 | 19 | 1 | +0 | 1 | 2000 | | | |
| 3 | TABLE ACCESS FULL | A1 | 10000 | 102 | 1 | +0 | 1 | 10000 | | | |
==================================================================================================================================


問題3
次に示すSQL文の駆動表(外部表)はどれでしょうか? 
なお、D1とD2の多重度は、D1:D2 = 1:100
(個人的にUMLの多重度表記のほうが好きなので、UML表記の多重度で記述します。

表と索引
create table d1 (
id number
, data varchar2(1000)
) nologging
/
alter table d1 add constraint pk_d1 primary key (id) using index nologging
/
create table d2 (
id number not null
, seq# number not null
, data varchar2(1000)
) nologging
/
alter table d2 add constraint pk_d2 primary key (id, seq#) using index nologging
/

統計情報
TABLE_NAME INDEX_NAME NUM_ROWS DISTINCT_KEYS CLUSTERING_FACTOR
-------------- -------------- ---------- ------------- -----------------
D1 PK_D1 200 200 9
D2 PK_D2 20000 20000 870

SELECT
/* SQL03 */
/*+
MONITOR
*/
*
FROM
d2
WHERE
EXISTS (
SELECT
1
FROM
d1
WHERE
d1.id = d2.id
AND d1.id IN (1,5)
)
/


私の答え(予想)
これはちょっと意地悪な問題ですが、わかる人ならわかるはず。だと思って(信じて)作った問題です。 :)

最近のオプティマイザは、相関副問合せを可能であれば結合に書き換える(unnest)傾向が強いのをご存知でしょうか? 
となれば、方向はだいだい見えてきます。

d1.id IN (1,5)から最大で2件ヒットすると予想できますよね。
さらに、Nested Loop結合に書き換え、d2を内部表として結合できれば無駄がない。
つまり、駆動表は d1が理想的なはず。

d2を外部表にしてしまうと、WHERE条件がないので結合に置き換えてしまうとHash結合または、d2を全表走査して相関副問合せを都度実行のいづれかになってしまうでしょうね。(unnestの傾向が強いのでHash結合になる可能性が高そう)
ヒットするデータ件数から考えると、どちらも無駄なデータアクセスが多くなる可能性は高いのではないでしょうか。


Oracle Database 12c 12.1.0.2.0の動き
おおお〜。予想通り、相関副問合せは、unnestされて結合に書き換えられています。 さらに 12cの新機能であるAdaptive Joinになっています。興味深い。

で本題である、外部表はどうなっているかというと....pk_d1をindex only scanしてNested Loop結合になっています。つまり、駆動表は、予想通り、d1になっています。Yahoo!
id=1でHash結合がありますが、試行はしたようですが、実際にはNested Loop結合だったようです。

SQL Monitoring Report

Global Stats
=======================================
| Elapsed | Other | Fetch | Buffer |
| Time(s) | Waits(s) | Calls | Gets |
=======================================
| 0.01 | 0.01 | 15 | 44 |
=======================================

SQL Plan Monitoring Details (Plan Hash Value=804853015)
==============================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | (%) | (# samples) |
==============================================================================================================================================
| 0 | SELECT STATEMENT | | | | 1 | +0 | 1 | 0 | | |
| 1 | HASH JOIN | | 200 | 5 | 1 | +0 | 1 | 0 | | |
| 2 | NESTED LOOPS | | 200 | 5 | 1 | +0 | 1 | 200 | | |
| 3 | NESTED LOOPS | | 200 | 5 | 1 | +0 | 1 | 200 | | |
| 4 | STATISTICS COLLECTOR | | | | 1 | +0 | 1 | 1 | | |
| 5 | INLIST ITERATOR | | | | 1 | +0 | 1 | 2 | | |
| 6 | INDEX UNIQUE SCAN | PK_D1 | 2 | 1 | 1 | +0 | 2 | 2 | | |
| 7 | INDEX RANGE SCAN | PK_D2 | 1 | 1 | 1 | +0 | 2 | 200 | | |
| 8 | TABLE ACCESS BY INDEX ROWID | D2 | 100 | 2 | 1 | +0 | 200 | 200 | | |
| 9 | INLIST ITERATOR | | | | | | | | | |
| 10 | TABLE ACCESS BY INDEX ROWID BATCHED | D2 | 100 | 2 | | | | | | |
| 11 | INDEX RANGE SCAN | PK_D2 | 1 | 1 | | | | | | |
==============================================================================================================================================


問題4
次に示すSQL文の駆動表(外部表)はどれでしょうか? 
(表と索引、および、統計情報は問題3と同じです。)

SELECT
/* SQL04 */
/*+
MONITOR
*/
*
FROM
d1
INNER JOIN d2
ON
d1.id > d2.id
AND d2.id BETWEEN 1 AND 5
AND d2.seq# BETWEEN 2 AND 4
/

私の答え(予想)
結合条件が ">" であることに気付きましたか? ;)


これは、Nested Loop結合にも、Hash結合にもなりません。 Merge(sort/merge)結合が選択されます。

内部表は索引が利用できないのでソートが発生します。(ソートは避けられない)
外部表では索引を利用してソート処理が回避可能であれば、回避する。

つまりソート処理の影響を最小にしようとするはず、なので...

d1:d2=1:100という比率からd2のソート処理を重いと判断し回避するために外部表にするだろうなぁ〜〜。
私は、d2が外部表になるだろうと予想しています。
(直積じゃないMerge結合なんて最近お目にかかったことないのですが...)

余談)
Hash結合のない時代のOracleで、Merge結合の実行計画を見せられて、なんでソートしてるんですかね〜と、某ベンダーの方に質問されて一瞬固まったことを思い出したw


Oracle Database 12c 12.1.0.2.0の動き
良かったw。 予想どおりソートを回避したようで、 外部表は、 d2ですね。

SQL Monitoring Report

Global Stats
=================================================
| Elapsed | Cpu | Other | Fetch | Buffer |
| Time(s) | Time(s) | Waits(s) | Calls | Gets |
=================================================
| 0.02 | 0.01 | 0.01 | 198 | 48 |
=================================================

SQL Plan Monitoring Details (Plan Hash Value=705618498)
=============================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Mem | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | (Max) | (%) | (# samples) |
=============================================================================================================================================
| 0 | SELECT STATEMENT | | | | 1 | +0 | 1 | 2955 | | | |
| 1 | MERGE JOIN | | 3966 | 10 | 1 | +0 | 1 | 2955 | | | |
| 2 | TABLE ACCESS BY INDEX ROWID | D2 | 20 | 4 | 1 | +0 | 1 | 15 | | | |
| 3 | INDEX RANGE SCAN | PK_D2 | 20 | 3 | 1 | +0 | 1 | 15 | | | |
| 4 | SORT JOIN | | 199 | 6 | 1 | +0 | 15 | 2955 | 145K | | |
| 5 | TABLE ACCESS FULL | D1 | 199 | 5 | 1 | +0 | 1 | 199 | | | |
=============================================================================================================================================


問題5
次に示すSQL文の駆動表(外部表)はどれでしょうか? 

表と索引定義
create table b1 (
id number
, data varchar2(1000)
) nologging
/
alter table b1 add constraint pk_b1 primary key(id) using index nologging
/
create table b3 (
id number
, seq# number
, data varchar2(1000)
) nologging
/
alter table b3 add constraint pk_b3 primary key (id, seq#) using index nologging
/
create table b2 (
id number
, seq# number
, subseq# number
, data varchar2(1000)
) nologging
/
alter table b2 add constraint pk_b2 primary key (id ,seq#, subseq#) using index nologging
/

統計情報
TABLE_NAME INDEX_NAME NUM_ROWS DISTINCT_KEYS CLUSTERING_FACTOR
-------------- -------------- ---------- ------------- -----------------
B1 PK_B1 20000 20000 870
B2 PK_B2 5000 5000 228
B3 PK_B3 500 500 23

ERDと多重度
b1 : b3 = 1 : 0..2
b3 : b2 = 1 : 0..10
(UML表記の多重度で記述しています。
20141221_120733

SELECT
/* SQL05 */
/*+
MONITOR
USE_HASH(b1 b3 b2)
*/
*
FROM
b1
INNER JOIN b3
ON
b1.id = b3.id
INNER JOIN b2
ON
b3.id = b2.id
AND b3.seq# = b2.seq#
/

私の答え(予想)
INNER JOINでHash結合かつ、WHERE句もないので簡単な問題ですよね。

外部表は、b3

ついでに、続けると、 b3とb2を結合すると最大で5000件、b3とb1を結合した場合、最大で500件なので、 結合順として理想的なのは、 b3, b1, b2 ですよね。


Oracle Database 12c 12.1.0.2.0の動き
結果は、予想通りでした〜。これは簡単だから!。

SQL Monitoring Report

Global Stats
=================================================
| Elapsed | Cpu | Other | Fetch | Buffer |
| Time(s) | Time(s) | Waits(s) | Calls | Gets |
=================================================
| 0.06 | 0.04 | 0.02 | 335 | 1476 |
=================================================

SQL Plan Monitoring Details (Plan Hash Value=3711653655)
===================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Mem | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | (Max) | (%) | (# samples) |
===================================================================================================================================
| 0 | SELECT STATEMENT | | | | 1 | +0 | 1 | 5000 | | | |
| 1 | HASH JOIN | | 5000 | 315 | 1 | +0 | 1 | 5000 | 1M | | |
| 2 | HASH JOIN | | 500 | 247 | 1 | +0 | 1 | 500 | 1M | | |
| 3 | TABLE ACCESS FULL | B3 | 500 | 9 | 1 | +0 | 1 | 500 | | | |
| 4 | TABLE ACCESS FULL | B1 | 20000 | 238 | 1 | +0 | 1 | 20000 | | | |
| 5 | TABLE ACCESS FULL | B2 | 5000 | 68 | 1 | +0 | 1 | 5000 | | | |
===================================================================================================================================


問題6
次に示すSQL文の駆動表(外部表)はどれでしょうか? 
(表と索引、および、統計情報は問題5と同じです。)

SELECT
/* SQL06 */
/*+
MONITOR
USE_HASH(b1 b3 b2)
*/
*
FROM
b1
LEFT OUTER JOIN b3
ON
b1.id = b3.id
LEFT OUTER JOIN b2
ON
b3.id = b2.id
AND b3.seq# = b2.seq#
/

私の答え(予想)
OUTER JOINなのですが、Hash結合なので、多分、予想通りにはならないだろうな〜〜と。

最近のオプティマイザは外部結合かつHash結合の場合、外部表と内部表をデータセットサイズに応じて入れ替える傾向が強いんですよね。PGAの使用サイズが小さくなるように....

とは言っても、机上の話なので、入れ替えないとしたらどうなるだろう..と、外部結合なので、内部表は、そのまま内部表として結合したほうがわかりやすいといえばわかりやすい。
(統計情報に乖離がある場合には入れ替えないほうが良い結果だったりすることもあります。)

注)()内を先に結合するものとします。
入れ替えなかった場合は、記述したままなので、(外部表、内部表)=外部表、内部表ということで、 (b1, b3), b2
データセットの小さい順に従えば、外部表と、内部表をいれかえて、b3, b1この結果セットの最大件数は元の外部表がb1ですから20,250件、b2が5,000件なので、また外部表と内部表を入れ替えて、結果として、b2, (b3, b1)ってことに...
(内部的に結合順が入れ替えられ、Right Joinに置き換えられることが多いんですよね〜いつ頃からだろう...10gあたりか。)

で、机上ならどちらを取るかということですが、机上では、ひとまず、SQL文の通りとしておくケースが多いです。あくまで私の場合ですよ。(難しいんですよ、これ。外れる可能性大w)
オプティマイザが内部的に外部表と内部表を入れ替えて問題がなければよし、問題があれば、内部的な入れ替えをヒントで止めるというのが私の基本方針なので、この辺りは、チューナーさんのさじ加減次第じゃないかなぁ、と思っています。

あはは、むずかし過ぎたか... 机上だと orz.

Oracle Database 12c 12.1.0.2.0の動き
結果を見てみると〜〜〜〜〜っ。恐れていたw 外部表と内部表の置き換え操作(LEFT OUTERがRIGHT OUTER)になってますね〜っ。あはは、見事に、外れました....orz.

SQL Monitoring Report

Global Stats
=================================================
| Elapsed | Cpu | Other | Fetch | Buffer |
| Time(s) | Time(s) | Waits(s) | Calls | Gets |
=================================================
| 0.34 | 0.21 | 0.13 | 1651 | 2648 |
=================================================

SQL Plan Monitoring Details (Plan Hash Value=1453650298)
======================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Mem | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | (Max) | (%) | (# samples) |
======================================================================================================================================
| 0 | SELECT STATEMENT | | | | 4 | +0 | 1 | 24750 | | | |
| 1 | HASH JOIN RIGHT OUTER | | 203K | 316 | 4 | +0 | 1 | 24750 | 3M | | |
| 2 | TABLE ACCESS FULL | B2 | 5000 | 68 | 1 | +0 | 1 | 5000 | | | |
| 3 | HASH JOIN RIGHT OUTER | | 20250 | 247 | 4 | +0 | 1 | 20250 | 1M | | |
| 4 | TABLE ACCESS FULL | B3 | 500 | 9 | 1 | +0 | 1 | 500 | | | |
| 5 | TABLE ACCESS FULL | B1 | 20000 | 238 | 4 | +0 | 1 | 20000 | | | |
======================================================================================================================================

問題ごとの解説をおまけで何回かに分けて、書いた方が良さそうだなw

ということで、おまけエントリーを書くかも...し、れ、な、い。


机上SQLチューニング、クイズ! 駆動表(外部表)はどれだ!!!!
机上SQLチューニング、クイズ! 駆動表(外部表)はどれだ!!!! (予想解答編)

| | コメント (1) | トラックバック (0)

2015年1月 1日 (木)

机上SQLチューニング、クイズ! 駆動表(外部表)はどれだ!!!! (予想解答編)

JPOUG Advent Calendar 2014 17日目のエントリーで公開したクイズの予想解答編です。
机上ですし、限られた情報しか提示していないので、実際にやってみたら違う...ということも十分ありえます。 (^^;;;;


外部表はどれがいいか予想できると、いろいろな制限のあるチューニング現場では役立つこともあるんです。LEADINGヒント等で結合順を考える場合にも役立ちますyo!
机上の予想なので間違うこともあります。オプティマイザも統計情報から予想しているわけで(Adaptiveな機能を除く)ハズすことも多いですから... (^^

Oracle® Database SQLチューニング・ガイド 12cリリース1(12.1) B71277-02 - 7 結合


問題1
次に示すSQL文の駆動表(外部表)はどれでしょうか? 

表と索引
create table a1 (
id number primary key
, data varchar2(1000)
) nologging
/
create table a2 (
id number primary key
, data varchar2(1000)
) nologging
/

統計情報
TABLE_NAME INDEX_NAME NUM_ROWS DISTINCT_KEYS CLUSTERING_FACTOR
-------------- -------------- ---------- ------------- -----------------
A1 SYS_C0010377 10000 10000 295
A2 SYS_C0010378 2000 2000 59

SELECT
/* SQL01 */
/*+
MONITOR
USE_NL(a1 a2)
*/
*
FROM
a1
INNER JOIN a2
ON
a1.id = a2.id
WHERE
a1.id BETWEEN 1 AND 100
/

私の答え(予想)
Nested Loop結合かつ、INNER JOINですから駆動表(外部表)はデータセットの小さい方。
(Nested Loop結合でOUTER JOINだと結合順は固定されるので駆動表は見つけやすいですが...INNER JOINの場合はそうはいかないですよね)

ただ、結合条件キーはどちらも主キーですし、WHERE a1.id BETWEEN 1 AND 100は、a2表にも適用されることになるでしょうから、a1、a2どちらも最大で100行ヒットする可能性はあります。
どちらでも正解になる可能性はありますが...w (最初から引っ掛けかよw

細かい条件は提示していないので、提示した情報だけで机上で駆動表(外部表)を決めるとすれば、全体のサイズが小さい、a2でいいんじゃないでしょうか?(私ならそうします。机上ですから)
(当初、もう少し詳細な情報を提示しようとしていたのですが忘れてました...なのでa1だと思った方も可能性は五分五分ですね。前提条件不足でした。...ごめんなさい。ごめんなさい。)


問題2
次に示すSQL文の駆動表(外部表)はどれでしょうか? 
(表と索引、および、統計情報は問題1と同じです。)

SELECT
/* SQL02 */
/*+
MONITOR
USE_HASH(a1 a2)
*/
*
FROM
a1
INNER JOIN a2
ON
a1.id = a2.id
/

私の答え(予想)
Hash結合でINNER JOINかつ、WHERE句はないのでこの問題は簡単ですよね! (これは迷わないはず!!!)

Hash結合も外部表はデータセットの小さい方ですから...この場合だと、a2が外部表になるはず。

問題3
次に示すSQL文の駆動表(外部表)はどれでしょうか? 
なお、D1とD2の多重度は、D1:D2 = 1:100
(個人的にUMLの多重度表記のほうが好きなので、UML表記の多重度で記述します。

表と索引
create table d1 (
id number
, data varchar2(1000)
) nologging
/
alter table d1 add constraint pk_d1 primary key (id) using index nologging
/
create table d2 (
id number not null
, seq# number not null
, data varchar2(1000)
) nologging
/
alter table d2 add constraint pk_d2 primary key (id, seq#) using index nologging
/

統計情報
TABLE_NAME INDEX_NAME NUM_ROWS DISTINCT_KEYS CLUSTERING_FACTOR
-------------- -------------- ---------- ------------- -----------------
D1 PK_D1 200 200 9
D2 PK_D2 20000 20000 870

SELECT
/* SQL03 */
/*+
MONITOR
*/
*
FROM
d2
WHERE
EXISTS (
SELECT
1
FROM
d1
WHERE
d1.id = d2.id
AND d1.id IN (1,5)
)
/


私の答え(予想)
これはちょっと意地悪な問題ですが、わかる人ならわかるはず。だと思って(信じて)作った問題です。 :)

最近のオプティマイザは、相関副問合せを可能であれば結合に書き換える(unnest)傾向が強いのをご存知でしょうか? 
となれば、方向はだいだい見えてきます。

d1.id IN (1,5)から最大で2件ヒットすると予想できますよね。
さらに、Nested Loop結合に書き換え、d2を内部表として結合できれば無駄がない。
つまり、駆動表は d1が理想的なはず。

d2を外部表にしてしまうと、WHERE条件がないので結合に置き換えてしまうとHash結合または、d2を全表走査して相関副問合せを都度実行のいづれかになってしまうでしょうね。(unnestの傾向が強いのでHash結合になる可能性が高そう)
ヒットするデータ件数から考えると、どちらも無駄なデータアクセスが多くなる可能性は高いのではないでしょうか。

問題4
次に示すSQL文の駆動表(外部表)はどれでしょうか? 
(表と索引、および、統計情報は問題3と同じです。)

SELECT
/* SQL04 */
/*+
MONITOR
*/
*
FROM
d1
INNER JOIN d2
ON
d1.id > d2.id
AND d2.id BETWEEN 1 AND 5
AND d2.seq# BETWEEN 2 AND 4
/

私の答え(予想)
結合条件が ">" であることに気付きましたか? ;)


これは、Nested Loop結合にも、Hash結合にもなりません。 Merge(sort/merge)結合が選択されます。

内部表は索引が利用できないのでソートが発生します。(ソートは避けられない)
外部表では索引を利用してソート処理が回避可能であれば、回避する。

つまりソート処理の影響を最小にしようとするはず、なので...

d1:d2=1:100という比率からd2のソート処理を重いと判断し回避するために外部表にするだろうなぁ〜〜。
私は、d2が外部表になるだろうと予想しています。
(直積じゃないMerge結合なんて最近お目にかかったことないのですが...)

余談)
Hash結合のない時代のOracleで、Merge結合の実行計画を見せられて、なんでソートしてるんですかね〜と、某ベンダーの方に質問されて一瞬固まったことを思い出したw


問題5
次に示すSQL文の駆動表(外部表)はどれでしょうか? 

表と索引定義
create table b1 (
id number
, data varchar2(1000)
) nologging
/
alter table b1 add constraint pk_b1 primary key(id) using index nologging
/
create table b3 (
id number
, seq# number
, data varchar2(1000)
) nologging
/
alter table b3 add constraint pk_b3 primary key (id, seq#) using index nologging
/
create table b2 (
id number
, seq# number
, subseq# number
, data varchar2(1000)
) nologging
/
alter table b2 add constraint pk_b2 primary key (id ,seq#, subseq#) using index nologging
/

統計情報
TABLE_NAME INDEX_NAME NUM_ROWS DISTINCT_KEYS CLUSTERING_FACTOR
-------------- -------------- ---------- ------------- -----------------
B1 PK_B1 20000 20000 870
B2 PK_B2 5000 5000 228
B3 PK_B3 500 500 23

ERDと多重度
b1 : b3 = 1 : 0..2
b3 : b2 = 1 : 0..10
(UML表記の多重度で記述しています。
20141221_120733

SELECT
/* SQL05 */
/*+
MONITOR
USE_HASH(b1 b3 b2)
*/
*
FROM
b1
INNER JOIN b3
ON
b1.id = b3.id
INNER JOIN b2
ON
b3.id = b2.id
AND b3.seq# = b2.seq#
/

私の答え(予想)
INNER JOINでHash結合かつ、WHERE句もないので簡単な問題ですよね。

外部表は、b3

ついでに、続けると、 b3とb2を結合すると最大で5000件、b3とb1を結合した場合、最大で500件なので、 結合順として理想的なのは、 b3, b1, b2 ですよね。


問題6
次に示すSQL文の駆動表(外部表)はどれでしょうか? 
(表と索引、および、統計情報は問題5と同じです。)

SELECT
/* SQL06 */
/*+
MONITOR
USE_HASH(b1 b3 b2)
*/
*
FROM
b1
LEFT OUTER JOIN b3
ON
b1.id = b3.id
LEFT OUTER JOIN b2
ON
b3.id = b2.id
AND b3.seq# = b2.seq#
/

私の答え(予想)
OUTER JOINなのですが、Hash結合なので、多分、予想通りにはならないだろうな〜〜と。

最近のオプティマイザは外部結合かつHash結合の場合、外部表と内部表をデータセットサイズに応じて入れ替える傾向が強いんですよね。PGAの使用サイズが小さくなるように....

とは言っても、机上の話なので、入れ替えないとしたらどうなるだろう..と、外部結合なので、内部表は、そのまま内部表として結合したほうがわかりやすいといえばわかりやすい。
(統計情報に乖離がある場合には入れ替えないほうが良い結果だったりすることもあります。)


注)()内を先に結合するものとします。
入れ替えなかった場合は、記述したままなので、(外部表、内部表)=外部表、内部表ということで、 (b1, b3), b2
データセットの小さい順に従えば、外部表と、内部表をいれかえて、b3, b1。
この結果セットの最大件数は元の外部表がb1ですから20,250件、b2が5,000件なので、また外部表と内部表を入れ替えて、結果として、b2, (b3, b1)ってことに...
(内部的に結合順が入れ替えられ、Right Joinに置き換えられることが多いんですよね〜いつ頃からだろう...10gあたりか。)

で、机上ならどちらを取るかということですが、机上では、ひとまず、SQL文の通りとしておくケースが多いです。あくまで私の場合ですよ。(難しいんですよ、これ。外れる可能性大w)
オプティマイザが内部的に外部表と内部表を入れ替えて問題がなければよし、問題があれば、内部的な入れ替えをヒントで止めるというのが私の基本方針なので、この辺りは、チューナーさんのさじ加減次第じゃないかなぁ、と思っています。

あはは、むずかし過ぎたか... 机上だと orz.

結合順がほぼ想定できる場合、状況次第で変わりそうな結合順、いろいろありますよね。
オプティマイザの気持ちになって考えてみると、いろいろ気づくことも多いんじゃないかなぁ。と思います。

オプティマイザってソートが嫌いなんだ〜、とか、PGA少ない方が好きなんだ〜とか....


役に立ったか、どうなのか不明なオチになりましたが.....

本年もよろしくお願いいたします。 m(_ _)m


あ、そうそう、一つ忘れてました。

次回は、Oracle Database 12c 12.1.0.2.0を使って試したオプティマイザが選択した駆動表(外部表)がどれだったかのか公開する予定です。


机上SQLチューニング、クイズ! 駆動表(外部表)はどれだ!!!!

| | コメント (0) | トラックバック (0)

2014年12月17日 (水)

机上SQLチューニング、クイズ! 駆動表(外部表)はどれだ!!!!

予備プラン発動中 :)

ということで、諸事情により開け忘れた窓を開けるためJPOUG Advent Calendar 2014 2回目の登場となりました。

JPOUG Advent Calendar 2014 17日目のエントリーです。


突然ですが、

「机上SQLチューーーーーニング、クイズ〜〜〜っ!!!!!!
 駆動表(外部表)はどれだ!!!!」


なお、このクイズには次の制限があります。

別途、本ブログで解答エントリーが公開(年明けを予定)されるまで、Oracle Databaseを利用して答え求めるのは禁止。 :-)
Oracle Databaseを利用せず、机上で、どの表を駆動表(外部表)にすれば理想的な実行計画になりそうか考えてみてね。

また、解答はOracle Database 12c Release 1 12.1.0.2.0 のオプティマイザをインストールしたまま(初期化パラメータはデフォルトのまま)の環境を使って行います。
(11gでも違わないと思いますが)

というクリスマスプレゼント :)

次に示されるSQL文の駆動表(外部表)はどれでしょうか? 

前提

統計情報と実データとの間に乖離はありません。
リテラル値で指定した検索条件に該当するデータは必ず存在します。


※引っ掛け問題もあるよ! :)


問題1
次に示すSQL文の駆動表(外部表)はどれでしょうか? 

表と索引
create table a1 (
id number primary key
, data varchar2(1000)
) nologging
/
create table a2 (
id number primary key
, data varchar2(1000)
) nologging
/

統計情報
TABLE_NAME INDEX_NAME NUM_ROWS DISTINCT_KEYS CLUSTERING_FACTOR
-------------- -------------- ---------- ------------- -----------------
A1 SYS_C0010377 10000 10000 295
A2 SYS_C0010378 2000 2000 59

SELECT
/* SQL01 */
/*+
MONITOR
USE_NL(a1 a2)
*/
*
FROM
a1
INNER JOIN a2
ON
a1.id = a2.id
WHERE
a1.id BETWEEN 1 AND 100
/

問題2
次に示すSQL文の駆動表(外部表)はどれでしょうか? 
(表と索引、および、統計情報は問題1と同じです。)

SELECT
/* SQL02 */
/*+
MONITOR
USE_HASH(a1 a2)
*/
*
FROM
a1
INNER JOIN a2
ON
a1.id = a2.id
/

問題3
次に示すSQL文の駆動表(外部表)はどれでしょうか? 
なお、D1とD2の多重度は、D1:D2 = 1:100
(個人的にUMLの多重度表記のほうが好きなので、UML表記の多重度で記述します。

表と索引
create table d1 (
id number
, data varchar2(1000)
) nologging
/
alter table d1 add constraint pk_d1 primary key (id) using index nologging
/
create table d2 (
id number not null
, seq# number not null
, data varchar2(1000)
) nologging
/
alter table d2 add constraint pk_d2 primary key (id, seq#) using index nologging
/

統計情報
TABLE_NAME INDEX_NAME NUM_ROWS DISTINCT_KEYS CLUSTERING_FACTOR
-------------- -------------- ---------- ------------- -----------------
D1 PK_D1 200 200 9
D2 PK_D2 20000 20000 870

SELECT
/* SQL03 */
/*+
MONITOR
*/
*
FROM
d2
WHERE
EXISTS (
SELECT
1
FROM
d1
WHERE
d1.id = d2.id
AND d1.id IN (1,5)
)
/


問題4
次に示すSQL文の駆動表(外部表)はどれでしょうか? 
(表と索引、および、統計情報は問題3と同じです。)

SELECT
/* SQL04 */
/*+
MONITOR
*/
*
FROM
d1
INNER JOIN d2
ON
d1.id > d2.id
AND d2.id BETWEEN 1 AND 5
AND d2.seq# BETWEEN 2 AND 4
/


問題5
次に示すSQL文の駆動表(外部表)はどれでしょうか? 

表と索引定義
create table b1 (
id number
, data varchar2(1000)
) nologging
/
alter table b1 add constraint pk_b1 primary key(id) using index nologging
/
create table b3 (
id number
, seq# number
, data varchar2(1000)
) nologging
/
alter table b3 add constraint pk_b3 primary key (id, seq#) using index nologging
/
create table b2 (
id number
, seq# number
, subseq# number
, data varchar2(1000)
) nologging
/
alter table b2 add constraint pk_b2 primary key (id ,seq#, subseq#) using index nologging
/

統計情報
TABLE_NAME INDEX_NAME NUM_ROWS DISTINCT_KEYS CLUSTERING_FACTOR
-------------- -------------- ---------- ------------- -----------------
B1 PK_B1 20000 20000 870
B2 PK_B2 5000 5000 228
B3 PK_B3 500 500 23

ERDと多重度
b1 : b3 = 1 : 0..2
b3 : b2 = 1 : 0..10
(UML表記の多重度で記述しています。
20141221_120733

SELECT
/* SQL05 */
/*+
MONITOR
USE_HASH(b1 b3 b2)
*/
*
FROM
b1
INNER JOIN b3
ON
b1.id = b3.id
INNER JOIN b2
ON
b3.id = b2.id
AND b3.seq# = b2.seq#
/


問題6
次に示すSQL文の駆動表(外部表)はどれでしょうか? 
(表と索引、および、統計情報は問題5と同じです。)

SELECT
/* SQL06 */
/*+
MONITOR
USE_HASH(b1 b3 b2)
*/
*
FROM
b1
LEFT OUTER JOIN b3
ON
b1.id = b3.id
LEFT OUTER JOIN b2
ON
b3.id = b2.id
AND b3.seq# = b2.seq#
/


冬休みの宿題〜〜〜〜っ。暇つぶしにトライしてみてくださいね。(ニヤニヤ 

解答エントリーの公開は年明けを予定していま〜す。

We wish your merry christmas and a happy new year!

次の扉は、Tamie Yamamotoさんです。

| | コメント (0) | トラックバック (0)

2014年8月19日 (火)

pivotとSQL*PlusとSETコマンドと #2

昨日の続きですw

v$sys_time_modelの列データを行データへpivotで変換し、かつシェルで定期的に取得してみたものの、出力形式は今ひとつ。 iostatやvmstatのように出力したい。

前回のエントリーの出力結果は以下の通りでした。

[oracle ˜]$ ./sample.sh

SQL*Plus: Release 12.1.0.2.0 Production on 日 8月 17 21:10:47 2014

Copyright (c) 1982, 2014, Oracle. All rights reserved.


Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
With the Partitioning, OLAP, Advanced Analytics and Real Application Testing options
に接続されました。
21:10:48 SCOTT>
DB_TIME DB_CPU BG_TIME BG_CPU
------------------------------- ------------------------------- ------------------------------- -------------------------------
66172667 7714783 127809578 27114874

経過: 00:00:00.02
21:10:48 SCOTT>
DB_TIME DB_CPU BG_TIME BG_CPU
------------------------------- ------------------------------- ------------------------------- -------------------------------
66179178 7720782 128085853 27160868

経過: 00:00:00.01
21:10:57 SCOTT>
DB_TIME DB_CPU BG_TIME BG_CPU
------------------------------- ------------------------------- ------------------------------- -------------------------------
66180864 7723782 128249102 27197862

経過: 00:00:00.00

じゃまな出力は、以下の通り

  • SQL*Plusのプロンプト
  • ヘッダー行
  • 経過時間
  • 出力の状態から見て、余分な改行

そして、不足している出力はメトリックのログ取得時のタイムスタンプ


以下のSQL*Plusシステム変数を調整追加すればなんとかなりそうな感じ。。。。

  • SQL*Plusのプロンプトは、 set sqlp "" で抑止。
  • ヘッダー行は、 set head off で抑止
  • 経過時間は、 set timi off で抑止
  • 余分な改行は、 たぶん、 set newp none で抑止
  • 直接関係ないけど、Excelにコピペするときにじゃまになるので set tab offでタブの混入抑止


不足しているログ取得時のタイムスタンプは、シェルのdateコマンドで取得した日時をSQL文に埋め込むことでなんとかなりそうな気がします。

と頭に浮かんだら忘れないうちに試してみますよ〜

★横に長くてごめんなさい。時間取れたらSyntaxHighlighterとか入れます詐欺 m(_ _)m

#!/bin/bash
#
(
echo "conn scott/tiger"
echo "set timi off time off tab off sqlp \"\" head off newp none"
echo "col db_time for 999999999999999999999999999999"
echo "col db_cpu for 999999999999999999999999999999"
echo "col bg_time for 999999999999999999999999999999"
echo "col bg_cpu for 999999999999999999999999999999"
while [ 1 ]
do
echo "SELECT db_time,db_cpu,bg_time,bg_cpu FROM (SELECT stat_name,value FROM v\$sys_time_model) PIVOT (MAX(value) FOR stat_name IN ('DB time' AS db_time,'DB CPU' AS db_cpu,'background elapsed time' AS bg_time,'background cpu time' AS bg_cpu));"
sleep 10
done
) | sqlplus /nolog


ん〜〜〜〜〜、なんか、惜しい!!!!  いい感じにななったのに。... しばし考える。。。。

[oracle ˜]$ ./sample.sh

SQL*Plus: Release 12.1.0.2.0 Production on 日 8月 17 21:45:43 2014

Copyright (c) 1982, 2014, Oracle. All rights reserved.

21:45:43 > 接続されました。
21:45:44 SCOTT> 67126005 8476651 182158980 37408311

67129138 8479651 182310474 37445305


きた〜〜〜、神が降りてきたのでちょっと書き換えた

#!/bin/bash
#
(
echo "conn scott/tiger"
echo "set timi off time off sqlp \"\""
echo "col db_time for 999999999999999999999999999999"
echo "col db_cpu for 999999999999999999999999999999"
echo "col bg_time for 999999999999999999999999999999"
echo "col bg_cpu for 999999999999999999999999999999"
while [ 1 ]
do
t=`date +'%DT%T'`
echo "SELECT SUBSTR('${t}',1,INSTR('${t}','T')-1) as logged_date,SUBSTR('${t}',INSTR('${t}','T')+1) as logged_time,db_time,db_cpu,bg_time,bg_cpu FROM (SELECT stat_name,value FROM v\$sys_time_model) PIVOT (MAX(value) FOR stat_name IN ('DB time' AS db_time,'DB CPU' AS db_cpu,'background elapsed time' AS bg_time,'background cpu time' AS bg_cpu));"
echo "set head off newp none"
sleep 10
done
) | sqlplus /nolog


ん〜〜〜、まだ余計な改行というか空行がある。。。なんだこれ。。。。再び、考え中........ あ、あれだ! 出力行数を返すやつ!

SQL*Plus: Release 12.1.0.2.0 Production on 日 8月 17 21:56:21 2014

Copyright (c) 1982, 2014, Oracle. All rights reserved.

21:56:21 > 接続されました。
21:56:22 SCOTT>
LOGGED_D LOGGED_T DB_TIME DB_CPU BG_TIME BG_CPU
-------- -------- ------------------------------- ------------------------------- ------------------------------- -------------------------------
08/17/14 21:56:21 67688748 8937570 198751230 40650823

08/17/14 21:56:31 67695983 8945569 199012673 40699815


で、できたのがこれ。

#!/bin/bash
#
(
echo "conn scott/tiger"
echo "set timi off time off sqlp \"\" feed off"
echo "col db_time for 999999999999999999999999999999"
echo "col db_cpu for 999999999999999999999999999999"
echo "col bg_time for 999999999999999999999999999999"
echo "col bg_cpu for 999999999999999999999999999999"
while [ 1 ]
do
t=`date +'%DT%T'`
echo "SELECT SUBSTR('${t}',1,INSTR('${t}','T')-1) as logged_date,SUBSTR('${t}',INSTR('${t}','T')+1) as logged_time,db_time,db_cpu,bg_time,bg_cpu FROM (SELECT stat_name,value FROM v\$sys_time_model) PIVOT (MAX(value) FOR stat_name IN ('DB time' AS db_time,'DB CPU' AS db_cpu,'background elapsed time' AS bg_time,'background cpu time' AS bg_cpu));"
echo "set head off newp none"
sleep 10
done

) | sqlplus /nolog

出力結果は以下のようになり、 iostatやvmstat風にヘッダー行は一度だけ、その後、定期的に取得されるメトリックが出力されていくイメージに! :)

[oracle ˜]$ ./sample.sh

SQL*Plus: Release 12.1.0.2.0 Production on 日 8月 17 22:02:59 2014

Copyright (c) 1982, 2014, Oracle. All rights reserved.

22:02:59 > 接続されました。
22:02:59 SCOTT>
LOGGED_D LOGGED_T DB_TIME DB_CPU BG_TIME BG_CPU
-------- -------- ------------------------------- ------------------------------- ------------------------------- -------------------------------
08/17/14 22:02:59 68026469 9252517 211057107 42641516
08/17/14 22:03:09 68034074 9261515 211217278 42686507
08/17/14 22:03:19 68039874 9267514 211414489 42730501
08/17/14 22:03:29 68045221 9272514 211576132 42769498
08/17/14 22:03:39 68051396 9276513 211844118 42862480
^C
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
With the Partitioning, OLAP, Advanced Analytics and Real Application Testing optionsとの接続が切断されました。


Enjoy!


pivotとSQL*PlusとSETコマンドと

| | コメント (0) | トラックバック (0)

2014年8月18日 (月)

pivotとSQL*PlusとSETコマンドと

Oracle Databaseの性能試験で以下のようなメトリックを定期取得して、分析やビジュアライズに利用している方も多いと思います。(思ってます。)
でも、ですねぇ。
以下のv$sys_time_model動的パフォーマンスビューも典型例なのですが、列持ちのメトリックが多いので集計にはかなり苦労します。というか、してます。

v$sys_time_model動的パフォーマンスビューの出力例)

SCOTT> SELECT stat_name, value from v$sys_time_model;

STAT_NAME VALUE
---------------------------------------------------------------- ----------
DB time 64073868
DB CPU 6549986
background elapsed time 42988476
background cpu time 11080311

・・・以下略・・・

列持ちなんですよね、 列持ち!(しつこいw

行持ちにしたいですよね。 どう料理しましょう。 まさか、手作業ではやってないですよね。

SQL文でやってますよね! 私もそうです。
ちなみに、UNION連打はしてませんからね!(キリっ!

昔はほかに手がなかったのですが、Oracle11gから便利で比較的読みやすい構文がサポートされています。

列持ちを行持ちにするといえば....そうです、あれです。 pivot

ということで、

pivotを使って、v$sys_time_modelを例にオレオレv$sys_time_modelを作り出してみます。
(これができれば、数あるオラクルの動的パフォーマンスビューをもっと好きになれるんじゃないかなぁ。と思います。)

では、早速

v$sys_time_modelの stat_name列の列値が、'DB time'、'DB CPU'、 'background elapsed time'、'background cpu time'の4つのメトリックを列持ちから行持ちに変え、オレオレv$sys_time_model作り出すSQL文です。
ビューは作りませんけど (^^;;;

SELECT 
db_time
,db_cpu
,bg_time
,bg_cpu
FROM
(
SELECT
stat_name
,value
FROM
v$sys_time_model
)
PIVOT
(
MAX(value)
FOR stat_name IN
(
'DB time' AS db_time
,'DB CPU' AS db_cpu
,'background elapsed time' AS bg_time
,'background cpu time' AS bg_cpu
)
)
;


これを実行すると以下のような結果になります。本来4行なのですが、1行にできるんです! 便利ですね。 pivot (pivotの逆の操作をする unpivotもあります)

   DB_TIME     DB_CPU    BG_TIME     BG_CPU
---------- ---------- ---------- ----------
63895360 6364016 32965031 8718671

いい感じになってきました。


しかし、まだ物足りないですよね。 そう!
取得時のタイムスタンプとか、例えば iostat や vmstatのように定期的に取得したくなってきます!!!!

「門外不出のOracle現場ワザ」 第5章 DBアクセスの空白地帯 コネクションプーリングを極めるの定期的にSQLを発行するシェルを作成するには? でも解説されているのでこの方法で取得されている方も多いと思います。:)

ただ、そのまんまだと以下のような出力になってしまいます。 iostatやvmstatの出力をイメージしちゃうと余分な表示が多いわけです。

[oracle ˜]$ ./sample.sh

SQL*Plus: Release 12.1.0.2.0 Production on 日 8月 17 21:10:47 2014

Copyright (c) 1982, 2014, Oracle. All rights reserved.


Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
With the Partitioning, OLAP, Advanced Analytics and Real Application Testing options
に接続されました。
21:10:48 SCOTT>
DB_TIME DB_CPU BG_TIME BG_CPU
------------------------------- ------------------------------- ------------------------------- -------------------------------
66172667 7714783 127809578 27114874

経過: 00:00:00.02
21:10:48 SCOTT>
DB_TIME DB_CPU BG_TIME BG_CPU
------------------------------- ------------------------------- ------------------------------- -------------------------------
66179178 7720782 128085853 27160868

経過: 00:00:00.01
21:10:57 SCOTT>
DB_TIME DB_CPU BG_TIME BG_CPU
------------------------------- ------------------------------- ------------------------------- -------------------------------
66180864 7723782 128249102 27197862

経過: 00:00:00.00

前述の出力は以下のコードで取得したのですが、実はそんなに手を加えなくても vmstatやiostatのような出力形式で、みなさんの大好きなExcelで集計しやすくすることができるんですよ。
どこを変更すればよいか分かった人、手を挙げて〜〜〜〜っ!

注)scottユーザにselect any dictionaryシステム権限付けてます。

#!/bin/bash
#
(
echo "conn scott/tiger"
echo "set timi on time on"
echo "col db_time for 999999999999999999999999999999"
echo "col db_cpu for 999999999999999999999999999999"
echo "col bg_time for 999999999999999999999999999999"
echo "col bg_cpu for 999999999999999999999999999999"
while [ 1 ]
do
echo "SELECT db_time,db_cpu,bg_time,bg_cpu FROM (SELECT stat_name,value FROM v\$sys_time_model) PIVOT (MAX(value) FOR stat_name IN ('DB time' AS db_time,'DB CPU' AS db_cpu,'background elapsed time' AS bg_time,'background cpu time' AS bg_cpu));"
sleep 10
done
) | sqlplus /nolog


つづきは、次のエントリーで :)

| | コメント (0) | トラックバック (0)

2014年7月26日 (土)

TABLE ACCESS BY INDEX ROWID BATCHED (Oracle Database 12c R1) ってなに! #3

つづきです。
バッファキャッシュにヒットした場合はどうかというと、物理I/Oは発生しないので、そのまんまの結果ですよね。(^^;;;

call     count       cpu    elapsed       disk      query    current        rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 195 0.11 0.18 0 6287 0 2907
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 197 0.11 0.18 0 6287 0 2907

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 110 (SCOTT)
Number of plan statistics captured: 1

Rows (1st) Rows (avg) Rows (max) Row Source Operation
---------- ---------- ---------- ---------------------------------------------------
2907 2907 2907 NESTED LOOPS (cr=6287 pr=0 pw=0 time=206651 us)
2907 2907 2907 NESTED LOOPS (cr=3380 pr=0 pw=0 time=116870 us cost=5362 size=1744812 card=2851)
2907 2907 2907 TABLE ACCESS BY INDEX ROWID BATCHED HIGH_CLUSTERING_FACTOR (cr=3108 pr=0 pw=0 time=42395 us cost=2861 size=872406 card=2851)
2907 2907 2907 INDEX RANGE SCAN PK_HIGH_CLUSTERING_FACTOR (cr=201 pr=0 pw=0 time=12292 us cost=8 size=0 card=2851)(object id 93727)
2907 2907 2907 INDEX UNIQUE SCAN PK_LOW_CLUSTERING_FACTOR (cr=272 pr=0 pw=0 time=36902 us cost=0 size=0 card=1)(object id 93725)
2907 2907 2907 TABLE ACCESS BY INDEX ROWID LOW_CLUSTERING_FACTOR (cr=2907 pr=0 pw=0 time=37280 us cost=1 size=306 card=1)


Rows Execution Plan
------- ---------------------------------------------------
0 SELECT STATEMENT MODE: ALL_ROWS
2907 NESTED LOOPS
2907 NESTED LOOPS
2907 TABLE ACCESS MODE: ANALYZED (BY INDEX ROWID BATCHED) OF 'HIGH_CLUSTERING_FACTOR' (TABLE)
2907 INDEX MODE: ANALYZED (RANGE SCAN) OF 'PK_HIGH_CLUSTERING_FACTOR' (INDEX (UNIQUE))
2907 INDEX MODE: ANALYZED (UNIQUE SCAN) OF 'PK_LOW_CLUSTERING_FACTOR' (INDEX (UNIQUE))
2907 TABLE ACCESS MODE: ANALYZED (BY INDEX ROWID) OF 'LOW_CLUSTERING_FACTOR' (TABLE)


Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 195 0.00 0.00
SQL*Net message from client 195 0.00 0.15
SQL*Net more data to client 193 0.00 0.01
********************************************************************************

12cで、optimizer_features_enable='11.2.0.1'にしてバッファキャッシュをクリアすれば、11gと同じ実行計画(TABLE ACCESS BY INDEX ROWID)になって、db file parallel readになるよね!

という確認もしておいた!

call     count       cpu    elapsed       disk      query    current        rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.42 1.59 0 0 0 0
Execute 1 0.00 0.01 0 0 0 0
Fetch 195 18.34 61.02 2450 6287 0 2907
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 197 18.77 62.63 2450 6287 0 2907

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 110 (SCOTT)
Number of plan statistics captured: 1

Rows (1st) Rows (avg) Rows (max) Row Source Operation
---------- ---------- ---------- ---------------------------------------------------
2907 2907 2907 NESTED LOOPS (cr=6287 pr=2450 pw=0 time=92881090 us)
2907 2907 2907 NESTED LOOPS (cr=3380 pr=2232 pw=0 time=59488238 us cost=5362 size=1744812 card=2851)
2907 2907 2907 TABLE ACCESS BY INDEX ROWID HIGH_CLUSTERING_FACTOR (cr=3108 pr=2221 pw=0 time=31142410 us cost=2861 size=872406 card=2851)
2907 2907 2907 INDEX RANGE SCAN PK_HIGH_CLUSTERING_FACTOR (cr=201 pr=7 pw=0 time=3493710 us cost=8 size=0 card=2851)(object id 93727)
2907 2907 2907 INDEX UNIQUE SCAN PK_LOW_CLUSTERING_FACTOR (cr=272 pr=11 pw=0 time=10377721 us cost=0 size=0 card=1)(object id 93725)
2907 2907 2907 TABLE ACCESS BY INDEX ROWID LOW_CLUSTERING_FACTOR (cr=2907 pr=218 pw=0 time=13380036 us cost=1 size=306 card=1)


Rows Execution Plan
------- ---------------------------------------------------
0 SELECT STATEMENT MODE: ALL_ROWS
2907 NESTED LOOPS
2907 NESTED LOOPS
2907 TABLE ACCESS MODE: ANALYZED (BY INDEX ROWID BATCHED) OF 'HIGH_CLUSTERING_FACTOR' (TABLE)
2907 INDEX MODE: ANALYZED (RANGE SCAN) OF 'PK_HIGH_CLUSTERING_FACTOR' (INDEX (UNIQUE))
2907 INDEX MODE: ANALYZED (UNIQUE SCAN) OF 'PK_LOW_CLUSTERING_FACTOR' (INDEX (UNIQUE))
2907 TABLE ACCESS MODE: ANALYZED (BY INDEX ROWID) OF 'LOW_CLUSTERING_FACTOR' (TABLE)


Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 195 0.00 0.21
Disk file operations I/O 1 0.00 0.00
db file sequential read 452 0.21 1.54
SQL*Net message from client 195 15.35 16.71
SQL*Net more data to client 193 0.00 0.47
db file parallel read 190 0.05 5.91
********************************************************************************


整形前SQLトレースより抜粋 (db file parallel readが索引で現れている

WAIT #140513423347200: nam='db file parallel read' ela= 16615 files=1 blocks=13 requests=13 obj#=93726 tim=6258017207

straceより抜粋...io_submit()、だよね〜。:)

io_submit(140513465466880, 13, ...中略... ) = 13


最後に、11g R2 11.2.0.1で同じことを確認(確認するまでもないんだけどね)

call     count       cpu    elapsed       disk      query    current        rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 169 0.58 6.88 2148 5439 0 2509
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 171 0.58 6.88 2148 5439 0 2509

Misses in library cache during parse: 0
Optimizer mode: ALL_ROWS
Parsing user id: 84 (SCOTT)

Rows Row Source Operation
------- ---------------------------------------------------
2509 NESTED LOOPS (cr=5439 pr=2148 pw=0 time=19292592 us)
2509 NESTED LOOPS (cr=2930 pr=1931 pw=0 time=24524478 us cost=5012 size=1530612 card=2501)
2509 TABLE ACCESS BY INDEX ROWID HIGH_CLUSTERING_FACTOR (cr=2683 pr=1922 pw=0 time=24392808 us cost=2510 size=765612 card=2502)
2509 INDEX RANGE SCAN PK_HIGH_CLUSTERING_FACTOR (cr=174 pr=5 pw=0 time=11616 us cost=8 size=0 card=2502)(object id 113222)
2509 INDEX UNIQUE SCAN PK_LOW_CLUSTERING_FACTOR (cr=247 pr=9 pw=0 time=0 us cost=0 size=0 card=1)(object id 113220)
2509 TABLE ACCESS BY INDEX ROWID LOW_CLUSTERING_FACTOR (cr=2509 pr=217 pw=0 time=0 us cost=1 size=306 card=1)


Rows Execution Plan
------- ---------------------------------------------------
0 SELECT STATEMENT MODE: ALL_ROWS
2509 NESTED LOOPS
2509 NESTED LOOPS
2509 TABLE ACCESS MODE: ANALYZED (BY INDEX ROWID) OF 'HIGH_CLUSTERING_FACTOR' (TABLE)
2509 INDEX MODE: ANALYZED (RANGE SCAN) OF 'PK_HIGH_CLUSTERING_FACTOR' (INDEX (UNIQUE))
2509 INDEX MODE: ANALYZED (UNIQUE SCAN) OF 'PK_LOW_CLUSTERING_FACTOR' (INDEX (UNIQUE))
2509 TABLE ACCESS MODE: ANALYZED (BY INDEX ROWID) OF 'LOW_CLUSTERING_FACTOR' (TABLE)


Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 169 0.00 0.00
SQL*Net message from client 169 24.57 25.54
Disk file operations I/O 1 0.00 0.00
db file parallel read 167 0.09 5.66
db file sequential read 357 0.01 0.92
SQL*Net more data to client 167 0.00 0.01
********************************************************************************

11gまでは、実行計画上には現れず実行時に内部的に行っていたI/O最適化の動きが、12cからは実行計画上でも確認できるようになったというのは分かり易くていい。
時間あったら、v$sesstatも載せるかも。 :)

さて、12c 12.1.0.2.0のダウンロードも終わったので、別の楽しみが増えましたよね、みなさん!

ダウンロード! しましたか〜〜〜〜〜っ! :)

みなさんの、12c ネタ祭りを楽しみにしております。 (^^


TABLE ACCESS BY INDEX ROWID BATCHED (Oracle Database 12c R1) ってなに! #1
TABLE ACCESS BY INDEX ROWID BATCHED (Oracle Database 12c R1) ってなに! #2

| | コメント (0) | トラックバック (0)

2014年7月25日 (金)

TABLE ACCESS BY INDEX ROWID BATCHED (Oracle Database 12c R1) ってなに! #2

Oracle Database 12c R1 12.1.0.2.0 EEが公開されましたが、12.1.0.1.0で試してあります。

注)バッファキャッシュをクリア後にSQL文を実行してあります。

SELECT
/*+
leading(t2 t1)
use_nl(t2 t1)
index(t2 pk_high_clustering_factor)
*/
t2.id
,t2.name
,t1.name
FROM
low_clustering_factor t1
INNER JOIN high_clustering_factor t2
ON
t1.id = t2.id
WHERE
t2.id BETWEEN 30001 AND 35000

上記のSQL文でTABLE ACCESS BY INDEX ROWID BATCHED操作を行わせてみた。

以下、SQLトレースの結果です。予想通り db file parallel readが発生しています。
高いクラスタリングファクタを持つ索引をアクセス、不連続なROWDをかき集めかき集めたROWIDをある程度まとめて1度のI/Oリクエストでバッファキャッシュへ読み込む動作ですよね!

call     count       cpu    elapsed       disk      query    current        rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.01 0.07 6 6 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 195 0.48 4.92 2288 6338 0 2907
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 197 0.50 4.99 2294 6344 0 2907

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 110 (SCOTT)
Number of plan statistics captured: 1

Rows (1st) Rows (avg) Rows (max) Row Source Operation
---------- ---------- ---------- ---------------------------------------------------
2907 2907 2907 NESTED LOOPS (cr=6338 pr=2288 pw=0 time=2150320 us)
2907 2907 2907 NESTED LOOPS (cr=3431 pr=2087 pw=0 time=6036546 us cost=3368 size=1159620 card=3514)
2907 2907 2907 TABLE ACCESS BY INDEX ROWID BATCHED HIGH_CLUSTERING_FACTOR (cr=3107 pr=2082 pw=0 time=5948676 us cost=1012 size=579810 card=3514)
2907 2907 2907 INDEX RANGE SCAN PK_HIGH_CLUSTERING_FACTOR (cr=201 pr=0 pw=0 time=15435 us cost=11 size=0 card=3514)(object id 93727)
2907 2907 2907 INDEX UNIQUE SCAN PK_LOW_CLUSTERING_FACTOR (cr=324 pr=5 pw=0 time=56981 us cost=0 size=0 card=1)(object id 93725)
2907 2907 2907 TABLE ACCESS BY INDEX ROWID LOW_CLUSTERING_FACTOR (cr=2907 pr=201 pw=0 time=312848 us cost=1 size=165 card=1)


Rows Execution Plan
------- ---------------------------------------------------
0 SELECT STATEMENT MODE: ALL_ROWS
2907 NESTED LOOPS
2907 NESTED LOOPS
2907 TABLE ACCESS (BY INDEX ROWID BATCHED) OF 'HIGH_CLUSTERING_FACTOR' (TABLE)
2907 INDEX (RANGE SCAN) OF 'PK_HIGH_CLUSTERING_FACTOR' (INDEX (UNIQUE))
2907 INDEX (UNIQUE SCAN) OF 'PK_LOW_CLUSTERING_FACTOR' (INDEX (UNIQUE))
2907 TABLE ACCESS (BY INDEX ROWID) OF 'LOW_CLUSTERING_FACTOR' (TABLE)


Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 195 0.00 0.00
db file sequential read 209 0.03 0.32
SQL*Net message from client 195 19.95 21.21
db file parallel read 194 0.09 4.33
SQL*Net more data to client 193 0.00 0.01
********************************************************************************

SQLトレース(整形前)のトレースログより : db file parallel read (この時は、最小 3blocks〜15blocksの範囲で行われていた。

WAIT #139657829206792: nam='db file parallel read' ela= 64710 files=1 blocks=12 requests=12 obj#=93726 tim=4037078687

ちなみにstraceでみるとio_submitが呼び出されていて11gの頃と変わりはなかった :)

つまり、実行計画上、db file parallel readやるからね!! 物理I/Oを行うときは! という意思が明確に表示されるようになった。 
分かり易くなっていい!!!!
これだと物理I/Oがあれば、db file parallel readやってるな〜と実行計画を見ただけで想像できますな:)




TABLE ACCESS BY INDEX ROWID BATCHED (Oracle Database 12c R1) ってなに! #1

| | コメント (0) | トラックバック (0)

2014年7月23日 (水)

TABLE ACCESS BY INDEX ROWID BATCHED (Oracle Database 12c R1) ってなに! #1

Oracle® Database SQLチューニング・ガイド 12cリリース1(12.1) - ROWIDによる表アクセス: 例にも記載され、昨年試していたときにも現れていた、table access by index rowid batched という操作って、実際のところdb file parallel readをやるよ! と実行計画に表すようになったということだよね! それだけだよね。

ということを確認してみた。(TODOと書いてから1年経過してしまった orz )

詳細はあした以降に書きまする。 :)

20140723_220538

| | コメント (0) | トラックバック (0)

2013年7月 8日 (月)

rownum使って満足しちゃってると.....おまけのおまけ FETCH FIRST N ROWS ONLY編

20130628_233812


Oracle Database 12c R1 12.1.0.1.0 がリリースされたので、自分用のメモとしても使うネタから :)


実行環境は Oracle Database 12c R1 12.1.0.1.0 EE/Oracle Linux 6.4/VirtualBox for OS X 11g R2と同じデータをインポートし統計情報を再取得しています。


まず、rownum から!

おおおおお〜、 まあ想定通りではあるのですが、11gとは違う点が一つ。

TABLE ACCESS BY INDEX ROWID BATCHEDと見慣れないオペレーションが...興味深いですね。これ :)

別途調べるTODO :)

23:14:41 SCOTT@pdborcl> r
1 select /*+ leading(tc) use_nl(tc tb ta) */ *
2 from
3 tc join tb
4 on
5 tc.version = tb.version
6 and tc.branch# = tb.branch#
7 join ta
8 on
9 tb.version = ta.version
10 where
11 tc.version between 1 and 10
12* and rownum <= 1000

1000行が選択されました。

経過: 00:00:00.13

実行計画
----------------------------------------------------------
Plan hash value: 1463769645

-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000 | 2952K| 4813 (1)| 00:00:01 |
|* 1 | COUNT STOPKEY | | | | | |
| 2 | NESTED LOOPS | | | | | |
| 3 | NESTED LOOPS | | 2002 | 5910K| 4813 (1)| 00:00:01 |
| 4 | NESTED LOOPS | | 4004 | 7894K| 2810 (1)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID BATCHED| TC | 4004 | 3953K| 585 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | PK_TC | | | 14 (0)| 00:00:01 |
| 7 | TABLE ACCESS BY INDEX ROWID | TB | 1 | 1008 | 1 (0)| 00:00:01 |
|* 8 | INDEX UNIQUE SCAN | PK_TB | 1 | | 0 (0)| 00:00:01 |
|* 9 | INDEX UNIQUE SCAN | PK_TA | 1 | | 0 (0)| 00:00:01 |
| 10 | TABLE ACCESS BY INDEX ROWID | TA | 1 | 1004 | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter(ROWNUM<=1000)
6 - access("TC"."VERSION">=1 AND "TC"."VERSION"<=10)
8 - access("TC"."VERSION"="TB"."VERSION" AND "TC"."BRANCH#"="TB"."BRANCH#")
filter("TB"."VERSION"<=10 AND "TB"."VERSION">=1)
9 - access("TB"."VERSION"="TA"."VERSION")
filter("TA"."VERSION">=1 AND "TA"."VERSION"<=10)


統計
----------------------------------------------------------
0 recursive calls
0 db block gets
2418 consistent gets
0 physical reads
0 redo size
1059870 bytes sent via SQL*Net to client
1270 bytes received via SQL*Net from client
68 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1000 rows processed


次に、Oracle Database 12c R1 12.1.0.1.0 から実装された FETCH FIRST N ROWS ONLYとの比較

FETCH FIRST N ROWS ONLYを利用した場合TCを全表走査してしまったので、rownum利用時と同じオブジェクト参照させるためヒントでPK_TC索引を利用するようチューニングしてあります
ただ..なぜか、TABLE ACCESS BY INDEX ROWID BATCHEDではなくTABLE ACCESS BY INDEX ROWIDとなっています。


さらに不思議なことに、COSTが異常に跳ね上がっています。Rowsの見積もり件数で大きな差があることが影響しているように見えるんだが、なんでこんなに違うんだろう!
いろいろ試してみると、不思議なことがおきそうな気配w


Buffer Getsはほぼ同じなので、全表走査になりやすい?場合、索引が利用できそうなら使わせたほうがいいですな。多分。

23:12:22 SCOTT@pdborcl> r
1 select /*+ leading(tc) use_nl(tc tb ta) index(tc pk_tc) */ *
2 from
3 tc join tb
4 on
5 tc.version = tb.version
6 and tc.branch# = tb.branch#
7 join ta
8 on
9 tb.version = ta.version
10 where
11 tc.version between 1 and 10
12* fetch first 1000 rows only

1000行が選択されました。

経過: 00:00:00.13

実行計画
----------------------------------------------------------
Plan hash value: 3242622317

------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 300K| 456M| 1141K (1)| 00:00:45 |
|* 1 | VIEW | | 300K| 456M| 1141K (1)| 00:00:45 |
|* 2 | WINDOW NOSORT STOPKEY | | 300K| 864M| 1141K (1)| 00:00:45 |
| 3 | NESTED LOOPS | | | | | |
| 4 | NESTED LOOPS | | 300K| 864M| 1141K (1)| 00:00:45 |
| 5 | NESTED LOOPS | | 600K| 1155M| 841K (1)| 00:00:33 |
| 6 | TABLE ACCESS BY INDEX ROWID| TC | 1200K| 1156M| 174K (1)| 00:00:07 |
|* 7 | INDEX RANGE SCAN | PK_TC | 1200K| | 3278 (1)| 00:00:01 |
| 8 | TABLE ACCESS BY INDEX ROWID| TB | 1 | 1008 | 1 (0)| 00:00:01 |
|* 9 | INDEX UNIQUE SCAN | PK_TB | 1 | | 0 (0)| 00:00:01 |
|* 10 | INDEX UNIQUE SCAN | PK_TA | 1 | | 0 (0)| 00:00:01 |
| 11 | TABLE ACCESS BY INDEX ROWID | TA | 1 | 1004 | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter("from$_subquery$_006"."rowlimit_$$_rownumber"<=1000)
2 - filter(ROW_NUMBER() OVER ( ORDER BY NULL )<=1000)
7 - access("TC"."VERSION">=1 AND "TC"."VERSION"<=10)
9 - access("TC"."VERSION"="TB"."VERSION" AND "TC"."BRANCH#"="TB"."BRANCH#")
filter("TB"."VERSION"<=10 AND "TB"."VERSION">=1)
10 - access("TB"."VERSION"="TA"."VERSION")
filter("TA"."VERSION">=1 AND "TA"."VERSION"<=10)


統計
----------------------------------------------------------
0 recursive calls
0 db block gets
2420 consistent gets
0 physical reads
0 redo size
1044954 bytes sent via SQL*Net to client
1270 bytes received via SQL*Net from client
68 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1000 rows processed

FETCH FISRSTを利用すると、rownumとは異なり、内部でROW_NUMBER() OVER( OVER BY NULL) <= N
のような変換が発生するんですね!!!! 


rownumの弱点はどうなったか確認してみます。

12c でも rownum は rownumですね! という確認から。

23:37:32 SCOTT@pdborcl> r
1 select /*+ leading(tc) use_nl(tc tb ta) */ *
2 from
3 tc join tb
4 on
5 tc.version = tb.version
6 and tc.branch# = tb.branch#
7 join ta
8 on
9 tb.version = ta.version
10 where
11 tc.version between 11 and 20
12* and rownum <= 1000

レコードが選択されませんでした。

経過: 00:00:08.21

実行計画
----------------------------------------------------------
Plan hash value: 1463769645

-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000 | 2952K| 37940 (1)| 00:00:02 |
|* 1 | COUNT STOPKEY | | | | | |
| 2 | NESTED LOOPS | | | | | |
| 3 | NESTED LOOPS | | 1101 | 3250K| 37940 (1)| 00:00:02 |
| 4 | NESTED LOOPS | | 22019 | 42M| 26928 (1)| 00:00:02 |
| 5 | TABLE ACCESS BY INDEX ROWID BATCHED| TC | 38230 | 36M| 5683 (1)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | PK_TC | | | 109 (0)| 00:00:01 |
| 7 | TABLE ACCESS BY INDEX ROWID | TB | 1 | 1008 | 1 (0)| 00:00:01 |
|* 8 | INDEX UNIQUE SCAN | PK_TB | 1 | | 0 (0)| 00:00:01 |
|* 9 | INDEX UNIQUE SCAN | PK_TA | 1 | | 0 (0)| 00:00:01 |
| 10 | TABLE ACCESS BY INDEX ROWID | TA | 1 | 1004 | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter(ROWNUM<=1000)
6 - access("TC"."VERSION">=11 AND "TC"."VERSION"<=20)
8 - access("TC"."VERSION"="TB"."VERSION" AND "TC"."BRANCH#"="TB"."BRANCH#")
filter("TB"."VERSION">=11 AND "TB"."VERSION"<=20)
9 - access("TB"."VERSION"="TA"."VERSION")
filter("TA"."VERSION">=11 AND "TA"."VERSION"<=20)


統計
----------------------------------------------------------
0 recursive calls
0 db block gets
1174942 consistent gets
176197 physical reads
0 redo size
913 bytes sent via SQL*Net to client
533 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed


FETCH FIRST N ROWS ONLYでも弱点は同じです!

23:36:02 SCOTT@pdborcl> r
1 select /*+ leading(tc) use_nl(tc tb ta) index(tc pk_tc) */ *
2 from
3 tc join tb
4 on
5 tc.version = tb.version
6 and tc.branch# = tb.branch#
7 join ta
8 on
9 tb.version = ta.version
10 where
11 tc.version between 11 and 20
12* fetch first 1000 rows only

レコードが選択されませんでした。

経過: 00:00:07.36

実行計画
----------------------------------------------------------
Plan hash value: 3242622317

------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 29999 | 45M| 1141K (1)| 00:00:45 |
|* 1 | VIEW | | 29999 | 45M| 1141K (1)| 00:00:45 |
|* 2 | WINDOW NOSORT STOPKEY | | 29999 | 86M| 1141K (1)| 00:00:45 |
| 3 | NESTED LOOPS | | | | | |
| 4 | NESTED LOOPS | | 29999 | 86M| 1141K (1)| 00:00:45 |
| 5 | NESTED LOOPS | | 599K| 1155M| 841K (1)| 00:00:33 |
| 6 | TABLE ACCESS BY INDEX ROWID| TC | 1200K| 1156M| 174K (1)| 00:00:07 |
|* 7 | INDEX RANGE SCAN | PK_TC | 1200K| | 3278 (1)| 00:00:01 |
| 8 | TABLE ACCESS BY INDEX ROWID| TB | 1 | 1008 | 1 (0)| 00:00:01 |
|* 9 | INDEX UNIQUE SCAN | PK_TB | 1 | | 0 (0)| 00:00:01 |
|* 10 | INDEX UNIQUE SCAN | PK_TA | 1 | | 0 (0)| 00:00:01 |
| 11 | TABLE ACCESS BY INDEX ROWID | TA | 1 | 1004 | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter("from$_subquery$_006"."rowlimit_$$_rownumber"<=1000)
2 - filter(ROW_NUMBER() OVER ( ORDER BY NULL )<=1000)
7 - access("TC"."VERSION">=11 AND "TC"."VERSION"<=20)
9 - access("TC"."VERSION"="TB"."VERSION" AND "TC"."BRANCH#"="TB"."BRANCH#")
filter("TB"."VERSION">=11 AND "TB"."VERSION"<=20)
10 - access("TB"."VERSION"="TA"."VERSION")
filter("TA"."VERSION">=11 AND "TA"."VERSION"<=20)


統計
----------------------------------------------------------
0 recursive calls
0 db block gets
1174942 consistent gets
175116 physical reads
0 redo size
913 bytes sent via SQL*Net to client
533 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed


最後に、ハッシュ結合にした場合のrownum vs fetch first n rows onlyの比較

NL結合にすべきか、ハッシュ結合にすべきかどうかは、悩むんですよ。状況に合わせて使い分ける必要があるのは、11gでも12cでも同じだと思います。理由は前回のエントリを参照のこと。

rownumでハッシュ結合にした場合、11g R2では黙っていても索引を利用していたのですが、12c R1では全表走査になる傾向が見られます。
11gの実行計画と同じオブジェクトを参照するようにヒントでチューニングしてありますが、やはりここでもTABLE ACCESS BY INDEX ROWID BATCHEDが行われていますね。

実に興味深い。

23:46:06 SCOTT@pdborcl> r
1 select /*+ leading(tc) use_hash(tc tb ta) index(ta pk_ta) */ *
2 from
3 tc join tb
4 on
5 tc.version = tb.version
6 and tc.branch# = tb.branch#
7 join ta
8 on
9 tb.version = ta.version
10 where
11 tc.version between 1 and 10
12* and rownum <= 1000

1000行が選択されました。

経過: 00:00:34.63

実行計画
----------------------------------------------------------
Plan hash value: 3316229909

--------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000 | 4923K| | 199K (1)| 00:00:08 |
|* 1 | COUNT STOPKEY | | | | | | |
|* 2 | HASH JOIN | | 2002 | 9857K| | 199K (1)| 00:00:08 |
| 3 | TABLE ACCESS BY INDEX ROWID BATCHED | TA | 10 | 10040 | | 4 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | PK_TA | 10 | | | 1 (0)| 00:00:01 |
|* 5 | HASH JOIN | | 4004 | 7894K| 1170M| 199K (1)| 00:00:08 |
|* 6 | TABLE ACCESS FULL | TC | 1200K| 1156M| | 139K (1)| 00:00:06 |
| 7 | TABLE ACCESS BY INDEX ROWID BATCHED| TB | 10001 | 9844K| | 1486 (0)| 00:00:01 |
|* 8 | INDEX RANGE SCAN | PK_TB | 10000 | | | 26 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter(ROWNUM<=1000)
2 - access("TB"."VERSION"="TA"."VERSION")
4 - access("TA"."VERSION">=1 AND "TA"."VERSION"<=10)
5 - access("TC"."VERSION"="TB"."VERSION" AND "TC"."BRANCH#"="TB"."BRANCH#")
6 - filter("TC"."VERSION"<=10 AND "TC"."VERSION">=1)
8 - access("TB"."VERSION">=1 AND "TB"."VERSION"<=10)


統計
----------------------------------------------------------
1258 recursive calls
0 db block gets
515770 consistent gets
516798 physical reads
0 redo size
1062679 bytes sent via SQL*Net to client
1270 bytes received via SQL*Net from client
68 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1000 rows processed

FETCH FIRST N ROWS ONLYを利用した場合

00:20:36 SCOTT@pdborcl> r
1 select /*+ leading(tc) use_hash(tc tb ta) index(ta pk_ta) index(tb pk_tb) */ *
2 from
3 tc join tb
4 on
5 tc.version = tb.version
6 and tc.branch# = tb.branch#
7 join ta
8 on
9 tb.version = ta.version
10 where
11 tc.version between 1 and 10
12* fetch first 1000 rows only

1000行が選択されました。

経過: 00:00:32.69

実行計画
----------------------------------------------------------
Plan hash value: 3430218324

-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 300K| 456M| | 199K (1)| 00:00:08 |
|* 1 | VIEW | | 300K| 456M| | 199K (1)| 00:00:08 |
|* 2 | WINDOW NOSORT STOPKEY | | 300K| 864M| | 199K (1)| 00:00:08 |
|* 3 | HASH JOIN | | 300K| 864M| | 199K (1)| 00:00:08 |
| 4 | TABLE ACCESS BY INDEX ROWID | TA | 10 | 10040 | | 4 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | PK_TA | 10 | | | 1 (0)| 00:00:01 |
|* 6 | HASH JOIN | | 600K| 1155M| 1170M| 199K (1)| 00:00:08 |
|* 7 | TABLE ACCESS FULL | TC | 1200K| 1156M| | 139K (1)| 00:00:06 |
| 8 | TABLE ACCESS BY INDEX ROWID| TB | 10001 | 9844K| | 1486 (0)| 00:00:01 |
|* 9 | INDEX RANGE SCAN | PK_TB | 10000 | | | 26 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter("from$_subquery$_006"."rowlimit_$$_rownumber"<=1000)
2 - filter(ROW_NUMBER() OVER ( ORDER BY NULL )<=1000)
3 - access("TB"."VERSION"="TA"."VERSION")
5 - access("TA"."VERSION">=1 AND "TA"."VERSION"<=10)
6 - access("TC"."VERSION"="TB"."VERSION" AND "TC"."BRANCH#"="TB"."BRANCH#")
7 - filter("TC"."VERSION"<=10 AND "TC"."VERSION">=1)
9 - access("TB"."VERSION">=1 AND "TB"."VERSION"<=10)


統計
----------------------------------------------------------
1456 recursive calls
0 db block gets
516181 consistent gets
514506 physical reads
0 redo size
1045990 bytes sent via SQL*Net to client
1270 bytes received via SQL*Net from client
68 SQL*Net roundtrips to/from client
75 sorts (memory)
0 sorts (disk)
1000 rows processed


Top N に関してはrownumでも fetch first N rows onlyでも弱点は弱点なのでrownumと同じ考え方でチューニングの方向を決めないと、ね。


rownum使って満足しちゃってると..... #1
rownum使って満足しちゃってると..... #2
rownum使って満足しちゃってると..... #3
rownum使って満足しちゃってると..... #4
rownum使って満足しちゃってると.....おまけ

| | コメント (0) | トラックバック (0)

2013年6月23日 (日)

rownum使って満足しちゃってると.....おまけ

rownumを使っているチューニングしました! (キリっ

と、しているのに以下の例ようにハッシュ結合が行われ残念な結果になっていることってありませんか? (私はよく目にします (@@)

rownumの弱点を説明している実行計画をよーーーく見返してください。 ハッシュ結合にはしていません

なぜだか分かりますか?

11:14:44 SCOTT> l
1 select *
2 from
3 tc join tb
4 on
5 tc.version = tb.version
6 and tc.branch# = tb.branch#
7 join ta
8 on
9 tb.version = ta.version
10 where
11 tc.version between 1 and 10
12* and rownum <= 1000
11:14:44 SCOTT> /

1000行が選択されました。

経過: 00:00:59.08

実行計画
----------------------------------------------------------
Plan hash value: 2217783216

------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000 | 4923K| | 201K (1)| 00:40:14 |
|* 1 | COUNT STOPKEY | | | | | | |
|* 2 | HASH JOIN | | 1001 | 4928K| | 201K (1)| 00:40:14 |
| 3 | TABLE ACCESS BY INDEX ROWID | TA | 10 | 10040 | | 3 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | PK_TA | 10 | | | 1 (0)| 00:00:01 |
|* 5 | HASH JOIN | | 2002 | 3947K| 1161M| 201K (1)| 00:40:14 |
|* 6 | TABLE ACCESS FULL | TC | 1190K| 1148M| | 141K (1)| 00:28:20 |
| 7 | TABLE ACCESS BY INDEX ROWID| TB | 10001 | 9844K| | 1456 (0)| 00:00:18 |
|* 8 | INDEX RANGE SCAN | PK_TB | | | | 26 (0)| 00:00:01 |
------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter(ROWNUM<=1000)
2 - access("TB"."VERSION"="TA"."VERSION")
4 - access("TA"."VERSION">=1 AND "TA"."VERSION"<=10)
5 - access("TC"."VERSION"="TB"."VERSION" AND "TC"."BRANCH#"="TB"."BRANCH#")
6 - filter("TC"."VERSION"<=10 AND "TC"."VERSION">=1)
8 - access("TB"."VERSION">=1 AND "TB"."VERSION"<=10)


統計
----------------------------------------------------------
1 recursive calls
0 db block gets
514382 consistent gets
514341 physical reads
0 redo size
1050353 bytes sent via SQL*Net to client
1245 bytes received via SQL*Net from client
68 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1000 rows processed

ハッシュ結合などは典型的なのですが、操作を始めるとその操作が終了するまで次の操作に移行しないんですよ。

上記の例では赤い部分のハッシュ結合が終了するまでCOUNT STOPKEY操作(Id=1の部分)つまり、rownumの効果が発揮できないわけです。(残念!


結合がありrownumを使って行数制限している場合には、”大抵の場合" Nested Loop結合にしたほうがいいんですよ! OLTP系のようにレスポンスを重視する傾向が強い場合は... (弱点もありますが...

レスポンス重視かスループット重視か、オプティマイザが実行計画を組み立てる際に影響をパラメータがありますよね、  レースカーのフロントウイングやりやウィングのように立てたり寝かしたりするようなパラメータが.... :)

話がそれてしまいましたが...w

11:13:28 SCOTT> l
1 select /*+ leading(tc) use_nl(tc tb ta) */ *
2 from
3 tc join tb
4 on
5 tc.version = tb.version
6 and tc.branch# = tb.branch#
7 join ta
8 on
9 tb.version = ta.version
10 where
11 tc.version between 1 and 10
12* and rownum <= 1000
11:13:29 SCOTT> /

1000行が選択されました。

経過: 00:00:00.29

実行計画
----------------------------------------------------------
Plan hash value: 2389411418

-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000 | 2952K| 3798 (1)| 00:00:46 |
|* 1 | COUNT STOPKEY | | | | | |
| 2 | NESTED LOOPS | | | | | |
| 3 | NESTED LOOPS | | 1001 | 2955K| 3798 (1)| 00:00:46 |
| 4 | NESTED LOOPS | | 2002 | 3947K| 2796 (1)| 00:00:34 |
| 5 | TABLE ACCESS BY INDEX ROWID| TC | 4004 | 3953K| 571 (0)| 00:00:07 |
|* 6 | INDEX RANGE SCAN | PK_TC | | | 13 (0)| 00:00:01 |
| 7 | TABLE ACCESS BY INDEX ROWID| TB | 1 | 1008 | 1 (0)| 00:00:01 |
|* 8 | INDEX UNIQUE SCAN | PK_TB | 1 | | 0 (0)| 00:00:01 |
|* 9 | INDEX UNIQUE SCAN | PK_TA | 1 | | 0 (0)| 00:00:01 |
| 10 | TABLE ACCESS BY INDEX ROWID | TA | 1 | 1004 | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter(ROWNUM<=1000)
6 - access("TC"."VERSION">=1 AND "TC"."VERSION"<=10)
8 - access("TC"."VERSION"="TB"."VERSION" AND "TC"."BRANCH#"="TB"."BRANCH#")
filter("TB"."VERSION"<=10 AND "TB"."VERSION">=1)
9 - access("TB"."VERSION"="TA"."VERSION")
filter("TA"."VERSION">=1 AND "TA"."VERSION"<=10)


統計
----------------------------------------------------------
0 recursive calls
0 db block gets
2418 consistent gets
0 physical reads
0 redo size
1050353 bytes sent via SQL*Net to client
1245 bytes received via SQL*Net from client
68 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1000 rows processed

>結合がありrownumを使って行数制限している場合には、”大抵の場合" Nested Loop結合にしたほうがいいんですよ! OLTP系の場合は特に. (弱点もありますが...

と書きましたが、”大抵の場合”と書いたのには理由があります。

あえてハッシュ結合にする場合もあるんです。 え!、え!、え〜〜〜〜〜っ!w

”常に遅いけど、Nested Loop結合+rownumにした場合の弱点”は絶対避けたいような場合です。

Nested Loop結合+rownumにした場合の弱点の処理時間が、ハッシュ結合+rownumにした場合の処理時間を大幅に上回り、想定しているバッチ処理時間を超過してしまう可能性が高い場合がそれです。

状況に合わせて使い分けたいですよね。机上だけだと難しいのですが、実マシン、実データで検証を繰り返していれば対処できる問題だと思います。


rownum使って満足しちゃってると..... #1
rownum使って満足しちゃってると..... #2
rownum使って満足しちゃってると..... #3
rownum使って満足しちゃってると..... #4

| | コメント (0) | トラックバック (0)

2013年6月11日 (火)

rownum使って満足しちゃってると..... #4

つづきです。

前回の記事でも、まだ、ぼや〜〜〜〜んとしている方向け! :) #そうじゃない方も


以下は、リアルタムSQLモニタリングの結果ですが、rownumが効いたケース(上の結果)と効かなかったケース(下の結果)の違いは一目瞭然!
rownumの操作を示すCOUNT STOPKEYが上限の1000に満たない状態でId=4〜8のTC表とTB表の結合で100万回ループするNESTED LOOP結合が行われた後、Id=3,9でTA表と100万回ループするNESTED LOOP結合が行われているがよくわかります。


前々回rownumが狙い通りに効いたケースのSQLリアルタイムモニタリング結果

=======================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | (%) | (# samples) |
=======================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 18 | +0 | 1 | 1000 | | | | |
| 1 | COUNT STOPKEY | | | | 18 | +0 | 1 | 1000 | | | | |
| 2 | NESTED LOOPS | | | | 18 | +0 | 1 | 1000 | | | | |
| 3 | NESTED LOOPS | | 1001 | 3798 | 18 | +0 | 1 | 1000 | | | | |
| 4 | NESTED LOOPS | | 2002 | 2796 | 18 | +0 | 1 | 1000 | | | | |
| 5 | TABLE ACCESS BY INDEX ROWID | TC | 4004 | 571 | 18 | +0 | 1 | 1000 | 53 | 1MB | | |
| 6 | INDEX RANGE SCAN | PK_TC | | 13 | 18 | +0 | 1 | 1000 | 3 | 49152 | | |
| 7 | TABLE ACCESS BY INDEX ROWID | TB | 1 | 1 | 18 | +0 | 1000 | 1000 | 2 | 65536 | | |
| 8 | INDEX UNIQUE SCAN | PK_TB | 1 | | 18 | +0 | 1000 | 1000 | 2 | 40960 | | |
| 9 | INDEX UNIQUE SCAN | PK_TA | 1 | | 18 | +0 | 1000 | 1000 | 1 | 8192 | | |
| 10 | TABLE ACCESS BY INDEX ROWID | TA | 1 | 1 | 18 | +0 | 1000 | 1000 | 1 | 8192 | | |
=======================================================================================================================================================

前回rownumが狙い通りに効かなかったケースのSQLリアルタイムモニタリング結果

=========================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | (%) | (# samples) |
=========================================================================================================================================
| 0 | SELECT STATEMENT | | | | | | 1 | | | |
| 1 | COUNT STOPKEY | | | | | | 1 | | | |
| 2 | NESTED LOOPS | | | | | | 1 | | | |
| 3 | NESTED LOOPS | | 1000 | 37910 | 2 | +2 | 1 | 0 | | |
| 4 | NESTED LOOPS | | 20001 | 27906 | 2 | +2 | 1 | 1M | | |
| 5 | TABLE ACCESS BY INDEX ROWID | TC | 40003 | 5675 | 3 | +1 | 1 | 1M | 25.00 | Cpu (1) |
| 6 | INDEX RANGE SCAN | PK_TC | | 109 | 2 | +2 | 1 | 1M | 25.00 | Cpu (1) |
| 7 | TABLE ACCESS BY INDEX ROWID | TB | 1 | 1 | 3 | +2 | 1M | 1M | 25.00 | Cpu (1) |
| 8 | INDEX UNIQUE SCAN | PK_TB | 1 | | 2 | +2 | 1M | 1M | 25.00 | Cpu (1) |
| 9 | INDEX UNIQUE SCAN | PK_TA | 1 | | | | 1M | | | |
| 10 | TABLE ACCESS BY INDEX ROWID | TA | 1 | 1 | | | | | | |
=========================================================================================================================================

rownumって、取得する行数の制限なのであって、NESTED LOOP結合のループ回数を制限するものではないんですよね。

rownumを使っているからもう安心! (キリっ! 

と、言えない状況もあるということはお忘れなく!

現状、この弱点を回避する方法は無いのですから....


rownum使って満足しちゃってると..... #1
rownum使って満足しちゃってると..... #2
rownum使って満足しちゃってると..... #3

| | コメント (1) | トラックバック (0)

2013年6月10日 (月)

rownum使って満足しちゃってると..... #3

つづきです。

rownumの弱点分かりましたよね?

まだ、ぼや〜〜〜〜んとしていますか?


では、以下を見てください。

前々回rownumが狙い通りに効いたケースと、前回rownumが狙い通りに効かなかったケースの駆動表と1つ目の内部表の結合結果です。

どちらのケースでも同じ件数(buffer getsも同じです)がヒットしています。

駆動表TCと1つ目の内部表TBの結合結果は検索範囲は違いますが同じです。

前々回rownumが狙い通りに効いたケースの駆動表TCと1つ目の内部表TBの結合結果

01:45:02 SCOTT> l
1 select count(1)
2 from
3 tc join tb
4 on
5 tc.version = tb.version
6 and tc.branch# = tb.branch#
7 where
8* tc.version between 1 and 10
01:45:03 SCOTT> /

COUNT(1)
----------
1000000

前回rownumが狙い通りに効かなかったケースの駆動表TCと1つ目の内部表TBの結合結果

01:45:52 SCOTT> l
1 select count(1)
2 from
3 tc join tb
4 on
5 tc.version = tb.version
6 and tc.branch# = tb.branch#
7 where
8* tc.version between 11 and 20
01:45:52 SCOTT> /

COUNT(1)
----------
1000000

違いは、2つ目の内部表の結合でヒットするデータが1000件以上あったか、0件だったかの違いだけなんです。


もう少し分かりやすく説明すると、以下の赤字部分まではどちらも同じですが...

-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000 | 2952K| 37910 (1)| 00:07:35 |
|* 1 | COUNT STOPKEY | | | | | |
| 2 | NESTED LOOPS | | | | | |
| 3 | NESTED LOOPS | | 1000 | 2952K| 37910 (1)| 00:07:35 |
| 4 | NESTED LOOPS | | 20001 | 38M| 27906 (1)| 00:05:35 |
| 5 | TABLE ACCESS BY INDEX ROWID| TC | 40003 | 38M| 5675 (1)| 00:01:09 |
|* 6 | INDEX RANGE SCAN | PK_TC | | | 109 (0)| 00:00:02 |
| 7 | TABLE ACCESS BY INDEX ROWID| TB | 1 | 1008 | 1 (0)| 00:00:01 |
|* 8 | INDEX UNIQUE SCAN | PK_TB | 1 | | 0 (0)| 00:00:01 |
|* 9 | INDEX UNIQUE SCAN | PK_TA | 1 | | 0 (0)| 00:00:01 |
| 10 | TABLE ACCESS BY INDEX ROWID | TA | 1 | 1004 | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------

以下の赤字部分ID=3のNESTED LOOP部分でヒットするデータが1000件以上あるか、0件かが分かれ道だったんです。

-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000 | 2952K| 37910 (1)| 00:07:35 |
|* 1 | COUNT STOPKEY | | | | | |
| 2 | NESTED LOOPS | | | | | |
| 3 | NESTED LOOPS | | 1000 | 2952K| 37910 (1)| 00:07:35 |
| 4 | NESTED LOOPS | | 20001 | 38M| 27906 (1)| 00:05:35 |
| 5 | TABLE ACCESS BY INDEX ROWID| TC | 40003 | 38M| 5675 (1)| 00:01:09 |
|* 6 | INDEX RANGE SCAN | PK_TC | | | 109 (0)| 00:00:02 |
| 7 | TABLE ACCESS BY INDEX ROWID| TB | 1 | 1008 | 1 (0)| 00:00:01 |
|* 8 | INDEX UNIQUE SCAN | PK_TB | 1 | | 0 (0)| 00:00:01 |
|* 9 | INDEX UNIQUE SCAN | PK_TA | 1 | | 0 (0)| 00:00:01 |
| 10 | TABLE ACCESS BY INDEX ROWID | TA | 1 | 1004 | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------


え、まだ、ぼや〜〜〜〜んとしています? 

んんん〜〜〜〜〜っ

つづく。



rownum使って満足しちゃってると..... #1
rownum使って満足しちゃってると..... #2

| | コメント (0) | トラックバック (0)

2013年6月 8日 (土)

rownum使って満足しちゃってると..... #2

rownumの弱点に気付きました?

では、以下のクエリを見てください。前回の例と検索範囲は多少変えてありますがSQL文やヒントは同じです。実行計画も全く同じです。 

が、

Elapsed timeは、2670msになり、Buffer Getsは、1174942、と、すごいことになっています。

Physical readが発生していないのでこの程度の差ですが、Elapsed timeは、約10倍、Buffer Getsは、なんと、約490倍にもなってます!

しかも、rows processedは、0 です!


なんということでしょう! 0行なのに、Elapsed timeとBuffer Getsが異常に増加してる.....(ニヤニヤ


これがrownumの弱点!

01:43:17 SCOTT> l
1 select /*+ leading(tc) use_nl(tc tb ta) */ *
2 from
3 tc join tb
4 on
5 tc.version = tb.version
6 and tc.branch# = tb.branch#
7 join ta
8 on
9 tb.version = ta.version
10 where
11 tc.version between 11 and 20
12* and rownum <= 1000
01:43:17 SCOTT> /

レコードが選択されませんでした。

経過: 00:00:02.67

実行計画
----------------------------------------------------------
Plan hash value: 2389411418

-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000 | 2952K| 37910 (1)| 00:07:35 |
|* 1 | COUNT STOPKEY | | | | | |
| 2 | NESTED LOOPS | | | | | |
| 3 | NESTED LOOPS | | 1000 | 2952K| 37910 (1)| 00:07:35 |
| 4 | NESTED LOOPS | | 20001 | 38M| 27906 (1)| 00:05:35 |
| 5 | TABLE ACCESS BY INDEX ROWID| TC | 40003 | 38M| 5675 (1)| 00:01:09 |
|* 6 | INDEX RANGE SCAN | PK_TC | | | 109 (0)| 00:00:02 |
| 7 | TABLE ACCESS BY INDEX ROWID| TB | 1 | 1008 | 1 (0)| 00:00:01 |
|* 8 | INDEX UNIQUE SCAN | PK_TB | 1 | | 0 (0)| 00:00:01 |
|* 9 | INDEX UNIQUE SCAN | PK_TA | 1 | | 0 (0)| 00:00:01 |
| 10 | TABLE ACCESS BY INDEX ROWID | TA | 1 | 1004 | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter(ROWNUM<=1000)
6 - access("TC"."VERSION">=11 AND "TC"."VERSION"<=20)
8 - access("TC"."VERSION"="TB"."VERSION" AND "TC"."BRANCH#"="TB"."BRANCH#")
filter("TB"."VERSION">=11 AND "TB"."VERSION"<=20)
9 - access("TB"."VERSION"="TA"."VERSION")
filter("TA"."VERSION">=11 AND "TA"."VERSION"<=20)


統計
----------------------------------------------------------
0 recursive calls
0 db block gets
1174942 consistent gets
0 physical reads
0 redo size
905 bytes sent via SQL*Net to client
508 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed

01:43:21 SCOTT>

私が寝る時間欲しさに、データ量をケチったw のでこの程度の差しかありませんが、データ量が多ければもっと酷い結果となっていたことでしょう。


謎は解けましたよね!

 どうしたんだ、Hey , Hey, べいべ〜♪  
 データベースエンジンはビンビンだせ〜♪
 いつものように〜、きめて、1000行までで止ようぜ〜〜♪
.....
 こんな夜に〜♪、 rownumが効かないなんて〜♪
 こんな夜に〜♪、 遅延しちまうなんて〜♪

次回へつづく。



rownum使って満足しちゃってると..... #1

| | コメント (0) | トラックバック (0)

2013年6月 7日 (金)

rownum使って満足しちゃってると..... #1

まずは、以下のクエリを見てください。(適当に作りすぎたので、よい例ではないですが...)

220ms程度で、Buffer Getsが2418程度あります。numrowで制限している最大件数が戻されています。

データが大量にヒットする可能性があるようですね。

rownumで返す行数を制限し、ネステッドループ結合にしてチューニングするケースは多々あります。


00:44:53 SCOTT> set autot trace exp stat
01:42:37 SCOTT> l
1 select /*+ leading(tc) use_nl(tc tb ta) */ *
2 from
3 tc join tb
4 on
5 tc.version = tb.version
6 and tc.branch# = tb.branch#
7 join ta
8 on
9 tb.version = ta.version
10 where
11 tc.version between 1 and 10
12* and rownum <= 1000
01:42:37 SCOTT> /

1000行が選択されました。

経過: 00:00:00.22

実行計画
----------------------------------------------------------
Plan hash value: 2389411418

-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000 | 2952K| 3798 (1)| 00:00:46 |
|* 1 | COUNT STOPKEY | | | | | |
| 2 | NESTED LOOPS | | | | | |
| 3 | NESTED LOOPS | | 1001 | 2955K| 3798 (1)| 00:00:46 |
| 4 | NESTED LOOPS | | 2002 | 3947K| 2796 (1)| 00:00:34 |
| 5 | TABLE ACCESS BY INDEX ROWID| TC | 4004 | 3953K| 571 (0)| 00:00:07 |
|* 6 | INDEX RANGE SCAN | PK_TC | | | 13 (0)| 00:00:01 |
| 7 | TABLE ACCESS BY INDEX ROWID| TB | 1 | 1008 | 1 (0)| 00:00:01 |
|* 8 | INDEX UNIQUE SCAN | PK_TB | 1 | | 0 (0)| 00:00:01 |
|* 9 | INDEX UNIQUE SCAN | PK_TA | 1 | | 0 (0)| 00:00:01 |
| 10 | TABLE ACCESS BY INDEX ROWID | TA | 1 | 1004 | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter(ROWNUM<=1000)
6 - access("TC"."VERSION">=1 AND "TC"."VERSION"<=10)
8 - access("TC"."VERSION"="TB"."VERSION" AND "TC"."BRANCH#"="TB"."BRANCH#")
filter("TB"."VERSION"<=10 AND "TB"."VERSION">=1)
9 - access("TB"."VERSION"="TA"."VERSION")
filter("TA"."VERSION">=1 AND "TA"."VERSION"<=10)


統計
----------------------------------------------------------
0 recursive calls
0 db block gets
2418 consistent gets
0 physical reads
0 redo size
1050353 bytes sent via SQL*Net to client
1245 bytes received via SQL*Net from client
68 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1000 rows processed

01:42:39 SCOTT>

しかし、rownumも万能ではないんですよね。弱点があるんです。

どのような弱点なのか、気付きましたか?

ヒントは、 ID=1のOperationであるCOUNT STOPKEYとそれ以降にあるNESTED LOOPSの動き。


ということで、次回へつづく。

| | コメント (0) | トラックバック (0)

2013年4月28日 (日)

db file scattered read と db file parallel read と db file sequential read (最終回)

無理矢理引っ張った感じですが、db file scattered read と db file parallel read と db file sequential readの最終回。

今日は、db file parallel read に注目してみようと思います。


db file parallel readはどのような待機イベントだったか再確認しておきましょう!

マニュアルでは以下のように説明されています。

db file parallel read
リカバリ時のイベントです。
バッファ・プリフェッチ中に、最適化(複数のシングル・ブロック読取りの実行ではない)として発生する可能性もあります。
リカバリ時に変更が必要となったデータベース・ブロックはデータベースからパラレルに読み込まれます。

すべてのI/Oが完了するまでの時間が待機時間となる

少々わかりずらいですが、「バッファ・プリフェッチ中に、最適化(複数のシングル・ブロック読取りの実行ではない)として発生する可能性もあります。」ってところがポイントですよね。(リカバリしているわけではないので!)

でてきましたね、 prefetch という単語が!

斜め読みしちゃうと?となりそうですが、「複数のシングル・ブロック読取りの実行ではない」という箇所からもシングル・ブロック読み取りの繰り返しではなく、一括読み込み的なI/O最適化に関連した動き、だろうな〜ということは想像できます.
db file scattered read と db file parallel read と db file sequential read (その6)でも簡単に記載しているので参考に)

連続したブロックを一括読取りするのは、 db file scattered read
単一ブロックをブロック単位で読み取るのは、 db file sequential read
そして、不連続な複数ブロックを一括読取りするのは、db file parallel read


不連続ってところもポイントですね! 連続してないんですよ!

そこで、db file scattered read と db file parallel read と db file sequential read (その1)に書いた赤字部分が鍵になってきます!

TABLE_NAME                     INDEX_NAME                       NUM_ROWS DISTINCT_KEYS CLUSTERING_FACTOR
------------------------------ ------------------------------ ---------- ------------- -----------------
HIGH_CLUSTERING_FACTOR PK_HIGH_CLUSTERING_FACTOR 100000 100000 99978
LOW_CLUSTERING_FACTOR PK_LOW_CLUSTERING_FACTOR 100000 100000 4348

CLUSTERING_FACTORってNUM_ROWSに近ければ近いほど、索引のキー順に行を読んでしまうと読んだ行数ど同じ程度のデータブロックを読み込む必要があるということを示しています。
(マニュアルにも書いてますよね。Oracle® Databaseパフォーマンス・チューニング・ガイド11gリリース2 (11.2) - 11.2.3.1 ブロックのI/O(行ではなく)の想定

マニュアル、読みました?? 

ほんとに? 
(マニュアル読むの大切ですよ〜マニュアル読んで、試して、大きくなった、私が通りますよ〜〜〜と。)

では、細かい説明はもういいですよね! (^^;;;

で、
INDEX RANGE SCANとなっている場合、CLUSTERING_FACTORが低い場合と高い場合では、読み込みブロック数に差がある、低い場合は、少ないブロック読み込みで済むが、高い場合はより多くのブロックを読み込む必要があるということになります :)


効率的なI/Oを目指すOracleさんなので....CLUSTERING_FACTORが高く..

select
/*+
leading(t2 t1)
use_nl(t2 t1)
index(t2 pk_high_clustering_factor)
*/
t2.id
,t2.name
,t1.name
from
low_clustering_factor t1
inner join high_clustering_factor t2
on
t1.id = t2.id
where
t2.id between 30001 and 35000
/

こんな実行計画になっていて、かつ、physical reads prefetch warmupが発生していない状況で、バッファキャッシュヒット率が悪いと物理読み込みが発生して....

Rows     Row Source Operation
------- ---------------------------------------------------
2421 NESTED LOOPS (cr=5252 pr=2841 pw=0 time=2771706 us)
2421 NESTED LOOPS (cr=2831 pr=2522 pw=0 time=4204941 us cost=5012 size=1530612 card=2501)
2421 TABLE ACCESS BY INDEX ROWID HIGH_CLUSTERING_FACTOR (cr=2589 pr=2458 pw=0 time=4188001 us cost=2510 size=765612 card=2502)
2421 INDEX RANGE SCAN PK_HIGH_CLUSTERING_FACTOR (cr=168 pr=121 pw=0 time=7663 us cost=8 size=0 card=2502)(object id 82774)
2421 INDEX UNIQUE SCAN PK_LOW_CLUSTERING_FACTOR (cr=242 pr=64 pw=0 time=0 us cost=0 size=0 card=1)(object id 82772)
2421 TABLE ACCESS BY INDEX ROWID LOW_CLUSTERING_FACTOR (cr=2421 pr=319 pw=0 time=0 us cost=1 size=306 card=1)

db file sequential read以外に、db file parallel readが発生するんですよね。

Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 163 0.00 0.00
SQL*Net message from client 163 0.96 3.36
db file parallel read 161 0.01 0.63
SQL*Net more data to client 161 0.00 0.00
db file sequential read 14 0.00 0.00
********************************************************************************

INDEX RANGE SCANとなっている場合、CLUSTERING_FACTORが低い場合と高い場合では、読み込みブロック数に差がある、低い場合は、少ないブロック読み込みで済むが、高い場合はより多くのブロックを読み込む必要があるということになりますよね :)

と書きましたが、それを確認する簡単な実験を一つ。

バッファキャッシュヒット率が100%の状態でauto traceした結果です。
同一件数ヒットするクエリかつ、実行計画もINDEX RANGE SCANなのですが、
CLUSTERING_FACTORが低い表は、consistent getsが少なく、CLUSTERING_FACTORが高い表は、consistent getsが多くなるという差が発生しているのに気づきましたか?
(どちらの表も同じ定義の表なのですが、CLUSTERING_FACTORだけは変えてあります。)


注)CLUSTERING_FACTORが高い場合オプティマイザは索引を利用したアクセス効率が悪いと判断しTABLE FULL SCANを行う場合もあります。(以下の例ではヒントでINDEX RANGE SCANを強制しています)

select * from low_clustering_factor where id between 1000 and 3057;

2058行が選択されました。

経過: 00:00:00.09

実行計画
----------------------------------------------------------
Plan hash value: 2077556633

--------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2059 | 615K| 95 (0)| 00:00:02 |
| 1 | TABLE ACCESS BY INDEX ROWID| LOW_CLUSTERING_FACTOR | 2059 | 615K| 95 (0)| 00:00:02 |
|* 2 | INDEX RANGE SCAN | PK_LOW_CLUSTERING_FACTOR | 2059 | | 5 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - access("ID">=1000 AND "ID"<=3057)


統計
----------------------------------------------------------
0 recursive calls
0 db block gets
365 consistent gets
0 physical reads
0 redo size
666586 bytes sent via SQL*Net to client
1923 bytes received via SQL*Net from client
139 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
2058 rows processed

select /*+ INDEX_ASC(t1 pk_high_clustering_factor) */ * from high_clustering_factor t1 where id between 1000 and 5000;

2058行が選択されました。

経過: 00:00:00.10

実行計画
----------------------------------------------------------
Plan hash value: 3928373190

---------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2002 | 598K| 2009 (0)| 00:00:25 |
| 1 | TABLE ACCESS BY INDEX ROWID| HIGH_CLUSTERING_FACTOR | 2002 | 598K| 2009 (0)| 00:00:25 |
|* 2 | INDEX RANGE SCAN | PK_HIGH_CLUSTERING_FACTOR | 2002 | | 7 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - access("ID">=1000 AND "ID"<=5000)


統計
----------------------------------------------------------
0 recursive calls
0 db block gets
2201 consistent gets
0 physical reads
0 redo size
666587 bytes sent via SQL*Net to client
1923 bytes received via SQL*Net from client
139 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
2058 rows processed


各SQL文実行時に物理I/Oが発生しCLUSTERING_FACTORが高ければ、db file parallel readが発生することに...

※SQLトレース(tkprofで整形済み)

SQL ID: ap97zhtgsmt34
Plan Hash: 2077556633
select *
from
low_clustering_factor where id between 1000 and 3057


call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 139 0.03 0.07 96 365 0 2058
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 141 0.03 0.07 96 365 0 2058

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

Rows Row Source Operation
------- ---------------------------------------------------
2058 TABLE ACCESS BY INDEX ROWID LOW_CLUSTERING_FACTOR (cr=365 pr=96 pw=0 time=94236 us cost=95 size=630054 card=2059)
2058 INDEX RANGE SCAN PK_LOW_CLUSTERING_FACTOR (cr=143 pr=6 pw=0 time=3630 us cost=5 size=0 card=2059)(object id 82772)


Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 139 0.00 0.00
db file sequential read 96 0.00 0.05
SQL*Net message from client 139 0.02 1.09
********************************************************************************

SQL ID: fmynvkvbvrv49
Plan Hash: 3928373190
select /*+ INDEX_ASC(t1 pk_high_clustering_factor) */ *
from
high_clustering_factor t1 where id between 1000 and 5000


call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 139 0.28 1.85 1647 2201 0 2058
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 141 0.28 1.85 1647 2201 0 2058

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

Rows Row Source Operation
------- ---------------------------------------------------
2058 TABLE ACCESS BY INDEX ROWID HIGH_CLUSTERING_FACTOR (cr=2201 pr=1647 pw=0 time=1756678 us cost=2009 size=612612 card=2002)
2058 INDEX RANGE SCAN PK_HIGH_CLUSTERING_FACTOR (cr=144 pr=6 pw=0 time=8099 us cost=7 size=0 card=2002)(object id 82774)


Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 139 0.00 0.00
db file sequential read 1594 0.01 1.66
SQL*Net message from client 139 0.02 1.68
db file parallel read 6 0.00 0.00
********************************************************************************


あ〜〜〜すっきり! :)

みなさん、よいゴールデンウィークを!




バックナンバー

db file scattered read と db file parallel read と db file sequential read (その1)
db file scattered read と db file parallel read と db file sequential read (その2)
db file scattered read と db file parallel read と db file sequential read (その3)
db file scattered read と db file parallel read と db file sequential read (その4)
db file scattered read と db file parallel read と db file sequential read (その5)
db file scattered read と db file parallel read と db file sequential read (その6)
db file scattered read と db file parallel read と db file sequential read (その7)
db file scattered read と db file parallel read と db file sequential read (その8)

| | コメント (0) | トラックバック (0)

2013年4月26日 (金)

db file scattered read と db file parallel read と db file sequential read (その8)

>次回はphysical reads prefetch warmupを発生させます。(引っ張り過ぎw

の予告通り、physical reads prefetch warmupを発生させてみます!

やったことをほぼそのまま書いておきますね:)

オラクルさん、ディクショナリーやらなんやらいろいろ読んでしまって、小さめのバッファキャッシュだとそれらでお腹いっぱいになったりするんだよねw (よく考えられてる:)


23:29:24 SYS> startup
ORACLEインスタンスが起動しました。

Total System Global Area 313880576 bytes
Fixed Size 1336176 bytes
Variable Size 125832336 bytes
Database Buffers 180355072 bytes
Redo Buffers 6356992 bytes
データベースがマウントされました。
データベースがオープンされました。
23:29:47 SYS> select sid,serial# from v$session where username='SCOTT';

SID SERIAL#
---------- ----------
63 11


23:30:06 SYS> select name,value from v$sesstat vss join v$statname vsn on vss.statistic# = vsn.statistic#
where (name like '%prefetch%' or name like '%physical read%') and sid=63 order by name
/
NAME VALUE
---------------------------------------------------------------- ----------
index crx upgrade (prefetch) 0
physical read IO requests 89
physical read bytes 729088
physical read flash cache hits 0
physical read requests optimized 0
physical read total IO requests 89
physical read total bytes 729088
physical read total multi block requests 0
physical reads 89
physical reads cache 89
physical reads cache prefetch 0
physical reads direct 0
physical reads direct (lob) 0
physical reads direct temporary tablespace 0
physical reads for flashback new 0
physical reads prefetch warmup 0
physical reads retry corrupt 0
prefetch clients - 16k 0
prefetch clients - 2k 0
prefetch clients - 32k 0
prefetch clients - 4k 0
prefetch clients - 8k 0
prefetch clients - default 0
prefetch clients - keep 0
prefetch clients - recycle 0
prefetch warmup blocks aged out before use 0
prefetch warmup blocks flushed out before use 0
prefetched blocks aged out before use 0


※指定セッションのセッションレベルのSQLトレース開始!


23:30:27 SYS> exec dbms_monitor.session_trace_enable(63,11,true,false,null);

PL/SQLプロシージャが正常に完了しました。


....この間に、db file * read祭りになるクエリをSCOTTユーザで実行します!....


※指定セッションのセッションレベルSQLトレース終了!


23:32:21 SYS> exec dbms_monitor.session_trace_disable(63,11);

PL/SQLプロシージャが正常に完了しました。


:) db file scattered readが発生しています! 対象セッションのセッション統計をみて確認みると....

※SQLトレース結果(tkprofで整形済み)


select
/*+
leading(t2 t1)
use_nl(t2 t1)
index(t2 pk_high_clustering_factor)
*/
t2.id
,t2.name
,t1.name
from
low_clustering_factor t1
inner join high_clustering_factor t2
on
t1.id = t2.id
where
t2.id between 30001 and 35000

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.01 0.01 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 163 0.27 1.81 2364 5252 0 2421
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 165 0.28 1.82 2364 5252 0 2421

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

Rows Row Source Operation
------- ---------------------------------------------------
2421 NESTED LOOPS (cr=5252 pr=2364 pw=0 time=7618698 us)
2421 NESTED LOOPS (cr=2831 pr=2104 pw=0 time=8830835 us cost=5012 size=1530612 card=2501)
2421 TABLE ACCESS BY INDEX ROWID HIGH_CLUSTERING_FACTOR (cr=2589 pr=2070 pw=0 time=8807144 us cost=2510 size=765612 card=2502)
2421 INDEX RANGE SCAN PK_HIGH_CLUSTERING_FACTOR (cr=168 pr=34 pw=0 time=3764 us cost=8 size=0 card=2502)(object id 82774)
2421 INDEX UNIQUE SCAN PK_LOW_CLUSTERING_FACTOR (cr=242 pr=34 pw=0 time=0 us cost=0 size=0 card=1)(object id 82772)
2421 TABLE ACCESS BY INDEX ROWID LOW_CLUSTERING_FACTOR (cr=2421 pr=260 pw=0 time=0 us cost=1 size=306 card=1)


Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 163 0.00 0.00
Disk file operations I/O 1 0.00 0.00
db file sequential read 251 0.03 0.40
db file scattered read 37 0.02 0.23
SQL*Net message from client 163 0.02 1.71
SQL*Net more data to client 161 0.00 0.00
db file parallel read 160 0.08 1.07
********************************************************************************

select
/*+
leading(t1 t2)
use_nl(t1 t2)
index(t1 pk_low_clustering_factor)
*/
t1.id
,t1.name
,t2.name
from
low_clustering_factor t1
inner join high_clustering_factor t2
on
t1.id = t2.id
where
t1.id between 1 and 5000

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 173 0.24 0.77 1302 3367 0 2566
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 175 0.24 0.78 1302 3367 0 2566

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

Rows Row Source Operation
------- ---------------------------------------------------
2566 NESTED LOOPS (cr=3367 pr=1302 pw=0 time=913653 us)
2566 NESTED LOOPS (cr=801 pr=232 pw=0 time=324655 us cost=5231 size=1530000 card=2500)
5000 TABLE ACCESS BY INDEX ROWID LOW_CLUSTERING_FACTOR (cr=564 pr=226 pw=0 time=78984 us cost=229 size=1530000 card=5000)
5000 INDEX RANGE SCAN PK_LOW_CLUSTERING_FACTOR (cr=181 pr=8 pw=0 time=5896 us cost=11 size=0 card=5000)(object id 82772)
2566 INDEX UNIQUE SCAN PK_HIGH_CLUSTERING_FACTOR (cr=237 pr=6 pw=0 time=0 us cost=0 size=0 card=1)(object id 82774)
2566 TABLE ACCESS BY INDEX ROWID HIGH_CLUSTERING_FACTOR (cr=2566 pr=1070 pw=0 time=0 us cost=1 size=306 card=1)


Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 173 0.00 0.00
db file sequential read 1302 0.00 0.62
SQL*Net message from client 172 0.01 1.66
SQL*Net more data to client 171 0.00 0.00

よかったよかった、うまく physical reads prefetch warmupが増加しててw (^^


NAME                                                                  VALUE
---------------------------------------------------------------- ----------
index crx upgrade (prefetch) 0
physical read IO requests 3294
physical read bytes 30957568
physical read flash cache hits 0
physical read requests optimized 0
physical read total IO requests 3294
physical read total bytes 30957568
physical read total multi block requests 15
physical reads 3779
physical reads cache 3779
physical reads cache prefetch 1916
physical reads direct 0
physical reads direct (lob) 0
physical reads direct temporary tablespace 0
physical reads for flashback new 0
physical reads prefetch warmup 485
physical reads retry corrupt 0
prefetch clients - 16k 0
prefetch clients - 2k 0
prefetch clients - 32k 0
prefetch clients - 4k 0
prefetch clients - 8k 0
prefetch clients - default 0
prefetch clients - keep 0
prefetch clients - recycle 0
prefetch warmup blocks aged out before use 20
prefetch warmup blocks flushed out before use 0
prefetched blocks aged out before use 0


physical reads prefetch warmupってその名の通り、warmupなんですよね。バッファキャッシュの。

いつもより余計に読み込んでるからって驚かないでね!

おまけ。

起動直後には、SYS関連のオブジェクトがバッファキャッシュに読み込まれてしまって、physical read prefetch warmupが発生しないこともなんどかありました。

で、試しに、バッファキャッシュをクリアしてみたんですね...そしたら、warmupするじゃないですか!


23:15:00 SYS> startup
ORACLEインスタンスが起動しました。

Total System Global Area 313880576 bytes
Fixed Size 1336176 bytes
Variable Size 125832336 bytes
Database Buffers 180355072 bytes
Redo Buffers 6356992 bytes
データベースがマウントされました。
データベースがオープンされました。
23:15:18 SYS> select status,count(1) from v$bh group by status;

STATUS COUNT(1)
---------- ----------
xcur 5413
cr 165


※このとき既にバッファキャッシュはイッパイに! なにが乗っていたかというと..

SYS関連のオブジェクト...


OBJECT_NAME                    STATUS       COUNT(1)
------------------------------ ---------- ----------
ACCESS$ xcur 49
AQ$_MGMT_TASK_QTABLE_I xcur 2
AQ$_QTABLE_AFFINITIES_PK xcur 1
AQ$_QUEUES xcur 1
AQ$_QUEUES_CHECK xcur 1
AQ$_QUEUE_TABLES xcur 1
AQ$_QUEUE_TABLES_PRIMARY xcur 1
AQ$_QUEUE_TABLE_AFFINITIES xcur 2
AQ$_SCHEDULES xcur 1
AQ$_SUBSCRIBER_TABLE xcur 2
AQ$_SUBSCRIBER_TABLE_PRIMARY xcur 1
ARGUMENT$ xcur 11
ASSOC1 xcur 1
C_COBJ# xcur 45
C_FILE#_BLOCK# cr 4
C_FILE#_BLOCK# xcur 141
C_OBJ# xcur 159
C_OBJ#_INTCOL# xcur 319
C_TOID_VERSION# xcur 2821
C_TS# xcur 13
C_USER# xcur 6
DAM_CONFIG_PARAM$ xcur 2
DBMS_LOCK_ALLOCATED xcur 1
DEPENDENCY$ xcur 60
EDITION$ xcur 2
EMDW_TRACE_CONFIG_IDX_01 xcur 1

.... 以下略 ...


バッファキャッシュ、フラ〜〜〜ッシュ! ❤


23:15:35 SYS> alter system flush buffer_cache;

システムが変更されました。


※バッファキャッシュにはfreeができました。


23:15:46 SYS> select status,count(1) from v$bh group by status;

STATUS COUNT(1)
---------- ----------
xcur 2
free 5576


※クエリ実行前のセッション統計


NAME                                                                  VALUE
---------------------------------------------------------------- ----------
index crx upgrade (prefetch) 0
physical read IO requests 88
physical read bytes 720896
physical read flash cache hits 0
physical read requests optimized 0
physical read total IO requests 88
physical read total bytes 720896
physical read total multi block requests 0
physical reads 88
physical reads cache 88
physical reads cache prefetch 0
physical reads direct 0
physical reads direct (lob) 0
physical reads direct temporary tablespace 0
physical reads for flashback new 0
physical reads prefetch warmup 0
physical reads retry corrupt 0
prefetch clients - 16k 0
prefetch clients - 2k 0
prefetch clients - 32k 0
prefetch clients - 4k 0
prefetch clients - 8k 0
prefetch clients - default 0
prefetch clients - keep 0
prefetch clients - recycle 0
prefetch warmup blocks aged out before use 0
prefetch warmup blocks flushed out before use 0
prefetched blocks aged out before use 0


いつものクエリを実行.....

...中略...


※実行後、該当セッションのセッション統計を見てみると...


NAME                                                                  VALUE
---------------------------------------------------------------- ----------
index crx upgrade (prefetch) 0
physical read IO requests 3026
physical read bytes 32858112
physical read flash cache hits 0
physical read requests optimized 0
physical read total IO requests 3026
physical read total bytes 32858112
physical read total multi block requests 27
physical reads 4011
physical reads cache 4011
physical reads cache prefetch 2376
physical reads direct 0
physical reads direct (lob) 0
physical reads direct temporary tablespace 0
physical reads for flashback new 0
physical reads prefetch warmup 985
physical reads retry corrupt 0
prefetch clients - 16k 0
prefetch clients - 2k 0
prefetch clients - 32k 0
prefetch clients - 4k 0
prefetch clients - 8k 0
prefetch clients - default 0
prefetch clients - keep 0
prefetch clients - recycle 0
prefetch warmup blocks aged out before use 65
prefetch warmup blocks flushed out before use 0
prefetched blocks aged out before use 0

バッファキャッシュクリアしても出ることあるんだね。


これでおしまい。  あ、一つ思い出したので、次回にでも。

つづく。




バックナンバー

db file scattered read と db file parallel read と db file sequential read (その1)
db file scattered read と db file parallel read と db file sequential read (その2)
db file scattered read と db file parallel read と db file sequential read (その3)
db file scattered read と db file parallel read と db file sequential read (その4)
db file scattered read と db file parallel read と db file sequential read (その5)
db file scattered read と db file parallel read と db file sequential read (その6)
db file scattered read と db file parallel read と db file sequential read (その7)

| | コメント (0) | トラックバック (0)

2013年4月24日 (水)

db file scattered read と db file parallel read と db file sequential read (その7)

前回、物理読み込みが発生しない状況を作りdb file * read関連の物理読み込みが発生しない状況ではphysical reads prefetch warmup/cache prefetchが発生していないことを確認しました。

ということで、続きです。


physical reads prefetch warmupってどのような状況で増加するのか、待機イベントはどのようなものなのか確認してみることに..... (もうわかってるくせに〜w)

physical reads prefetch warmupは以下のように説明されています。
- バッファ・キャッシュの自動プリウォーム中にディスクから読み取られたデータ・ブロックの数 / class - 8 : cache

また、津島博士のパフォーマンス講座 第13回 キャッシュ周りについて でも説明されています。(この手の解説をマニュアルで読んだ記憶がないが、どこかに記載されてるのかな?....)

では、どのような待機イベントが発生するのか...
試してガッテン、じゃなくて、試して納得!でおなじみの、しばちょう先生のセッションの資料(ODDD2011)によると、db file scattered readが発生するようですね。


なるほど....どの程度かわかりませんが、バッファキャッシュにある程度データブロックが乗っかっているとphysical reads prefetch warmupって発生しないんだろうなぁ。というところまでは、なんとなく理解できました。:)


さっそく、physical reads prefetch warmupを発生させてみましょう!...

と、その前に、physical reads prefetch warmupは発生させず、physical reads cache prefetchだけを発生させてみますw

バッファキャッシュは本検証で実行するクエリでアクセスする表や索引以外のブロックで満たしてあります。

v$bhの状態


SYS> select status,count(1) from v$bh group by status;

STATUS COUNT(1)
---------- ----------
xcur 8903
cr 20

という状況(物理読み込みが発生しない状況)でdb file * read祭りになるクエリを実行してみることにします。

クエリを実行する前のセッション統計(prefetch関連のみ)は以下の通り。


22:04:51 SYS> l
1 select
2 name
3 ,value
4 from
5 v$sesstat vss
6 join v$statname vsn
7 on
8 vss.statistic# = vsn.statistic#
9 where
10 (
11 name like '%prefetch%'
12 or name like '%physical read%'
13 )
14 and sid=195
15 order by
16* name
22:04:52 SYS> /


NAME VALUE
---------------------------------------------------------------- ----------
index crx upgrade (prefetch) 0
physical read IO requests 479
physical read bytes 7012352
physical read flash cache hits 0
physical read requests optimized 0
physical read total IO requests 479
physical read total bytes 7012352
physical read total multi block requests 2
physical reads 856
physical reads cache 856
physical reads cache prefetch 377
physical reads direct 0
physical reads direct (lob) 0
physical reads direct temporary tablespace 0
physical reads for flashback new 0
physical reads prefetch warmup 0
physical reads retry corrupt 0
prefetch clients - 16k 0
prefetch clients - 2k 0
prefetch clients - 32k 0
prefetch clients - 4k 0
prefetch clients - 8k 0
prefetch clients - default 0
prefetch clients - keep 0
prefetch clients - recycle 0
prefetch warmup blocks aged out before use 10
prefetch warmup blocks flushed out before use 0
prefetched blocks aged out before use 0


※SQLトレース(tkprofにて整形済み)


select
/*+
leading(t2 t1)
use_nl(t2 t1)
index(t2 pk_high_clustering_factor)
*/
t2.id
,t2.name
,t1.name
from
low_clustering_factor t1
inner join high_clustering_factor t2
on
t1.id = t2.id
where
t2.id between 30001 and 35000

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 163 0.37 1.46 2084 5252 0 2421
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 165 0.38 1.47 2084 5252 0 2421

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

Rows Row Source Operation
------- ---------------------------------------------------
2421 NESTED LOOPS (cr=5252 pr=2084 pw=0 time=898088 us)
2421 NESTED LOOPS (cr=2831 pr=1866 pw=0 time=2127562 us cost=5012 size=1530612 card=2501)
2421 TABLE ACCESS BY INDEX ROWID HIGH_CLUSTERING_FACTOR (cr=2589 pr=1855 pw=0 time=2109985 us cost=2510 size=765612 card=2502)
2421 INDEX RANGE SCAN PK_HIGH_CLUSTERING_FACTOR (cr=168 pr=8 pw=0 time=14251 us cost=8 size=0 card=2502)(object id 82774)
2421 INDEX UNIQUE SCAN PK_LOW_CLUSTERING_FACTOR (cr=242 pr=11 pw=0 time=0 us cost=0 size=0 card=1)(object id 82772)
2421 TABLE ACCESS BY INDEX ROWID LOW_CLUSTERING_FACTOR (cr=2421 pr=218 pw=0 time=0 us cost=1 size=306 card=1)


Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 163 0.00 0.00
db file sequential read 624 0.01 0.84
SQL*Net message from client 163 0.02 2.22
SQL*Net more data to client 161 0.00 0.00
db file parallel read 141 0.02 0.42
********************************************************************************

select
/*+
leading(t1 t2)
use_nl(t1 t2)
index(t1 pk_low_clustering_factor)
*/
t1.id
,t1.name
,t2.name
from
low_clustering_factor t1
inner join high_clustering_factor t2
on
t1.id = t2.id
where
t1.id between 1 and 5000

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 173 0.31 0.85 1383 3367 0 2566
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 175 0.31 0.85 1383 3367 0 2566

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

Rows Row Source Operation
------- ---------------------------------------------------
2566 NESTED LOOPS (cr=3367 pr=1383 pw=0 time=1180156 us)
2566 NESTED LOOPS (cr=801 pr=233 pw=0 time=340045 us cost=5231 size=1530000 card=2500)
5000 TABLE ACCESS BY INDEX ROWID LOW_CLUSTERING_FACTOR (cr=564 pr=227 pw=0 time=98730 us cost=229 size=1530000 card=5000)
5000 INDEX RANGE SCAN PK_LOW_CLUSTERING_FACTOR (cr=181 pr=9 pw=0 time=7998 us cost=11 size=0 card=5000)(object id 82772)
2566 INDEX UNIQUE SCAN PK_HIGH_CLUSTERING_FACTOR (cr=237 pr=6 pw=0 time=0 us cost=0 size=0 card=1)(object id 82774)
2566 TABLE ACCESS BY INDEX ROWID HIGH_CLUSTERING_FACTOR (cr=2566 pr=1150 pw=0 time=0 us cost=1 size=306 card=1)


Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 173 0.00 0.00
db file sequential read 1383 0.01 0.66
SQL*Net message from client 172 0.02 2.24
SQL*Net more data to client 171 0.00 0.00

実行後のセッション統計を見てみると....おおおおおおおお!

physical reads cache prefetchは増加しているのでprefetchは発生していますが、physical reads prefetch warmupは発生していません。


NAME                                                                  VALUE
---------------------------------------------------------------- ----------
index crx upgrade (prefetch) 0
physical read IO requests 3963
physical read bytes 35553280
physical read flash cache hits 0
physical read requests optimized 0
physical read total IO requests 3963
physical read total bytes 35553280
physical read total multi block requests 2
physical reads 4340
physical reads cache 4340
physical reads cache prefetch 1696
physical reads direct 0
physical reads direct (lob) 0
physical reads direct temporary tablespace 0
physical reads for flashback new 0
physical reads prefetch warmup 0
physical reads retry corrupt 0
prefetch clients - 16k 0
prefetch clients - 2k 0
prefetch clients - 32k 0
prefetch clients - 4k 0
prefetch clients - 8k 0
prefetch clients - default 0
prefetch clients - keep 0
prefetch clients - recycle 0
prefetch warmup blocks aged out before use 10
prefetch warmup blocks flushed out before use 0
prefetched blocks aged out before use 0

physical reads prefetch warmupとphysical reads cache prefetchが同時に増加していた時に比べ、発生している待機イベントに違いがあることに気づきましたか? :) そう、db file scattered readが発生していないんですよ!

次回はphysical reads prefetch warmupを発生させます。(引っ張り過ぎw


ところで、prefetch warmup blocks aged out before useという統計値が増加していますよね。
キャッシュバッファを暖めただけで利用されずに捨てられたデータブロック数をカウントしているようです。(マニュアルに記載されていないので、想像ですが)

悲しいですね、使われずに捨てられるなんて。


バックナンバー

db file scattered read と db file parallel read と db file sequential read (その1)
db file scattered read と db file parallel read と db file sequential read (その2)
db file scattered read と db file parallel read と db file sequential read (その3)
db file scattered read と db file parallel read と db file sequential read (その4)
db file scattered read と db file parallel read と db file sequential read (その5)
db file scattered read と db file parallel read と db file sequential read (その6)

| | コメント (0) | トラックバック (0)

2013年4月14日 (日)

db file scattered read と db file parallel read と db file sequential read (その6)

続きです。

system callからも読み取れますが、各待機イベントでは
db file sequential read - 単一ブロック読み込み
db file parallel read - 不連続ブロックの複数ブロックの読み込み
db file scattered read - 連続したブロックの複数ブロック読み込み

が行われ、読み込まれたブロックはバッファキャッシュに乗せられるわけです。

注)
今回は発生しないようにしていますが、Oracle11g R2では実行計画上フルスキャンとなっていても、db file scattered readまたはdirect path read(バッファキャッシュを経由しない)になる場合があります。(smart flash cacheが無い場合)

db file scattered read vs direct path read関連の話題は以下のブログを参考に :)
10046 trace name context forever - Oracle OpenWorld 2012 Unconference presented by JPOUG
10046 trace name context forever - Smart Flash Cache 簡単なパフォーマンス比較
技術情報| Insight Techology, Inc. Part 7 - 11gR2からのフルスキャン
Ask Tom "Direct path reads 11gR2" - Thanks for the question regarding "Direct path reads 11gR2", version 11.2.0.1

direct path readが発生していないので :)
...キャッシュヒット率100%ならば発生しないってこと、ですよね。

試してみます!

バッファキャッシュには以下のSQL文でアクセスする表以外のオブジェクトも含め事前に乗せてあります。
以下SQL文では表、索引ともバッファキャッシュヒット率100%になるようにしてあります。

v$bhの状態(STATUS=freeがないので空きブロックの無い状態)

STATUS       COUNT(1)
---------- ----------
xcur 8904
cr 21

SQLトレースを見ると物理読み込みは発生していないこと、db file * read待機イベントが発生していないことが確認できます!

SQLトレース(tkprofにて整形後)

select
/*+
leading(t2 t1)
use_nl(t2 t1)
index(t2 pk_high_clustering_factor)
*/
t2.id
,t2.name
,t1.name
from
low_clustering_factor t1
inner join high_clustering_factor t2
on
t1.id = t2.id
where
t2.id between 30001 and 35000

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 163 0.06 0.07 0 5252 0 2421
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 165 0.06 0.07 0 5252 0 2421

Misses in library cache during parse: 0
Optimizer mode: ALL_ROWS
Parsing user id: 84

Rows Row Source Operation
------- ---------------------------------------------------
2421 NESTED LOOPS (cr=5252 pr=0 pw=0 time=63861 us)
2421 NESTED LOOPS (cr=2831 pr=0 pw=0 time=67887 us cost=5012 size=1530612 card=2501)
2421 TABLE ACCESS BY INDEX ROWID HIGH_CLUSTERING_FACTOR (cr=2589 pr=0 pw=0 time=51456 us cost=2510 size=765612 card=2502)
2421 INDEX RANGE SCAN PK_HIGH_CLUSTERING_FACTOR (cr=168 pr=0 pw=0 time=7260 us cost=8 size=0 card=2502)(object id 82774)
2421 INDEX UNIQUE SCAN PK_LOW_CLUSTERING_FACTOR (cr=242 pr=0 pw=0 time=0 us cost=0 size=0 card=1)(object id 82772)
2421 TABLE ACCESS BY INDEX ROWID LOW_CLUSTERING_FACTOR (cr=2421 pr=0 pw=0 time=0 us cost=1 size=306 card=1)


Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 163 0.00 0.00
SQL*Net message from client 163 0.02 2.51
SQL*Net more data to client 161 0.00 0.00
********************************************************************************

select
/*+
leading(t1 t2)
use_nl(t1 t2)
index(t1 pk_low_clustering_factor)
*/
t1.id
,t1.name
,t2.name
from
low_clustering_factor t1
inner join high_clustering_factor t2
on
t1.id = t2.id
where
t1.id between 1 and 5000

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 173 0.04 0.05 0 3367 0 2566
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 175 0.04 0.05 0 3367 0 2566

Misses in library cache during parse: 0
Optimizer mode: ALL_ROWS
Parsing user id: 84

Rows Row Source Operation
------- ---------------------------------------------------
2566 NESTED LOOPS (cr=3367 pr=0 pw=0 time=44887 us)
2566 NESTED LOOPS (cr=801 pr=0 pw=0 time=32245 us cost=5231 size=1530000 card=2500)
5000 TABLE ACCESS BY INDEX ROWID LOW_CLUSTERING_FACTOR (cr=564 pr=0 pw=0 time=17496 us cost=229 size=1530000 card=5000)
5000 INDEX RANGE SCAN PK_LOW_CLUSTERING_FACTOR (cr=181 pr=0 pw=0 time=3624 us cost=11 size=0 card=5000)(object id 82772)
2566 INDEX UNIQUE SCAN PK_HIGH_CLUSTERING_FACTOR (cr=237 pr=0 pw=0 time=0 us cost=0 size=0 card=1)(object id 82774)
2566 TABLE ACCESS BY INDEX ROWID HIGH_CLUSTERING_FACTOR (cr=2566 pr=0 pw=0 time=0 us cost=1 size=306 card=1)


Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 173 0.00 0.00
SQL*Net message from client 172 0.07 2.01
SQL*Net more data to client 171 0.00 0.00

db file * read祭りだった際には明らかに増加していた physical reads prefetch warmupやphysical reads cache prefetchも含め、
物理読み込みが発生しない状況であれば、それらの待機イベントも発生しないのは当然と言えば当然ですよね。 :)

SQL実行前のシステム統計(physical readとprefetch関連のみ)


---------------------------------------------------------------- ----------
index crx upgrade (prefetch) 0
physical read IO requests 2201
physical read bytes 18030592
physical read flash cache hits 0
physical read requests optimized 0
physical read total IO requests 2201
physical read total bytes 18030592
physical read total multi block requests 0
physical reads 2201
physical reads cache 2201
physical reads cache prefetch 741
physical reads direct 0
physical reads direct (lob) 0
physical reads direct temporary tablespace 0
physical reads for flashback new 0
physical reads prefetch warmup 0
physical reads retry corrupt 0
prefetch clients - 16k 0
prefetch clients - 2k 0
prefetch clients - 32k 0
prefetch clients - 4k 0
prefetch clients - 8k 0
prefetch clients - default 0
prefetch clients - keep 0
prefetch clients - recycle 0
prefetch warmup blocks aged out before use 0
prefetch warmup blocks flushed out before use 0
prefetched blocks aged out before use 0

SQL文実行後のシステム統計(physical readとprefetch関連のみ)

NAME                                                                  VALUE
---------------------------------------------------------------- ----------
index crx upgrade (prefetch) 0
physical read IO requests 2201
physical read bytes 18030592
physical read flash cache hits 0
physical read requests optimized 0
physical read total IO requests 2201
physical read total bytes 18030592
physical read total multi block requests 0
physical reads 2201
physical reads cache 2201
physical reads cache prefetch 741
physical reads direct 0
physical reads direct (lob) 0
physical reads direct temporary tablespace 0
physical reads for flashback new 0
physical reads prefetch warmup 0
physical reads retry corrupt 0
prefetch clients - 16k 0
prefetch clients - 2k 0
prefetch clients - 32k 0
prefetch clients - 4k 0
prefetch clients - 8k 0
prefetch clients - default 0
prefetch clients - keep 0
prefetch clients - recycle 0
prefetch warmup blocks aged out before use 0
prefetch warmup blocks flushed out before use 0
prefetched blocks aged out before use 0

物理読み込みが発生しない状況を作りdb file * read関連の物理読み込みが発生しない状況で、physical reads prefetch warmup/cache prefetchが発生していないことは確認しました。

次回はphysical reads prefetch warmupってどんな時に発生しやすいのか確認してみます。その際、発生する待機イベントってなんだろう? (もうわかってるでしょ、でしょ?)

つづく

”SSD”、”SSD”、ってプロジェクトでお金握ってる人の耳元で毎日つぶやいていれば、

いつの日か、「あ、そうだ! SSDがあるじゃまいか」と、自分から言うようになるんじゃないかなと思っているんだが、だれか実践してくれないかなw 


バックナンバー

db file scattered read と db file parallel read と db file sequential read (その1)
db file scattered read と db file parallel read と db file sequential read (その2)
db file scattered read と db file parallel read と db file sequential read (その3)
db file scattered read と db file parallel read と db file sequential read (その4)
db file scattered read と db file parallel read と db file sequential read (その5)

| | コメント (0) | トラックバック (0)

2013年4月13日 (土)

db file scattered read と db file parallel read と db file sequential read (その5)

続きです。

@yoheiaさんがprefetch的な動き...と言っていますね。気になる気になる単語です :)

こことか、
http://www.oracle.com/technetwork/jp/content/007-087711-ja.html

ここなどにもちょろっと書かれていいます。
TANEL PODER'S BLOG: Advanced Oracle Troubleshooting Guide – Part 10: Index unique scan doing multiblock reads?!

マニュアルで記載されている箇所ってあるんでしたっけ? (誰となく..

v$sesstatから該当セッションのセッション統計から"phsical read"または"prefetch"を含む統計値を見みてみると...

SQL文の実行前と実行後を比較するとどの統計値が増加したか一目瞭然です。赤字部分にちゅうもーく!

Oracle® Databaseリファレンス11g リリース2 (11.2)を見てみると、

physical reads cache prefetch
- 事前にフェッチされた連続および不連続ブロックの数 / class - 8 : cache


physical reads prefetch warmup
- バッファ・キャッシュの自動プリウォーム中にディスクから読み取られたデータ・ブロックの数 / class - 8 : cache

と記載されています。どちらもprefetchと関連がありそうですね:)

physical reads cache prefetchの説明にもありますが、連続及び不連続ブロックという部分、待機イベントにも現れていますよね。

連続ブロックを物理読み込みする待機イベントと言えば、db file scatterd readですし、
不連続ブロックを一括読み込みするといえば、 db file parallel readですよね。:)

※SQL文実行前

NAME                                                                  VALUE
---------------------------------------------------------------- ----------
index crx upgrade (prefetch) 0
physical read IO requests 48
physical read bytes 393216
physical read flash cache hits 0
physical read requests optimized 0
physical read total IO requests 48
physical read total bytes 393216
physical read total multi block requests 0
physical reads 48
physical reads cache 48
physical reads cache prefetch 0
physical reads direct 0
physical reads direct (lob) 0
physical reads direct temporary tablespace 0
physical reads for flashback new 0
physical reads prefetch warmup 0
physical reads retry corrupt 0
prefetch clients - 16k 0
prefetch clients - 2k 0
prefetch clients - 32k 0
prefetch clients - 4k 0
prefetch clients - 8k 0
prefetch clients - default 0
prefetch clients - keep 0
prefetch clients - recycle 0
prefetch warmup blocks aged out before use 0
prefetch warmup blocks flushed out before use 0
prefetched blocks aged out before use 0


※SQL文実行後

NAME                                                                  VALUE
---------------------------------------------------------------- ----------
index crx upgrade (prefetch) 0
physical read IO requests 4141
physical read bytes 42541056
physical read flash cache hits 0
physical read requests optimized 0
physical read total IO requests 4141
physical read total bytes 42541056
physical read total multi block requests 34
physical reads 5193
physical reads cache 5193
physical reads cache prefetch 4599
physical reads direct 0
physical reads direct (lob) 0
physical reads direct temporary tablespace 0
physical reads for flashback new 0
physical reads prefetch warmup 1052
physical reads retry corrupt 0
prefetch clients - 16k 0
prefetch clients - 2k 0
prefetch clients - 32k 0
prefetch clients - 4k 0
prefetch clients - 8k 0
prefetch clients - default 0
prefetch clients - keep 0
prefetch clients - recycle 0
prefetch warmup blocks aged out before use 0
prefetch warmup blocks flushed out before use 0
prefetched blocks aged out before use 0


ところで、prefetchもprefetch warmupも物理読み込みなのは分かりましたが、どのような状況だと発生するのでしょう? もしくは発生しやすいのでしょう?

そのヒントはその1に..

次回へつづく




バックナンバー

db file scattered read と db file parallel read と db file sequential read (その1)
db file scattered read と db file parallel read と db file sequential read (その2)
db file scattered read と db file parallel read と db file sequential read (その3)
db file scattered read と db file parallel read と db file sequential read (その4)

| | コメント (0) | トラックバック (0)

2013年4月 4日 (木)

db file scattered read と db file parallel read と db file sequential read (その3)

続きです。

リクエストにお答えして生SQLトレース(抜粋)を見てみましょう :)

最初に実行したSQL文のSQLトレースを見てみることにします。

select
/*+
leading(t2 t1)
use_nl(t2 t1)
index(t2 pk_high_clustering_factor)
*/
t2.id
,t2.name
,t1.name
from
low_clustering_factor t1
inner join high_clustering_factor t2
on
t1.id = t2.id
where
t2.id between 30001 and 35000
/

実行計画は以下の通り、index range/unique scan + nested loop joinが行われていました。

Rows     Row Source Operation
------- ---------------------------------------------------
2421 NESTED LOOPS (cr=5252 pr=2841 pw=0 time=2771706 us)
2421 NESTED LOOPS (cr=2831 pr=2522 pw=0 time=4204941 us cost=5012 size=1530612 card=2501)
2421 TABLE ACCESS BY INDEX ROWID HIGH_CLUSTERING_FACTOR (cr=2589 pr=2458 pw=0 time=4188001 us cost=2510 size=765612 card=2502)
2421 INDEX RANGE SCAN PK_HIGH_CLUSTERING_FACTOR (cr=168 pr=121 pw=0 time=7663 us cost=8 size=0 card=2502)(object id 82774)
2421 INDEX UNIQUE SCAN PK_LOW_CLUSTERING_FACTOR (cr=242 pr=64 pw=0 time=0 us cost=0 size=0 card=1)(object id 82772)
2421 TABLE ACCESS BY INDEX ROWID LOW_CLUSTERING_FACTOR (cr=2421 pr=319 pw=0 time=0 us cost=1 size=306 card=1)

待機イベント見てみると、index range/unique scan + table access by index rowidという実行計画からは想像できない待機イベントが....(実は狙って発生させてるくせに〜w

Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 163 0.00 0.00
db file scattered read 107 0.01 0.30
SQL*Net message from client 163 0.96 3.36
db file parallel read 161 0.01 0.63
SQL*Net more data to client 161 0.00 0.00
db file sequential read 14 0.00 0.00
********************************************************************************

(生)SQLトレース抜粋

...中略...
PARSE #4:c=1000,e=1534,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=1,plh=1087412721,tim=1364725159601671
EXEC #4:c=1000,e=97,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=1087412721,tim=1364725159601925
WAIT #4: nam='SQL*Net message to client' ela= 7 driver id=1650815232 #bytes=1 p3=0 obj#=82773 tim=1364725159602105 
WAIT #4: nam='db file scattered read' ela= 19499 file#=7 block#=371968 blocks=32 obj#=82774 tim=1364725159621743
WAIT #4: nam='db file scattered read' ela= 11156 file#=7 block#=373703 blocks=32 obj#=82774 tim=1364725159633042
WAIT #4: nam='db file scattered read' ela= 1699 file#=7 block#=375476 blocks=32 obj#=82773 tim=1364725159635032
WAIT #4: nam='db file scattered read' ela= 5889 file#=7 block#=367232 blocks=32 obj#=82772 tim=1364725159641300
WAIT #4: nam='db file scattered read' ela= 17223 file#=7 block#=368599 blocks=32 obj#=82771 tim=1364725159658667
FETCH #4:c=2999,e=56653,p=160,cr=6,cu=0,mis=0,r=1,dep=0,og=1,plh=1087412721,tim=1364725159658788
WAIT #4: nam='SQL*Net message from client' ela= 430 driver id=1650815232 #bytes=1 p3=0 obj#=82771 tim=1364725159659311 
WAIT #4: nam='db file parallel read' ela= 10307 files=1 blocks=14 requests=14 obj#=82773 tim=1364725159669764 
WAIT #4: nam='db file scattered read' ela= 1931 file#=7 block#=374755 blocks=29 obj#=82773 tim=1364725159671819 
WAIT #4: nam='SQL*Net message to client' ela= 3 driver id=1650815232 #bytes=1 p3=0 obj#=82773 tim=1364725159671902 
WAIT #4: nam='SQL*Net more data to client' ela= 40 driver id=1650815232 #bytes=8330 p3=0 obj#=82773 tim=1364725159672223
FETCH #4:c=2000,e=12910,p=43,cr=38,cu=0,mis=0,r=15,dep=0,og=1,plh=1087412721,tim=1364725159672272
WAIT #4: nam='SQL*Net message from client' ela= 9227 driver id=1650815232 #bytes=1 p3=0 obj#=82773 tim=1364725159681536 
WAIT #4: nam='db file parallel read' ela= 5766 files=1 blocks=12 requests=12 obj#=82773 tim=1364725159687404 
WAIT #4: nam='db file scattered read' ela= 1922 file#=7 block#=376416 blocks=32 obj#=82773 tim=1364725159689681
WAIT #4: nam='SQL*Net message to client' ela= 3 driver id=1650815232 #bytes=1 p3=0 obj#=82773 tim=1364725159689951 
WAIT #4: nam='SQL*Net more data to client' ela= 13 driver id=1650815232 #bytes=8278 p3=0 obj#=82773 tim=1364725159690473
FETCH #4:c=4000,e=8972,p=44,cr=32,cu=0,mis=0,r=15,dep=0,og=1,plh=1087412721,tim=1364725159690547
WAIT #4: nam='SQL*Net message from client' ela= 7980 driver id=1650815232 #bytes=1 p3=0 obj#=82773 tim=1364725159698617 
WAIT #4: nam='db file parallel read' ela= 2716 files=1 blocks=13 requests=13 obj#=82773 tim=1364725159701431 
WAIT #4: nam='db file scattered read' ela= 2179 file#=7 block#=374240 blocks=32 obj#=82773 tim=1364725159703739 
WAIT #4: nam='SQL*Net message to client' ela= 5 driver id=1650815232 #bytes=1 p3=0 obj#=82773 tim=1364725159704315 
WAIT #4: nam='SQL*Net more data to client' ela= 40 driver id=1650815232 #bytes=8278 p3=0 obj#=82773 tim=1364725159704541 
FETCH #4:c=3999,e=5937,p=45,cr=32,cu=0,mis=0,r=15,dep=0,og=1,plh=1087412721,tim=1364725159704589
WAIT #4: nam='SQL*Net message from client' ela= 11759 driver id=1650815232 #bytes=1 p3=0 obj#=82773 tim=1364725159716396 
WAIT #4: nam='db file parallel read' ela= 1999 files=1 blocks=13 requests=13 obj#=82773 tim=1364725159718604 
WAIT #4: nam='db file scattered read' ela= 1667 file#=7 block#=374066 blocks=32 obj#=82773 tim=1364725159720394 
WAIT #4: nam='SQL*Net message to client' ela= 3 driver id=1650815232 #bytes=1 p3=0 obj#=82773 tim=1364725159720497 
WAIT #4: nam='SQL*Net more data to client' ela= 43 driver id=1650815232 #bytes=8278 p3=0 obj#=82773 tim=1364725159720707 
FETCH #4:c=1000,e=4261,p=45,cr=32,cu=0,mis=0,r=15,dep=0,og=1,plh=1087412721,tim=1364725159720752
WAIT #4: nam='SQL*Net message from client' ela= 6700 driver id=1650815232 #bytes=1 p3=0 obj#=82773 tim=1364725159727511 
WAIT #4: nam='db file parallel read' ela= 5874 files=1 blocks=14 requests=14 obj#=82773 tim=1364725159733551 
WAIT #4: nam='db file sequential read' ela= 337 file#=7 block#=374065 blocks=1 obj#=82773 tim=1364725159733959 
WAIT #4: nam='SQL*Net message to client' ela= 4 driver id=1650815232 #bytes=1 p3=0 obj#=82773 tim=1364725159734041 
WAIT #4: nam='SQL*Net more data to client' ela= 77 driver id=1650815232 #bytes=8278 p3=0 obj#=82773 tim=1364725159734271 
...以下略...


db file * read待機イベントとアクセスブロック数及び参照オブジェクトとオブジェクト名を見やすくすると以下のようになります。
db_file_multiblock_read_count=32なのでscatterd readのblocksは理解できるのですが、parallel readのblocksってどこで制御してるんだ..わからんw..というか調べきれてない..

db file scattered read  blocks=32 (INDEX:PK_HIGH_CLUSTERING_FACTOR)
db file scattered read blocks=32 (INDEX:PK_HIGH_CLUSTERING_FACTOR)
db file scattered read blocks=32 (TABLE:HIGH_CLUSTERING_FACTOR)
db file scattered read blocks=32 (INDEX:PK_LOW_CLUSTERING_FACTOR)
db file scattered read blocks=32 (TABLE:LOW_CLUSTERING_FACTOR)
db file parallel read blocks=14 (TABLE:HIGH_CLUSTERING_FACTOR)
db file scattered read blocks=29 (TABLE:HIGH_CLUSTERING_FACTOR)
db file parallel read blocks=12 (TABLE:HIGH_CLUSTERING_FACTOR)
db file scattered read blocks=32 (TABLE:HIGH_CLUSTERING_FACTOR)
db file parallel read blocks=13 (TABLE:HIGH_CLUSTERING_FACTOR)
db file scattered read blocks=32 (TABLE:HIGH_CLUSTERING_FACTOR)
db file parallel read blocks=13 (TABLE:HIGH_CLUSTERING_FACTOR)
db file scattered read blocks=32 (TABLE:HIGH_CLUSTERING_FACTOR)
db file parallel read blocks=14 (TABLE:HIGH_CLUSTERING_FACTOR)
db file sequential read blocks=1 (TABLE:HIGH_CLUSTERING_FACTOR)

発生しているdb file * read待機イベントでアクセスしているオブジェクトは以下の通り。
実行計画上 index unique/range scan なのですが、索引でもscatterd readが発生することがあるんですよ〜っ。

db file parallel read
- obj# = 82773 - HIGH_CLUSTERING_FACTOR(表)

db file scattered read
- obj# = 82771 - LOW_CLUSTERING_FACTOR(表)
- obj# = 82772 - PK_LOW_CLUSTERING_FACTOR(索引)
- obj# = 82773 - HIGH_CLUSTERING_FACTOR(表)
- obj# = 82774 - PK_HIGH_CLUSTERING_FACTOR(索引)

db file sequential read
- obj# = 82771 - LOW_CLUSTERING_FACTOR(表)
- obj# = 82773 - HIGH_CLUSTERING_FACTOR(表)


2つ目のクエリは db file parallel readが発生しないだけで傾向は同じです。 :)

次回へつづく。


バックナンバー

db file scattered read と db file parallel read と db file sequential read (その1)
db file scattered read と db file parallel read と db file sequential read (その2)

| | コメント (0) | トラックバック (0)

2013年4月 1日 (月)

db file scattered read と db file parallel read と db file sequential read (その2)

答え分かりましたかねぇ?

環境情報など再現方法などは次回にしますが、どうなるかやってみましょう。(事前にSQLトレースを有効にしてあります。)


※各SQLはヒントで実行計画を固定してあります。
最初は参考資料3の実行計画になるクエリから実行します。

-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2501 | 1494K| 5012 (1)| 00:01:01 |
| 1 | NESTED LOOPS | | | | | |
| 2 | NESTED LOOPS | | 2501 | 1494K| 5012 (1)| 00:01:01 |
| 3 | TABLE ACCESS BY INDEX ROWID| HIGH_CLUSTERING_FACTOR | 2502 | 747K| 2510 (1)| 00:00:31 |
|* 4 | INDEX RANGE SCAN | PK_HIGH_CLUSTERING_FACTOR | 2502 | | 8 (0)| 00:00:01 |
|* 5 | INDEX UNIQUE SCAN | PK_LOW_CLUSTERING_FACTOR | 1 | | 0 (0)| 00:00:01 |
| 6 | TABLE ACCESS BY INDEX ROWID | LOW_CLUSTERING_FACTOR | 1 | 306 | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------

SELECT
/*+
leading(t2 t1)
use_nl(t2 t1)
index(t2 pk_high_clustering_factor)
*/
t2.id
,t2.name
,t1.name
FROM
low_clustering_factor t1
INNER JOIN high_clustering_factor t2
ON
t1.id = t2.id
WHERE
t2.id BETWEEN 30001 AND 35000
/

次は参考資料2の実行計画になるクエリを実行します。

-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2500 | 1494K| 5231 (1)| 00:01:03 |
| 1 | NESTED LOOPS | | | | | |
| 2 | NESTED LOOPS | | 2500 | 1494K| 5231 (1)| 00:01:03 |
| 3 | TABLE ACCESS BY INDEX ROWID| LOW_CLUSTERING_FACTOR | 5000 | 1494K| 229 (0)| 00:00:03 |
|* 4 | INDEX RANGE SCAN | PK_LOW_CLUSTERING_FACTOR | 5000 | | 11 (0)| 00:00:01 |
|* 5 | INDEX UNIQUE SCAN | PK_HIGH_CLUSTERING_FACTOR | 1 | | 0 (0)| 00:00:01 |
| 6 | TABLE ACCESS BY INDEX ROWID | HIGH_CLUSTERING_FACTOR | 1 | 306 | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------

SELECT
/*+
leading(t1 t2)
use_nl(t1 t2)
index(t1 pk_low_clustering_factor)
*/
t1.id
,t1.name
,t2.name
FROM
low_clustering_factor t1
INNER JOIN high_clustering_factor t2
ON
t1.id = t2.id
WHERE
t1.id BETWEEM 1 AND 5000
/

どのような待機イベントが発生しているかSQLトレース結果(tkprofで整形済み)を見てみると...

最初のクエリのトレース結果

call     count       cpu    elapsed       disk      query    current        rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 163 0.30 1.06 2841 5252 0 2421
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 165 0.30 1.06 2841 5252 0 2421

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

Rows Row Source Operation
------- ---------------------------------------------------
2421 NESTED LOOPS (cr=5252 pr=2841 pw=0 time=2771706 us)
2421 NESTED LOOPS (cr=2831 pr=2522 pw=0 time=4204941 us cost=5012 size=1530612 card=2501)
2421 TABLE ACCESS BY INDEX ROWID HIGH_CLUSTERING_FACTOR (cr=2589 pr=2458 pw=0 time=4188001 us cost=2510 size=765612 card=2502)
2421 INDEX RANGE SCAN PK_HIGH_CLUSTERING_FACTOR (cr=168 pr=121 pw=0 time=7663 us cost=8 size=0 card=2502)(object id 82774)
2421 INDEX UNIQUE SCAN PK_LOW_CLUSTERING_FACTOR (cr=242 pr=64 pw=0 time=0 us cost=0 size=0 card=1)(object id 82772)
2421 TABLE ACCESS BY INDEX ROWID LOW_CLUSTERING_FACTOR (cr=2421 pr=319 pw=0 time=0 us cost=1 size=306 card=1)


Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 163 0.00 0.00
db file scattered read 107 0.01 0.30
SQL*Net message from client 163 0.96 3.36
db file parallel read 161 0.01 0.63
SQL*Net more data to client 161 0.00 0.00
db file sequential read 14 0.00 0.00

2つ目のクエリのトレース結果

call     count       cpu    elapsed       disk      query    current        rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 173 0.28 0.72 1874 3367 0 2566
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 175 0.28 0.73 1874 3367 0 2566

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

Rows Row Source Operation
------- ---------------------------------------------------
2566 NESTED LOOPS (cr=3367 pr=1874 pw=0 time=802588 us)
2566 NESTED LOOPS (cr=801 pr=381 pw=0 time=115791 us cost=5231 size=1530000 card=2500)
5000 TABLE ACCESS BY INDEX ROWID LOW_CLUSTERING_FACTOR (cr=564 pr=286 pw=0 time=42241 us cost=229 size=1530000 card=5000)
5000 INDEX RANGE SCAN PK_LOW_CLUSTERING_FACTOR (cr=181 pr=32 pw=0 time=26744 us cost=11 size=0 card=5000)(object id 82772)
2566 INDEX UNIQUE SCAN PK_HIGH_CLUSTERING_FACTOR (cr=237 pr=95 pw=0 time=0 us cost=0 size=0 card=1)(object id 82774)
2566 TABLE ACCESS BY INDEX ROWID HIGH_CLUSTERING_FACTOR (cr=2566 pr=1493 pw=0 time=0 us cost=1 size=306 card=1)


Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 173 0.00 0.00
db file scattered read 381 0.01 0.42
db file sequential read 287 0.00 0.14
SQL*Net message from client 172 0.04 2.83
SQL*Net more data to client 171 0.00 0.00


ということで、

A) db file scattered read
B) db file parallel read
C) db file sequential read

A)、B)、C)すべての待機イベント発生しました!!! :)  

実に興味深い。。。。 実行計画見てても、分からないですよね、こればかりは。


次回へ続く。




バックナンバー
db file scattered read と db file parallel read と db file sequential read (その1)

| | コメント (0) | トラックバック (0)

2013年3月30日 (土)

db file scattered read と db file parallel read と db file sequential read (その1)

突然ですが、問題です!

・Oracle11g R2です。(隠しパラメータは変更していません)
・Oracleインスタンスは起動したばかりです。
・参考資料1)のような索引が、それぞれの表に作成されています。
・駆動表は入れ替わっていますが、参考資料2)と3)のような実行計画で2つのSQL文が実行されました。

発生する待機イベントはどれでしょうか?

A) db file scattered read
B) db file parallel read
C) db file sequential read

参考資料1)

TABLE_NAME                     INDEX_NAME                       NUM_ROWS DISTINCT_KEYS CLUSTERING_FACTOR
------------------------------ ------------------------------ ---------- ------------- -----------------
HIGH_CLUSTERING_FACTOR PK_HIGH_CLUSTERING_FACTOR 100000 100000 99978
LOW_CLUSTERING_FACTOR PK_LOW_CLUSTERING_FACTOR 100000 100000 4348

参考資料2)

-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2500 | 1494K| 5231 (1)| 00:01:03 |
| 1 | NESTED LOOPS | | | | | |
| 2 | NESTED LOOPS | | 2500 | 1494K| 5231 (1)| 00:01:03 |
| 3 | TABLE ACCESS BY INDEX ROWID| LOW_CLUSTERING_FACTOR | 5000 | 1494K| 229 (0)| 00:00:03 |
|* 4 | INDEX RANGE SCAN | PK_LOW_CLUSTERING_FACTOR | 5000 | | 11 (0)| 00:00:01 |
|* 5 | INDEX UNIQUE SCAN | PK_HIGH_CLUSTERING_FACTOR | 1 | | 0 (0)| 00:00:01 |
| 6 | TABLE ACCESS BY INDEX ROWID | HIGH_CLUSTERING_FACTOR | 1 | 306 | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------

参考資料3)

-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2501 | 1494K| 5012 (1)| 00:01:01 |
| 1 | NESTED LOOPS | | | | | |
| 2 | NESTED LOOPS | | 2501 | 1494K| 5012 (1)| 00:01:01 |
| 3 | TABLE ACCESS BY INDEX ROWID| HIGH_CLUSTERING_FACTOR | 2502 | 747K| 2510 (1)| 00:00:31 |
|* 4 | INDEX RANGE SCAN | PK_HIGH_CLUSTERING_FACTOR | 2502 | | 8 (0)| 00:00:01 |
|* 5 | INDEX UNIQUE SCAN | PK_LOW_CLUSTERING_FACTOR | 1 | | 0 (0)| 00:00:01 |
| 6 | TABLE ACCESS BY INDEX ROWID | LOW_CLUSTERING_FACTOR | 1 | 306 | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------

次回へつづく.... :) Enjoy!

| | コメント (0) | トラックバック (0)

2013年1月 4日 (金)

まだ、レガシーブラウザと戦うのですか...

あけましておめでとうございます。


新年最初のエントリは、IE7、IE8との不毛な戦いは、もうやめましょうよ! というか、IE7、IE8を使うというのであれば、無理言わないほうが幸せですよね...という思いを込めて.....

そして、IE9 64bit版が遅い...謎。

Webkit SunSpider 0.9.1 - http://www.webkit.org/perf/sunspider/sunspider.html
20130103_193042


Google V8 javascript benchmark suit version 7 - http://v8.googlecode.com/svn/data/benchmarks/v7/run.html
20130103_193310


Google Octane V1 - http://octane-benchmark.googlecode.com/svn/latest/index.html
20130103_193422


これら有名なベンチマークとは別にdocument.write()だけを10000回実行してみたのですが...それでも結構な差がでます。
20130103_193153

document.write()を10000回実行した状態をDynaTrance AJAX Editionで覗いてみると.....javascriptの実行ではなくHTML DOM APIの処理速度が......深いですよね〜〜この世界は....ほんと。
20130103_182302_2

開発が終わってしまったSafari 5.1.7 for Windowsもなんですが.....

IE8はこんな状態です.....


計測もしませんでしたが...IE7なんて、こんな状態ですからね....

Img_0638


以上、正月休み中、毎日息子とウルトラマン戦いごっこしていてひらめいたネタでした。


本年もよろしくお願いいたします。


試験環境はVirtualBox4.2.4 for OS X上のWindowsXPとWindows7(64bit)とおまけでOS X Mountain Lion上でも行いました。 どちらのGestOSも4CPUとしてあります。

グラフの値は、各テスト、各ブラウザで10回行い最大値、最小値を除いた平均値です。

WindowsXP
20130103_194730

Windows7(64bit)
20130103_194156

ホストOSは、OS X Mountain Lion
20130103_112857


| | コメント (0) | トラックバック (0)

2012年12月25日 (火)

Indexがあれば、いいってもんじゃない。中身がたいせつ

吉田さんが、なぜ24日を選んだのか理由がわかった...


さて、JPOUG Advent Calendar、25番目の扉を開けます。

ネタは、そう、やはり、索引です。:)


以下のようなバッチ処理があったと思ってくださいな。

リリース直後、データが少ない状態で統計情報をロックしたまま数年が経過したバッチ処理、リリース当初は特に目立った遅延問題も発生せず安定していた(らしい)。

ところが、ところがです。
最近は、少しでも処理量が増加しようものなら100%遅延するという状況となっていた。

関係者へのヒアリングから、どうやら、グルグル系のバッチであることが見えてきました。(またか!w)

AWRレポートから問題となるSQL文(グルグル回るところが一番怪しいんですけどね、大抵は)を特定しました。(以下参照)

Execution Plan
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 843 (100)| |
| 1 | SORT AGGREGATE | | 1 | 19 | | |
| 2 | TABLE ACCESS BY INDEX ROWID| HOGE | 1 | 19 | 843 (1)| 00:00:11 |
| 3 | INDEX RANGE SCAN | HOGE_I01 | 1 | | 842 (1)| 00:00:11 |
-----------------------------------------------------------------------------------------

Full SQL Text

SQL ID SQL Text
------------ -----------------------------------------------------------------
43a7z10a4ckx SELECT COUNT(1) FROM HOGE WHERE
CATEGORY = :B4 AND CLASS = :B3 AND SUBCLASS = :B2 AND ID = :B5
AND SUBID = :B1 AND IS_DELETED = 0

実行計画だけを見ていると、Index Range Scan?、なにが問題なの?(キリっ という感じがしますが、
それに騙されてはいけいけないんです。

次のセクションを見てください。
(本来10万回実行されるのですが終わらないので途中でキャンセルしましたw、約1時間で5000回も回ってません。)

Plan 1(PHV: 468154321)
----------------------

Plan Statistics DB/Inst: DISCUS/discus Snaps: 129-130
-> % Total DB Time is the Elapsed Time of the SQL statement divided
into the Total Database Time multiplied by 100

Stat Name Statement Per Execution % Snap
---------------------------------------- ---------- -------------- -------
Elapsed Time (ms) 3,185,031 640.9 99.8
CPU Time (ms) 3,092,467 622.2 99.8
Executions 4,970 N/A N/A
Buffer Gets 7.7276E+07 15,548.5 99.9
Disk Reads 13,999 2.8 93.4
Parse Calls 1 0.0 0.0
Rows 4,969 1.0 N/A
User I/O Wait Time (ms) 92,540 N/A N/A
Cluster Wait Time (ms) 0 N/A N/A
Application Wait Time (ms) 0 N/A N/A
Concurrency Wait Time (ms) 0 N/A N/A
Invalidations 0 N/A N/A
Version Count 1 N/A N/A
Sharable Mem(KB) 22 N/A N/A
-------------------------------------------------------------

Index Range Scanで平均Rows=1の割にBuffer Getsが異常に多い、処理時間のほとんどがCPU処理時間。

これらがなにを意味するのか............

確認のためexplain planで取得した実行計画のPredicate Informationを見てみると..

Plan hash value: 468154321

-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 19 | 795 (1)| 00:00:10 |
| 1 | SORT AGGREGATE | | 1 | 19 | | |
|* 2 | TABLE ACCESS BY INDEX ROWID| HOGE | 1 | 19 | 795 (1)| 00:00:10 |
|* 3 | INDEX RANGE SCAN | HOGE_I01 | 1 | | 794 (1)| 00:00:10 |
-----------------------------------------------------------------------------------------

PLAN_TABLE_OUTPUT
---------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------

2 - filter("IS_DELETED"=0)
3 - access("CATEGORY"=TO_NUMBER(:V1) AND "CLASS"=TO_NUMBER(:V2) AND
"SUBCLASS"=TO_NUMBER(:V3) AND "ID"=TO_NUMBER(:V4) AND "SUBID"=TO_NUMBER(:V5))
filter("ID"=TO_NUMBER(:V4) AND "SUBID"=TO_NUMBER(:V5))

索引アクセス時にID列とSUBID列でフィルタ処理が行われています。:) この部分の比率が非常に大きい。
ようするに大量のデータを読み込み、フィルタ処理でそのほとんどを捨てている!!! 
(もったいない、もったいないです。もったいないお化けが出てきそうです)

索引を見てみます。

INDEX_NAME                     COLUMN_NAME
------------------------------ --------------------
HOGE_I01 CATEGORY
HOGE_I01 CLASS
HOGE_I01 SUBCLASS
HOGE_I01 STATUS
HOGE_I01 ID
HOGE_I01 SUBID

索引の先頭3列(CATEGORY,CLASS,SUBCLASS)でデータ数をカウントしてみると、400,000行ありました...
40万行から、たった1行を取り出すフィルタリングを10万回繰り返している...グルグル系恐るべし(@@)


解決方法は....

索引の列順を入れ替えれば解決するはず!!!
status列はカーディナリティが非常に低い列であることは確認ずみであつたため最後尾に移動しました。
先頭5列(CATEGORY,CLASS,SUBCLASS,ID,SUBID)でほぼデータが絞りこめるようになるはずです。
(AWRレポートのRowsを見ると最終的には1行or0行となることが確認できます)

作り直した索引は以下のようになります。
STATUS列が最後になっているところがポイントです。違いはたったそれだけです。
(Index Only Accessではありませんよ、今回は。Index Only Accessでグルグルをギリギリまでチューニングしなくても十分な効果が得られたのでこれ以上なにもしません。)

INDEX_NAME                     COLUMN_NAME
------------------------------ --------------------
HOGE_I02 CATEGORY
HOGE_I02 CLASS
HOGE_I02 SUBCLASS
HOGE_I02 ID
HOGE_I02 SUBID
HOGE_I02 STATUS


索引を作り直した後はご覧の通り、じゃじゃ〜〜〜〜〜〜ん!
想定17時間程度のダメバッチが30分ほどで終了しました。ニッコリ。CPUにも随分優しくなってますね。

Plan 1(PHV: 2833120447)
-----------------------

Plan Statistics DB/Inst: DISCUS/discus Snaps: 131-132
-> % Total DB Time is the Elapsed Time of the SQL statement divided
into the Total Database Time multiplied by 100

Stat Name Statement Per Execution % Snap
---------------------------------------- ---------- -------------- -------
Elapsed Time (ms) 1,846,702 18.5 99.2
CPU Time (ms) 43,208 0.4 89.1
Executions 100,000 N/A N/A
Buffer Gets 3,971,771 39.7 99.1
Disk Reads 290,822 2.9 99.1
Parse Calls 1 0.0 0.0
Rows 100,000 1.0 N/A
User I/O Wait Time (ms) 1,830,618 N/A N/A
Cluster Wait Time (ms) 0 N/A N/A
Application Wait Time (ms) 0 N/A N/A
Concurrency Wait Time (ms) 0 N/A N/A
Invalidations 0 N/A N/A
Version Count 1 N/A N/A
Sharable Mem(KB) 22 N/A N/A
-------------------------------------------------------------

Execution Plan
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 5 (100)| |
| 1 | SORT AGGREGATE | | 1 | 19 | | |
| 2 | TABLE ACCESS BY INDEX ROWID| HOGE | 1 | 19 | 5 (0)| 00:00:01 |
| 3 | INDEX RANGE SCAN | HOGE_I02 | 1 | | 4 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------

Full SQL Text

SQL ID SQL Text
------------ -----------------------------------------------------------------
43a7z10a4ckx SELECT COUNT(1) FROM HOGE WHERE CATEGORY = :B4 AND CLASS = :B3 AN
D SUBCLASS = :B2 AND ID = :B5 AND SUBID = :B1 AND IS_DELETED = 0


念のためexplain plan でも確認してみます。ご覧の通りフィルタリングはis_deleted列のみでindex range scan時のフィルタリングは全く発生していません。

もったいないお化けを退治できましたv

Plan hash value: 2833120447

-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 19 | 5 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 19 | | |
|* 2 | TABLE ACCESS BY INDEX ROWID| HOGE | 1 | 19 | 5 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | HOGE_I02 | 1 | | 4 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------

PLAN_TABLE_OUTPUT
---------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------

2 - filter("IS_DELETED"=0)
3 - access("CATEGORY"=TO_NUMBER(:B4) AND "CLASS"=TO_NUMBER(:B3) AND
"SUBCLASS"=TO_NUMBER(:B2) AND "ID"=TO_NUMBER(:B5) AND "SUBID"=TO_NUMBER(:B1))

複合索引の列順って、ほんとうに大切ですよね :)

NOTE:
今回のような遅延ケースでは索引を適切に再構成するほかに、ヒントでIndex skip scanを強制する方法もありますが、索引を適切な列の並びに再構成する方法と比べ劣るのも事実です。
大人の事情に金縛りに合うこともあるでしょうし.....。状況に合わせて使い分けるとよいと思います。

これで、JPOUG Advent Calendarの全ての小窓は開かれました。

メリークリスマス、そして、良いお年をお迎えください。


最後に、Pipelined Table Function で Christmas Tree ☆☆☆☆

 

| | コメント (0) | トラックバック (0)

2012年10月21日 (日)

Unconference at db tech showcase 2012の資料公開 :)

db tech showcase 2012
Unconference at db tech showcase 2012

db tech showcaseの一角をJPOUGが占拠してUnconferenceを開催しました。 db tech showcase関係者の皆様、このような機会を与えて頂き大変感謝します。
そして、お疲れさまでした。



Index Only Access 3部作の最終回?! として 「Index Only Accessが実装されるたった一つの理由」と題したセッションを行いました。
実行計画を取得するために操作したデータベースの中には人生二度目のデータベース複数もあり、かなりの時間を裂いて調べた割にはセッション時間が少々短めになってしまいました。m(_ _)m

なぜ、このテーマを選んだか.

PostgreSQLがリリースされてから9.1まで実装されなかったIndex Only Accessでしたが、9.2でついに実装されました。

そして..db tech showcase 2012は...

SQL> select dbms_name from all_dbms where dbms_category like "%";

DBMS_NAME
------------------
Oracle
DB2
MySQL
PostgreSQL
SQL Server
Vecterwise
MongoDB
Symfoware
Clustrix
InfiniDB
.
.
.
.

的な雰囲気となっていることもあり、Index Only Accessの魚拓をあつめて比較、Index Only Accessが実装される理由について今一度、考えてみたいな..と。
タイトル見ただけで理由が想像できた方は、資料見なくても大丈夫だと思いますよ。:) 
 

H/Wの性能が急速に伸びてきている影響もあるように感じますが、無駄に広範囲な検索や、無駄にビッグなデータとなっていること気にしていないのではないか? というケースが多くなっていると感じています。
DBMSはアクセスするデータをより少なくするための工夫をしているのに...エンジニアがそれをうまく使っていない、使えていない、設計できてない...そんな"感じ"がするんです。

セッション資料を公開しました。
S1a


じゃ、like "%てない" 状況をどうすればいいか....答えは、小田さんのセッションの中にあった。。。。:)


#不慣れなDBMSもあり、こんなメトリックみたほうが分かりやすいよ〜、などのツッコミ歓迎します.


| | コメント (0) | トラックバック (0)

2012年9月 2日 (日)

Index Only Accessネタのおまけのおまけ

Caché de Index Only Access できるのか?

CachéってSQLもサポートしてるので、Index Only Accessサポートしてるのかな〜と素朴な疑問から調べてみたもののマニュアルには記載されていない。
書かれていなきゃどうなるか、試すのが手っ取り早いですね。
(ダウンロードするにはフォームからリクエストする必要があります。1セッション限定の試用ライセンスのようです。他の制限は該当ページ読んでくださいな。)


OracleのIndex Only Accessは以下のエントリを参考にしてください。
いん!、イン!、Index どっぷり Inde Only Access生活w - Oracle OpenWorld Unconference presented by JPOUG
JPOUG SET EVENTS 20120721 - 「(続)いん!、イン!、Index 大人の事情縛りのSQLチューニング」資料公開

MySQL/PostgreSQLのIndex Only Accessは以下のエントリを参考にしてください。
Index Only Accessネタのおまけ


いつもはMacBook Air使うんですが、今回は都合により母艦のVirtualBoxを使いました (^^;;

MacPro Mid 2012 12Core/24GB (OS X Mountain Lion Server)
VirtualBox4.1.20 for MacOS X
GuestOS:CentOS5.8 x86_64 (CPU数とメモリサイズはMacBook Airのx86環境と同じ)

Caché 2012.1 for Linux x86_64


まず、ターミナルで接続して表、索引、データ登録、他のデータベースで言う統計情報取得から。

[oracle@pleco ˜]$ csession cache -U samples

ノード: pleco.macdeoracle.jp インスタンス: CACHE

SAMPLES>

SAMPLES>d $System.SQL.Shell()
SQL Command Line Shell
----------------------------------------------------

The command prefix is currently set to: <<nothing>>.
Enter q to quit, ? for help.
SAMPLES>>
SAMPLES>>create table tab3(unique_id numeric(10) not null, item_code char(15) not null, data varchar(500), is_delete numeric(1), status_code char(2))
1. create table tab3(unique_id numeric(10) not null, item_code char(15) not null, data varchar(500), is_delete numeric(1), status_code char(2))

0 Rows Affected
statement prepare time: 0.0118s, elapsed execute time: 0.2405s.
---------------------------------------------------------------------------
SAMPLES>>
SAMPLES>>alter table tab3 add constraint tab3_pk primary key(unique_id)
3. alter table tab3 add constraint tab3_pk primary key(unique_id)

0 Rows Affected
statement prepare time: 0.0092s, elapsed execute time: 4.9465s.
---------------------------------------------------------------------------
SAMPLES>>
SAMPLES>>create table tab311 (unique_id numeric(10) not null,sub_item_code char(10),data varchar(500),is_delete numeric(1))
4. create table tab311 (unique_id numeric(10) not null,sub_item_code char(10),data varchar(500),is_delete numeric(1))

0 Rows Affected
statement prepare time: 0.0115s, elapsed execute time: 0.1953s.
---------------------------------------------------------------------------
SAMPLES>>create table tab31 (item_code char(15) not null,sub_item_code char(10),data varchar(500),is_delete numeric(1))
5. create table tab31 (item_code char(15) not null,sub_item_code char(10),data varchar(500),is_delete numeric(1))

0 Rows Affected
statement prepare time: 0.0133s, elapsed execute time: 0.1985s.
---------------------------------------------------------------------------
SAMPLES>>
SAMPLES>>alter table tab31 add constraint tab31_pk primary key(item_code)
7. alter table tab31 add constraint tab31_pk primary key(item_code)

0 Rows Affected
statement prepare time: 0.0102s, elapsed execute time: 0.2589s.
---------------------------------------------------------------------------
SAMPLES>>alter table tab311 add constraint tab311_pk primary key(unique_id)
8. alter table tab311 add constraint tab311_pk primary key(unique_id)

0 Rows Affected
statement prepare time: 0.0116s, elapsed execute time: 0.2627s.
---------------------------------------------------------------------------
SAMPLES>>create index tab311_ix on tab311(sub_item_code)
9. create index tab311_ix on tab311(sub_item_code)

0 Rows Affected
statement prepare time: 0.0111s, elapsed execute time: 0.2870s.
---------------------------------------------------------------------------
SAMPLES>>q

確認は管理ポータルで!(どこみればよいか分からなかったので楽な方法で..)

Desc_tab3

Tab3_indexes_no_index_only

Desc_tab31

Tab31_indexes_no_index_only

Desc_tab311

Tab311_indexes_no_index_only_scan

Oracleなら統計情報取得、他のRDBMSならアナライズ(昔はオラクルもアナライズだったんですけどね)、Cachéの世界では「テーブルチューニング」というそうな。
(管理ポータルから実行しています)
Table_analyze

Tab31_analyze

Tab311_analyze

表と索引ができたのでデータ登録。
これまたOracleとは勝っても違うし、DSMの時代(DIGITAL STANDARD MUMPS)から大きく拡張されてるのでかなり辛いな〜MUMPS思い出すというより別物に近い感じw。あの当時はSQL使えねーしというより必要ねーし、だったしね (^^;;;

以下、データ作成中のログ..
グルグル回しているのは気にしないでねw

[oracle@pleco ˜]$ csession cache -U samples

ノード: pleco.macdeoracle.JP インスタンス: CACHE

SAMPLES>
SAMPLES>
SAMPLES>s rs=##class(%SQL.Statement).%ExecDirect(,"start transaction")
TL1:SAMPLES>s sql="insert into SQLUser.tab3 values(?,to_char((?#500000)+1,'FM099999999999999'),repeat('*',250-length(to_char(?)))||to_char(?),0,'00')"
TL1:SAMPLES>s stmt=##class(%SQL.Statement).%New() s stat=stmt.%Prepare(sql)
TL1:SAMPLES>f i=1:1:1000000 {s rs=stmt.%Execute(i,i,i,i)}
TL1:SAMPLES>s rs=##class(%SQL.Statement).%ExecDirect(,"commit")
SAMPLES>d $System.SQL.Shell()

SQL Command Line Shell
----------------------------------------------------

The command prefix is currently set to: <<nothing>>.
Enter q to quit, ? for help.
SAMPLES>>select count(1) from tab3
7. select count(1) from tab3

Aggregate_1
1000000

1 Rows(s) Affected
statement prepare time: 0.0009s, elapsed execute time: 2.6558s.
---------------------------------------------------------------------------
SAMPLES>>select min(unique_id) as "min", max(unique_id) as "max" from tab3
8. select min(unique_id) as "min", max(unique_id) as "max" from tab3

min max
1 1000000

1 Rows(s) Affected
statement prepare time: 0.1423s, elapsed execute time: 0.0006s.
---------------------------------------------------------------------------
SAMPLES>>select min(item_code) "min",max(item_code) "max" from tab3
9. select min(item_code) "min",max(item_code) "max" from tab3

min max
000000000000001 000000000500000

1 Rows(s) Affected
statement prepare time: 0.1324s, elapsed execute time: 3.4683s.
---------------------------------------------------------------------------
SAMPLES>>
SAMPLES>>q

SAMPLES>halt
[oracle@pleco ˜]$

SAMPLES>d $System.SQL.SetAutoCommit(2)
SAMPLES>s rs=##class(%SQL.Statement).%ExecDirect(,"start transaction")
TL1:SAMPLES>s sql="insert into SQLUser.tab31 values(to_char(?,'FM099999999999999'),to_char((?#500000)+1,'FM0999999999'),repeat('*',250-length(to_char(?)))||to_char(?),0)"
TL1:SAMPLES>s stmt=##class(%SQL.Statement).%New() s stat=stmt.%Prepare(sql)
TL1:SAMPLES>f i=1:1:2000000 {s rs=stmt.%Execute(i,i,i,i)}
TL1:SAMPLES>s rs=##class(%SQL.Statement).%ExecDirect(,"commit")

SAMPLES>d $System.SQL.Shell()
SQL Command Line Shell
----------------------------------------------------

The command prefix is currently set to: <<nothing>>.
Enter q to quit, ? for help.
SAMPLES>>select count(1) from tab31
2. select count(1) from tab31

Aggregate_1
2000000

1 Rows(s) Affected
statement prepare time: 0.1349s, elapsed execute time: 6.0684s.
---------------------------------------------------------------------------
SAMPLES>>select min(item_code) "item_code(MIN)",max(item_code) "item_code(MAX)" from tab31
3. select min(item_code) "item_code(MIN)",max(item_code) "item_code(MAX)" from tab31

item_code(MIN) item_code(MAX)
000000000000001 000000002000000

1 Rows(s) Affected
statement prepare time: 0.1361s, elapsed execute time: 0.0010s.
---------------------------------------------------------------------------
SAMPLES>>select min(sub_item_code) "sub_item_code(MIN)",max(sub_item_code) "sub_item_code(MAX)" from tab31
4. select min(sub_item_code) "sub_item_code(MIN)",max(sub_item_code) "sub_item_code(MAX)" from tab31

sub_item_code(MIN) sub_item_code(MAX)
0000000001 0000500000

1 Rows(s) Affected
statement prepare time: 0.1391s, elapsed execute time: 6.7417s.
---------------------------------------------------------------------------
SAMPLES>>q
SAMPLES>

SAMPLES>d $System.SQL.SetAutoCommit(2)
SAMPLES>s rs=##class(%SQL.Statement).%ExecDirect(,"start transaction")
TL1:SAMPLES>s sql="insert into SQLUser.tab311 values(?,to_char((?#500000)+1,'FM0999999999'),repeat('*',250-length(to_char(?)))||to_char(?),0)"
TL1:SAMPLES>s stmt=##class(%SQL.Statement).%New() s stat=stmt.%Prepare(sql)
TL1:SAMPLES>f i=1:1:2000000 {s rs=stmt.%Execute(i,i,i,i)}
TL1:SAMPLES>s rs=##class(%SQL.Statement).%ExecDirect(,"commit")

SAMPLES>d $System.SQL.Shell()

SQL Command Line Shell
----------------------------------------------------

The command prefix is currently set to: <<nothing>>.
Enter q to quit, ? for help.
SAMPLES>>select count(1) from tab311
5. select count(1) from tab311

Aggregate_1
2000000

1 Rows(s) Affected
statement prepare time: 0.0009s, elapsed execute time: 3.7845s.
---------------------------------------------------------------------------
SAMPLES>>select min(sub_item_code) "sub_item_code(MIN)", max(sub_item_code) "sub_item_code(MAX)" from tab311
6. select min(sub_item_code) "sub_item_code(MIN)", max(sub_item_code) "sub_item_code(MAX)" from tab311

sub_item_code(MIN) sub_item_code(MAX)
0000000001 0000500000

1 Rows(s) Affected
statement prepare time: 0.1558s, elapsed execute time: 0.0004s.
---------------------------------------------------------------------------
SAMPLES>>q


本題からはずれるのですが... SQL使ってるとはいっても、ベースはMUMPSというかM言語だからね〜というお話を。。

tab3表及び主キー制約で作成された索引に対応するglobals。索引を見るとSparse Multidimensional Arrayをうまく利用しているのがよくわかる :)
Cachéの表や索引の実態はSparse Multidimensional Arrayなんだお。

1:      ^User.tab3D    =     1512812
2: ^User.tab3D(512813) = $lb("",1,"000000000000002","***************** ...中略... *******************************1",0,"00")
3: ^User.tab3D(512814) = $lb("",2,"000000000000003","***************** ...中略... *******************************2",0,"00")
4: ^User.tab3D(512815) = $lb("",3,"000000000000004","***************** ...中略... *******************************3",0,"00")
...以下略...

tab3表の主キー索引

1:      ^User.tab3I("tab3pk",1,512813)    =     ""
2: ^User.tab3I("tab3pk",2,512814) = ""
3: ^User.tab3I("tab3pk",3,512815) = ""
...以下略...

次は、tab31表と主キー索引

1:      ^User.tab31D    =     2000000
2: ^User.tab31D(1) = $lb("","000000000000001","0000000002","***************** ...中略... *************************1",0)
3: ^User.tab31D(2) = $lb("","000000000000002","0000000003","***************** ...中略... *************************2",0)
4: ^User.tab31D(3) = $lb("","000000000000003","0000000004","***************** ...中略... *************************3",0)
...以下略...


1:      ^User.tab31I("tab31pk"," 000000000000001",1)    =     ""
2: ^User.tab31I("tab31pk"," 000000000000002",2) = ""
3: ^User.tab31I("tab31pk"," 000000000000003",3) = ""
...以下略...


最後に、tab311表と主キー索引及び、非ユニーク索引に対応したSparse Multidimensional Arrayの内容

1:      ^User.tab311D    =     2000000
2: ^User.tab311D(1) = $lb("",1,"0000000002","********************************* ...中略... ************************1",0)
3: ^User.tab311D(2) = $lb("",2,"0000000003","********************************* ...中略... ************************2",0)
4: ^User.tab311D(3) = $lb("",3,"0000000004","********************************* ...中略... ************************3",0)
...以下略...

1:      ^User.tab311I("tab311ix"," 0000000001",500000)    =     ""
2: ^User.tab311I("tab311ix"," 0000000001",1000000) = ""
3: ^User.tab311I("tab311ix"," 0000000001",1500000) = ""
4: ^User.tab311I("tab311ix"," 0000000001",2000000) = ""
5: ^User.tab311I("tab311ix"," 0000000002",1) = ""
6: ^User.tab311I("tab311ix"," 0000000002",500001) = ""
7: ^User.tab311I("tab311ix"," 0000000002",1000001) = ""
8: ^User.tab311I("tab311ix"," 0000000002",1500001) = ""
9: ^User.tab311I("tab311ix"," 0000000003",2) = ""
10: ^User.tab311I("tab311ix"," 0000000003",500002) = ""
11: ^User.tab311I("tab311ix"," 0000000003",1000002) = ""
12: ^User.tab311I("tab311ix"," 0000000003",1500002) = ""
13: ^User.tab311I("tab311ix"," 0000000004",3) = ""
14: ^User.tab311I("tab311ix"," 0000000004",500003) = ""
15: ^User.tab311I("tab311ix"," 0000000004",1000003) = ""
16: ^User.tab311I("tab311ix"," 0000000004",1500003) = ""
...以下略...


実行するクエリはOracle/MySQL/PostgreSQLで実行したものと同じです。

select
t1.unique_id,
t1.item_code,
(
select
max(t3.unique_id)
from
tab31 t2 join tab311 t3
on
t2.sub_item_code = t3.sub_item_code
and t3.is_delete = 0
where
t2.item_code = t1.item_code
and t2.is_delete = 0
) current_sub_item
from
tab3 t1
where
t1.unique_id between 1 and 10000
and t1.is_delete = 0
and t1.status_code = '00'


以下、管理ポータルのSQL実行プラン表示で取得した実行計画とコスト。
Index Only AccessではないのでCacheのSQLの世界で表アクセスを意味するオペレーションである、Read Master mapがスカラー問合せ部分に現れている。
ちなみに、索引アクセスを意味するオペレーションは、Read index mapのようだ。

実行プランが以下に表示されます:
クエリ文字列

SELECT t1 . unique_id , t1 . item_code , ( SELECT MAX ( t3 . unique_id ) FROM tab31 t2 JOIN tab311 t3 ON t2 . sub_item_code = t3 . sub_item_code AND t3 .
is_delete = ? WHERE t2 . item_code = t1 . item_code AND t2 . is_delete = ? ) current_sub_item FROM tab3 t1 WHERE t1 . unique_id BETWEEN ? AND ? AND t1 .
is_delete = ? AND t1 . status_code = ?

クエリプラン
相対コスト = 74082

Call module B, which populates bitmap temp-file A.
Read bitmap temp-file A, looping on ID.
For each row:
Read master map SQLUser.tab3.IDKEY, using the given idkey value.
Output the row.

module B
Read index map SQLUser.tab3.tab3_pk, looping on unique_id (with a range condition) and ID.
For each row:
Add ID bit to bitmap temp-file A.

subquery
Call module E.
Determine subquery result.

module E
Read index map SQLUser.tab31.tab31_pk, using the given %SQLUPPER(item_code), and looping on ID.
For each row:
Read master map SQLUser.tab31.IDKEY, using the given idkey value.
Read index map SQLUser.tab311.tab311_ix, using the given %SQLUPPER(sub_item_code), and looping on ID.
For each row:
Read master map SQLUser.tab311.IDKEY, using the given idkey value.
Accumulate the max(unique_id).


では、Index Only Accessによるチューニング。 Covering Indexを2つ作成します。(Oracle/MySQL/PostgreSQLで作成した索引と同じ名称にしてあります)

[oracle@pleco ˜]$ csession cache -U samples

ノード: pleco.macdeoracle.JP インスタンス: CACHE

SAMPLES>d $System.SQL.Shell()
SQL Command Line Shell
----------------------------------------------------

The command prefix is currently set to: <<nothing>>.
Enter q to quit, ? for help.
SAMPLES>>create index tab31_demo_ix on tab31(item_code, is_delete, sub_item_code)
1. create index tab31_demo_ix on tab31(item_code, is_delete, sub_item_code)

0 Rows Affected
statement prepare time: 4.8277s, elapsed execute time: 9.6483s.
---------------------------------------------------------------------------
SAMPLES>>create index tab311_demo_ix on tab311(sub_item_code, is_delete, unique_id)
2. create index tab311_demo_ix on tab311(sub_item_code, is_delete, unique_id)

0 Rows Affected
statement prepare time: 0.0130s, elapsed execute time: 8.6777s.
---------------------------------------------------------------------------
SAMPLES>>

索引は正しく作成されています。(管理ポータルから確認した結果)
Tab31_indexes_index_only

Tab311_indexes_inde_only

お〜〜〜〜〜っ!。 CacheのSQLワールドでもIndex Only Accessになってる〜〜〜。 (ヒントのような仕組みはないようなのですが、狙い通りの索引が利用されていますね)
スカラー副問合せの実行計画から Read Master Map(表に対応するSparse Multidimensional Array)をアクセスする操作が消え、Read index Map(索引に対応するSparse Multidimensional Array)をアクセスする操作だけになっていることが確認できた。 :)

以下、管理ポータルの「SQL実行」>「実行計画」で取得した実行計画

クエリプラン
相対コスト = 74082

Call module B, which populates bitmap temp-file A.
Read bitmap temp-file A, looping on ID.
For each row:
Read master map SQLUser.tab3.IDKEY, using the given idkey value.
Output the row.

module B
Read index map SQLUser.tab3.tab3_pk, looping on unique_id (with a range condition) and ID.
For each row:
Add ID bit to bitmap temp-file A.

subquery
Call module E.
Determine subquery result.

module E
Read index map SQLUser.tab31.tab31_demo_ix, using the given %SQLUPPER(item_code) and is_delete, and looping on %SQLUPPER(sub_item_code) and ID.
For each row:
Read index map SQLUser.tab311.tab311_demo_ix, using the given %SQLUPPER(sub_item_code) and is_delete, and looping on unique_id and ID.
For each row:
Accumulate the max(unique_id).

| | コメント (0) | トラックバック (0)

2012年8月21日 (火)

Index Only Accessネタのおまけ

2012/10/13追記
MySQL(InnoDB)の主キーはClustered Indexなので主キーアクセスである場合はCovering indexは不要ですね。(^^;; 
PostgreSQL9.2のIndex-only scanですが、vacuumさえしっかりやっていればcovering indexを利用するようになることを確認。

前述の2点を後日追加予定です。



随分間があいてしまいました、m(_ _)m

Oracle以外でも多数の商用/OSS RDBMSでIndex Only Accessできるんですよね。
ということで、MySQL/PostgreSQLの実行計画ではどのように表示されるのか、Oracleのように索引だけ作れば勝ってにIndex Only Accessやってくれるのか?、などなど簡単に確かめてみました。(備忘録)

OracleのIndex Only Accessは以下のエントリを参考にしてください。
いん!、イン!、Index どっぷり Inde Only Access生活w - Oracle OpenWorld Unconference presented by JPOUG
JPOUG SET EVENTS 20120721 - 「(続)いん!、イン!、Index 大人の事情縛りのSQLチューニング」資料公開

今回使ったのは、MySQL 5.5.27/PostgreSQL 9.1.4そしてBeta阪ですが9.2 Beta4です。
環境はOracleが乗ってる環境と同じです。

MacBook Air late 2010 13inch 2GB (MacOS X Lion)
VirtualBox4.1.18 for MacOS X
GuestOS:CentOS5.8 x86

ちなみに、データ作りに時間を割けなかったので、クラスタリングファクターはどちらのも低めとなっています。実行計画上どのように見えるのか知りたかっただけなので. (^^;;;

MySQL 5.5.27

Oracleのデモでも使っていたスカラー副問合せで試してみました。最初は、Index Rancge Scanをグルグル。
この場合は索引、表をそれぞれアクセスします。

mysql> explain 
-> select
-> t1.unique_id,
-> t1.item_code,
-> (
-> select
-> max(t3.unique_id)
-> from
-> tab31 t2 join tab311 t3
-> on
-> t2.sub_item_code = t3.sub_item_code
-> and t3.is_delete = 0
-> where
-> t2.item_code = t1.item_code
-> and t2.is_delete = 0
-> ) current_sub_item
-> from
-> tab3 t1
-> where
-> t1.unique_id between 1 and 10000
-> and t1.is_delete = 0
-> and t1.status_code = '00'
-> ;
+----+--------------------+-------+--------+---------------+-----------+---------+------------------------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+--------+---------------+-----------+---------+------------------------+-------+-------------+
| 1 | PRIMARY | t1 | range | PRIMARY | PRIMARY | 5 | NULL | 20108 | Using where |
| 2 | DEPENDENT SUBQUERY | t2 | eq_ref | PRIMARY | PRIMARY | 45 | scott.t1.item_code | 1 | Using where |
| 2 | DEPENDENT SUBQUERY | t3 | ref | tab311_ix | tab311_ix | 31 | scott.t2.sub_item_code | 1 | Using where |
+----+--------------------+-------+--------+---------------+-----------+---------+------------------------+-------+-------------+
3 rows in set (0.00 sec)

mysql>


Index Only Accessさせた場合の実行計画です。
Extra列に Using indexと出ていればIndex Only Accessになっています。
ちなみに、Oracleとちがって勝ってにCovering Indexを使ってくれなかったのでSQLヒントを使っています。Oracleのヒントと随分書き方違うので戸惑うよ(^^;;;

mysql> explain
-> select
-> t1.unique_id,
-> t1.item_code,
-> (
-> select
-> max(t3.unique_id)
-> from
-> tab31 t2 ignore index(primary) join tab311 t3 ignore index(tab311_ix)
-> on
-> t2.sub_item_code = t3.sub_item_code
-> and t3.is_delete = 0
-> where
-> t2.item_code = t1.item_code
-> and t2.is_delete = 0
-> ) current_sub_item
-> from
-> tab3 t1
-> where
-> t1.unique_id between 1 and 10000
-> and t1.is_delete = 0
-> and t1.status_code = '00'
-> ;
+----+--------------------+-------+-------+----------------+----------------+---------+------------------------------+-------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+-------+----------------+----------------+---------+------------------------------+-------+--------------------------+
| 1 | PRIMARY | t1 | range | PRIMARY | PRIMARY | 5 | NULL | 20108 | Using where |
| 2 | DEPENDENT SUBQUERY | t2 | ref | tab31_demo_ix | tab31_demo_ix | 47 | scott.t1.item_code,const | 10000 | Using where; Using index |
| 2 | DEPENDENT SUBQUERY | t3 | ref | tab311_demo_ix | tab311_demo_ix | 33 | scott.t2.sub_item_code,const | 10000 | Using where; Using index |
+----+--------------------+-------+-------+----------------+----------------+---------+------------------------------+-------+--------------------------+
3 rows in set (0.00 sec)

mysql>


PostgreSQL 9.1

PostgreSQL9.1まではIndex Only Accessが実装されていないとのこと。(実は今年になってはじめて知ったことなのですが、その時は、「え?! そうなの?」って感じでした)

※実行時間のバラツキがあるため実行統計情報から表や索引ブロックアクセス状況を確認しています。

scott=> select * from pg_statio_user_tables where relname in ('tab31','tab311');
relid | schemaname | relname | heap_blks_read | heap_blks_hit | idx_blks_read | idx_blks_hit | toast_blks_read | toast_blks_hit | tidx_blks_read | tidx_blks_hit
-------+------------+---------+----------------+---------------+---------------+--------------+-----------------+----------------+----------------+---------------
16401 | public | tab311 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0
16395 | public | tab31 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0

scott=> select * from pg_statio_user_indexes where relname in ('tab31','tab311');
relid | indexrelid | schemaname | relname | indexrelname | idx_blks_read | idx_blks_hit
-------+------------+------------+---------+----------------+---------------+--------------
16401 | 16411 | public | tab311 | tab311_pk | 0 | 0
16395 | 16454 | public | tab31 | tab31_demo_ix | 0 | 0
16401 | 16455 | public | tab311 | tab311_demo_ix | 0 | 0

scott=> explain analyze verbose
scott-> select
scott-> t1.unique_id,
scott-> t1.item_code,
scott-> (
scott(> select
scott(> max(t3.unique_id)
scott(> from
scott(> tab31 t2 join tab311 t3
scott(> on
scott(> t2.sub_item_code = t3.sub_item_code
scott(> and t3.is_delete = 0
scott(> where
scott(> t2.item_code = t1.item_code
scott(> and t2.is_delete = 0
scott(> ) current_sub_item
scott-> from
scott-> tab3 t1
scott-> where
scott-> t1.unique_id between 1 and 10000
scott-> and t1.is_delete = 0
scott-> and t1.status_code = '00'
scott-> ;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------
Index Scan using tab3_pk on public.tab3 t1 (cost=0.00..2050286.39 rows=10326 width=22) (actual time=45.613..6560.751 rows=10000 loops=1)
Output: t1.unique_id, t1.item_code, (SubPlan 1)
Index Cond: ((t1.unique_id >= 1::numeric) AND (t1.unique_id <= 10000::numeric))
Filter: ((t1.is_delete = 0::numeric) AND (t1.status_code = '00'::bpchar))
SubPlan 1
-> Aggregate (cost=198.47..198.48 rows=1 width=6) (actual time=0.650..0.651 rows=1 loops=10000)
Output: max(t3.unique_id)
-> Nested Loop (cost=0.00..198.46 rows=4 width=6) (actual time=0.143..0.621 rows=40 loops=10000)
Output: t3.unique_id
-> Index Scan using tab31_pk on public.tab31 t2 (cost=0.00..8.58 rows=1 width=11) (actual time=0.010..0.011 rows=1 loops=10000)
Output: t2.item_code, t2.sub_item_code, t2.data, t2.is_delete
Index Cond: (t2.item_code = t1.item_code)
Filter: (t2.is_delete = 0::numeric)
-> Index Scan using tab311_ix on public.tab311 t3 (cost=0.00..189.27 rows=49 width=17) (actual time=0.130..0.565 rows=40 loops=10000)
Output: t3.unique_id, t3.sub_item_code, t3.data, t3.is_delete
Index Cond: (t3.sub_item_code = t2.sub_item_code)
Filter: (t3.is_delete = 0::numeric)
Total runtime: 6566.636 ms
(18 行)

時間: 6618.795 ms
scott=>

適切な索引を利用しているのでIndex Unique/Range Scanとテーブルブロックへのアクセスが確認できます。(当然といえば当然ですよね)

scott=> select * from pg_statio_user_tables where relname in ('tab31','tab311');
relid | schemaname | relname | heap_blks_read | heap_blks_hit | idx_blks_read | idx_blks_hit | toast_blks_read | toast_blks_hit | tidx_blks_read | tidx_blks_hit
-------+------------+---------+----------------+---------------+---------------+--------------+-----------------+----------------+----------------+---------------
16401 | public | tab311 | 15389 | 36920 | 1983 | 30033 | 0 | 0 | 0 | 0
16395 | public | tab31 | 401 | 9599 | 64 | 30057 | 0 | 0 | 0 | 0

scott=> select * from pg_statio_user_indexes where relname in ('tab31','tab311');
relid | indexrelid | schemaname | relname | indexrelname | idx_blks_read | idx_blks_hit
-------+------------+------------+---------+----------------+---------------+--------------
16401 | 16411 | public | tab311 | tab311_pk | 0 | 0
16395 | 16454 | public | tab31 | tab31_demo_ix | 64 | 30057
16401 | 16455 | public | tab311 | tab311_demo_ix | 1983 | 30033

Index Only AccessさせるためのCovering Index(FAT index)を作ってみましたが、Index Range/Unique Scanのままですね。
ちなみに、PostgreSQLってOracleやMySQLのようなSQLヒントがありません。(あったらごめんなさい。調べきれてないだけです。m(_ _)m
Covering Indexだけを残して邪魔な索引を削除することでCovering indexをアクセスさせています。

scott=> select * from pg_statio_user_tables where relname in ('tab31','tab311');
relid | schemaname | relname | heap_blks_read | heap_blks_hit | idx_blks_read | idx_blks_hit | toast_blks_read | toast_blks_hit | tidx_blks_read | tidx_blks_hit
-------+------------+---------+----------------+---------------+---------------+--------------+-----------------+----------------+----------------+---------------
16401 | public | tab311 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0
16395 | public | tab31 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0

scott=> select * from pg_statio_user_indexes where relname in ('tab31','tab311');
relid | indexrelid | schemaname | relname | indexrelname | idx_blks_read | idx_blks_hit
-------+------------+------------+---------+----------------+---------------+--------------
16401 | 16411 | public | tab311 | tab311_pk | 0 | 0
16395 | 16454 | public | tab31 | tab31_demo_ix | 0 | 0
16401 | 16455 | public | tab311 | tab311_demo_ix | 0 | 0


scott=> explain analyze verbose
scott-> select
scott-> t1.unique_id,
scott-> t1.item_code,
scott-> (
scott(> select
scott(> max(t3.unique_id)
scott(> from
scott(> tab31 t2 join tab311 t3
scott(> on
scott(> t2.sub_item_code = t3.sub_item_code
scott(> and t3.is_delete = 0
scott(> where
scott(> t2.item_code = t1.item_code
scott(> and t2.is_delete = 0
scott(> ) current_sub_item
scott-> from
scott-> tab3 t1
scott-> where
scott-> t1.unique_id between 1 and 10000
scott-> and t1.is_delete = 0
scott-> and t1.status_code = '00'
scott-> ;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------------------
Index Scan using tab3_pk on public.tab3 t1 (cost=0.00..2109597.62 rows=10326 width=22) (actual time=0.677..1808.056 rows=10000 loops=1)
Output: t1.unique_id, t1.item_code, (SubPlan 1)
Index Cond: ((t1.unique_id >= 1::numeric) AND (t1.unique_id <= 10000::numeric))
Filter: ((t1.is_delete = 0::numeric) AND (t1.status_code = '00'::bpchar))
SubPlan 1
-> Aggregate (cost=204.21..204.22 rows=1 width=6) (actual time=0.176..0.176 rows=1 loops=10000)
Output: max(t3.unique_id)
-> Nested Loop (cost=0.00..204.20 rows=4 width=6) (actual time=0.024..0.137 rows=40 loops=10000)
Output: t3.unique_id
-> Index Scan using tab31_demo_ix on public.tab31 t2 (cost=0.00..8.76 rows=1 width=11) (actual time=0.007..0.009 rows=1 loops=10000)
Output: t2.item_code, t2.sub_item_code, t2.data, t2.is_delete
Index Cond: ((t2.item_code = t1.item_code) AND (t2.is_delete = 0::numeric))
-> Index Scan using tab311_demo_ix on public.tab311 t3 (cost=0.00..194.84 rows=49 width=17) (actual time=0.013..0.068 rows=40 loops=10000)
Output: t3.unique_id, t3.sub_item_code, t3.data, t3.is_delete
Index Cond: ((t3.sub_item_code = t2.sub_item_code) AND (t3.is_delete = 0::numeric))
Total runtime: 4762.819 ms
(16 行)

時間: 4855.100 ms
scott=>

実行計画上、Filterはなくなりましたが、表ブロックもアクセスしているのでIndex Only Accessにはなっていません。(ほんとにIndex Only AccessというかIndex-only Scanは9.1までは実装されていないんですね。)

scott=> select * from pg_statio_user_tables where relname in ('tab31','tab311');
relid | schemaname | relname | heap_blks_read | heap_blks_hit | idx_blks_read | idx_blks_hit | toast_blks_read | toast_blks_hit | tidx_blks_read | tidx_blks_hit
-------+------------+---------+----------------+---------------+---------------+--------------+-----------------+----------------+----------------+---------------
16401 | public | tab311 | 15390 | 36919 | 1982 | 30034 | 0 | 0 | 0 | 0
16395 | public | tab31 | 401 | 9599 | 62 | 30059 | 0 | 0 | 0 | 0

scott=> select * from pg_statio_user_indexes where relname in ('tab31','tab311');
relid | indexrelid | schemaname | relname | indexrelname | idx_blks_read | idx_blks_hit
-------+------------+------------+---------+----------------+---------------+--------------
16401 | 16411 | public | tab311 | tab311_pk | 0 | 0
16395 | 16454 | public | tab31 | tab31_demo_ix | 62 | 30059
16401 | 16455 | public | tab311 | tab311_demo_ix | 1982 | 30034

PostgreSQL 9.2 Beta4

最後に、PostgreSQL 9.2 Beta4 です。このリリースではPostgreSQLでは初めて、Index Only Access(マニュアルでは Index-only Scanと記載されています)
PostgreSQL方面の方がIndex-only Scanと書くかたが多いのもこの影響でしょうね。日本人からするとIndex Only AccessよりIndex Only Scanの方が発音しやすい?(私だけか?)気がしますw

scott=> explain analyze verbose
scott-> select
scott-> t1.unique_id,
scott-> t1.item_code,
scott-> (
scott(> select
scott(> max(t3.unique_id)
scott(> from
scott(> tab31 t2 join tab311 t3
scott(> on
scott(> t2.sub_item_code = t3.sub_item_code
scott(> and t3.is_delete = 0
scott(> where
scott(> t2.item_code = t1.item_code
scott(> and t2.is_delete = 0
scott(> ) current_sub_item
scott-> from
scott-> tab3 t1
scott-> where
scott-> t1.unique_id between 1 and 10000
scott-> and t1.is_delete = 0
scott-> and t1.status_code = '00'
scott-> ;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------
Index Scan using tab3_pk on public.tab3 t1 (cost=0.00..1925706.85 rows=9421 width=22) (actual time=41.205..6202.096 rows=10000 loops=1)
Output: t1.unique_id, t1.item_code, (SubPlan 1)
Index Cond: ((t1.unique_id >= 1::numeric) AND (t1.unique_id <= 10000::numeric))
Filter: ((t1.is_delete = 0::numeric) AND (t1.status_code = '00'::bpchar))
SubPlan 1
-> Aggregate (cost=204.32..204.33 rows=1 width=6) (actual time=0.613..0.614 rows=1 loops=10000)
Output: max(t3.unique_id)
-> Nested Loop (cost=0.00..204.31 rows=4 width=6) (actual time=0.110..0.584 rows=40 loops=10000)
Output: t3.unique_id
-> Index Scan using tab31_pk on public.tab31 t2 (cost=0.00..11.35 rows=1 width=11) (actual time=0.012..0.013 rows=1 loops=10000)
Output: t2.item_code, t2.sub_item_code, t2.data, t2.is_delete
Index Cond: (t2.item_code = t1.item_code)
Filter: (t2.is_delete = 0::numeric)
-> Index Scan using tab311_ix on public.tab311 t3 (cost=0.00..192.47 rows=49 width=17) (actual time=0.095..0.525 rows=40 loops=10000)
Output: t3.unique_id, t3.sub_item_code, t3.data, t3.is_delete
Index Cond: (t3.sub_item_code = t2.sub_item_code)
Filter: (t3.is_delete = 0::numeric)
Total runtime: 6207.935 ms
(18 行)

時間: 6313.789 ms
scott=>

9.2でもIndex Only Accessでなければ表ブロックもアクセスしますよね〜。そりゃそうだ。:)

scott=> select * from pg_statio_user_tables where relname in ('tab31','tab311');
relid | schemaname | relname | heap_blks_read | heap_blks_hit | idx_blks_read | idx_blks_hit | toast_blks_read | toast_blks_hit | tidx_blks_read | tidx_blks_hit
-------+------------+---------+----------------+---------------+---------------+--------------+-----------------+----------------+----------------+---------------
16404 | public | tab311 | 15390 | 36919 | 1649 | 29996 | 0 | 0 | 0 | 0
16398 | public | tab31 | 401 | 9599 | 39 | 30037 | 0 | 0 | 0 | 0

scott=> select * from pg_statio_user_indexes where relname in ('tab31','tab311');
relid | indexrelid | schemaname | relname | indexrelname | idx_blks_read | idx_blks_hit
-------+------------+------------+---------+--------------+---------------+--------------
16404 | 16414 | public | tab311 | tab311_pk | 0 | 0
16398 | 16432 | public | tab31 | tab31_pk | 39 | 30037
16404 | 16434 | public | tab311 | tab311_ix | 1649 | 29996

いよいよ、PostgreSQL9.2 Beta4のIndex-only Scanの番です。:)
お〜〜〜っ、 Index Scan using xxxxという部分が、Index Only Scan using xxxxとなっています! が、 Heap Fetches 400000とある? どゆこと?

scott=> explain analyze verbose
scott-> select
scott-> t1.unique_id,
scott-> t1.item_code,
scott-> (
scott(> select
scott(> max(t3.unique_id)
scott(> from
scott(> tab31 t2 join tab311 t3
scott(> on
scott(> t2.sub_item_code = t3.sub_item_code
scott(> and t3.is_delete = 0
scott(> where
scott(> t2.item_code = t1.item_code
scott(> and t2.is_delete = 0
scott(> ) current_sub_item
scott-> from
scott-> tab3 t1
scott-> where
scott-> t1.unique_id between 1 and 10000
scott-> and t1.is_delete = 0
scott-> and t1.status_code = '00'
scott-> ;

QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
Index Scan using tab3_pk on public.tab3 t1 (cost=0.00..2000896.05 rows=9421 width=22) (actual time=0.410..1728.699 rows=10000 loops=1)
Output: t1.unique_id, t1.item_code, (SubPlan 1)
Index Cond: ((t1.unique_id >= 1::numeric) AND (t1.unique_id <= 10000::numeric))
Filter: ((t1.is_delete = 0::numeric) AND (t1.status_code = '00'::bpchar))
SubPlan 1
-> Aggregate (cost=212.30..212.31 rows=1 width=6) (actual time=0.168..0.168 rows=1 loops=10000)
Output: max(t3.unique_id)
-> Nested Loop (cost=0.00..212.29 rows=4 width=6) (actual time=0.023..0.131 rows=40 loops=10000)
Output: t3.unique_id
-> Index Only Scan using tab31_demo_ix on public.tab31 t2 (cost=0.00..13.13 rows=1 width=11) (actual time=0.006..0.007 rows=1 loops=10000)
Output: t2.item_code, t2.is_delete, t2.sub_item_code
Index Cond: ((t2.item_code = t1.item_code) AND (t2.is_delete = 0::numeric))
Heap Fetches: 10000
-> Index Only Scan using tab311_demo_ix on public.tab311 t3 (cost=0.00..198.67 rows=49 width=17) (actual time=0.013..0.066 rows=40 loops=10000)
Output: t3.sub_item_code, t3.is_delete, t3.unique_id
Index Cond: ((t3.sub_item_code = t2.sub_item_code) AND (t3.is_delete = 0::numeric))
Heap Fetches: 400000
Total runtime: 5412.612 ms
(18 行)

時間: 5465.400 ms
scott=>

やはり! Heap Fetchesとあるのでおかしいと思っていたら...orz. なんで表ブロックアクセスしてんの〜〜〜っ。実行計画は、Index Only Scan。謎。

scott=> select * from pg_statio_user_tables where relname in ('tab31','tab311');
relid | schemaname | relname | heap_blks_read | heap_blks_hit | idx_blks_read | idx_blks_hit | toast_blks_read | toast_blks_hit | tidx_blks_read | tidx_blks_hit
-------+------------+---------+----------------+---------------+---------------+--------------+-----------------+----------------+----------------+---------------
16404 | public | tab311 | 15389 | 36