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年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月 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月29日 (土)

SQLとPL/SQLで「モルダー、あなた疲れてるのよ」を出力する方法

もうかれこれ1年以上経過しているので、何を今更という感じがしないでもないですがw

ここ2週間くらい、SQLチューニングじゃない方向で、非常に忙しかったこともあり

気分的に疲れてしまったので、気分転換のために作ってみました。

Ruby でモルダー、あなた疲れてるのよを出力する方法

Pythonでモルダー案件

Groovyで「モルダー、あなた疲れてるのよ」に対処する

と、どう見てもPL/SQLやSQL案件ではないネタですが、PL/SQL de Python Challenge精神(どんな精神じゃw)で無理やり案件化して見ました。

疲れてる時に疲れるネタやるの?、バカなの? 
とお感じの方もお多いかと存じますが、可能ならもっと変態的なネタに発展することを願いつつ、ご挨拶と代えさせていただきます

データベースエンジニアでも、「モルダー、あなた疲れてるよの」という文字列を出力させる無茶振り案件に突っ込まれるかもしれません。
そんなときの参考になれば幸いです。


スペースで区切ったワードをシャッフルし、「モルダー、あなた疲れてるのよ」が出力されたら終了です。


色々な「モルダー、あなた疲れてるのよ」がありますので、よろしくお願いします!

Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production
に接続されました。
orcl@SYSTEM> @mulder_you_tired

スカリーより
--------------------------------------------------------------------------------
1: 疲れてる、あなたのよモルダー
2: のよ、あなたモルダー疲れてる
3: モルダー、疲れてるあなたのよ
4: 疲れてる、あなたモルダーのよ
5: モルダー、あなたのよ疲れてる
6: のよ、あなたモルダー疲れてる
7: 疲れてる、モルダーあなたのよ
8: モルダー、のよ疲れてるあなた
9: のよ、疲れてるモルダーあなた
10: 疲れてる、のよモルダーあなた
11: モルダー、あなたのよ疲れてる
12: のよ、疲れてるあなたモルダー
13: モルダー、のよあなた疲れてる
14: 疲れてる、あなたのよモルダー
15: 疲れてる、あなたモルダーのよ
16: モルダー、あなたのよ疲れてる
17: のよ、あなたモルダー疲れてる
18: モルダー、あなた疲れてるのよ


コードは以下のとおり、WITH FUNCTIONで文字列を生成しCLOBで1行で返しています。
改行コード(UTL_TCP.CRLFを利用、UTL_TCPパッケージなんて使っている人は少ないと思いますがw)を付加し出力時に改行させ複数行に見せています。
WITH FUNCTIONをSELECT文のセレクトリスト部分で利用しています。(TABLE FunctionならFROM句で使えますが、TABLE Functionではないので)

09:54:34 orcl@SYSTEM> !cat mulder_you_tired.sql
set long 100000
set pagesize 10000
WITH function mulderYouAreTired (dummy char)
RETURN CLOB
IS
cMulderYouTired CONSTANT VARCHAR2(42) := 'モルダー、あなた疲れてるのよ';
vMessage VARCHAR2(42);
vMessageFromScully CLOB := EMPTY_CLOB();
line# BINARY_INTEGER := 1;
--
FUNCTION generateMessage
RETURN VARCHAR2
IS
TYPE word_arr IS TABLE OF VARCHAR2(12) INDEX BY BINARY_INTEGER;
cPunctuation CONSTANT CHAR(3) := '、';
vWords VARCHAR2(45) := 'モルダー あなた 疲れてる のよ';
vMessageFromScully VARCHAR2(42);
vMessage word_arr;
vWord VARCHAR2(13);
isSetWord BOOLEAN := FALSE;
idx BINARY_INTEGER := 1;
BEGIN
--
FOR i IN 1..REGEXP_COUNT(vWords, ' ') + 1 LOOP
vMessage(i) := NULL;
END LOOP;
--
WHILE INSTR(vWords, ' ') != 0 LOOP
vWord := SUBSTR(vWords, 1, INSTR(vWords, ' '));
vWords := REPLACE(vWords, vWord);
isSetWord := FALSE;
WHILE NOT isSetWord LOOP
idx := TRUNC(DBMS_RANDOM.VALUE(vMessage.FIRST, vMessage.LAST + 1));
IF vMessage(idx) IS NULL THEN
vMessage(idx) := TRIM(vWord);
isSetWord := TRUE;
END IF;
END LOOP;
END LOOP;
--
FOR j IN vMessage.FIRST..vMessage.LAST LOOP
IF vMessage(j) IS NULL THEN
vMessage(j) := vWords;
END IF;
END LOOP;
--
FOR x IN vMessage.FIRST..vMessage.LAST LOOP
IF x = 2 THEN
vMessageFromScully := vMessageFromScully || cPunctuation;
END IF;
vMessageFromScully := vMessageFromScully || vMessage(x);
END LOOP;
RETURN vMessageFromScully;
END generateMessage;
BEGIN
LOOP
vMessage := generateMessage();
vMessageFromScully :=
vMessageFromScully || TO_CHAR(line#)
|| ': ' || vMessage || UTL_TCP.CRLF;
line# := line# + 1;
EXIT WHEN vMessage = cMulderYouTired;
END LOOP;
RETURN vMessageFromScully;
END;
SELECT
mulderYouAreTired(null) AS "スカリーより"
FROM
dual;
/

| | コメント (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年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)

2017年3月22日 (水)

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

Previously on Mac De Oracle.

CDBとPDBの間で迷子になりそう PART3 - SQL*PlusのAutotrace編 - その2
auto traceでcontainers句の実行計画をながめたら、原型を留めないほど、変わり果てた実行計画をみて、ここから原型の実行計画をどうやったら見つけられるんだろうかと、途方にくれたところまででしたw


パラレル実行され、各pdbのemp表からempno=7369のデータがindex range scanで取得されているはず。
そして、再帰SQL文として、原文が各PDBで実行されているんだろうなぁ。
といところまではマニュアルの記述と、変わり果てた実行計画をみて、なんとなくイメージはできるんですが。。。

では、どうやったら各PDBで実行されているはずのSQL文や実行計画が見えるんだろう??

auto traceでは見ることはできなかったので、次!、SQL監視で試してみます。(ダメだと思いますが、念のため)

C##HOGE@orcl12c> select /*+ monitor */ * from containers(emp) where empno=7369;

EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO CON_ID
---------- ---------- --------- ---------- -------- ---------- ---------- ---------- ----------
7369 SMITH CLERK 7902 80-12-17 800 20 4
7369 SMITH CLERK 7902 80-12-17 800 20 3


SQL監視をテキストモードで取得する自作スクリプトを実行して、実行計画を見ると....
んーっ。パラレルであるところは気づきやすいですが、やはり、原型を留めぬ変わり果てた実行計画しかみえませんねぇ。

C##HOGE@orcl12c> @show_realplan
1に値を入力してください:
旧 1: select dbms_sqltune.report_sql_monitor('&1') from dual
新 1: select dbms_sqltune.report_sql_monitor('') from dual

DBMS_SQLTUNE.REPORT_SQL_MONITOR('')
--------------------------------------
SQL Monitoring Report

SQL Text
------------------------------
select /*+ monitor */ * from containers(emp) where empno=7369

・・・中略・・・

Global Stats
=================================================
| Elapsed | Cpu | Other | Fetch | Buffer |
| Time(s) | Time(s) | Waits(s) | Calls | Gets |
=================================================
| 0.01 | 0.01 | 0.00 | 2 | 6 |
=================================================

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 Plan Monitoring Details (Plan Hash Value=1439328272)
===================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | (%) | (# samples) |
===================================================================================================================================
| 0 | SELECT STATEMENT | | | | 1 | +0 | 5 | 2 | | |
| 1 | PX COORDINATOR | | | | 1 | +0 | 5 | 2 | | |
| 2 | PX SEND QC (RANDOM) | :TQ10000 | 1 | | 1 | +0 | 4 | 2 | | |
| 3 | PX PARTITION LIST ALL | | 1 | | 1 | +0 | 4 | 2 | | |
| 4 | FIXED TABLE FULL | X$CDBVW$ | 1 | | 1 | +0 | 4 | 2 | | |
===================================================================================================================================

次は、AWRレポートでチャレンジ!

SYS@orcl12c> exec dbms_workload_repository.create_snapshot;

・・・中略・・・

C##HOGE@orcl12c> show con_name

CON_NAME
------------------------------
CDB$ROOT

C##HOGE@orcl12c> select * from containers(emp) where empno=7369;

・・・中略・・・

SYS@orcl12c> exec dbms_workload_repository.create_snapshot;

・・・中略・・・

SYS@orcl12c> @?/rdbms/admin/awrrpt

・・・中略・・・


....おかしいなぁ。捕まえられない!

AWRレポートで捕まえることができなかったのは環境上の影響(Shared Pool Sizeが小さ過ぎでエージアウトしちゃった、とか)もあったのかもしれません。しかもいろいろ動いていたので辛かったはず。
Shared Pool Sizeの下限値を大きめにして、

再チャレンジ。。。ん? 

こんどは取れたっぽい。原型とは多少違いますが、かなり近いSQL文が見つかりました! そいつに違いない!!

というところで、今日は時間切れ、To be continued....


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

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

2017年3月21日 (火)

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

間が空いてしまいましたが、

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

のつづきです。


前回は、共通ユーザを作成し、CDB$ROOT、及び残りの2つのPDBでauto traceができるようになって、さりげなく、containers句なんてのを試していたところまででした。

今日はcontainers句で遊びはじめるところからスタート!
まず初めに、containters句でなにができるのかマニュアルで確認しておきましょう。(マニュアル読めよ! お約束w

45.10 CONTAINERS句を使用したコンテナ間の問合せ
http://docs.oracle.com/cd/E82638_01/ADMIN/viewing-information-about-cdbs-and-pdbs-with-sql-plus.htm#GUID-B302A0DA-8A56-4C18-B140-ADD5E682DE60

45.10.1 CONTAINERS句を使用したコンテナ間の問合せの概要
http://docs.oracle.com/cd/E82638_01/ADMIN/viewing-information-about-cdbs-and-pdbs-with-sql-plus.htm#GUID-AD8C9596-67BB-47FA-A728-16F9C9B0AADF

45.10.3 アプリケーションPDB間のアプリケーション共通オブジェクトの問合せ
http://docs.oracle.com/cd/E82638_01/ADMIN/viewing-information-about-cdbs-and-pdbs-with-sql-plus.htm#GUID-7B4E4926-19C6-47A5-A4E3-6BD279F080F0

containers句を利用すると、
CDB$ROOTの共通ユーザから各PDBの同一共通ユーザにある同一表を問い合わせることができるようになる。
また、containers句を利用したクエリーから再帰SQLが生成され、デフォルトではパラレル化される。。と。
なお、パラレル化はされるが、パラレル文のキューイング対象でもない! (え! 対象外なの?)

この時点で、癖者感がw

再帰的SQL実行と、さらり書かれているところ。。気になる気になる、気になりすぎて眠れないw

気になったら確かめないと!!

CDB$ROOTのsysユーザにて全PDBが起動していることを確認!

SYS@orcl12c> show pdbs

CON_ID CON_NAME OPEN MODE RESTRICTED
---------- ------------------------------ ---------- ----------
2 PDB$SEED READ ONLY NO
3 PDBORCL12C READ WRITE NO
4 PDBORCL12CLONED READ WRITE NO


CDB$ROOTの共通ユーザ:C##HOGEにEMP表(空)があるこを確認
CDB$ROOTにも同一名のオブジェクトが存在しないとエラーになるのでご注意を。(ここは別エントリで書くかも)

SYS@orcl12c> conn c##hoge/hoge@orcl12c
接続されました。
C##HOGE@orcl12c> select table_name from user_tables where table_name='EMP';

TABLE_NAME
------------------------------
EMP

C##HOGE@orcl12c> select count(*) from emp;

COUNT(*)
----------
0


以下、残る2つのPDBで、共通ユーザ:C##HOGEユーザにEMP表(データあり)が存在することを確認!

C##HOGE@orcl12c> conn c##hoge/hoge@pdborcl12c
接続されました。
C##HOGE@pdborcl12c> select table_name from user_tables where table_name='EMP';

TABLE_NAME
------------------------------
EMP

C##HOGE@pdborcl12c> select count(*) from emp;

COUNT(*)
----------
14

C##HOGE@pdborcl12c> conn c##hoge/hoge@pdborcl12cloned
接続されました。
C##HOGE@pdborcl12cloned> select table_name from user_tables where table_name='EMP';

TABLE_NAME
------------------------------
EMP

C##HOGE@pdborcl12cloned> select count(*) from emp;

COUNT(*)
----------
14


containers句を含む以下のクエリーをCDB$ROOTの共通ユーザ:C##HOGEユーザから実行!

SYS@orcl12c> conn c##hoge/hoge
接続されました。
C##HOGE@orcl12c> select * from containers(emp) where empno=7369;

EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO CON_ID
---------- ---------- --------- ---------- -------- ---------- ---------- ---------- ----------
7369 SMITH CLERK 7902 80-12-17 800 20 4
7369 SMITH CLERK 7902 80-12-17 800 20 3

auto traceでどんな実行計画なのか見てみます(CDB$ROOTでauto traceをやりたかったのはこういうことだったんでっす!w)

マニュアルに記載されている通り、パラレルクエリーになってます!
しかし、実行計画には、emp表という名前は見当たらず!! しかも、EMPNOには主キー制約があるのに、Predicate Information にはfilter("EMPNO"=7369)とある。

....."生成される再帰的SQL文ということばが浮かんで消える":) 不思議な実行計画が!!!!!!

でも、この実行計画、なんとなく見覚え.....ないですか????

C##HOGE@orcl12c> set autot trace exp stat
C##HOGE@orcl12c> select * from containers(emp) where empno=7369;

実行計画
----------------------------------------------------------
Plan hash value: 1439328272

----------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Pstart| Pstop | TQ |IN-OUT| PQ Distrib |
----------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 100 | 0 (0)| | | | | |
| 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 | |
----------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

4 - filter("EMPNO"=7369)


統計
----------------------------------------------------------
303 recursive calls
0 db block gets
249 consistent gets
9 physical reads
0 redo size
1220 bytes sent via SQL*Net to client
552 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
18 sorts (memory)
0 sorts (disk)
2 rows processed


見覚えがあると思ったら、まさにこれですよね。
12.1.0.2で突然登場した機能のようにも見えるが、実は、12.1.0.1のころからあった機能らしい。CDB$VIEW clauseとして。
実行計画もそっくり

12.1.0.2 CDB views are now using CONTAINERS() / dbi services BLOG
https://blog.dbi-services.com/12102-cdb-views-are-now-using-containers/

SQL> select * from v$version;

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

SQL> show con_name

CON_NAME
------------------------------
CDB$ROOT
SQL>
SQL> select * from cdb$view(dual);


実行計画
----------------------------------------------------------
Plan hash value: 4256768476

-----------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop | TQ |IN-OUT| PQ Distrib |
-----------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 2 | 0 (0)| 00:00:01 | | | | | |
| 1 | PX COORDINATOR | | | | | | | | | | |
| 2 | PX SEND QC (RANDOM) | :TQ10000 | 1 | 2 | 0 (0)| 00:00:01 | | | Q1,00 | P->S | QC (RAND) |
| 3 | PX PARTITION LIST ALL| | 1 | 2 | 0 (0)| 00:00:01 | 1 | 254 | Q1,00 | PCWC | |
| 4 | FIXED TABLE FULL | X$CDBVW$42ae64ee | 1 | 2 | 0 (0)| 00:00:01 | | | Q1,00 | PCWP | |
-----------------------------------------------------------------------------------------------------------------------------------------


統計
----------------------------------------------------------
12 recursive calls
0 db block gets
6 consistent gets
0 physical reads
0 redo size
688 bytes sent via SQL*Net to client
544 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
2 rows processed

45.10.3 アプリケーションPDB間のアプリケーション共通オブジェクトの問合せ
これ、アプリケーションからお気軽に使っちゃってよい機能なんだろうか。。。どのようなユースケースを想定した機能なんだろう?。。。

to be continued...

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

2017年1月 9日 (月)

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

今回迷子になったのは、昔からあるSQL*PlusのAutotraceで.

Autotraceの使い方は、$ORACLE_HOME/sqlplus/admin/plustrce.sqlをSYSユーザーで実行し、PLUSTRACEロール作成されたら必要なユーザーへ該当ロールを付与。
あとは、SQL*Plusでautotraceを有効にする。

これだけです。

が、

マルチテナント(シングルテナント含む)化した途端に、迷子になりそうな場面に出会います!
(これにハマったことのあるかたは、#ローカルロール! ってハッシュタグでtw...しなくてもいいですw)

マルチテナントでは、共通ユーザー、共通ロール、ローカルユーザー、ローカルロールという2つのタイプのユーザーとロールが登場しました。
CDB$ROOT/各PDBのディクショナリービュー同様、頭では理解したつもりでも指が勝手タイプして迷子になっちゃいうこともあるw (実はOracleさんも?。。。だったりしてね。

余談はこれぐらいにして、とにかく試して見ましょう。

バージョンは以下のとおり

SYS@orcl12c> 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

マルチテナントにしてあります。(シングルテナントでも同じですが)

SYS@orcl12c> show pdbs

CON_ID CON_NAME OPEN MODE RESTRICTED
---------- ------------------------------ ---------- ----------
2 PDB$SEED READ ONLY NO
3 PDBORCL12C READ WRITE NO
4 PDBORCL12CLONED READ WRITE NO


今日の主役、共通ユーザを作成します。
共通ユーザーとするためには、c##(デフォルト)という接頭子をユーザ名に付加する必要があります。(なれるまで辛いw)

SYS@orcl12c> create user c##hoge identified by hoge
2 default tablespace users
3 temporary tablespace temp
4 quota unlimited on users
5 container=all;

ユーザーが作成されました。


CDB$ROOT含む全PDBにC##HOGEユーザが作成されました。
(実はシステム定義の管理ユーザーは共通ユーザー扱になっていることも忘れちゃいけないんですよね。おそらく同じというか特徴はあるはず)

SYS@orcl12c> select con_id,username,common from containers(dba_users) where username in ('SYS','C##HOGE')

CON_ID USERNAME COM
---------- ------------------------------ ---
1 SYS YES
1 C##HOGE YES
4 SYS YES
4 C##HOGE YES
3 SYS YES
3 C##HOGE YES

※さりげなく、PDB CONTAINER句(赤字部分)を使ってますが、もともとそれで遊びたかっただけなんです。


今後のお遊び向けにいくつかのシステム権限の付与...

SYS@orcl12c> grant create session ,resource to c##hoge container=all;

権限付与が成功しました。

SYS@orcl12c> grant create table, create view to c##hoge container=all;

権限付与が成功しました。

SYS@orcl12c> grant create synonym to c##hoge container=all;

権限付与が成功しました。


共通ユーザーでもSQL*Plusのautotrace使いたいな〜と。
(最近はアダプティブな実行計画の影響で影が薄いAutotraceやExplain planですが、とりあえず見たい時や、実行統計を軽く見たいときには便利なんで)

と、思ったとことから、迷子になりまして、、はいw

CDB$ROOTの共通ユーザー接続して、autotraceを有効にしようとしました。
みなれたエラーがでるわけですよ。ロール作ってないので:)

”SP2-0618: セッション識別子が見つかりません。PLUSTRACEロールが有効かを確認してください。”

SYS@orcl12c> conn c##hoge/hoge
接続されました。
C##HOGE@orcl12c> show con_name

CON_NAME
------------------------------
CDB$ROOT

C##HOGE@orcl12c>
C##HOGE@orcl12c> set autot on
SP2-0618: セッション識別子が見つかりません。PLUSTRACEロールが有効かを確認してください。
SP2-0611: STATISTICSレポートを使用可能にするときにエラーが発生しました。


いつものように、$ORACLE_HOME/sqlplus/admin/plustrce.sqlを実行してPLUSTRACEロールを作成しようとすると。。。。。
なんと、ロールが作成できません!!!

そりゃ、そうですよね。CDB$ROOTには共通なものが載るわけですから、。。。
マルチテナント化してあるので、ロールも共通ロールを作成する必要があります。。。オラクルさん。。。。痛恨のミス?!w

PLUSTRACEというロールをCDB$ROOTに作成しようとするとローカルロールを作成している扱いになるので、共通ロールにしてねというエラーが返されます。

どうすんだよwこれw
と、初っ端から迷子ですw (MOSは未確認。なにか記載されてそうな気はしますがw)

C##HOGE@orcl12c> conn / as sysdba
接続されました。
SYS@orcl12c> @?/sqlplus/admin/plustrce
SYS@orcl12c>

...中略...

1SYS@orcl12c> create role plustrace;
create role plustrace
*
行1でエラーが発生しました。:
ORA-65096: 共通ユーザーまたはロール名が無効です

共通ロールにしろというんだから、素直に共通ロールを作ってみます。
$ORACLE_HOME/sqlplus/admin/plustrce.sqlを元に、ロール名をPLUSTRACEからC##PLUSTRACEに変更したスクリプトを作成しました。

共通ロールについては、以下も参照のこと。
Oracle® Databaseセキュリティ・ガイド 12cリリース1 (12.1) 共通ロールの作成

以下のように、C##(共通ユーザやロールのデフォルト接頭子)を付加し共通ユーザ向けロールを作成すればできるようになりますよね!。使う機会があるかどうかは別ですがw

C##HOGE@orcl12c> !cat $ORACLE_HOME/sqlplus/admin/plustrce_common.sql 

drop role c##plustrace;
create role c##plustrace;

grant select on v_$sesstat to c##plustrace;
grant select on v_$statname to c##plustrace;
grant select on v_$mystat to c##plustrace;
grant c##plustrace to dba with admin option;

C##PLUSTRACEの作成ログ
うまくいきました!!!

SYS@orcl12c> drop role c##plustrace;
drop role c##plustrace
*
行1でエラーが発生しました。:
ORA-01919: ロール'C##PLUSTRACE'は存在しません

SYS@orcl12c> create role c##plustrace;

ロールが作成されました。

SYS@orcl12c> grant select on v_$sesstat to c##plustrace;

権限付与が成功しました。

SYS@orcl12c> grant select on v_$statname to c##plustrace;

権限付与が成功しました。

SYS@orcl12c> grant select on v_$mystat to c##plustrace;

権限付与が成功しました。

SYS@orcl12c> grant c##plustrace to dba with admin option;

権限付与が成功しました。

作成したautotrace用共通ロールをcontainter=allで共通ユーザに付与したのですが。。。。
実は、container=allとしても。PDB側では今まで通り、$ORACLE_HOME/sqlplus/admin/plustrce.sqlを各PDBのSYSユーザー実行し、PLUSTRACEロール(ローカルロール)をPDBに存在する共通ユーザ個別に付与する必要があるようです。(面倒くさい><)

SYS@orcl12c> grant c##plustrace to c##hoge container=all;

C##HOGE@orcl12c> conn / as sysdba
接続されました。
SYS@orcl12c>
SYS@orcl12c> conn c##hoge/hoge
接続されました。
C##HOGE@orcl12c> show con_name

CON_NAME
------------------------------
CDB$ROOT
C##HOGE@orcl12c> set autot trace exp stat
C##HOGE@orcl12c> select * from dual;

実行計画
----------------------------------------------------------
Plan hash value: 272002086

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 2 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| DUAL | 1 | 2 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------

統計
----------------------------------------------------------
0 recursive calls
0 db block gets
3 consistent gets
0 physical reads
0 redo size
561 bytes sent via SQL*Net to client
552 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed


PDB側のC##HOGEユーザーには繼承されない。。。。。かるく迷子になってる状況。

C##HOGE@orcl12c> conn c##hoge/hoge@pdborcl12c
接続されました。
C##HOGE@pdborcl12c>
C##HOGE@pdborcl12c> show con_name

CON_NAME
------------------------------
PDBORCL12C
C##HOGE@pdborcl12c> set autot trace exp stat
SP2-0618: セッション識別子が見つかりません。PLUSTRACEロールが有効かを確認してください。
SP2-0611: STATISTICSレポートを使用可能にするときにエラーが発生しました。

各PDBのローカルユーザーは、以前からあるplustrce.sqlを各PDBごとに個別に作成、付与する必要があるみたい...もう完全に迷子w。
(本当はこれをネタしたかったわけじゃなかったんですけどね、トホホ)

C##HOGE@pdborcl12c> conn sys@pdborcl12c as sysdba
パスワードを入力してください:
接続されました。
SYS@pdborcl12c>
SYS@pdborcl12c>
SYS@pdborcl12c> show con_name

CON_NAME
------------------------------
PDBORCL12C
SYS@pdborcl12c>
SYS@pdborcl12c> @?/sqlplus/admin/plustrce

...中略...

SYS@pdborcl12c> conn c##hoge/hoge@pdborcl12c
接続されました。
C##HOGE@pdborcl12c> set autot trace exp stat
C##HOGE@pdborcl12c> select * from dual;


実行計画
----------------------------------------------------------
Plan hash value: 272002086

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 2 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| DUAL | 1 | 2 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------

統計
----------------------------------------------------------
1 recursive calls
0 db block gets
3 consistent gets
2 physical reads
0 redo size
561 bytes sent via SQL*Net to client
552 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed


次回、本当に試したかったことへ、つづく....(予定)
久々にその機能本気で使うのかって、ネタになりそうで。。。いいかも(こんな展開は予想してなかっただけに;)


以上、はじめての、 Yahoo! Japan Coworking Space LODGE より


関連エントリー FAQ になりそうな、12c MTA環境での統計履歴管理 ー CDBとPDBの間で迷子になりそう PART2

| | コメント (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年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月27日 (日)

OracleのB*Tree索引にはNULLが含まれる場合があるんです! - その性質を使ってチューニングすることもあるよ:) その3


前回までのMac De Oracle

OracleのB*Tree索引にはNULLが含まれる場合があるんです! - その性質を使ってチューニングすることもあるよ:)
OracleのB*Tree索引にはNULLが含まれる場合があるんです! - その性質を使ってチューニングすることもあるよ:) その2

ということで、続きで〜〜すっ。

こんな表定義で

orcl@SCOTT> desc tab01
Name Null? Type
--------- -------- --------
FOO NUMBER
BAR NUMBER
HOGE NOT NULL CHAR(2)
ID NOT NULL NUMBER

こんな索引があって

****** Index column info : tab01 ******

INDEX_NAME COLUMN_NAME DESC
------------------------------ ------------------------------ ----
IX1_TAB01 FOO ASC

IX2_TAB01 BAR ASC
FOO ASC

IX3_TAB01 ID ASC
FOO ASC

IX4_TAB01 ID ASC
BAR ASC
FOO ASC

PK_TAB01 ID ASC


こんなデータで

orcl@SCOTT> set null [NULL]
orcl@SCOTT> select * from tab01

FOO BAR HO ID
---------- ---------- -- ----------
[NULL] [NULL] ** 1
1 [NULL] ** 2
[NULL] 1 ** 3
1 1 ** 4


2列の複合索引、どちらの列もNULLだと、やはり、NULLは索引に含まれないので IS NULL検索だと索引は利用されないですよねぇ〜。このような状態ではヒントで索引利用を強制利用させようとしても無理です。

orcl@SCOTT> r
1 select
2 /*+
3 gather_plan_statistics
4 index(tab01 ix2_tab01)
5 no_index(tab01 ix4_tab01)
6 no_index(tab01 ix3_tab01)
7 */
8 *
9 from
10 tab01
11 where
12 foo is null
13* and bar is null
     
FOO BAR HO ID
---------- ---------- -- ----------
[NULL] [NULL] ** 1

・・・略・・・
-------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 8 |
|* 1 | TABLE ACCESS FULL| TAB01 | 1 | 1 | 1 |00:00:00.01 | 8 |
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter(("FOO" IS NULL AND "BAR" IS NULL))

でも、NOT NUL制約の列が1列でも含まれている索引であればNULLは索引に含まれます。(前回までの復習も兼ねた確認)
第1列がWHERE句で記述されていないので索引スキップスキャンになっていますが.....IS NULL検索を索引アクセスだけで行っているのがよく分かる例の一つです:)

orcl@SCOTT> r
1 select
2 /*+
3 gather_plan_statistics
4 */
5 *
6 from
7 tab01
8 where
9 foo is null
10* and bar is null

FOO BAR HO ID
---------- ---------- -- ----------
[NULL] [NULL] ** 1

・・・略・・・
--------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |
--------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 3 | 1 |
| 1 | TABLE ACCESS BY INDEX ROWID BATCHED| TAB01 | 1 | 1 | 1 |00:00:00.01 | 3 | 1 |
|* 2 | INDEX SKIP SCAN | IX4_TAB01 | 1 | 1 | 1 |00:00:00.01 | 2 | 1 |
--------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - access("BAR" IS NULL AND "FOO" IS NULL)
filter(("FOO" IS NULL AND "BAR" IS NULL))


最後にもう少しわかりやすい例を。

SQL文を少々書き換えてIndex Only Scanになるようにしました。索引にNULLが含まれていないと索引だけのアクセスで済むわけがないわけで、これ以上わかりやすい例はないと思います:)

まず、索引が利用できない例から。
FOO列とBAR列だけの複合索引をヒントで強制利用させようとしていますが、この索引は2列ともnullableなので2列をIS NULL検索しても索引が利用されません!
全ての列がNULLである場合、キーエントリーは索引に作成されない。単一列でも複合索引でも同じであることが確認できます。

orcl@SCOTT> r
1 select
2 /*+
3 gather_plan_statistics
4 index(tab01 ix2_tab01)
5 no_index(tab01 ix4_tab01)
6 no_index(tab01 ix3_tab01)
7 */
8 *
9 from
10 tab01
11 where
12 foo is null
13* and bar is null

FOO BAR HO ID
---------- ---------- -- ----------
[NULL] [NULL] ** 1

・・・略・・・
-------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 8 |
|* 1 | TABLE ACCESS FULL| TAB01 | 1 | 1 | 1 |00:00:00.01 | 8 |
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter(("FOO" IS NULL AND "BAR" IS NULL))

おっと、
大切なのを忘れてました。

FOO列(nullable(、BAR列(nullable)の複合索引を IS NOT NULL AND IS NULLで検索した場合はどうなるか?
答えは以下の通り。IS NULL と IS NOT NULLの組み合わせでも、索引が利用されます。:)

BAR IS NULLで範囲検索しFOO IS NOT NULLでフィルタリングしています。 NULLが含まれていないと不可能な索引レンジスキャンと索引読み時のフィルタリング!

orcl@SCOTT> r
1 select
2 /*+
3 gather_plan_statistics
4 no_index(tab01 ix3_tab01)
5 no_index(tab01 ix4_tab01)
6 */
7 foo
8 , bar
9 from
10 tab01
11 where
12 foo is not null
13* and bar is null

FOO BAR
---------- ----------
1 [NULL]

・・・略・・・
-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.02 | 1 | 1 |
|* 1 | INDEX RANGE SCAN| IX2_TAB01 | 1 | 1 | 1 |00:00:00.02 | 1 | 1 |
-------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------

1 - access("BAR" IS NULL)
filter("FOO" IS NOT NULL)

その逆も!

orcl@SCOTT> r
1 select
2 /*+
3 gather_plan_statistics
4 no_index(tab01 ix3_tab01)
5 no_index(tab01 ix4_tab01)
6 */
7 foo
8 , bar
9 from
10 tab01
11 where
12 foo is null
13* and bar is not null

FOO BAR
---------- ----------
[NULL] 1

・・・略・・・
----------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 2 |
|* 1 | INDEX SKIP SCAN | IX2_TAB01 | 1 | 1 | 1 |00:00:00.01 | 2 |
----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - access("FOO" IS NULL)
filter(("FOO" IS NULL AND "BAR" IS NOT NULL))


もういっちょ!
2列とものnullableな索引だとindex fast full scanにはできないけど、index full scanにはできるんですよ〜。

orcl@SCOTT7gt; r
1 select
2 /*+
3 gather_plan_statistics
4 no_index(tab01 ix3_tab01)
5 no_index(tab01 ix4_tab01)
6 */
7 foo
8 , bar
9 from
10 tab01
11 where
12 foo is not null
13* and bar is not null

FOO BAR
---------- ----------
1 1

・・・略・・・
----------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 2 |
|* 1 | INDEX FULL SCAN | IX2_TAB01 | 1 | 1 | 1 |00:00:00.01 | 2 |
----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter(("FOO" IS NOT NULL AND "BAR" IS NOT NULL))

Note
-----
- statistics feedback used for this statement

後に、NOT NULL列が含まれる索引なら index fast full scanでもできるはず!
独り言;index only scan+index fast full scanなんてのもセグメントサイズが小さければ物理読み込み量削減には効果があるんですよねぇ〜 ;-)

orcl@SCOTT> r
1 select
2 /*+
3 gather_plan_statistics
4 no_index(tab01 ix3_tab01)
5 index_ffs(tab01 ix4_tab01)
6 no_index(tab01 ix2_tab01)
7 */
8 foo
9 , bar
10 from
11 tab01
12 where
13 foo is not null
14* and bar is not null

FOO BAR
---------- ----------
1 1

・・・略・・・
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.03 | 4 | 1 |
|* 1 | INDEX FAST FULL SCAN| IX4_TAB01 | 1 | 1 | 1 |00:00:00.03 | 4 | 1 |
-----------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter(("FOO" IS NOT NULL AND "BAR" IS NOT NULL))


オラクルの索引にNULLは絶対含まれない、というのは都市伝説! 
IS NOT NULLは索引使えないとかIS NULLは索引使えないというというのも違うんですよね。 

OracleのB*Tree索引では、索引に含まれる全列がNULLの場合以外はNULLが含まれてまっす! というのが正しいですよね!?

この手の問題でピンチになったら、思い出してみてください ;)
USE THE INDEX ONLY SCAN, LUKE! w

| | コメント (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月30日 (水)

Pipelined table function で ascii art

クリスマスも過ぎ、新年へのカウントダウンも始まっていますが、
Pipelined table functionを使って簡単なASCIIアートをJPOUG Advent Calendar 2012描画してましたが中身を公開してなかったな〜
ということで、少々手直しして公開!

公開しておくとPipelined table functionとかtable functionを書かなきゃなった時に思い出しやすいのでw 自分向けなんですけどね。 (^^;;


参考:
Oracle Database Online Documentation 12c Release 1 (12.1)
Unstructured Data and Content Management
13 Using Pipelined and Parallel Table Functions



create or replace type messageTblType as table of varchar2(300)
/


注)
以下関数ではdbms_lock.sleep()を利用しているため、事前にexecute権限を付与しておく必要があります。
また、エラー処理は端折ってます. (^^;;
 

create or replace function tree(lheight pls_integer, linterval_change_color float)
return messageTblType
pipelined
as
height_of_tree pls_integer := lheight;
center pls_integer;
i pls_integer := 1;
siz_of_trunk pls_integer := 3;
pos_of_trunk pls_integer;
interval_change_color float := linterval_change_color;
begin
center := ceil((1 + height_of_tree + ( height_of_tree - 2)) / 2);
pos_of_trunk := case when ceil(height_of_tree / 5) > 10 then 10 else ceil(height_of_tree / 5) end;
for l in 1..75 loop
pipe row(chr(27)||'[2J'||chr(27)||'[1;'||to_char(ceil(dbms_random.value(1,7))+30)||'m');
i := 1;
--draw leaves
while (i < height_of_tree) loop
pipe row(
chr(27)||'['||to_char(i,'FM0999')||';'||to_char(center - i,'FM0999')||'H'||lpad('*', i + (i - 2) + 1, '*')
);
i := i + 1;
end loop;
--draw trunk
for j in -1..pos_of_trunk loop
pipe row(
chr(27)||'['||to_char(j + height_of_tree,'FM0999')||';'||to_char(center - 2,'FM0999')||'H'||lpad('*', siz_of_trunk, '*')
);
end loop;
dbms_lock.sleep(interval_change_color);
end loop;
pipe row(chr(27)||'[0m');
return;
end;
/
show errors


以下、実行コードの例
Treeのサイズを10/20/50、色の変更感覚を0.2秒として実行してます。


set linesize 300
set pagesize 0
set array 1
select * from table(tree(10, 0.2));
select * from table(tree(20, 0.2));
select * from table(tree(50, 0.2));

実行結果はYoutubeで:)


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

2015年12月26日 (土)

クリスマスのお遊び - SQL de Fractals :)

Twitterを見ていたら以下のtweetが目に入った! お! 面白そう。

元のサイトを覗いてみたら :) w
EXPLAIN EXTENDED -
http://explainextended.com/2013/12/31/happy-new-year-5/

元ネタはPostgreSQLだったので、Oracle Database 12c R1で書き換えて遊んでみた。 :)
遊びながら覚えるSQLってのも面白いよね。久々にLISTAGGなんて使ったよw
20151226_23132


元々あるOracleの方言、階層問合せと、11gR2から実装された階層再帰問合せを組み合わせてます。
PostgreSQLでは、generate_series()関数が利用されていますが、Oracleでは階層問合せかCUBEとrownumを組み合わせて生成するしかないのでちょいとカッコ悪いかもw
ただ、ARRAY_TO_STRING(ARRAY_AGG(s ORDER BY r), '') の箇所は、Oracleなら LISTAGG WITHIN...と1つの関数で代替できたので判りやすいかも。

WITH
q (r, i, rx, ix, g) AS
(
SELECT
CAST(r.r AS DOUBLE PRECISION) * 0.02 AS r
, CAST(i.i AS DOUBLE PRECISION) * 0.02 AS i
, CAST(.0 AS DOUBLE PRECISION) AS rx
, CAST(.0 AS DOUBLE PRECISION) AS ix
, 0 AS g
FROM
(
SELECT
LEVEL - 61 AS r
FROM
DUAL
CONNECT BY
LEVEL <= 80
) r,
(
SELECT
LEVEL - 51 AS i
FROM
DUAL
CONNECT BY
LEVEL <= 100
) i
UNION ALL
SELECT
r
, i
, CASE
WHEN ABS(rx * rx + ix * ix) <= 2
THEN
rx * rx - ix * ix
END + r AS rx
, CASE
WHEN ABS(rx * rx + ix * ix) <= 2
THEN
2 * rx * ix
END + i AS ix
, g + 1 AS g
FROM
q
WHERE
rx IS NOT NULL
AND g < 99
)
SELECT
LISTAGG(s,'') WITHIN GROUP ( ORDER BY r ) AS Mandelbrot
FROM
(
SELECT
i
, r
, SUBSTR(' .:-=+*#%@', MAX(g) / 10 + 1, 1) s
FROM
q
GROUP BY
i
, r
) q
GROUP BY
i
ORDER BY
i
;


実行計画も載せておきますね
こんな実行計画になるのか〜〜〜面白〜い :) :) :)

Global Stats
=================================================
| Elapsed | Cpu | Other | Fetch | Buffer |
| Time(s) | Time(s) | Waits(s) | Calls | Gets |
=================================================
| 35 | 35 | 0.05 | 8 | 22M |
=================================================

SQL Plan Monitoring Details (Plan Hash Value=1659179395)
============================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Mem | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | (Max) | (%) | (# samples) |
============================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 1 | +35 | 1 | 100 | | | |
| 1 | SORT GROUP BY | | 2 | 8 | 1 | +35 | 1 | 100 | 499K | | |
| 2 | VIEW | | 2 | 8 | 1 | +35 | 1 | 8000 | | | |
| 3 | HASH GROUP BY | | 2 | 8 | 34 | +2 | 1 | 8000 | 1M | | |
| 4 | VIEW | | 2 | 8 | 34 | +2 | 1 | 424K | | | |
| 5 | UNION ALL (RECURSIVE WITH) BREADTH FIRST | | | | 35 | +1 | 1 | 424K | 16M | 100.00 | Cpu (35) |
| 6 | MERGE JOIN CARTESIAN | | 1 | 4 | 1 | +2 | 1 | 8000 | | | |
| 7 | VIEW | | 1 | 2 | 1 | +2 | 1 | 80 | | | |
| 8 | CONNECT BY WITHOUT FILTERING | | | | 1 | +2 | 1 | 80 | 2048 | | |
| 9 | FAST DUAL | | 1 | 2 | 1 | +2 | 1 | 1 | | | |
| 10 | BUFFER SORT | | 1 | 4 | 1 | +2 | 80 | 8000 | 4096 | | |
| 11 | VIEW | | 1 | 2 | 1 | +2 | 1 | 100 | | | |
| 12 | CONNECT BY WITHOUT FILTERING | | | | 1 | +2 | 1 | 100 | 2048 | | |
| 13 | FAST DUAL | | 1 | 2 | 1 | +2 | 1 | 1 | | | |
| 14 | RECURSIVE WITH PUMP | | | | 34 | +2 | 100 | 416K | | | |
============================================================================================================================================================


追記!
Sql_de_fractals_frost_pattern


WITH
q (r, i, rx, ix, g) AS
(
SELECT
CAST(r.r AS DOUBLE PRECISION) * 0.0002 AS r
, CAST(i.i AS DOUBLE PRECISION) * 0.0002 AS i
, CAST(r.r AS DOUBLE PRECISION) * 0.0002 AS rx
, CAST(i.i AS DOUBLE PRECISION) * 0.0002 AS ix
, 0 AS g
FROM
(
SELECT
LEVEL - 201 AS r
FROM
DUAL
CONNECT BY
LEVEL <= 120
) r,
(
SELECT
LEVEL - 1 AS i
FROM
DUAL
CONNECT BY
LEVEL <= 100
) i
UNION ALL
SELECT
r
, i
, CASE
WHEN ABS(rx * rx + ix * ix) < 1E+8
THEN
rx * rx - ix * ix
END - 0.70176 AS rx
, CASE
WHEN ABS(rx * rx + ix * ix) < 1E+8
THEN
2 * rx * ix
END + 0.3842 AS ix
, g + 1 AS g
FROM
q
WHERE
rx IS NOT NULL
AND g < 99
)
SELECT
LISTAGG(s,'') WITHIN GROUP ( ORDER BY r ) AS frost_patterns
FROM
(
SELECT
i
, r
, SUBSTR(' .:-=+*#%@', MAX(g) / 10 + 1, 1) s
FROM
q
GROUP BY
i
, r
) q
GROUP BY
i
ORDER BY
i
;


| | コメント (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年6月24日 (水)

理論から学ぶデータベース実践入門 ~リレーショナルモデルによる効率的なSQL

久々に脳にイヤな感じの汗をかいたw

一度読んだだけでは理解できず、何度か繰り返し読み、自分なりに消化する必要のあるタイプの書籍だと思います。
(日々のトレーニング向けに一冊、みたいな)

話は変わりますが、

ワンスアポンナタイム。あるところで。
神チューナーのお一人と取り組んでいたプロジェクトで、イマイチな感じのSQLsを書いてる方々向けに神チューナーが、一言。

ド・モルガンの法則により....」

彼の一言で、場がフリーズしたのを見てニヨニヨしていた私。
(それまで、B*Treeインデックスだの、Index only scanだの、駆動表だの言っていたのにいきなりですからね!w)


そんな一言を投げかけられてもフリーズしない基礎体力をつけるには、この手の書籍はピッタリじゃないかと思うわけです。はい。


| | コメント (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月30日 (水)

Oracle Database 12c R1 12.1.0.2.0 In-memory option はじめの一歩。

こまけーことは置いといて、
Oracle Database 12c R1 12.1.0.2.0 In-memory optionが利用できるようになったので簡単に試してみた。


環境は以下の通り

Oracle VM VirtualBox 4.3.12 for OS X
Oracle Linux 6.4
Oracle Database 12c EE 12.1.0.2.0 for Linux x86-64


時間が無かったので、マニュアルを斜め読みしつつ、とにかくインストール....
inmemory系初期化パラメータの確認。 (12.1.0.1と12.1.0.2の初期化パラメータの差分確認はあとで:TODO)

inmemory_sizeパラメータが0なのでin-memory column storeは機能しない状態がデフォルトなのか..

Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
With the Partitioning, OLAP, Advanced Analytics and Real Application Testing options
に接続されました。
SYS>
SYS> 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
SYS>

以下の表を作成してあります。 10万行のうちb='1'である行は10行だけにしてあります。

SCOTT> desc foobar
名前 NULL? 型
----------------------------------------- -------- ----------------------------
A NUMBER
B CHAR(1)


SCOTT> select count(1) from foobar;

COUNT(1)
----------
100000


今まで通りに表を作成すると、in-memory column storeはdisabledで作成されるのか! ふむふむ。

SCOTT> select table_name,inmemory from user_tables where table_name='FOOBAR';

TABLE_NAME INMEMORY
------------------------------ --------
FOOBAR DISABLED

索引は作成していないので以下のようなクエリを実行すれば、TABLE ACCESS FULLですよね。

SCOTT> select /*+ gather_plan_statistics */ count(1) from foobar where b = '1';

COUNT(1)
----------
10

SCOTT> @show_actual_plan

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------
SQL_ID 4zurd70pg2qna, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count(1) from foobar where b = '1'

Plan hash value: 2479556450

---------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 191 |
| 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.01 | 191 |
|* 2 | TABLE ACCESS FULL| FOOBAR | 1 | 15 | 10 |00:00:00.01 | 191 |
---------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - filter("B"='1')

Note
-----
- dynamic statistics used: dynamic sampling (level=2)


23行が選択されました。


in-memory column storeを利用するための準備〜〜〜。
alter table文でin-memory column storeを有効化。(簡単だな!)

SCOTT> alter table foobar inmemory;

表が変更されました。


おっと、忘れてました、 inmemory_sizeパラメータを設定しないといけないんだった...

ちなみに、inmemory_sizeパラメータは静的パラメータなので、scope=spfileを付けることをお忘れなく...

SYS> alter system set inmemory_size = 10m;
alter system set inmemory_size = 10m
*
行1でエラーが発生しました。:
ORA-02097: 指定した値が無効なので、パラメータを変更できません。 ORA-02095:
指定した初期化パラメータを変更できません。


メモリ関係のパラメータ変えて起動しなくなるって過去やらかしたことがあるので、手が勝ってに動くw

SYS> create pfile from spfile;

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

変更できたました。
SYS> alter system set inmemory_size = 10m scope=spfile;

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

SYS>

....中略....

起動してみましょう! あらららら。

SYS> startup
ORA-64353: in-memory area size cannot be less than 100MB

ううううう、なんということでしょう! w このパラメータ間違うと起動しないのな!!!!! 注意しないと。 > 俺


in-memory初期化パラメータは 100MB以上に設定しましょうね。マニュアルにも書いてますから。(読みましょうね。 (^^;;;;

気を取り直して、再チャレンジ :)

[oracle@emperor ˜]$ sqlplus / as sysdba

SQL*Plus: Release 12.1.0.2.0 Production on 火 7月 29 06:34:46 2014

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

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

SYS> create spfile from pfile;

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

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

Total System Global Area 3875536896 bytes
Fixed Size 3717856 bytes
Variable Size 2248148256 bytes
Database Buffers 1610612736 bytes
Redo Buffers 13058048 bytes
データベースがマウントされました。
データベースがオープンされました。

SYS> alter system set inmemory_size = 100m scope=spfile;

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

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


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

Total System Global Area 3875536896 bytes
Fixed Size 3717856 bytes
Variable Size 2231371040 bytes
Database Buffers 1509949440 bytes
Redo Buffers 13058048 bytes
In-Memory Area 117440512 bytes
データベースがマウントされました。
データベースがオープンされました。
SYS>
SYS>


In-Memory Areaとリストされています。
うまくいったようなので in-memory column storeを試してみます!!!!

SCOTT> select /*+ gather_plan_statistics */ count(1) from foobar where b = '1';

COUNT(1)
----------
10

SCOTT>
SCOTT>
SCOTT> @show_actual_plan

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------
SQL_ID 4zurd70pg2qna, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count(1) from foobar where b = '1'

Plan hash value: 2479556450

------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 8 |
| 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.01 | 8 |
|* 2 | TABLE ACCESS INMEMORY FULL| FOOBAR | 1 | 15 | 10 |00:00:00.01 | 8 |
------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - inmemory("B"='1')
filter("B"='1')

Note
-----
- dynamic statistics used: dynamic sampling (level=2)


24行が選択されました。


in-memory column storeを利用しない場合、Buffer Getsが191ありましたが、in-memory column storeを利用した場合、Buffer Getsが8と大幅に減少しています!!!。データにもよるだろうけど、このケースだと、4%程度になってますね。

KEEP乗せとか手動でやらなくて済むケースあるんだろうな〜と、遠くを眺めながら.......in-memory column store初めの一歩はここまで。


つづく。

| | コメント (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)

2014年6月28日 (土)

InterSystems Symposia 2014に行ってきた

db tech showcase 2014 Osakaより先だったのだけど...
InterSystems Symposia 2014に行ってきた。という話。

CachéはSQLでアクセスする必要はないのだけど、RDBMSからの乗り換え組でSQLから乗り換えコストの低減目的で導入されたインターフェースだというのは間違いなし、
昔からM言語をやってきた人達からすれば、SQLアクセスだと! はぁ〜? 的な感じも無くはないし、おまけ的なイメージは強いんだろうな。
その分のオーバーヘッドもあるしな〜。 と思ってたのですが....

最近の動きを見ているとSQLアクセスにも本気なのかな。と思わせる様子があるんだよね。

Caché SQL 最新情報の資料P3部分でダイレクトアクセス(元からあるNoSQLな部分)とSQLアクセスのメリット、デメリットの解説やビットマップインデックスのメンテナンス機能。

20140628_124018
Caché SQL 最新情報のセッション資料は上のリンクからダウンロードできますよ!


そして、 Push Subquery Conditions into UNION LegsなどのSQL実行計画最適化、OracleのUSE_CONCAT相当など多数のヒント、パラレルクエリなど"おまけのインターフェース"とは思えなくなってきた感がある。


ちなみに、Caché自体はスパース多次元配列なので行指向、列指向のようなことはないんだけど、列指向的な加工もできちゃうわけで、それはそれで面白いかなと、勝ってに妄想してみたりw

たまには、右脳を活性化しないとね、イメージ力大事 :)

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

2014年6月13日 (金)

SQL Developer 4の素敵なコマンドライン de SQL整形 :)

Version 4.0.1ではWindowsでも同じ問題がでていたようですね。以下OTNフォーラムにも投稿されていたようです。
OS X版 Version 4.0.1でも同じだったので不具合だったようです。
https://community.oracle.com/thread/3562219


Version 4.0.2がリリースされたので改善されたか確かめてみました。 OS X版のSQLDeveloper 4.0.2 で!

OTN-JPではまだ、4.0.1のようですが、USでは 4.0.2のようです。OTN-JPでもそのうち4.0.2になるのでしょうね :)
以下はUSへリンクです。
http://www.oracle.com/technetwork/developer-tools/sql-developer/downloads/index.html

20140613_45308


OS X版のsdcliを実行するには実行権を付与する必要があります。sdcliは以下のパスにあります。かなり深いところにあります!
(OS X版は以下深いところにあるので、SQLDeveloperのアイコン上で右クリック>コンテクストメニュー>パッケージの内容を表示から辿ってパスを探してもいいですし、以下のパス直接コピペでも行けます。)

20140613_45551

20140613_45613


discus-mother:˜ oracle$ cd /Applications/SQLDeveloper.app/Contents/Resources/sqldeveloper/sqldeveloper/bin
discus-mother:bin oracle$ chmod +x sdcli
discus-mother:bin oracle$ ll
total 672
-rw-r--r--@ 1 oracle staff 49279 5 3 02:48 SQLDeveloperIcons.icns
-rw-r--r--@ 1 oracle staff 0 5 3 02:48 jdk.conf
-rw-r--r--@ 1 oracle staff 440 5 3 02:48 logging-debug.conf
-rw-r--r--@ 1 oracle staff 363 5 3 02:48 logging.conf
-rwxrwxr-x@ 1 oracle staff 586 5 3 02:50 sdcli
-rw-r--r--@ 1 oracle staff 99 5 3 02:50 sdcli-Darwin.conf
-rw-r--r--@ 1 oracle staff 446 5 3 02:50 sdcli.boot
-rw-r--r--@ 1 oracle staff 854 5 3 02:50 sdcli.conf
-rw-r--r--@ 1 oracle staff 71205 5 3 02:48 splash.gif
-rw-r--r--@ 1 oracle staff 71205 5 3 02:48 splash.png
-r-xr-xr-x@ 1 oracle staff 3220 5 3 02:48 sqldeveloper
-rw-r--r--@ 1 oracle staff 415 5 3 02:48 sqldeveloper-Darwin.conf
-rw-r--r--@ 1 oracle staff 661 5 3 02:48 sqldeveloper-debug.conf
-rw-r--r--@ 1 oracle staff 204 5 3 02:48 sqldeveloper-nondebug.conf
-rw-r--r--@ 1 oracle staff 445 5 3 02:48 sqldeveloper.bat
-rw-r--r--@ 1 oracle staff 340 5 3 02:48 sqldeveloper.boot
-rw-r--r--@ 1 oracle staff 1175 5 3 02:48 sqldeveloper.conf
-rw-r--r--@ 1 oracle staff 83456 5 3 02:48 sqldeveloper64W.exe
-rw-r--r--@ 1 oracle staff 131 5 3 02:54 version.properties
discus-mother:bin oracle$


パスは通っていないので設定する必要があります。

discus-mother:˜ oracle$ cat .bashrc
alias ll='ls -lv'

#for SQLDeveloper sdcli
export PATH=$PATH:/Applications/SQLDeveloper.app/Contents/Resources/sqldeveloper/sqldeveloper/bin

discus-mother:˜ oracle$


Version 4.0.1では以下の状況でExceptionが発生していましたが、Version 4.0.2では解決してます!!! 

discus-mother:˜ oracle$ sdcli

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

使用可能な機能:
cart: データベース・カート・バッチ・タスク
dba: 基本バッチDBAタスク
format: SQL Format Task
migration: Database Migration Tasks
reports: 基本バッチ・レポート・タスク
unittest: ユニット・テスト・バッチ・タスク
discus-mother:˜ oracle$

ということで、
前から試したかった SQL文の整形機能。 GUIだとSQLワークシートでSQL文整形機能が提供されているためデータベースへ接続必要だったんですよね。
機能としては素敵だったのですが、データベース接続が行えない環境では使えない機能だったんですよ。
そんな環境で読むに耐えない巨大で未整形なSQL文を渡され涙目だった日々を思い出しますw

コマンドラインならデータベース接続不要なのがいいっす!

discus-mother:˜ oracle$ sdcli format

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

format input=<入力ファイルまたはディレクトリ> output=<出力ファイルまたはディレクトリ>
成功しました。
discus-mother:˜ oracle$


以下のようなSQL文を整形してみます。

discus-mother:˜ oracle$ cat sample_unformated.sql
select a.id,a.name,b.location,b.phone_no from foo a innter join bar b on a.id = b.id order by a.name;


意外と地味な感じの処理終了メッセージですが、整形できたようです。整形結果を確認!

discus-mother:˜ oracle$ sdcli format input=sample_unformated.sql output=sample_formated.sql

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

成功しました。
discus-mother:˜ oracle$


いいですね〜 :) 最後の改行がないのですは、ご愛嬌ということで.

discus-mother:˜ oracle$ cat sample_formated.sql
SELECT a.id,
a.name,
b.location,
b.phone_no
FROM foo a innter
JOIN bar b
ON a.id = b.id
ORDER BY a.name;discus-mother:˜ oracle$
discus-mother:˜ oracle$

では、超便利な一括整形のテスト。 以下のような未整形なSQLファイルを含むディレクトリと整形後SQLファイルを格納するディレクトリを用意して....

discus-mother:˜ oracle$ mkdir unformated_sqls
discus-mother:˜ oracle$ mkdir formated_sqls
discus-mother:˜ oracle$ dir=unformated_sqls; for fname in `ls $dir`; do echo -e \\n\\nfile : $fname; cat $dir/$fname; done;


file : sample1.sql
select a.id,a.name,b.location,b.phone_no from foo a innter join bar b on a.id = b.id order by a.name;


file : sample2.sql
select a.id,a.name,b.location,b.phone_no from foo a innter join bar b on a.id = b.id order by a.name;


一括整形の場合はディレクトリ名のみ指定すればOK!
おおおおおおおおお〜〜〜〜、できた〜〜〜〜SQL文の一括整形!!!  素敵!!

discus-mother:˜ oracle$ sdcli format input=unformated_sqls output=formated_sqls

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

成功しました。


かくに〜〜〜〜〜ん!!

discus-mother:˜ oracle$ dir=formated_sqls; for fname in `ls $dir`; do echo -e \\n\\nfile : $fname; cat $dir/$fname; done;


file : sample1.sql
SELECT a.id,
a.name,
b.location,
b.phone_no
FROM foo a innter
JOIN bar b
ON a.id = b.id
ORDER BY a.name;

file : sample2.sql
SELECT a.id,
a.name,
b.location,
b.phone_no
FROM foo a innter
JOIN bar b
ON a.id = b.id
ORDER BY a.name;


Good job!!!!! SQL Developer Team!


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

2014年5月24日 (土)

シーケンスでパーティションだと〜っ! (Oracle Database 12c R1ではドキュメントに記載なし...だが...) - おまけ

シーケンスでパーティションだと〜っ! (Oracle Database 12c R1ではドキュメントに記載なし...だが...) を書いた後に、もしかして、パーティション表でIdentity columnを使うと勝ってにパーティショニングされたシーケンスが自動生成されたりしないのかな〜っ。

と思って念のために試してみた。


結果は見ての通り....

次のリリースではなにかあるのかもね....。

SCOTT@nonpdb12c> select * from v$version;

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


以下のように、id列をIdentity columnにし、そのid列をパーティショニングキーにしたハッシュパーティション表を作成してみた。

SCOTT@nonpdb12c> r
1 select
2 identity_column
3 ,column_name
4 ,data_type
5 ,data_default
6 from
7 user_tab_columns
8 where
9 table_name = 'HASH_PART_TABLE_USING_ID_COL'
10* order by column_id

IDE COLUMN_NAME DATA_TYPE DATA_DEFAULT
--- ------------------------------ -------------------- ------------------------------
YES ID NUMBER "SCOTT"."ISEQ$$_93596".nextval
NO DATA VARCHAR2

SCOTT@nonpdb12c> r
1 select
2 table_name
3 ,partitioning_type
4 ,partition_count
5 ,partitioning_key_count
6 from
7 user_part_tables
8 where
9* table_name = 'HASH_PART_TABLE_USING_ID_COL'

TABLE_NAME PARTITION PARTITION_COUNT PARTITIONING_KEY_COUNT
------------------------------ --------- --------------- ----------------------
HASH_PART_TABLE_USING_ID_COL HASH 4 1

SCOTT@nonpdb12c> r
1 select
2 name
3 ,object_type
4 ,column_name
5 from
6 user_part_key_columns
7 where
8* name = 'HASH_PART_TABLE_USING_ID_COL'

NAME OBJEC COLUMN_NAME
------------------------------ ----- ------------------------------
HASH_PART_TABLE_USING_ID_COL TABLE ID

対応するシーケンスはどうかというと、PARTITION_COUNTはNULLのまま....ですね。あははは。

SCOTT@nonpdb12c> select * from user_sequences where sequence_name = 'ISEQ$$_93596';

SEQUENCE_NAME MIN_VALUE MAX_VALUE INCREMENT_BY C O CACHE_SIZE LAST_NUMBER PARTITION_COUNT S K
------------------------------ ---------- --------------------------------- ------------ - - ---------- ----------- --------------- - -
ISEQ$$_93596 1 9999999999999999999999999999 1 N N 20 1 N N

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

2014年4月30日 (水)

シーケンスでパーティションだと〜っ! (Oracle Database 12c R1ではドキュメントに記載なし...だが...)

Identity Columnがどのシーケンスを利用しているかを確認するディクショナリビューを調べていて見つけた、シーケンスのパーティション化!
とはいっても現時点ではマニュアルには解説はないので使い方は不明なままですが、RACとかで便利な感じとか、単にパラレル処理でいい感じになるのかとか、いろいろ想像を巡らしております。はい。

SCOTT@nonpdb12c> break on table_name skip 1
SCOTT@nonpdb12c> select table_name,has_identity from user_tables where has_identity='YES';

TABLE_NAME HAS_IDENTITY
------------------------------ ------------
TABLE_FOOBAR_IDENTITY2 YES

TABLE_FOOBAR_IDENTITY YES


SCOTT@nonpdb12c> select * from user_tab_identity_cols;

TABLE_NAME COLUMN_NAM GENERATION IDENTITY_OPTIONS
------------------------------ ---------- ---------- --------------------------------------------------------------------------------------------------------------------------------------------
TABLE_FOOBAR_IDENTITY ID BY DEFAULT START WITH: 1, INCREMENT BY: 1, MAX_VALUE: 9999999999999999999999999999, MIN_VALUE: 1, CYCLE_FLAG: N, CACHE_SIZE: 20, ORDER_FLAG: N

TABLE_FOOBAR_IDENTITY2 ID ALWAYS START WITH: 1, INCREMENT BY: 1, MAX_VALUE: 9999999999999999999999999999, MIN_VALUE: 1, CYCLE_FLAG: N, CACHE_SIZE: 20, ORDER_FLAG: N


SCOTT@nonpdb12c> select table_name,column_name,data_default,identity_column from user_tab_columns where identity_column='YES';

TABLE_NAME COLUMN_NAM DATA_DEFAULT IDENTITY_COLUMN
------------------------------ ---------- -------------------------------------------------------------------------------- ---------------
TABLE_FOOBAR_IDENTITY ID "SCOTT"."ISEQ$$_93570".nextval YES

TABLE_FOOBAR_IDENTITY2 ID "SCOTT"."ISEQ$$_93589".nextval YES


SCOTT@nonpdb12c> select * from user_sequences;

SEQUENCE_NAME MIN_VALUE MAX_VALUE INCREMENT_BY C O CACHE_SIZE LAST_NUMBER PARTITION_COUNT S K
------------------------------ ---------- ---------- ------------ - - ---------- ----------- --------------- - -
ISEQ$$_93570 1 1.0000E+28 1 N N 20 101 N N
ISEQ$$_93589 1 1.0000E+28 1 N N 20 101 N N


ん? PARTITION_COUNTだと? シーケンスでパーティションだと〜〜〜〜〜っ!


Oracle Database 12c R1のマニュアルの構文解説にもまったく記載されていないが..........
Oracle® Database SQL言語リファレンス 12cリリース1 (12.1) CREATE SEQUENCE

SCOTT@nonpdb12c> create sequence p_seq start with 1 maxvalue 9999999 partition 4;

順序が作成されました。

おおおおおおおおおおおおおお〜〜〜できたw Partitioned Sequence!!!!!!

SCOTT@nonpdb12c> select * from user_sequences;

SEQUENCE_NAME MIN_VALUE MAX_VALUE INCREMENT_BY C O CACHE_SIZE LAST_NUMBER PARTITION_COUNT S K
------------------------------ ---------- ---------- ------------ - - ---------- ----------- --------------- - -
ISEQ$$_93570 1 1.0000E+28 1 N N 20 101 N N
ISEQ$$_93589 1 1.0000E+28 1 N N 20 101 N N
P_SEQ 1 9999999 1 N N 20 1 4 N N


RACで効果がありそうな機能だが....、そもそも、どう使うんだ、謎。

次のリリースではなにか使えるのか...現リリースでも裏で使われているのか......

参考
dbi services Blog:Oracle Partitioned Sequences - a future new feature in 12c?

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

2014年4月29日 (火)

Oracle Database 12cで実装されたidentity columnのメモ

Oracle® Database SQL言語リファレンス 12cリリース1 (12.1) CREATE TABLE identity_clause


制限事項に、「CREATE TABLE AS SELECTを使用すると、列に対するIDのプロパティが継承されなくなります。」と記載されているのでかるく確認 :)

SCOTT@nonpdb12c> CREATE TABLE table_foobar_identity2
2 (
3 id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY
4 ,data VARCHAR2(10)
5 );

表が作成されました。

SCOTT@nonpdb12c> create table table_foobar_identity2_tmp as select * from table_foobar_identity2;

表が作成されました。

SCOTT@nonpdb12c> break on table_name skip 1
SCOTT@nonpdb12c> select table_name,column_name,identity_column from user_tab_columns where table_name like 'TABLE_FOOBAR%' order by table_name,column_id;

TABLE_NAME COLUMN_NAME IDE
------------------------------ ------------------------------ ---
TABLE_FOOBAR_IDENTITY2 ID YES
DATA NO

TABLE_FOOBAR_IDENTITY2_TMP ID NO
DATA NO

CREATE TABLE AS SELECTで復籍してもidentity columnは継承されてない。ふむふむ。


同マニュアルの注意には、「ID列を作成するときには、パフォーマンスを向上するために、デフォルトの20よりも大きな値を使用してCACHE句を指定してください。」
とある.
シーケンスを利用しているので注意するところは一緒ということね。

SCOTT@nonpdb12c> select * from user_sequences;

SEQUENCE_NAME MIN_VALUE MAX_VALUE INCREMENT_BY C O CACHE_SIZE LAST_NUMBER PARTITION_COUNT S K
------------------------------ ---------- ---------- ------------ - - ---------- ----------- --------------- - -
ISEQ$$_93570 1 1.0000E+28 1 N N 20 1 N N

参考
ORACLE-BASE:Identity Columns in Oracle Database 12c Release 1 (12.1)
Inside Oracle – Julian Dyke:Oracle 12c New Feature – Identity Columns

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

2014年1月 6日 (月)

SELECT ~ FOR UPDATE SKIP LOCKED その4 - もしもITL不足だったら...

skip lockedのつづきです。

ITLエントリー不足時のskip lockedの動きを確認してみるか....


100行が1ブロックに収まるような表を作成しておく...計算上、ITLエントリーは最大で4エントリー程度になるように....したつもり....
(ASSMで、INITRANSはデフォルト、ブロックサイズは8KB、PCTFREEは0%)

SCOTT@pdborcl> r
1 select
2 objectid
3 ,file#
4 ,block#
5 ,count(id) as num_of_rows
6 from
7 (
8 select
9 dbms_rowid.rowid_object(rowid) as objectid
10 ,dbms_rowid.rowid_relative_fno(rowid) as file#
11 ,dbms_rowid.rowid_block_number(rowid) as block#
12 ,id
13 from
14 q
15 )
16 group by
17 objectid
18 ,file#
19 ,block#
20 order by
21 objectid
22 ,file#
23* ,block#

OBJECTID FILE# BLOCK# NUM_OF_ROWS
---------- ---------- ---------- -----------
93077 9 972461 100

※セッション1 - lockできた

SESSION1@pdborcl> select * from q where id = '0001' for update;

ID
----
TEXT_STRING
----------------------------------------------------------------------
0001
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

※セッション2 - lockできた。 この時点で 1 + 1 = 2 のITLエントリは使い切っている。

SESSION2@pdborcl> select * from q where id = '0002' for update;

ID
----
TEXT_STRING
---------------------------------------------------------------------
0002
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

※セッション3 - lockできた。 ITLがブロック内の空きスペースに作れたため :)

SESSION3@pdborcl> select * from q where id = '0003' for update;

ID
----
TEXT_STRING
---------------------------------------------------------------------
0003
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

※セッション4 - 計算上のITLエントリー数の上限値. 問題なくlockできた。

SESSION4@pdborcl> select * from q where id = '0004' for update;

ID
----
TEXT_STRING
---------------------------------------------------------------------
0004
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

※セッション5 - 狙い通り! 5つめのITLエントリーを作成するだけの空きがブロック内にない状況なので待機しちゃう :)

SESSION5@pdborcl> select * from q where id = '0005' for update;


待機イベント見れば一目瞭然、ITLエントリー不足で待機してますね!

SYS@pdborcl> select username,event from v$session where username = 'SCOTT'

USERNAME EVENT
---------- ----------------------------------------
SCOTT SQL*Net message from client
SCOTT SQL*Net message from client
SCOTT enq: TX - allocate ITL entry
SCOTT SQL*Net message from client
SCOTT SQL*Net message from client

ここまでは、ITL不足な状況のfor update文ではよく見かける光景ですよね :)


for update skip lockedにすると.....

※セッション1 - locked!

SESSION1@pdborcl> select * from q where id = '0001' for update skip locked;

ID
----
TEXT_STRING
---------------------------------------------------------------------
0001
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

※セッション2 - locked!!

SESSION2@pdborcl> select * from q where id = '0002' for update skip locked;

ID
----
TEXT_STRING
---------------------------------------------------------------------
0002
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

※セッション3 - locked!!!

SESSON3@pdborcl> select * from q where id = '0003' for update skip locked;

ID
----
TEXT_STRING
---------------------------------------------------------------------
0003
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

※セッション4 - locked!!!!

SESSION4@pdborcl> select * from q where id = '0004' for update skip locked;

ID
----
TEXT_STRING
---------------------------------------------------------------------
0004
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

※セッション5(ITLエントリーが確保できず id = '0005'の行をlockすることができないので空振りします。興味深い動きですよね。)

SESSION5@pdborcl> select * from q where id = '0005' for update skip locked;

レコードが選択されませんでした。


次回へつづく.....かもしれない。



SELECT ~ FOR UPDATE SKIP LOCKED その1 - @sh2ndさんエントリの復習など
SELECT ~ FOR UPDATE SKIP LOCKED その2
SELECT ~ FOR UPDATE SKIP LOCKED その3

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

2014年1月 5日 (日)

SELECT ~ FOR UPDATE SKIP LOCKED その3

skip lockedのつづきです。

skip lockedの特徴を確認しておきますか!

skip lockedはロックの獲得ができた行だけをロック待機なしで返してくれる :)
IDが'0001', '0002', '0003', '0005'の行をロックしておく。

SESSION1@pdborcl> select id from q where id in ('0001', '0002', '0003', '0005') for update skip locked;

ID
----
0001
0002
0003
0005

IDが '0001','0002','0003'の行は先行のトランザクションで既にロックされているため、ロックできた行だけがロック待機なしで返される。

SESSION2@pdborcl> select id from q where id in ('0001', '0002', '0003', '0004', '0005', '0006') for update skip locked;

ID
----
0004
0006

もちろん、ロックできる行が1行もなければロックを待機せず空振!

SESSION3@pdborcl> select id from q where id in ('0001', '0002', '0003') for update skip locked;

レコードが選択されませんでした。


つづく。


SELECT ~ FOR UPDATE SKIP LOCKED その1 - @sh2ndさんエントリの復習など
SELECT ~ FOR UPDATE SKIP LOCKED その2

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

2013年12月31日 (火)

SELECT ~ FOR UPDATE SKIP LOCKED その2

つづきです。


そういえば、skip lockedって構文がマニュアルでは解説されていないリリースでふつーーーーに、使われててビビったことあったな。
(マニュアルに書かれていないリリースでは、自己責任使ってねw)


Oracle12c R1 - SKIP LOCKED
http://docs.oracle.com/cd/E49329_01/server.121/b71278/statements_10002.htm#SQLRF55374


Oracle11g R2 - (skip locked、推奨事項などが追記された)
http://docs.oracle.com/cd/E16338_01/server.112/b56299/statements_10002.htm#i2126016


Oracle11g R1 - (skip locked登場)
http://otndnld.oracle.co.jp/document/products/oracle11g/111/doc_dvd/server.111/E05750-03/statements_10.htm#7292


Oracle10g R2 (ここまでのマニュアルにはskip lookedは記載されていない。AQがらみで使われていたのは確かだ....)
http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/server.102/B19201-02/statements_10.html#6799


どのあたりのバージョンから登場していたのかはJonathan Lewisさんのブログからたどれば分かると思うよ...
http://jonathanlewis.wordpress.com/2010/05/31/skip-locked/


Oracle11g R2のドキュメントからは以下のような追記されている。


「この機能は、Oracle Streams Advanced Queuingなどのマルチコンシューマ・キュー環境で使用するために設計されています。
 キュー・コンシューマは、他のコンシューマによってロックされた行はスキップして未ロックの行を取得できるので、
 他のコンシューマの操作が終了するまで待つ必要はなくなります。
 SKIP LOCKED機能を直接使用するかわりに、Oracle Streams Advanced Queuing APIを使用することをお薦めします。」

と。

以下のリリースで確認したが skip lockedに関して動作の差は無いようだ。
2013/1/5追記
動作の差は無いようだ、とは書いたが、結果オーライってことでございます。内部動作までは見えないので(キリつ

・Oracle11g R1 11.1.0.7.0
・Oracle11g R2 11.2.0.1.0
・Oracle11g R2 11.2.0.2.0
・Oracle12c R1 12.1.0.1.0


※セッション1
SESSION1> select * from q order by id;

ID DATA
---------- ----------
1 a
2 b
3 c

※セッション1
SESSION1> SELECT id, data FROM q WHERE id = (SELECT MIN(id) FROM q) FOR UPDATE SKIP LOCKED;

ID DATA
---------- ----------
1 a

※セッション2
SKIP LOCKEDなのでしようとしていたレコードがロックできなければ空振りし、ロック獲得を待機しません
SESSION2> SELECT id, data FROM q WHERE id = (SELECT MIN(id) FROM q) FOR UPDATE SKIP LOCKED;

レコードが選択されませんでした。


※セッション1
SESSION1> DELETE FROM q WHERE id = 1;

1行が削除されました。

※セッション2
何度やっても同じですよね :)
SESSION2> SELECT id, data FROM q WHERE id = (SELECT MIN(id) FROM q) FOR UPDATE SKIP LOCKED;

レコードが選択されませんでした。


※セッション1
SESSION1> COMMIT;

コミットが完了しました。

SESSION1>

※セッション2
ここで初めて、対象レコードがロックできます!
マルチコンシューマ・キュー環境向けって意味がよ〜〜〜〜く分かる動きだと思います
SESSION2> SELECT id, data FROM q WHERE id = (SELECT MIN(id) FROM q) FOR UPDATE SKIP LOCKED;

ID DATA
---------- ----------
2 b


skip lockedって面白いよね。

skip lockedの特徴について、もう少し書く予定、多分、来年へつづく。。。。。


みなさま、良いお年を!



SELECT ~ FOR UPDATE SKIP LOCKED その1 - @sh2ndさんエントリの復習など

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

SELECT ~ FOR UPDATE SKIP LOCKED その1 - @sh2ndさんのエントリの復習など

JPOUT Advent Calender 2013の@sh2ndさんのエントリーが面白かったので大晦日に酒飲みながら... :)
まずは twitterでのやり取りなど....


20131231_113305


20131231_163205

イケテナイとか、良いとか、自由に言えるのはユーザーだからこそだと思うんだ。
セールストークじゃない生の情報って大切だ。


ということで、skip locked へ行く前に、Oracle11g R1 11.1.0.7.0 および、Oracle12c R1 12.1.0.1.0 で @sh2ndさんのエントリーの復習から :)

※セッション1
SESSION1> select * from q order by id;

ID DATA
---------- ----------
1 a
2 b
3 c

※セッション1
SESSION1> SELECT id, data FROM q WHERE id = (SELECT MIN(id) FROM q) FOR UPDATE;

ID DATA
---------- ----------
1 a

※セッション2
SESSION2> SELECT id, data FROM q WHERE id = (SELECT MIN(id) FROM q) FOR UPDATE;
(ここで待たされる。待つことは正しい動き)

※セッション1
SESSION1> DELETE FROM q WHERE id = 1;

1行が削除されました。

※セッション1
SESSION1> commit;

コミットが完了しました。

SESSION1>

※セッション2はここでロックを獲得できる。
ID DATA
---------- ----------
2 b

SESSION2>


Oracle11g R2 11.2.0.1.0とOracle11g R2 11.2.0.2.0は、Wrong Resultだったんだと思うが...

※セッション1
SESSION1> select * from q order by id;

ID DATA
---------- ----------
1 a
2 b
3 c

※セッション1
SESSION1> SELECT id, data FROM q WHERE id = (SELECT MIN(id) FROM q) FOR UPDATE;

ID DATA
---------- ----------
1 a

※セッション2
SESSION2> SELECT id, data FROM q WHERE id = (SELECT MIN(id) FROM q) FOR UPDATE;
(ここで待たされる。というところまでは同じ)


※セッション1
SESSION1> DELETE FROM q WHERE id = 1;

1行が削除されました。

※セッション1
SESSION1> COMMIT;

コミットが完了しました。

SESSION1>

※セッション2 (注1
(しか〜〜〜し、なんとレコードが選択されませんでした!!!!!)
レコードが選択されませんでした。

SESSION2>

結果
OracleREAD COMMITTED
11.1.0.7.0ID=2を取得
11.2.0.1.0空振り
11.2.0.2.0空振り
12.1.0.1.0ID=2を取得


注1)
Oracle11g 11.2.0.3.0以降では修正されているとのこと...(私は手持ちのが無かったので未確認...だれか書いてw)

SELECT ~ FOR UPDATE SKIP LOCKEDへつづく...

| | コメント (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月 8日 (月)

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 の続きです。

再びリクエストにお答えしてw、SQLとレースを有効した状態でstraceして該当待機イベントでどのようなsystem callになっているのか確認してみたよ > @yoheia :)


db file sequential read

WAIT #4: nam='db file sequential read' ela= 53584 file#=7 block#=375476 blocks=1 obj#=82773 tim=1365258291285100

....
pread64(17, "\6\242\0\0\372\267\5\0\\]\216\0\0\0\1\6\226y\0\0\1\0\0\0UC\1\0009]\216\0"..., 8192, 3070181376) = 8192

db file scattered read

WAIT #4: nam='db file scattered read' ela= 14462 file#=7 block#=374752 blocks=26 obj#=82773 tim=1365258291450333

....
pread64(17, "\6\242\0\0\340\267\5\0005]\216\0\0\0\1\6}\372\0\0\1\0\0\0UC\1\0002]\216\0"..., 212992, 3069968384) = 212992

db file parallel read

WAIT #4: nam='db file parallel read' ela= 25040 files=1 blocks=11 requests=11 obj#=82773 tim=1365258291641614

....
io_submit(1261568, 11, {{0x7981c8, 0, 0, 0, 17}, {0x79834c, 0, 0, 0, 17}, {0x7984d0, 0, 0, 0, 17}, {0x798654, 0, 0, 0, 17}, {0x7987d8, 0, 0, 0, 17}, {0x79895c, 0, 0, 0, 17}, {0x798ae0, 0, 0, 0, 17}, {0x798c64, 0, 0, 0, 17}, {0x798de8, 0, 0, 0, 17}, {0x798f6c, 0, 0, 0, 17}, {0x7990f0, 0, 0, 0, 17}}) = 11
io_getevents(1261568, 11, 128, {{0x79834c, 0x79834c, 8192, 0}, {0x7984d0, 0x7984d0, 8192, 0}, {0x7981c8, 0x7981c8, 8192, 0}, {0x798654, 0x798654, 8192, 0}, {0x7987d8, 0x7987d8, 8192, 0}, {0x798ae0, 0x798ae0, 8192, 0}, {0x798c64, 0x798c64, 8192, 0}, {0x798de8, 0x798de8, 8192, 0}, {0x7990f0, 0x7990f0, 8192, 0}, {0x798f6c, 0x798f6c, 8192, 0}, {0x79895c, 0x79895c, 8192, 0}}, {600, 0}) = 11

このdb file parallel readも狙って出せるくらいだから、たまたまそういう状況では発生しちゃうわけですよ。

以下のSQLトレースは、全てHIGH_CLUSTERING_FACTOR(表)に対する待機イベントですが、何が起きているか想像するとワクワクしてきませんか? (変態だな)

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='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 

ということで、いろいろと、力尽きたので今日はここまで。

次回へつづく。


バックナンバー

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)

| | コメント (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 me