2016年12月24日 (土)

わすれられない言葉 - Unforgettable word

Jonathan LewisがOracle ACEDを辞されたというニュースはショックだった。
So Long ACED - Oracle Scratchpad / Jonathan Lewis

Oracle ACEDだけでなく、Oracle ACE全体で同様の手続きが必要になったということについて、日本在住のOracle ACEには最近、しかも一部の方のみに連絡されたのみという不手際が影響して混乱している状況となっている。2016/12/24現在


Jonathan LewisはOracle ACEDではなくても、ワクワクする記事を書いてくれると思います。


そして、

彼のサインに添えられた、忘れられない一言。

Img_3179_1









追伸

私がこのような壇上に上がるようになれたのも、ユーザーグループのみんな、ユーザーグループイベントを盛り上げてくれるみんな、そして、Oracle ACEに推薦してくれた友人たちのおかげです。 

今年一年、ありがとうございました。

多分、今年最後のエントリになると思うので。。。。


みなさん、メリークリスマス、よいお年を。


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

2016年3月13日 (日)

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

PART1ってあったっけ? というのは置いといて、
PART III以降もなにかありそうなアトモスフィアなのですが、ここで書いておかないとハマりそうなので備忘録代わりに。


みなさん、統計履歴って知ってます?
dbms_stats.gather_*_stats を実行するとオプティマイザ統計情報が取得された表、索引、列統計が自動的にバックアップされることを。
そのバックアップは統計履歴と呼ばれています。(履歴統計と呼んでる人のほうが多いと思うのですが。。。マニュアルだと統計履歴って書いてますね。。。。誰も履歴統計を統計履歴だろ、それ! と突っ込んでくれなてなかった気がw)

Oracle® Database SQLチューニング・ガイド 12cリリース1(12.1) オプティマイザ統計の保存の管理
Oracle® Database PL/SQLパッケージおよびタイプ・リファレンス 12c リリース1(12.1) DBMS_STATS.GET_STATS_HISTORY_RETENTIONファンクション
Oracle® Database PL/SQLパッケージおよびタイプ・リファレンス 12c リリース1(12.1) DBMS_STATS.ALTER_STATS_HISTORY_RETENTIONプロシージャ
Oracle® Database PL/SQLパッケージおよびタイプ・リファレンス 12c リリース1(12.1) PURGE_STATSプロシージャ
Oracle® Database SQL言語リファレンス 12cリリース1 (12.1) SYS_CONTEXT


で、オプティマイザ統計収集が原因と思われる事象があった場合、統計履歴をリストアしてトラブルを華麗に回避なんて手法もあるんです。
古くは小田さんの記事でも取り上げられています。
門外不出のOracle現場ワザ 第4章 Oracleデータベースの頭脳 「オプティマイザ」徹底研究


と、ここまでは、11gとか12cでも非MTA環境のお話です。

先日、12cのMTA環境で統計履歴をリストアしようとしたら。。。統計履歴。。。なんとなく言いづらい。。。履歴統計が無いw
履歴統計の保存期間はデフォルトで31日なのでパージされる前に保存期間を無期限に変更しました。。。(その時はMTA環境の罠にハマったとは気付かず、無期限にしたからもう気にしなくていいや! と思ったんです)

ところが、いざ履歴統計をみると。。。。パージされてる。。。。。焦りましたw 

どういうことだったのか、最初に書いてしまうと、

履歴統計は、CDB/PDB、それぞれ独立して管理されてまっす!。

(え〜〜〜〜〜〜〜〜っ。知らんかったというか、マニュアルのどこかに記載されているのなら、どなたかそのページへのリンクをおしえてくだしぁ。。。)


久々に長〜い前置きはこれぐらいにして確認してみましょう。(--;

PDBが1つあるシングルテナント構成(1 PDB/CDB)で確認してみました。

CDB及び、PDBの履歴統計保存期間を確認します。(デフォルト設定のままです)
なにもしてないデフォルト状態では、CDBもPDBも履歴統計保存期間は31日となっています。


CDB側の履歴統計保存期間

SYSTEM@orcl12c> @show_stats_hist_retention

cdb name container name
-------------------- --------------------
orcl12c CDB$ROOT

stats history retention
-----------------------
31

PDB側の履歴統計保存期間

SYSTEM@pdborcl12c> @show_stats_hist_retention

cdb name container name
-------------------- --------------------
orcl12c PDBORCL12C

stats history retention
-----------------------
31


では最初にどちらも履歴統計を保存しない状態にしてみます。

CDB
履歴統計を保存なしに設定して既存履歴統計をパージ。
事前作成しておいた stats_hist_retention.sqlを実行。(スクリプトはエントリの最後に記載しておきました。)

DBMS_STATS.GATHER_DATABASE_STATSを実行しても履歴統計数の増加なし。想定通り。

SYSTEM@orcl12c> exec dbms_stats.alter_stats_history_retention(0);

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

SYSTEM@orcl12c> exec dbms_stats.purge_stats(null);

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

SYSTEM@orcl12c> @stats_hist_retention

cdb name container name
-------------------- --------------------
orcl12c CDB$ROOT


stats history retention
-----------------------
0

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


number of tab stats histories
-----------------------------
0


PDB

SYSTEM@pdborcl12c> exec dbms_stats.alter_stats_history_retention(0);

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

SYSTEM@pdborcl12c> exec dbms_stats.purge_stats(null);

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

SYSTEM@pdborcl12c> @stats_hist_retention

cdb name container name
-------------------- --------------------
orcl12c PDBORCL12C

stats history retention
-----------------------
0

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

number of tab stats histories
-----------------------------
0

さて、次にCDBの履歴統計保存期間を無期限に変更し、PDBは履歴統計保存なしにしてみます。

CDBの履歴統計数は増加し、PDB側は変化なしということになるはず。。。


CDB

SYSTEM@orcl12c> exec dbms_stats.alter_stats_history_retention(-1);

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

SYSTEM@orcl12c> @stats_hist_retention

cdb name container name
-------------------- --------------------
orcl12c CDB$ROOT

stats history retention
-----------------------
-1

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

number of tab stats histories
-----------------------------
2499

CDB側の履歴統計数が増加した。まあ、そうだよね〜。

PDB
PDB側は履歴統計無しのままなので変化はないです。個別に設定できてるからそうなんでしょうね。多分。

SYSTEM@pdborcl12c> @stats_hist_retention

cdb name container name
-------------------- --------------------
orcl12c PDBORCL12C


stats history retention
-----------------------
0

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

number of tab stats histories
-----------------------------
0


逆にしてみます。CDBは履歴統計無し、PDBの履歴統計保存期間を無期限にしてみます。
履歴統計はCDB/PDB個別管理ならCDBの履歴統計なし、PDB側には履歴統計ありという構成もできるはず!!

なお、確認しやすくするために、CDB側は履歴統計無しに変更後、履歴統計を手動パージしておきます。

CDB

SYSTEM@orcl12c> exec dbms_stats.alter_stats_history_retention(0);

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

SYSTEM@orcl12c> exec dbms_stats.purge_stats(null);

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

SYSTEM@orcl12c> @stats_hist_retention

cdb name container name
-------------------- --------------------
orcl12c CDB$ROOT

stats history retention
-----------------------
0

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

number of tab stats histories
-----------------------------
0




PDB
これが確認できれば問題なさそうですね。CDB/PDBの履歴統計保存期間の管理及びパージは個別管理できるってことで。。。。
Workload RepositoryはCDBでのみ管理できるようになっているので間違いやすいぞ、と
この辺りをまとめたマニュアルがあれば解りやすいのになぁ(ボソっ

SYSTEM@pdborcl12c> exec dbms_stats.alter_stats_history_retention(-1);

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

SYSTEM@pdborcl12c> @stats_hist_retention

cdb name container name
-------------------- --------------------
orcl12c PDBORCL12C

stats history retention
-----------------------
-1

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

number of tab stats histories
-----------------------------
2659


履歴統計は、CDB/PDB個別管理なので、履歴統計保存期間、履歴統計のパージはCDB/PDBそれぞれで行えるようですが、
Workload Repository:AWRのスナップショットの保存期間やパージなどは、CDBのみでしか行えない。(AWRのスナップショットはCDB/PDBどこで実行しても全体が取得されます。)


それに、PDBで変更できる初期化パラメータと変更できないパラメータのまとめがあればもっと便利かも。
以前調べたPDB側で変更できる初期化パラメータの保存場所とかも合わせてまとめとくか。。。

PDB毎に初期化パラメータを変更できるんですよ!(制限はあるけど) #1
PDB毎に初期化パラメータを変更できるんですよ!(制限はあるけど) #2 - PDBの初期化パラメータは何処!?
PDB毎に初期化パラメータを変更できるんですよ!(制限はあるけど) #3 - 消えたPDBの初期化パラメータの謎... Truth is out there.

いろいろ整理できてないので只今混乱中w


今回試した環境
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


使ったクエリーなど
stats_hist_retention.sql

SELECT 
SYS_CONTEXT('USERENV', 'CDB_NAME') AS "cdb name"
, SYS_CONTEXT('USERENV', 'CON_NAME') AS "container name"
FROM
dual;

SELECT
DBMS_STATS.GET_STATS_HISTORY_RETENTION AS "stats history retention"
FROM
dual;

EXEC DBMS_STATS.GATHER_DATABASE_STATS;

SELECT
COUNT(1) AS "number of tab stats histories"
FROM
dba_tab_stats_history;


show_stats_hist_retention.sql

SELECT 
SYS_CONTEXT('USERENV', 'CDB_NAME') AS "cdb name"
, SYS_CONTEXT('USERENV', 'CON_NAME') AS "container name"
FROM
dual;

SELECT
DBMS_STATS.GET_STATS_HISTORY_RETENTION AS "stats history retention"
FROM
dual;

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

2016年2月14日 (日)

JPOUG Tech Talk Night #6 @ 21cafe


2016/3/5追記

JPOUG Tech Talk Night #6 - Togetterまとめ
http://togetter.com/li/943287



2/23(火) - 19:00 - 20:30 に今年最初のJPOUG Tech Talk Night #6 (すでに満席ですが....) を開催しま〜す。

詳細は以下⇩⇩⇩⇩⇩
JPOUG Tech Talk Night #6
doorkeeper : JPOUG Tech Talk Night #6
Oracle Database の オプティマイザ統計運用 について語ります



たん、たん、たぬきの....か〜〜ぜもないのに、ぶ〜らぶら

5452836527_d7794263f7_o
https://www.flickr.com/photos/small-life/albums/72157625948037655
(CC BY-NC 2.0) Attribution-NonCommercial 2.0 Generic

的なところが話題になるんでしょうかね?


でも、
風もないのにぶ〜らぶらしないのが実行計画

ぶ〜らぶらする理由の代表的なものはなんだろう....しばし考え中....


そうだ!いろいろなぶ〜らぶら、つまり揺れ、振れ、偏り。

ぶ〜らぶら、その1

オプティマイザ統計のぶ〜らぶら、とか実データとの乖離状況のぶ〜らぶら。

ぶ〜らぶら、その2

特定データの偏り、その偏り自体が時間と共に変化するぶ〜らぶら。

ぶ〜らぶら、その3

検索範囲とヒットする行セットが、大きく変化する、揺れ、振れるぶ〜らぶら。

ぶ〜らぶら、その4

その1〜その3が混じって、ぶ〜らぶら


どう考えても、か〜ぜもないのにぶ〜らぶらするわけじゃない....ですよね。

なにかが、ぶ〜らぶらしてるから、実行計画もぶ〜らぶらするんじゃないかなぁ。

ぶ〜らぶらするのって普通でしょ? オプティマイザへインプットされる情報がぶ〜らぶらしてるんだから...、

それにしても、オプティマイザ、peakyすぎるだろう。。とか。

いろいろな方々の、いろいろな思いが交錯して、さらに事態はややこしいことにw。

どんなに、ぶ〜らぶらしても性能要件さえ満たしていればいいんですが、性能要件を満たせない(データモデルの影響も少なくないと思いますが)ほどぶ〜らぶらしちゃうと問題になってきます。

安定させろ、

オプティマイザ統計固めろ

隠れてない隠しパラメータオフれ!

いろいろ止めろ!とか....


でも最近思うんです、結局のところ、やることの多さと労力には大きな差ないのかもしれない....とか....ぼそっ。

ぶ〜らぶらしてる方が健康的かも、 

いやっ! ちがう、ぶ〜らぶらしない方がいい!!!!

ボクサーパンツ vs. トランクス どっがいいんだ的なw オチのない永遠のテーマなのかもね。




追伸
実行計画を固定したとしても、データ量が増加してくれば、自動的に処理時間は伸びる傾向を示すわけで、ある時までは良かった実行計画が、時を経て悪者扱いされることもなくはない...


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

2015年10月 1日 (木)

JPOUG> SET EVENTS 20151017 を開催します!

JPOUG主催

JPOUG> SET EVENTS 20151017

を日本オラクル青山センター 13階セミナールームで
2015年10月17日(土)13:00~17:30(開場および受付開始: 12:30)に開催します。

ハッシュタグ #JPOUG

今回は、
開発設計、チューニング関連の力セッション、インフラ関連の技セッション、入門者向けの知セッションと3本立てです。

JPOUGボードメンバーでもある渡部さん、通しで3枠!!  知セッションを担当します!
Oracle Database 入学式のような感じになるのでしょうか? Oracle Database初心者におすすめです。


そして、ミックさんをはじめ、JPOUG主催イベント初登場のスピーカーが多いのも今回の特徴です。


セッションの詳細は以下サイトをご覧ください。
http://www.jpoug.org/2015/09/01/setevents20151017
Logo20151017_851x478

私は裏方ですので、うろうろしているか、受付とか、やってると思います :)




ところで、皆さん。 Oracle Database 7.3とOracle Database 12.1のSELECT文がどれだけ進化、複雑化したかイメージできてますか?

SELECT文があれば更新削除以外は、なんでもできるんじゃないかと、錯覚するほと変わってきています。
これから先も変化すると思います。(基本は同じですが。)

付いていけてますか?  

まさか、Oracle Database 7.3とか、8とか8iぐらいのSELECT文で止まったままになってないでしょうか?
使いこなせてますか?

たまに、自問自答してます......

Oracle Database 7.3とOracle Database 12.1のSELECT文の文法の差をこうやってみると.....お・ど・ろ・き・ま・す・よ!!!!

20150926_223247




7.3と12.1のSELECT文の差の詳細はマニュアルで!

Oracle Database 7.3 SQL Reference
Oracle Database 12.1 SQL Reference - SELECT

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

2015年5月25日 (月)

「Oracleの現場を効率化する100の技」

自分の興味のある章から読み始まられるような構成だったので、2章、3章、1章、5章、4章、6章の順に読んでみました。:)


各Tipsを詳細に書けばこのサイズの書籍では収まらない。内容を詰め込んだなぁ、という印象があります。:)

My Oracle SupportのDoc Idが参考情報として記載されていて便利

MOSを参照できない人には少々辛いと思いますが、内容を詰め込んだ影響で書ききれなかった部分はMOSで!
というのはわからなくもない。

Enterprise Edition限定なのか、オプション機能なのか記載されている!!

この手の書籍では珍しいと思うのですが、解説している”技”が、Enterprise Edition限定なのか、オプション機能が必要なのか、各セクション部分に記載されている。(これ、なにげに便利です)


第1章[パフォーマンス管理のTips]


SPMに多くのページを割いていてリファレンスとしてもよい印象を受けます。
パフォーマンスを管理する方法は他にもあり、SPMはパフォーマンス管理手法の一つですが、身につけておいて損はないです :)

パフォーマンス管理の実践、現場に漂うアトモスフィアを感じつつ、大人の事情にも配慮w しなければいけないので、可能な限り引き出しは多くしておきたいですよね。
Covering Indexネタはもう少し書いて欲しかったというのは、私個人の希望 (^^;


第2章[トラブルシューティングのTips]

トラブルシューター必見のTips虎の巻。 
トラブルシューター以外には無縁じゃないの? そんなことはないです。開発者でもこれらを知っていれば身を(システムといった方がよいのかw)守れますよ。
(私自身、開発の現場で身を守るために溜め込んだ知識 or ネタwがトラブルシュートやチューニングの現場で役立っています。)

Statspackの解説は無かったのですが、Statspackならここみてね。という解説があってよかったのではないかと思いました。
Oracleのコンサルタントさんが出向く現場はAWRを利用している現場が多いことも影響しているのかもしれませんが、トラブルシューターというかパフォーマンスチューナーとしての本音を言わせてもらうと、AWRなどが使える環境のほうが解析が楽なのは事実なのですが、 私が見てきた現場の1/2程度はstatspackだったんですよね。


第3章[アーキテクチャのTips]

この書籍の目次を見たとき、アーキテクチャが第1章じゃないんだ? なぜだろう? と感じたのですが、読んでみてその理由がわかったような気がします。
性能やサイジング、さらにはトラブルシューティングにも参考になる解説が多いですね。(これは現場から吸い上げられたことがベースになっているからでしょうね)


第4章[開発・運用に役立つTips]

開発、運用に役立つTipsということですが、トラブルシューターや性能試験担当にも役立つネタが多いですね。

リソースマネージャに関して比較的細かく解説されています。リソースマネージャの利用を検討されている現場では参考になるのではないでしょうか。
(私も、リソースマネージャネタをブログに書こうかな :)


第5章[システムテストのTips]

大規模なシステムを想定、かつ、システム移行やアップグレード意識した内容に内容になっています。

機能的にもEEやEEのオプションが必要なものが多いですが、システムテストで見るべき、実施すべきポイントは網羅されているので解説されている機能を利用しない、できない、現場でも参考になることは多いのではないかと思います。

第6章[データマイニングのTips]

なぜ、ここでデータマイニング、しかもRなのだ。って感じる方は多いかもしれません。
ちなみに、私はOracle Rを使ったこと(Rも)はありません。

ただ、読み進めていくと気づくことがあります、ヒートマップを使ったOracleのセグメント統計の傾向分析例を見て、書きたかったのはこれじゃないかな、と。


リファレンスやMOSのインデックスとして使おうとすると、電子版は重要かもしれません! 現場への移動中や、コーヒーブレイク中にちょっと気になって、iPadのiBooksやKindlで確認するようなスタイルは普通になってきていると思うんですよね。
ちなみに、SQLアンチパターンなんて、iPhoneのiBooksで読み返したりすることがあるくらいです。iPhoneだと読みにくいですが、タイムリーに読める、つまみ読みできることは、重要だと思うんですよね。




著者のお一人、塩原さんに献本いただきました。ありがとうございます


余談。
ヒートマップを見ていて、db tech showcase 2014の山下さんのオレオレヒートマップを思い出した。


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

2015年5月17日 (日)

ClubDB2 200回記念に参加してきた :)

ミックさんの出版記念というか、200回記念に、37回目以来、7年ぶりにClubDB2 200回 「SQLにおける手続き型の復権」に参加してきた。

ClubDB2なのに、Oracleを使ってデモをしていたのは、ヒントが使えるからですよね。(ここ重要)
ヒントを使って実行計画を変えたとき、駆動表や、索引使用の有無でどのような結果になるのか追いやすいんですよ == 勉強しやすいといったほうがよいかもしれませんね。

おっと、 本題から外れすぎた w m(_ _)m

Clubdb2_200

1年振りにミックさんにお会いして、久々にミック節を聞かせてもらいました。 楽しかったです!
(1年振り以上ですよ! とミックさんから言われたんだけど、そうだっけ?w)

そして、37回目にもご一緒だった、木村明治さん!。
ミックさんと共著された書籍が発売されてますよね :)。


ClubDB2といいながら、MySQL系の方や、PostgreSQL系の方もちらほら参加している点も面白いですよね。

最後は予想通り、ミックさんのサイン会でした:)


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

Oracle Database 入学式 保護者の方はご遠慮ください 3/13, 4/15

Oracle Database 入学式 保護者の方はご遠慮ください
と題して、3月13日、4月15日に入門者向け勉強会を株式会社コーソルさんの会場をお借りして開催しました。
(私は受付たり、tweetしていただけですが :)

サブタイトルにあるように、マサカリをなげそうな保護者の方が紛れ込まないよう、
金属探知機で入念にチェック(嘘ですw

Oracle Database Entrance Ceremony – Touchdown / 諸橋さん

入学式を担当してくださった、先生方の紹介

Introduction of Oracle Database Architecture / 渡部 亮太
新・門外不出のOracle現場ワザ エキスパートが明かす運用・管理の極意 /小田 圭二

以前関わったことのあるプロジェクトの方から、今回は参加できなかったが次回は是非参加したいとのメッセージが届いているんですが、タイトルが”入学式”なので、次回って来年かも。
(タイトル変えればなんとかなる?か?w  

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

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)

2014年12月 9日 (火)

私のチューニング、アダプティブなオプティマイザにも、今のところ勝てそうな気がするぞ〜〜〜っ。と思った師走のある日。

このエントリは JPOUG Advent Calendar 2014 9日目のエントリです。 昨日のエントリは wmo6hashさんの Goodbye, Patch Set. さよなら、PSR。でした。

アダプティブなオプティマイザ関連のアドベントカレンダーネタに、ついカッとなったところで、アダプティブな結合を試してみようと、一杯の珈琲を飲み、気持ちを落ち着けたアカウントがこちらになりますw


環境は以下の通り。

Oracle Linux Server release 6.6
Linux 3.8.13-44.1.3.el6uek.x86_64 #2 SMP Wed Oct 15 19:53:10 PDT 2014 GNU/Linux


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


以下のような2表を用意しました。他のネタに作った表なのですごーく適当ですが、ID列は単一列の主キー制約があります。その他に索引はありません。

SCOTT> desc maybe_driving_tab
名前 NULL? 型
----------------------------------------- -------- ----------------------------
ID NOT NULL NUMBER
ALTID NUMBER
DATA VARCHAR2(2000)
STATUS NOT NULL NUMBER(2)

SCOTT> desc maybe_inner_tab
名前 NULL? 型
----------------------------------------- -------- ----------------------------
ID NOT NULL NUMBER
ALTID NUMBER
DATA VARCHAR2(2000)
STATUS NOT NULL NUMBER(2)


そして、これまた適当にデータを登録して統計を取っちゃいます。

SCOTT> truncate table maybe_driving_tab;
SCOTT> truncate table maybe_inner_tab;
SCOTT> insert into maybe_driving_tab values(1,1,1,1);
SCOTT> insert into maybe_driving_tab values(2,1,1,1);
SCOTT> insert into maybe_inner_tab select * from maybe_driving_tab;
SCOTT> commit;
SCOTT> exec dbms_stats.gather_table_stats(ownname=>'SCOTT',tabname=>'MAYBE_DRIVING_TAB',cascade=>true,no_invalidate=>false,);
SCOTT> exec dbms_stats.gather_table_stats(ownname=>'SCOTT',tabname=>'MAYBE_INNER_TAB',cascade=>true,no_invalidate=>false);

この時点で代表的な統計情報を見てみると、こんな感じです。

SCOTT> select table_name,index_name,num_rows,distinct_keys,clustering_factor from user_indexes where table_name like 'MAYBE%'

TABLE_NAME INDEX_NAME NUM_ROWS DISTINCT_KEYS CLUSTERING_FACTOR
------------------------------ ------------------------------ ---------- ------------- -----------------
MAYBE_DRIVING_TAB PK_MAYBE_DRIVING_TAB 2 2 1
MAYBE_INNER_TAB PK_MAYBE_INNER_TAB 2 2 1

今回、主役となるid列は、連番で登録します。このタイプはクラスタリングファクタも低くなりindex range scanの有効範囲が広めになる傾向があります。しかし、このデータを登録する前に統計情報を取得(2件の時に)したため、実データと統計情報は大きく乖離しています。Oracle Database 12c 12.1.0.2.0 のオプティマイザにちょっとした意地悪をしています。

BEGIN
FOR i IN 3..200000 LOOP
INSERT INTO maybe_driving_tab VALUES(
i
,CEIL(DBMS_RANDOM.VALUE(1,400001))
,LPAD(i,500,'*')
,MOD(i,2))
;
INSERT INTO maybe_inner_tab VALUES(
i
,CEIL(DBMS_RANDOM.VALUE(1,400001))
,LPAD(i,500,'*')
,MOD(i,2))
;
IF MOD(i,100) = 0 THEN
COMMIT;
END IF;
END LOOP;
COMMIT;
END;
/


統計取得後に、これだけ大量のデータを登録していれば....統計情報は失効しています

SCOTT> select table_name,num_rows,stale_stats from user_tab_statistics where table_name like 'MAYBE%';

TABLE_NAME NUM_ROWS STA
------------------------------ ---------- ---
MAYBE_DRIVING_TAB 2 YES
MAYBE_INNER_TAB 2 YES


統計情報上は2行ですが...実際は20万行あります (^^;; かなり意地の悪い状況にしてありますが、状況的になくもないと思います。

SCOTT> select count(1) from maybe_driving_tab;

COUNT(1)
----------
200000

SCOTT> select count(1) from maybe_inner_tab;

COUNT(1)
----------
200000


はい、準備はできました。ここからが本題ですよ〜〜っ。

次のようなSQL文を実行してみます。
AUTOTRACEのNote部分に、12cから登場したadaptive planが適用されていることが示されてます。(見逃さないでくださいよ。
adaptiveな機能が多くなり、リテラル値を使っていても実際の実行計画とは異なる実行計画が示されることが多くなってきてるんです。
dbms_xplan.display_cursor(format=>'ALLSTATS LAST')、SQLトレース、または、SQL監視機能を使って実際に選択されている実行計画の確認が重要になった、ということですからね!! これ大事ですよ。

SCOTT> r
1 SELECT
2 /* SQL101 */
3 /*+
4 MONITOR
5 */
6 *
7 FROM
8 maybe_driving_tab t1
9 INNER JOIN maybe_inner_tab t2
10 ON
11 t1.id = t2.id
12 WHERE
13* t1.id BETWEEN 1 and 30000

30000行が選択されました。

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

--------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 44 | 4 (0)| 00:00:01 |
| 1 | NESTED LOOPS | | 2 | 44 | 4 (0)| 00:00:01 |
| 2 | NESTED LOOPS | | 2 | 44 | 4 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID BATCHED| maybe_driving_tab | 2 | 22 | 2 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | PK_maybe_driving_tab | 2 | | 1 (0)| 00:00:01 |
|* 5 | INDEX UNIQUE SCAN | PK_maybe_inner_tab | 1 | | 0 (0)| 00:00:01 |
| 6 | TABLE ACCESS BY INDEX ROWID | maybe_inner_tab | 1 | 11 | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------------

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

4 - access("T1"."ID">=1 AND "T1"."ID"<=30000)
5 - access("T1"."ID"="T2"."ID")
filter("T2"."ID">=1 AND "T2"."ID"<=30000)

Note
-----
- this is an adaptive plan


統計
----------------------------------------------------------
0 recursive calls
0 db block gets
8573 consistent gets
2159 physical reads
0 redo size
31686000 bytes sent via SQL*Net to client
22541 bytes received via SQL*Net from client
2001 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
30000 rows processed

では、実際に実行された実行計画をSQL監視機能(オプションが必要な機能です)を使って見てみます。

11gまでは見たこともない不思議な光景になっていますよね。これがAdaptive Joinの実行計画。ほうほうって感じです。

この実行計画の中には2つの実行計画が混在しています。実際に実行された操作もあれば、実行されない操作も含まれています。

どの操作が実行されたかみるには、Execs列をみます。

ね、ね、ね。 
Nested Loop結合じゃなくて、Hash結合が実行されています。
はじめは、Nested Loop結合をやろうとしてるのわかりますか?

ID=4のStatistics Collectorとう操作で実行時の統計(このケースでは行数でしょうかね??? 想像ですが)をみて、Nested Loop結合を寸止めして(妥当な表現かあやしいが)、ハッシュ結合に切り替えた様子が見えますよね。。。。ちょいと感動したw

ただし、index range scanするには行数的に微妙な状況になりつつありそうなところだと思います。

======================================================================================================================================================================================
| 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 | | | | 5 | +0 | 1 | 30000 | | | | | |
| 1 | HASH JOIN | | 2 | 4 | 5 | +0 | 1 | 30000 | | | 20M | | |
| 2 | NESTED LOOPS | | 2 | 4 | | | 1 | | | | | | |
| 3 | NESTED LOOPS | | 2 | 4 | | | 1 | | | | | | |
| 4 | STATISTICS COLLECTOR | | | | 1 | +0 | 1 | 0 | | | | | |
| 5 | TABLE ACCESS BY INDEX ROWID BATCHED | maybe_driving_tab | 2 | 2 | 1 | +0 | 1 | 30000 | | | | | |
| 6 | INDEX RANGE SCAN | PK_maybe_driving_tab | 2 | 1 | 1 | +0 | 1 | 30000 | | | | | |
| 7 | INDEX UNIQUE SCAN | PK_maybe_inner_tab | 1 | | | | | | | | | | |
| 8 | TABLE ACCESS BY INDEX ROWID | maybe_inner_tab | 1 | 1 | | | | | | | | | |
| 9 | TABLE ACCESS BY INDEX ROWID BATCHED | maybe_inner_tab | 1 | 1 | 5 | +0 | 1 | 30000 | 2108 | 16MB | | | |
| 10 | INDEX RANGE SCAN | PK_maybe_inner_tab | 1 | | 5 | +0 | 1 | 30000 | 51 | 408KB | | | |
======================================================================================================================================================================================


さらに、取得件数を倍にしてみました。

どうなるでしょう。

割合的には、30%の範囲検索です。(統計情報次第では全表走査+ハッシュ結合に切り替わることも無くはない状況ですが.....)

SELECT
/* SQL102 */
/*+
MONITOR
*/
*
FROM
maybe_driving_tab t1
INNER JOIN maybe_inner_tab t2
ON
t1.id = t2.id
WHERE
t1.id BETWEEN 1 and 60000
/

60000行が選択されました。

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

--------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 44 | 4 (0)| 00:00:01 |
| 1 | NESTED LOOPS | | 2 | 44 | 4 (0)| 00:00:01 |
| 2 | NESTED LOOPS | | 2 | 44 | 4 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID BATCHED| maybe_driving_tab | 2 | 22 | 2 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | PK_maybe_driving_tab | 2 | | 1 (0)| 00:00:01 |
|* 5 | INDEX UNIQUE SCAN | PK_maybe_inner_tab | 1 | | 0 (0)| 00:00:01 |
| 6 | TABLE ACCESS BY INDEX ROWID | maybe_inner_tab | 1 | 11 | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------------

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

4 - access("T1"."ID">=1 AND "T1"."ID"<=60000)
5 - access("T1"."ID"="T2"."ID")
filter("T2"."ID">=1 AND "T2"."ID"<=60000)

Note
-----
- this is an adaptive plan


統計
----------------------------------------------------------
1 recursive calls
0 db block gets
17142 consistent gets
9230 physical reads
0 redo size
63391324 bytes sent via SQL*Net to client
44541 bytes received via SQL*Net from client
4001 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
60000 rows processed


全体の30%の範囲検索でもギリギリ、Index range scanしているようです。 index rowid batchedとう操作が効いているためでしょうか。そのあとに、ハッシュ結合をしています。
index rowid batchedという不連続ブロックの一括読み込みを繰り返して、全体の30%程度のindex range scanを 1度のオペレーションで行っている箇所(ID=5や9)は興味深いですよね。
(クラスタリングファクタが低いと、実はtable full scanと大差なかったりして〜〜〜と思ったり、思わなかったり)

======================================================================================================================================================================================
| 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 | | | | 9 | +0 | 1 | 60000 | | | | | |
| 1 | HASH JOIN | | 2 | 4 | 9 | +0 | 1 | 60000 | | | 36M | | |
| 2 | NESTED LOOPS | | 2 | 4 | 1 | +0 | 1 | 1 | | | | | |
| 3 | NESTED LOOPS | | 2 | 4 | 1 | +0 | 1 | 1 | | | | | |
| 4 | STATISTICS COLLECTOR | | | | 1 | +0 | 1 | 60000 | | | | | |
| 5 | TABLE ACCESS BY INDEX ROWID BATCHED | maybe_driving_tab | 2 | 2 | 1 | +0 | 1 | 60000 | 4615 | 36MB | | | |
| 6 | INDEX RANGE SCAN | PK_maybe_driving_tab | 2 | 1 | 1 | +0 | 1 | 60000 | | | | | |
| 7 | INDEX UNIQUE SCAN | PK_maybe_inner_tab | 1 | | | | | | | | | | |
| 8 | TABLE ACCESS BY INDEX ROWID | maybe_inner_tab | 1 | 1 | | | | | | | | | |
| 9 | TABLE ACCESS BY INDEX ROWID BATCHED | maybe_inner_tab | 1 | 1 | 9 | +0 | 1 | 60000 | 4615 | 36MB | | | |
| 10 | INDEX RANGE SCAN | PK_maybe_inner_tab | 1 | | 9 | +0 | 1 | 60000 | | | | | |
======================================================================================================================================================================================


次に、全体の40%程度の範囲検索にしてみます。
全表走査+ハッシュ結合が理想だと思うのですが、adaptive joinでhash結合になっているので、全表走査になりそうな予感はしますが...

SELECT
/* SQL103 */
/*+
MONITOR
*/
*
FROM
maybe_driving_tab t1
INNER JOIN maybe_inner_tab t2
ON
t1.id = t2.id
WHERE
t1.id BETWEEN 1 and 80000
/

80000行が選択されました。

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

--------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 44 | 4 (0)| 00:00:01 |
| 1 | NESTED LOOPS | | 2 | 44 | 4 (0)| 00:00:01 |
| 2 | NESTED LOOPS | | 2 | 44 | 4 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID BATCHED| maybe_driving_tab | 2 | 22 | 2 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | PK_maybe_driving_tab | 2 | | 1 (0)| 00:00:01 |
|* 5 | INDEX UNIQUE SCAN | PK_maybe_inner_tab | 1 | | 0 (0)| 00:00:01 |
| 6 | TABLE ACCESS BY INDEX ROWID | maybe_inner_tab | 1 | 11 | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------------

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

4 - access("T1"."ID">=1 AND "T1"."ID"<=80000)
5 - access("T1"."ID"="T2"."ID")
filter("T2"."ID">=1 AND "T2"."ID"<=80000)

Note
-----
- this is an adaptive plan


統計
----------------------------------------------------------
4 recursive calls
0 db block gets
22856 consistent gets
3393 physical reads
0 redo size
84528237 bytes sent via SQL*Net to client
59215 bytes received via SQL*Net from client
5335 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
80000 rows processed


ん〜〜〜〜〜っ。adaptive joinでhash結合にはなるもの全表走査にはなかなかなってくれません。むむむ。

==============================================================================================================================================================================================================
| 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 | | | | 12 | +0 | 1 | 80000 | | | | | | | | |
| 1 | HASH JOIN | | 2 | 4 | 12 | +0 | 1 | 80000 | 21 | 2MB | 21 | 2MB | 48M | 3M | | |
| 2 | NESTED LOOPS | | 2 | 4 | 1 | +0 | 1 | 1 | | | | | | | | |
| 3 | NESTED LOOPS | | 2 | 4 | 1 | +0 | 1 | 1 | | | | | | | | |
| 4 | STATISTICS COLLECTOR | | | | 1 | +0 | 1 | 80000 | | | | | | | | |
| 5 | TABLE ACCESS BY INDEX ROWID BATCHED | maybe_driving_tab | 2 | 2 | 1 | +0 | 1 | 80000 | 1539 | 12MB | | | | | | |
| 6 | INDEX RANGE SCAN | PK_maybe_driving_tab | 2 | 1 | 1 | +0 | 1 | 80000 | | | | | | | | |
| 7 | INDEX UNIQUE SCAN | PK_maybe_inner_tab | 1 | | | | | | | | | | | | | |
| 8 | TABLE ACCESS BY INDEX ROWID | maybe_inner_tab | 1 | 1 | | | | | | | | | | | | |
| 9 | TABLE ACCESS BY INDEX ROWID BATCHED | maybe_inner_tab | 1 | 1 | 12 | +0 | 1 | 80000 | 1539 | 12MB | | | | | | |
| 10 | INDEX RANGE SCAN | PK_maybe_inner_tab | 1 | | 12 | +0 | 1 | 80000 | | | | | | | | |
==============================================================================================================================================================================================================


ちょっと、ここで寄り道してみます。
adaptive joinを無効にしたら、多分、一番だめな感じのNested Loop結合になるはずなので確認しておきましょう。
隠しパラメータを %adaptive%で検索して見つけたのがそのものズバリの隠しパラメータそ使つかいます。 :)

AUTOTRACEから adaptive planというNoteが消えましたねぇ。(^^

SELECT
/* SQL104 */
/*+
MONITOR
OPT_PARAM('_optimizer_nlj_hj_adaptive_join' 'false')
*/
*
FROM
maybe_driving_tab t1
INNER JOIN maybe_inner_tab t2
ON
t1.id = t2.id
WHERE
t1.id BETWEEN 1 and 80000
/


80000行が選択されました。

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

--------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 44 | 4 (0)| 00:00:01 |
| 1 | NESTED LOOPS | | 2 | 44 | 4 (0)| 00:00:01 |
| 2 | NESTED LOOPS | | 2 | 44 | 4 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID BATCHED| maybe_driving_tab | 2 | 22 | 2 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | PK_maybe_driving_tab | 2 | | 1 (0)| 00:00:01 |
|* 5 | INDEX UNIQUE SCAN | PK_maybe_inner_tab | 1 | | 0 (0)| 00:00:01 |
| 6 | TABLE ACCESS BY INDEX ROWID | maybe_inner_tab | 1 | 11 | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------------

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

4 - access("T1"."ID">=1 AND "T1"."ID"<=80000)
5 - access("T1"."ID"="T2"."ID")
filter("T2"."ID">=1 AND "T2"."ID"<=80000)


統計
----------------------------------------------------------
0 recursive calls
0 db block gets
102603 consistent gets
12608 physical reads
0 redo size
84528237 bytes sent via SQL*Net to client
59215 bytes received via SQL*Net from client
5335 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
80000 rows processed


統計情報との乖離が大きいので、Nested Loop結合でindex range scanしてますねぇ〜。駆動表はtable access index rowid batchedになっていますが....
内部表は8万回もindex indexunique scanされてますね。

=============================================================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | (%) | (# samples) |
=============================================================================================================================================================
| 0 | SELECT STATEMENT | | | | 11 | +0 | 1 | 80000 | | |
| 1 | NESTED LOOPS | | 2 | 4 | 11 | +0 | 1 | 80000 | | |
| 2 | NESTED LOOPS | | 2 | 4 | 11 | +0 | 1 | 80000 | | |
| 3 | TABLE ACCESS BY INDEX ROWID BATCHED | maybe_driving_tab | 2 | 2 | 11 | +0 | 1 | 80000 | | |
| 4 | INDEX RANGE SCAN | PK_maybe_driving_tab | 2 | 1 | 11 | +0 | 1 | 80000 | | |
| 5 | INDEX UNIQUE SCAN | PK_maybe_inner_tab | 1 | | 11 | +0 | 80000 | 80000 | | |
| 6 | TABLE ACCESS BY INDEX ROWID | maybe_inner_tab | 1 | 1 | 11 | +0 | 80000 | 80000 | | |
=============================================================================================================================================================

寄り道はここまでにして、
オプティマイザがやってくれないなら、ヒントでチューニングしちゃいましょ。
hash結合+全表走査になるように....

SELECT
/* SQL105 */
/*+
MONITOR
OPT_PARAM('_optimizer_nlj_hj_adaptive_join' 'false')
FULL(t1)
FULL(t2)
USE_HASH(t1 t2)
*/
*
FROM
maybe_driving_tab t1
INNER JOIN maybe_inner_tab t2
ON
t1.id = t2.id
WHERE
t1.id BETWEEN 1 and 80000
/

80000行が選択されました。


いい感じになったような気がしますが、consistent getsは、index range scan + table access bby index rowid batchedの方が少ないんですねぇ〜。
実データのクラスタリングファクタが高かったら違う結果になるような気がします。
今回は試しませんが。

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

----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 44 | 6 (0)| 00:00:01 |
|* 1 | HASH JOIN | | 2 | 44 | 6 (0)| 00:00:01 |
|* 2 | TABLE ACCESS FULL| maybe_driving_tab | 2 | 22 | 3 (0)| 00:00:01 |
|* 3 | TABLE ACCESS FULL| maybe_inner_tab | 2 | 22 | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------

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

1 - access("T1"."ID"="T2"."ID")
2 - filter("T1"."ID">=1 AND "T1"."ID"<=80000)
3 - filter("T2"."ID">=1 AND "T2"."ID"<=80000)


統計
----------------------------------------------------------
0 recursive calls
0 db block gets
33390 consistent gets
30906 physical reads
0 redo size
84528237 bytes sent via SQL*Net to client
59215 bytes received via SQL*Net from client
5335 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
80000 rows processed


==============================================================================================================================================================
| 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 | | | | 12 | +0 | 1 | 80000 | | | | | |
| 1 | HASH JOIN | | 2 | 6 | 11 | +0 | 1 | 80000 | | | 50M | | |
| 2 | TABLE ACCESS FULL | maybe_driving_tab | 2 | 3 | 1 | +0 | 1 | 80000 | 252 | 121MB | | | |
| 3 | TABLE ACCESS FULL | maybe_inner_tab | 2 | 3 | 11 | +0 | 1 | 80000 | 251 | 121MB | | | |
==============================================================================================================================================================


統計との乖離があるのは確かなので統計情報を取り直してみます。(オプティマイザはどういった計画を導きだすか....)

SCOTT> exec dbms_stats.gather_table_stats(ownname=>'SCOTT',tabname=>'MAYBE_DRIVING_TAB',no_invalidate=>false,cascade=>true);
SCOTT> exec dbms_stats.gather_table_stats(ownname=>'SCOTT',tabname=>'MAYBE_INNER_TAB',no_invalidate=>false,cascade=>true);


実行!

SELECT
/* SQL108 */
/*+
MONITOR
*/
*
FROM
maybe_driving_tab t1
INNER JOIN maybe_inner_tab t2
ON
t1.id = t2.id
WHERE
t1.id BETWEEN 1 and 80000
/

80000行が選択されました。


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

------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 80000 | 78M| | 12776 (1)| 00:00:01 |
|* 1 | HASH JOIN | | 80000 | 78M| 40M| 12776 (1)| 00:00:01 |
|* 2 | TABLE ACCESS FULL| maybe_driving_tab | 80000 | 39M| | 4398 (1)| 00:00:01 |
|* 3 | TABLE ACCESS FULL| maybe_inner_tab | 80000 | 39M| | 4398 (1)| 00:00:01 |
------------------------------------------------------------------------------------------------

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

1 - access("T1"."ID"="T2"."ID")
2 - filter("T1"."ID"<=80000 AND "T1"."ID">=1)
3 - filter("T2"."ID"<=80000 AND "T2"."ID">=1)

Note
-----
- this is an adaptive plan


統計
----------------------------------------------------------
0 recursive calls
0 db block gets
35859 consistent gets
15453 physical reads
0 redo size
84528237 bytes sent via SQL*Net to client
59215 bytes received via SQL*Net from client
5335 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
80000 rows processed


===========================================================================================================================================================================
| 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 | | | | 12 | +0 | 1 | 80000 | | | | | |
| 1 | HASH JOIN | | 80000 | 12776 | 11 | +0 | 1 | 80000 | | | 51M | | |
| 2 | NESTED LOOPS | | 80000 | 12776 | | | 1 | | | | | | |
| 3 | NESTED LOOPS | | | | | | 1 | | | | | | |
| 4 | STATISTICS COLLECTOR | | | | 1 | +0 | 1 | 0 | | | | | |
| 5 | TABLE ACCESS FULL | maybe_driving_tab | 80000 | 4398 | 1 | +0 | 1 | 80000 | 252 | 121MB | | | |
| 6 | INDEX UNIQUE SCAN | PK_maybe_inner_tab | | | | | | | | | | | |
| 7 | TABLE ACCESS BY INDEX ROWID | maybe_inner_tab | 1 | 4398 | | | | | | | | | |
| 8 | TABLE ACCESS FULL | maybe_inner_tab | 80000 | 4398 | 11 | +0 | 1 | 80000 | | | | | |
===========================================================================================================================================================================

うん、やっと、オプティマイザと私の意見が一致したようだw
おら、オプティマイザより先にこの結果想定してたんだ! アダプティブなオプティマイザにちょっとだけ勝ったw ぞ〜〜〜〜っ. (クレヨン シンちゃん風に...)

そして....
table access by index rowid batched って状況次第ではあるものの、意外にいいかも。と思ったのであった。 (^^/
....今後の為に、さらにネタを集めたほうがいいかもしれない...


最後に、adaptiveな機能が多くなっていますが、その基礎になるのはやはり、統計情報と、その向こうにあるデータです。
機能に目が移りがちですが忘れちゃいけないのが、これらです。


以上、役に立つような、立たないようなネタでした。

あ、一つ忘れてました〜〜っ。
optimizer_dynamic_samplingが効きそうな気もしますが、今回はデフォルト(2)だったので結果は未確認です。だれかやってみて〜〜:)


JPOUG Advent Calendar 2014
明日は、yoku0825さんです。お楽しみに〜。

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

2014年6月21日 (土)

db tech showcase 2014 Osaka に行ってきた

I love your data(どこかのパクリw) な人たちが、いろいろなDBMSを見聞きし、それぞれの思いで、何年か先の未来に、それぞれの思いを馳せる

そんな、集まりが、 db tech showcase 2014 Osaka 

(ん....俺っぽくない出だしを書いててワロタ....)


に臨時休業(自分ではこれも仕事のうちなんだがw 仕事って思ってないだけw)して参加した。
https://www.facebook.com/db.tech.showcase

Slideshare : セッション資料はここ


話を聞いていたら、オレオレレプリケーションできそうな気になるから不思議 :) .
オレオレ、ゴルゲやオレオレ、attunityとか、自分で作って試してみると、面倒くさいポイントとか見えていいかもね。
Attunity Replicateの画面を初めてみたけど、シンプルで好きなデザイン。
B31 : LogMinerってレプリケーションソフトで使われてるけどどうなってる? / 森田俊哉(インサイトテクノロジー)


Oracle以外の話も聴きたくてNoSQL系などをチョイス。 割り切った実装で特定用途でその力を発揮する。割り切り大事。
D32 : Amazon Redshift Deep Dive / 大久保順(アマゾンデータサービスジャパン)


B33 : Riak: 本物の高可用性を実現する仕組みとは? / 佐藤 貴彦 (Bashoジャパン)


D34 : データウェアハウス・エンジンTeradataのご紹介とビッグデータ統合アーキテクチャー / 山本 泰史(日本テラデータ)


The Machine!にも関連するのだろうけど、Memristorの話題も!
D35 : インメモリーデータベース徹底比較 / 小森博之(日本HP)


そしてスペシャルセッション、遠い未来じゃないはなし
A36 : ウエアラブルとO2Oが切り拓くICTの新地平 / 村上憲郎

vessylもそんな”もの”の一つかもしれない。飲みものの分析ができるんだからトイレにも応用できるんじゃないか的な :)
日本のトイレがそうなるかは分からないけど、先にやってくれたら面白いかもね。
毎日が健康診断、データはかかりつけの医師に共有されていて、気になるデータが見つかると、洗面台のミラー風マルチタッチデバイスに情報がプッシュされ...必要なら、その場で通院予約、その後待たされることなく診察なんて時間の無駄がなくていいな〜と、ぼーっと妄想していたり。

楽しいやね。 :)

そういえばそんなシーンのある映画で思い出したのがこれ



T-シャツ、ありがとうございました。
Bqilsptcyaae4vjjpglarge

東京から大阪への新幹線で日帰りだと電池切れ感が半端ないので一泊することをおすすめしますw


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

シーケンス.NEXTVALが使えないからぐるぐる〜〜〜っとしていいですか? (30歳 エンジニア 男性)

CREATE TABLE 〜 AS SELECT文でシーケンス.NEXTVALって使えないからぐるぐる〜〜〜っとしたループ処理しないといけないですよね〜。

と質問されたのですが、1文で書けますからね!
(SQL文でワンライナーって言いそうになったけど、飲み込んだw)


準備

SCOTT> l
1 CREATE TABLE table_foobar
2 (
3 id NUMBER PRIMARY KEY
4 ,data VARCHAR2(10)
5* )
SCOTT> /

表が作成されました。

SCOTT> l
1 INSERT INTO table_foobar
2 SELECT
3 LEVEL
4 ,'D'||TO_CHAR(LEVEL,'FM099999999')
5 FROM
6 dual
7 CONNECT BY
8* LEVEL <= 100.
SCOTT> /

100行が作成されました。

SCOTT> commit;

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

SCOTT> SELECT * FROM table_foobar ORDER BY id;

ID DATA
---------- ----------
1 D000000001
2 D000000002
3 D000000003
4 D000000004
5 D000000005
6 D000000006
7 D000000007
8 D000000008
9 D000000009
10 D000000010

   ...中略...

90 D000000090
91 D000000091
92 D000000092
93 D000000093
94 D000000094
95 D000000095
96 D000000096
97 D000000097
98 D000000098
99 D000000099
100 D000000100

100行が選択されました。


前述のデータを複製しtable_foobar_tmp表を作成するとします。
なお、DATA列はそのままで、ID列は、次のシーケンスを利用してID = 1..100の順にシーケンスから採番しなおしたい。
(以下のシーケンスの定義からすると、 ID=1 は、1000、 ID=2は、1001にしたい。)

SCOTT> CREATE SEQUENCE seq_foobar start with 1000 maxvalue 999999999;

順序が作成されました。

1文で書けますよね!

SCOTT> l
1 CREATE TABLE table_foobar_tmp
2 AS
3 SELECT
4 seq_foobar.NEXTVAL AS id
5 ,t01.data
6 FROM
7 (
8 SELECT
9 id
10 ,data
11 FROM
12 table_foobar
13 ORDER BY
14 id
15* ) t01
SCOTT> /

表が作成されました。

SCOTT> SELECT * FROM table_foobar_tmp ORDER BY ID;

ID DATA
---------- ----------
1000 D000000001
1001 D000000002
1002 D000000003
1003 D000000004
1004 D000000005
1005 D000000006
1006 D000000007
1007 D000000008
1008 D000000009
1009 D000000010
1010 D000000011

   ...中略...

1090 D000000091
1091 D000000092
1092 D000000093
1093 D000000094
1094 D000000095
1095 D000000096
1096 D000000097
1097 D000000098
1098 D000000099
1099 D000000100

100行が選択されました。

はい、できました!


マニュアルには、「NEXTVALへの参照が含まれる単一のSQL文の中では、Oracleは、次の各行につき1回順序を増加させます。」
と記載されているので、質問してきた方は、マニュアルを読んでいないか、マニュアル読んでなくても実際に試していない食わず嫌い状態だったか、
以下のようなシーケンスの制限に遭遇して、できないんだ!と思い込んでしまった。

病は気からという状態だったのでしょうね。:)


SCOTT> l
1 CREATE TABLE table_foobar_tmp_NG
2 AS
3 SELECT
4 seq_foobar.NEXTVAL AS ID
5 ,t01.data
6 FROM
7 table_foobar t01
8 ORDER BY
9* t01.id
SCOTT> /
seq_foobar.NEXTVAL AS ID
*
行4でエラーが発生しました。:
ORA-02287: ここでは順序番号は使用できません。

エラーメッセージを見ても、Action がRemove the sequence numberだけですからね....

SCOTT> !oerr ORA 2287
02287, 00000, "sequence number not allowed here"
// *Cause: The specified sequence number (CURRVAL or NEXTVAL) is inappropriate
// here in the statement.
// *Action: Remove the sequence number.

そんな時はマニュアルを..
Oracle® Database SQL言語リファレンス 11gリリース2 (11.2) 順序疑似列

順序値の使用方法には、CREATE TABLE ... AS SELECTで使用できると書いてるのに。なんでだろう?
と,、なるかもしれないですが、よ〜〜〜くマニュアルを読んでくださいよ〜っ。

順序値の制限事項にGROUP BY句やORDER BY句を持つSELECT文では使用できないとも書かれているところにちゅうも〜〜〜〜く!

ORDER BY句を含んでいるのでORA-02287回避のために、サブクエリにして別クエリブロック化、シーケンスを利用しているクエリブロックにはORDER BY句を含まないようにしているところがポイント :)

テストデータ作るときとか、知ってると便利ですよ〜と。

SCOTT> l
1 CREATE TABLE table_foobar_tmp
2 AS
3 SELECT
4 seq_foobar.NEXTVAL AS id
5 ,t01.data
6 FROM
7 (
8 SELECT
9 id
10 ,data
11 FROM
12 table_foobar
13 ORDER BY
14 id
15* ) t01
SCOTT> /

Enjoy SQL!

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

2014年2月14日 (金)

JPOUG Tech Talk Night #4 @ IIJ  開催のおしらせ

JPOUG Tech Talk Night #4やりますよ〜

今回は、IIJさんの会場をお借します。ありがとうございます!


20140214_01812

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

2013年11月17日 (日)

db tech showcase tokyo 2013 - A35 - JPOUG特濃:潮溜まりでジャブジャブ、SQLチューニング

11/13〜15に開催されたdb tech showcase tokyo 2013 の最終日、午後の4枠で特濃JPOUGとてセッションを行いました。

貴重な機会を提供いただいたインサイトテクノロジーの皆様ありがとうございました。
また、お忙しい中、セッションに参加してくださった皆様、ありがとうございました。

A35
15:00-15:45 / 「JPOUG特濃:潮溜まりでジャブジャブ、SQLチューニング」 のセッション資料を公開しました。

塩分濃いめの潮溜まりで釣り上げたSQLは治療できるかどうかもわからない病になっていました….
治療できたか、できなかったのか……

曲者すぎる難病もありますが、何かの機会に思い出していただければと思います。

みなさん、楽しい時間をありがとう。

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

2013年8月 8日 (木)

Difference of Initialization Parameters between 11g r1 (11.1.0.6.0) and 12c r1 (12.1.0.1.0) - including hidden params

※2014/8/4 修正、追記
比較したバージョンが誤っていたため訂正しました。正しくは、11g R1 11.1.0.6.0でした。


型の差分までは取得しませんでしが、Oracle11g R2 (11.2.0.1.0)とOracle12c R1 (12.1.0.1.0)の初期化パラメータ(隠しパラメータ含む)の差分をHTML化しました。(クエリ結果をHTML形式でspoolしただけですが)

まだどのような差分があるのか見切れていませんが、じ〜〜〜っくりと見ようと思っています :)

参考 (パラメータ数)
11g R2 11.2.0.1.0
11g R1 11.1.0.6.0
パラメータ総数 : 2049
隠しパラメータ数 : 1755 (うち ダブルアンダースコア隠しパラメータ数 : 10 )

11g R1 11.2.0.1.0
パラメータ総数 : 2399
隠しパラメータ数 : 2057 (うち ダブルアンダースコア隠しパラメータ数 : 10 )

12c R1 12.1.0.1.0
パラメータ総数 : 3351
隠しパラメータ数 : 2984 (うち ダブルアンダースコア隠しパラメータ数 : 11 )

隠しパラメータの増加が目立ちます.... (@@)

Difference of Initialization Parameters between 11g r1 (11.1.0.6.0) and 12c r1 (12.1.0.1.0)

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

2013年7月13日 (土)

Oracle Database 12c R1 : コンテナデータベースとプラガブルデータベースを歩きやすくしてみる

世界を見渡せば、かなりの方がOracle Database 12cのインストール手順を公開し始めているのでインストール後の話を書いておきますね。

インストール時にPluggable Databaseも同時に作成してもtnsnames.oraにはContainer Databaseへの接続定義しか無かったんです。(私のインストール手順に問題がなければ..なんですが)
無くてもEasy connectを使えば接続できるので問題はないんですが...

ただ、Easy connectで毎回タイプするのも面倒(少々長いし)なので、tnsnames.oraにPluggable Database接続用定義を作成しておきましょう。

定義内容はいままでと変わらず。 SERVICE_NAMEの箇所を接続先のPluggable Database名にするだけ。
(Pluggable Database名と書いたが、マニュアル読みきれてないので表現は間違っているかもしれません。間違っていたら後で修正します)

[oracle@emperortetra]$ cat $ORACLE_HOME/network/admin/tnsnames.ora
# tnsnames.ora Network Configuration File: /opt/u01/product/12.1.0/dbhome_1/network/admin/tnsnames.ora
# Generated by Oracle configuration tools.

# for Container Database : orcl12c
ORCL12C =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = emperortetra)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = orcl12c)
)
)

# for Pluggable Database : pdborcl
PDBORCL =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = emperortetra)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = pdborcl)
)
)

本題である、歩きやすくするための設定がこれ。

複数のPluggable DatabaseとContainer Database間を旅することも多くなりそうですから、今、自分はどこにいるのか把握しやすくしておいたほうが吉かと。

12c以前から定番で設定している内容ではあるのですが。赤字部分がポイント

目立たないけど、地味に大切なのが、 set tab off だと思っているのは私だけだろうか!w 
(ここのところ、set tab offじゃない人のコピペしてきた、位置ズレまくりの実行計画見せられててイライラしている影響もある :)


それ以外の設定も、設定してるだけで、「むむむ、この方できるかも!」 と、
思わせる(思わせるだけですからね!w つかみはOK的な感じなので、あとはあなた次第)設定内容なので大切と言えば大切。
(login.sqlでもOK)

[oracle@emperortetra]$ cat $ORACLE_HOME/sqlplus/admin/glogin.sql
--
-- Copyright (c) 1988, 2005, Oracle. All Rights Reserved.
--
-- NAME
-- glogin.sql
--
-- DESCRIPTION
-- SQL*Plus global login "site profile" file
--
-- Add any SQL*Plus commands here that are to be executed when a
-- user starts SQL*Plus, or uses the SQL*Plus CONNECT command.
--
-- USAGE
-- This script is automatically run
--
set linesize 180
set pagesize 10000
set timi on
set time on
define _EDITOR=vi
col table_name for a30
col column_name for a30
col segment_name for a30
col tablespace_name for a30
col index_name for a30
col partition_name for a30
col sub_partition_name for a30
set sqlp "_USER'@'_CONNECT_IDENTIFIER> "
set tab off
[oracle@emperortetra]$


リスナーを起動してステータスを見てみましょう (u01が/optの下にあるのは内緒w じゃないw
まだ、データベースを起動していないのでこんな感じです。

[oracle@emperortetra]$ lsnrctl status

LSNRCTL for Linux: Version 12.1.0.1.0 - Production on 06-7月 -2013 09:10:04

Copyright (c) 1991, 2013, Oracle. All rights reserved.

(DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=EXTPROC1521)))に接続中
リスナーのステータス
------------------------
別名 LISTENER
バージョン TNSLSNR for Linux: Version 12.1.0.1.0 - Production
開始日 06-7月 -2013 09:06:38
稼働時間 0 日 0 時間 3 分 26 秒
トレース・レベル off
セキュリティ ON: Local OS Authentication
SNMP OFF
パラメータ・ファイル /opt/u01/product/12.1.0/dbhome_1/network/admin/listener.ora
ログ・ファイル /opt/u01/diag/tnslsnr/emperortetra/listener/alert/log.xml
リスニング・エンドポイントのサマリー...
(DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1521)))
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=emperortetra)(PORT=1521)))
リスナーはサービスをサポートしていません。
コマンドは正常に終了しました。
[oracle@emperortetra]$


Container Databaseの起動(12cより前のOracle Databaseと同じですね)

Container Databaseである orcl12c がSQL*Plusのプロンプトに表示されていますよね!! :)

[oracle@emperortetra]$ sqlplus / as sysdba

SQL*Plus: Release 12.1.0.1.0 Production on 土 7月 6 09:25:22 2013

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

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

09:25:22 SYS@orcl12c> startup
ORACLEインスタンスが起動しました。

Total System Global Area 1653518336 bytes
Fixed Size 2289016 bytes
Variable Size 1040188040 bytes
Database Buffers 603979776 bytes
Redo Buffers 7061504 bytes
データベースがマウントされました。
データベースがオープンされました。
09:25:32 SYS@orcl12c>



Pluggable Databaseを起動します!

09:25:34 SYS@orcl12c> alter pluggable database pdborcl open;

プラガブル・データベースが変更されました。

経過: 00:00:00.86


Pluggable DatabaseのSYS、SYSTEMおよび、SCOTTユーザに接続してみます!
09:25:53 SYSTEM@orcl12c> conn sys@pdborcl as sysdba
パスワードを入力してください:
接続されました。
09:26:06 SYS@pdborcl>
09:26:07 SYS@pdborcl>
09:26:07 SYS@pdborcl> conn system@pdborcl
パスワードを入力してください:
接続されました。
09:26:34 SYSTEM@pdborcl>
09:26:34 SYSTEM@pdborcl>
09:26:35 SYSTEM@pdborcl> conn scott@pdborcl
パスワードを入力してください:
接続されました。
09:26:50 SCOTT@pdborcl>
09:26:51 SCOTT@pdborcl>
09:26:51 SCOTT@pdborcl>


SQL*Plusのプロンプトに接続先の情報を表示しておくと便利ですよね。Database間を歩きやすく、迷子になりにくくなりますよ! :)

おまけ
Container Databaseおよび、Pluggable Database起動後のlistenerのステータスは以下の通り。

[oracle@emperortetra]$ lsnrctl status

LSNRCTL for Linux: Version 12.1.0.1.0 - Production on 06-7月 -2013 09:28:09

Copyright (c) 1991, 2013, Oracle. All rights reserved.

(DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=EXTPROC1521)))に接続中
リスナーのステータス
------------------------
別名 LISTENER
バージョン TNSLSNR for Linux: Version 12.1.0.1.0 - Production
開始日 06-7月 -2013 09:06:38
稼働時間 0 日 0 時間 21 分 31 秒
トレース・レベル off
セキュリティ ON: Local OS Authentication
SNMP OFF
パラメータ・ファイル /opt/u01/product/12.1.0/dbhome_1/network/admin/listener.ora
ログ・ファイル /opt/u01/diag/tnslsnr/emperortetra/listener/alert/log.xml
リスニング・エンドポイントのサマリー...
(DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1521)))
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=emperortetra)(PORT=1521)))
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcps)(HOST=emperortetra)(PORT=5500))(Security=(my_wallet_directory=/opt/u01/admin/orcl12c/xdb_wallet))(Presentation=HTTP)(Session=RAW))
サービスのサマリー...
サービス"orcl12c"には、1件のインスタンスがあります。
インスタンス"orcl12c"、状態READYには、このサービスに対する1件のハンドラがあります...
サービス"orcl12cXDB"には、1件のインスタンスがあります。
インスタンス"orcl12c"、状態READYには、このサービスに対する1件のハンドラがあります...
サービス"pdborcl"には、1件のインスタンスがあります。
インスタンス"orcl12c"、状態READYには、このサービスに対する1件のハンドラがあります...
コマンドは正常に終了しました。
[oracle@emperortetra]$

Enjoy Oracle Database 12c R1 :)


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

2013年7月 4日 (木)

OTN Forum - すげ〜デザイン変わってる :)

OTN Forumのデザインが変わってることに、今、気づくなど..

変わりすぎてて、一瞬、ヤバいとこに来たかとおもったw

https://forums.oracle.com/community/developer/japanese

20130704_64943

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

db file scattered read と db file parallel read と db file sequential read (その3)

続きです。

リクエストにお答えして生SQLトレース(抜粋)を見てみましょう :)

最初に実行したSQL文のSQLトレースを見てみることにします。

select
/*+
leading(t2 t1)
use_nl(t2 t1)
index(t2 pk_high_clustering_factor)
*/
t2.id
,t2.name
,t1.name
from
low_clustering_factor t1
inner join high_clustering_factor t2
on
t1.id = t2.id
where
t2.id between 30001 and 35000
/

実行計画は以下の通り、index range/unique scan + nested loop joinが行われていました。

Rows     Row Source Operation
------- ---------------------------------------------------
2421 NESTED LOOPS (cr=5252 pr=2841 pw=0 time=2771706 us)
2421 NESTED LOOPS (cr=2831 pr=2522 pw=0 time=4204941 us cost=5012 size=1530612 card=2501)
2421 TABLE ACCESS BY INDEX ROWID HIGH_CLUSTERING_FACTOR (cr=2589 pr=2458 pw=0 time=4188001 us cost=2510 size=765612 card=2502)
2421 INDEX RANGE SCAN PK_HIGH_CLUSTERING_FACTOR (cr=168 pr=121 pw=0 time=7663 us cost=8 size=0 card=2502)(object id 82774)
2421 INDEX UNIQUE SCAN PK_LOW_CLUSTERING_FACTOR (cr=242 pr=64 pw=0 time=0 us cost=0 size=0 card=1)(object id 82772)
2421 TABLE ACCESS BY INDEX ROWID LOW_CLUSTERING_FACTOR (cr=2421 pr=319 pw=0 time=0 us cost=1 size=306 card=1)

待機イベント見てみると、index range/unique scan + table access by index rowidという実行計画からは想像できない待機イベントが....(実は狙って発生させてるくせに〜w

Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 163 0.00 0.00
db file scattered read 107 0.01 0.30
SQL*Net message from client 163 0.96 3.36
db file parallel read 161 0.01 0.63
SQL*Net more data to client 161 0.00 0.00
db file sequential read 14 0.00 0.00
********************************************************************************

(生)SQLトレース抜粋

...中略...
PARSE #4:c=1000,e=1534,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=1,plh=1087412721,tim=1364725159601671
EXEC #4:c=1000,e=97,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=1087412721,tim=1364725159601925
WAIT #4: nam='SQL*Net message to client' ela= 7 driver id=1650815232 #bytes=1 p3=0 obj#=82773 tim=1364725159602105 
WAIT #4: nam='db file scattered read' ela= 19499 file#=7 block#=371968 blocks=32 obj#=82774 tim=1364725159621743
WAIT #4: nam='db file scattered read' ela= 11156 file#=7 block#=373703 blocks=32 obj#=82774 tim=1364725159633042
WAIT #4: nam='db file scattered read' ela= 1699 file#=7 block#=375476 blocks=32 obj#=82773 tim=1364725159635032
WAIT #4: nam='db file scattered read' ela= 5889 file#=7 block#=367232 blocks=32 obj#=82772 tim=1364725159641300
WAIT #4: nam='db file scattered read' ela= 17223 file#=7 block#=368599 blocks=32 obj#=82771 tim=1364725159658667
FETCH #4:c=2999,e=56653,p=160,cr=6,cu=0,mis=0,r=1,dep=0,og=1,plh=1087412721,tim=1364725159658788
WAIT #4: nam='SQL*Net message from client' ela= 430 driver id=1650815232 #bytes=1 p3=0 obj#=82771 tim=1364725159659311 
WAIT #4: nam='db file parallel read' ela= 10307 files=1 blocks=14 requests=14 obj#=82773 tim=1364725159669764 
WAIT #4: nam='db file scattered read' ela= 1931 file#=7 block#=374755 blocks=29 obj#=82773 tim=1364725159671819 
WAIT #4: nam='SQL*Net message to client' ela= 3 driver id=1650815232 #bytes=1 p3=0 obj#=82773 tim=1364725159671902 
WAIT #4: nam='SQL*Net more data to client' ela= 40 driver id=1650815232 #bytes=8330 p3=0 obj#=82773 tim=1364725159672223
FETCH #4:c=2000,e=12910,p=43,cr=38,cu=0,mis=0,r=15,dep=0,og=1,plh=1087412721,tim=1364725159672272
WAIT #4: nam='SQL*Net message from client' ela= 9227 driver id=1650815232 #bytes=1 p3=0 obj#=82773 tim=1364725159681536 
WAIT #4: nam='db file parallel read' ela= 5766 files=1 blocks=12 requests=12 obj#=82773 tim=1364725159687404 
WAIT #4: nam='db file scattered read' ela= 1922 file#=7 block#=376416 blocks=32 obj#=82773 tim=1364725159689681
WAIT #4: nam='SQL*Net message to client' ela= 3 driver id=1650815232 #bytes=1 p3=0 obj#=82773 tim=1364725159689951 
WAIT #4: nam='SQL*Net more data to client' ela= 13 driver id=1650815232 #bytes=8278 p3=0 obj#=82773 tim=1364725159690473
FETCH #4:c=4000,e=8972,p=44,cr=32,cu=0,mis=0,r=15,dep=0,og=1,plh=1087412721,tim=1364725159690547
WAIT #4: nam='SQL*Net message from client' ela= 7980 driver id=1650815232 #bytes=1 p3=0 obj#=82773 tim=1364725159698617 
WAIT #4: nam='db file parallel read' ela= 2716 files=1 blocks=13 requests=13 obj#=82773 tim=1364725159701431 
WAIT #4: nam='db file scattered read' ela= 2179 file#=7 block#=374240 blocks=32 obj#=82773 tim=1364725159703739 
WAIT #4: nam='SQL*Net message to client' ela= 5 driver id=1650815232 #bytes=1 p3=0 obj#=82773 tim=1364725159704315 
WAIT #4: nam='SQL*Net more data to client' ela= 40 driver id=1650815232 #bytes=8278 p3=0 obj#=82773 tim=1364725159704541 
FETCH #4:c=3999,e=5937,p=45,cr=32,cu=0,mis=0,r=15,dep=0,og=1,plh=1087412721,tim=1364725159704589
WAIT #4: nam='SQL*Net message from client' ela= 11759 driver id=1650815232 #bytes=1 p3=0 obj#=82773 tim=1364725159716396 
WAIT #4: nam='db file parallel read' ela= 1999 files=1 blocks=13 requests=13 obj#=82773 tim=1364725159718604 
WAIT #4: nam='db file scattered read' ela= 1667 file#=7 block#=374066 blocks=32 obj#=82773 tim=1364725159720394 
WAIT #4: nam='SQL*Net message to client' ela= 3 driver id=1650815232 #bytes=1 p3=0 obj#=82773 tim=1364725159720497 
WAIT #4: nam='SQL*Net more data to client' ela= 43 driver id=1650815232 #bytes=8278 p3=0 obj#=82773 tim=1364725159720707 
FETCH #4:c=1000,e=4261,p=45,cr=32,cu=0,mis=0,r=15,dep=0,og=1,plh=1087412721,tim=1364725159720752
WAIT #4: nam='SQL*Net message from client' ela= 6700 driver id=1650815232 #bytes=1 p3=0 obj#=82773 tim=1364725159727511 
WAIT #4: nam='db file parallel read' ela= 5874 files=1 blocks=14 requests=14 obj#=82773 tim=1364725159733551 
WAIT #4: nam='db file sequential read' ela= 337 file#=7 block#=374065 blocks=1 obj#=82773 tim=1364725159733959 
WAIT #4: nam='SQL*Net message to client' ela= 4 driver id=1650815232 #bytes=1 p3=0 obj#=82773 tim=1364725159734041 
WAIT #4: nam='SQL*Net more data to client' ela= 77 driver id=1650815232 #bytes=8278 p3=0 obj#=82773 tim=1364725159734271 
...以下略...


db file * read待機イベントとアクセスブロック数及び参照オブジェクトとオブジェクト名を見やすくすると以下のようになります。
db_file_multiblock_read_count=32なのでscatterd readのblocksは理解できるのですが、parallel readのblocksってどこで制御してるんだ..わからんw..というか調べきれてない..

db file scattered read  blocks=32 (INDEX:PK_HIGH_CLUSTERING_FACTOR)
db file scattered read blocks=32 (INDEX:PK_HIGH_CLUSTERING_FACTOR)
db file scattered read blocks=32 (TABLE:HIGH_CLUSTERING_FACTOR)
db file scattered read blocks=32 (INDEX:PK_LOW_CLUSTERING_FACTOR)
db file scattered read blocks=32 (TABLE:LOW_CLUSTERING_FACTOR)
db file parallel read blocks=14 (TABLE:HIGH_CLUSTERING_FACTOR)
db file scattered read blocks=29 (TABLE:HIGH_CLUSTERING_FACTOR)
db file parallel read blocks=12 (TABLE:HIGH_CLUSTERING_FACTOR)
db file scattered read blocks=32 (TABLE:HIGH_CLUSTERING_FACTOR)
db file parallel read blocks=13 (TABLE:HIGH_CLUSTERING_FACTOR)
db file scattered read blocks=32 (TABLE:HIGH_CLUSTERING_FACTOR)
db file parallel read blocks=13 (TABLE:HIGH_CLUSTERING_FACTOR)
db file scattered read blocks=32 (TABLE:HIGH_CLUSTERING_FACTOR)
db file parallel read blocks=14 (TABLE:HIGH_CLUSTERING_FACTOR)
db file sequential read blocks=1 (TABLE:HIGH_CLUSTERING_FACTOR)

発生しているdb file * read待機イベントでアクセスしているオブジェクトは以下の通り。
実行計画上 index unique/range scan なのですが、索引でもscatterd readが発生することがあるんですよ〜っ。

db file parallel read
- obj# = 82773 - HIGH_CLUSTERING_FACTOR(表)

db file scattered read
- obj# = 82771 - LOW_CLUSTERING_FACTOR(表)
- obj# = 82772 - PK_LOW_CLUSTERING_FACTOR(索引)
- obj# = 82773 - HIGH_CLUSTERING_FACTOR(表)
- obj# = 82774 - PK_HIGH_CLUSTERING_FACTOR(索引)

db file sequential read
- obj# = 82771 - LOW_CLUSTERING_FACTOR(表)
- obj# = 82773 - HIGH_CLUSTERING_FACTOR(表)


2つ目のクエリは db file parallel readが発生しないだけで傾向は同じです。 :)

次回へつづく。


バックナンバー

db file scattered read と db file parallel read と db file sequential read (その1)
db file scattered read と db file parallel read と db file sequential read (その2)

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

2013年2月 3日 (日)

jdk-7u13にアップデート

http://docs.oracle.com/javase/


20130203_61623


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

2012年12月25日 (火)

Indexがあれば、いいってもんじゃない。中身がたいせつ

吉田さんが、なぜ24日を選んだのか理由がわかった...


さて、JPOUG Advent Calendar、25番目の扉を開けます。

ネタは、そう、やはり、索引です。:)


以下のようなバッチ処理があったと思ってくださいな。

リリース直後、データが少ない状態で統計情報をロックしたまま数年が経過したバッチ処理、リリース当初は特に目立った遅延問題も発生せず安定していた(らしい)。

ところが、ところがです。
最近は、少しでも処理量が増加しようものなら100%遅延するという状況となっていた。

関係者へのヒアリングから、どうやら、グルグル系のバッチであることが見えてきました。(またか!w)

AWRレポートから問題となるSQL文(グルグル回るところが一番怪しいんですけどね、大抵は)を特定しました。(以下参照)

Execution Plan
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 843 (100)| |
| 1 | SORT AGGREGATE | | 1 | 19 | | |
| 2 | TABLE ACCESS BY INDEX ROWID| HOGE | 1 | 19 | 843 (1)| 00:00:11 |
| 3 | INDEX RANGE SCAN | HOGE_I01 | 1 | | 842 (1)| 00:00:11 |
-----------------------------------------------------------------------------------------

Full SQL Text

SQL ID SQL Text
------------ -----------------------------------------------------------------
43a7z10a4ckx SELECT COUNT(1) FROM HOGE WHERE
CATEGORY = :B4 AND CLASS = :B3 AND SUBCLASS = :B2 AND ID = :B5
AND SUBID = :B1 AND IS_DELETED = 0

実行計画だけを見ていると、Index Range Scan?、なにが問題なの?(キリっ という感じがしますが、
それに騙されてはいけいけないんです。

次のセクションを見てください。
(本来10万回実行されるのですが終わらないので途中でキャンセルしましたw、約1時間で5000回も回ってません。)

Plan 1(PHV: 468154321)
----------------------

Plan Statistics DB/Inst: DISCUS/discus Snaps: 129-130
-> % Total DB Time is the Elapsed Time of the SQL statement divided
into the Total Database Time multiplied by 100

Stat Name Statement Per Execution % Snap
---------------------------------------- ---------- -------------- -------
Elapsed Time (ms) 3,185,031 640.9 99.8
CPU Time (ms) 3,092,467 622.2 99.8
Executions 4,970 N/A N/A
Buffer Gets 7.7276E+07 15,548.5 99.9
Disk Reads 13,999 2.8 93.4
Parse Calls 1 0.0 0.0
Rows 4,969 1.0 N/A
User I/O Wait Time (ms) 92,540 N/A N/A
Cluster Wait Time (ms) 0 N/A N/A
Application Wait Time (ms) 0 N/A N/A
Concurrency Wait Time (ms) 0 N/A N/A
Invalidations 0 N/A N/A
Version Count 1 N/A N/A
Sharable Mem(KB) 22 N/A N/A
-------------------------------------------------------------

Index Range Scanで平均Rows=1の割にBuffer Getsが異常に多い、処理時間のほとんどがCPU処理時間。

これらがなにを意味するのか............

確認のためexplain planで取得した実行計画のPredicate Informationを見てみると..

Plan hash value: 468154321

-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 19 | 795 (1)| 00:00:10 |
| 1 | SORT AGGREGATE | | 1 | 19 | | |
|* 2 | TABLE ACCESS BY INDEX ROWID| HOGE | 1 | 19 | 795 (1)| 00:00:10 |
|* 3 | INDEX RANGE SCAN | HOGE_I01 | 1 | | 794 (1)| 00:00:10 |
-----------------------------------------------------------------------------------------

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

2 - filter("IS_DELETED"=0)
3 - access("CATEGORY"=TO_NUMBER(:V1) AND "CLASS"=TO_NUMBER(:V2) AND
"SUBCLASS"=TO_NUMBER(:V3) AND "ID"=TO_NUMBER(:V4) AND "SUBID"=TO_NUMBER(:V5))
filter("ID"=TO_NUMBER(:V4) AND "SUBID"=TO_NUMBER(:V5))

索引アクセス時にID列とSUBID列でフィルタ処理が行われています。:) この部分の比率が非常に大きい。
ようするに大量のデータを読み込み、フィルタ処理でそのほとんどを捨てている!!! 
(もったいない、もったいないです。もったいないお化けが出てきそうです)

索引を見てみます。

INDEX_NAME                     COLUMN_NAME
------------------------------ --------------------
HOGE_I01 CATEGORY
HOGE_I01 CLASS
HOGE_I01 SUBCLASS
HOGE_I01 STATUS
HOGE_I01 ID
HOGE_I01 SUBID

索引の先頭3列(CATEGORY,CLASS,SUBCLASS)でデータ数をカウントしてみると、400,000行ありました...
40万行から、たった1行を取り出すフィルタリングを10万回繰り返している...グルグル系恐るべし(@@)


解決方法は....

索引の列順を入れ替えれば解決するはず!!!
status列はカーディナリティが非常に低い列であることは確認ずみであつたため最後尾に移動しました。
先頭5列(CATEGORY,CLASS,SUBCLASS,ID,SUBID)でほぼデータが絞りこめるようになるはずです。
(AWRレポートのRowsを見ると最終的には1行or0行となることが確認できます)

作り直した索引は以下のようになります。
STATUS列が最後になっているところがポイントです。違いはたったそれだけです。
(Index Only Accessではありませんよ、今回は。Index Only Accessでグルグルをギリギリまでチューニングしなくても十分な効果が得られたのでこれ以上なにもしません。)

INDEX_NAME                     COLUMN_NAME
------------------------------ --------------------
HOGE_I02 CATEGORY
HOGE_I02 CLASS
HOGE_I02 SUBCLASS
HOGE_I02 ID
HOGE_I02 SUBID
HOGE_I02 STATUS


索引を作り直した後はご覧の通り、じゃじゃ〜〜〜〜〜〜ん!
想定17時間程度のダメバッチが30分ほどで終了しました。ニッコリ。CPUにも随分優しくなってますね。

Plan 1(PHV: 2833120447)
-----------------------

Plan Statistics DB/Inst: DISCUS/discus Snaps: 131-132
-> % Total DB Time is the Elapsed Time of the SQL statement divided
into the Total Database Time multiplied by 100

Stat Name Statement Per Execution % Snap
---------------------------------------- ---------- -------------- -------
Elapsed Time (ms) 1,846,702 18.5 99.2
CPU Time (ms) 43,208 0.4 89.1
Executions 100,000 N/A N/A
Buffer Gets 3,971,771 39.7 99.1
Disk Reads 290,822 2.9 99.1
Parse Calls 1 0.0 0.0
Rows 100,000 1.0 N/A
User I/O Wait Time (ms) 1,830,618 N/A N/A
Cluster Wait Time (ms) 0 N/A N/A
Application Wait Time (ms) 0 N/A N/A
Concurrency Wait Time (ms) 0 N/A N/A
Invalidations 0 N/A N/A
Version Count 1 N/A N/A
Sharable Mem(KB) 22 N/A N/A
-------------------------------------------------------------

Execution Plan
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 5 (100)| |
| 1 | SORT AGGREGATE | | 1 | 19 | | |
| 2 | TABLE ACCESS BY INDEX ROWID| HOGE | 1 | 19 | 5 (0)| 00:00:01 |
| 3 | INDEX RANGE SCAN | HOGE_I02 | 1 | | 4 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------

Full SQL Text

SQL ID SQL Text
------------ -----------------------------------------------------------------
43a7z10a4ckx SELECT COUNT(1) FROM HOGE WHERE CATEGORY = :B4 AND CLASS = :B3 AN
D SUBCLASS = :B2 AND ID = :B5 AND SUBID = :B1 AND IS_DELETED = 0


念のためexplain plan でも確認してみます。ご覧の通りフィルタリングはis_deleted列のみでindex range scan時のフィルタリングは全く発生していません。

もったいないお化けを退治できましたv

Plan hash value: 2833120447

-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 19 | 5 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 19 | | |
|* 2 | TABLE ACCESS BY INDEX ROWID| HOGE | 1 | 19 | 5 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | HOGE_I02 | 1 | | 4 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------

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

2 - filter("IS_DELETED"=0)
3 - access("CATEGORY"=TO_NUMBER(:B4) AND "CLASS"=TO_NUMBER(:B3) AND
"SUBCLASS"=TO_NUMBER(:B2) AND "ID"=TO_NUMBER(:B5) AND "SUBID"=TO_NUMBER(:B1))

複合索引の列順って、ほんとうに大切ですよね :)

NOTE:
今回のような遅延ケースでは索引を適切に再構成するほかに、ヒントでIndex skip scanを強制する方法もありますが、索引を適切な列の並びに再構成する方法と比べ劣るのも事実です。
大人の事情に金縛りに合うこともあるでしょうし.....。状況に合わせて使い分けるとよいと思います。

これで、JPOUG Advent Calendarの全ての小窓は開かれました。

メリークリスマス、そして、良いお年をお迎えください。


最後に、Pipelined Table Function で Christmas Tree ☆☆☆☆

 

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

2012年12月23日 (日)

やはり、来たね VirtualBoxアップデート :)

この時期に仕事やっつけてないと、クリスマスが大変だものね〜 :) と

https://www.virtualbox.org/wiki/Changelog

20121223_142825

20121223_143046


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

2012年11月14日 (水)

Oracle 11g R2の日本語マニュアルがePub/Mobipocketで提供されてた :)

Oracle 11g R2の日本語マニュアルもePub/Mobipocketで提供されたようなので(一部英語のまま)さっそく..

http://docs.oracle.com/cd/E16338_01/ebooklist/ebooklist.html

20121114_230709


20121114_230450

ということで、Oracle11g R2 日本語マニュアルの棚ができあがり :) on iPad

Photo


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

2012年11月10日 (土)

遅まきながら、JDK7 on Maountain Lion

JDK7インストールするのすっかり忘れてた。><

http://www.oracle.com/technetwork/java/javase/downloads/jdk7u9-downloads-1859576.html


20121107_222823

20121107_222826

20121107_222852

以前は「アプリケーション」>「ユーティリティ」フォルダにあったけど、Javaのコントロールパネルはめでたく「システム環境設定」に移動しましたとさ。OS Xだとこの位置の方が自然だよね。
20121107_223054


20121107_223101


20121107_223109


20121107_223114


20121107_223131

これでオッケー。


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

2012年11月 9日 (金)

MacOS X Leopard on VirtualBox 4.2.4 for OS X #3

前回めでたく起動した、MacOS X 10.5 Leopardですが。統合アップデートで10.5.8にしてしまうと仕様通り起動しなくなります。
遊びたい方は、統合アップデートしないのが吉だけど、セキュリティパッチも適用されていない状態なのでネットへのアクセスは控えたほうがいいかなと。

20121108_225444

該当部分を拡大した画像は以下
Unsupported CPUってでてますよね。:)

20121108_225523

ちなみに、Snow Leopard のDVDでインストールしようとする際はUnsupported CPUとでてpanic起こすようになってます。Serverならいいんでしょうけどね。うちにあるDVDはTiger Serverなんで試せないw

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

2012年11月 8日 (木)

MacOS X Leopard on VirtualBox 4.2.4 for OS X #2

起動しないことになってるMacOS X Leopard 10.5だと起動しちゃうんだよね。小細工なしにというネタの2回目。今回は、installから起動まで。:) 言っとくけど不安定だからねw


VMのDVDドライブの設定がパススルーになっていることを確認してね!
で、Leopard のDVDをセットしてからVMを起動すると。

なんと、VirtualBoxのスプラッシュを蹴破って起動するLeopard。 なかなかワイルド!

01
02


しばらすくるとInstallerが起動して見慣れた画面に!

03


Virtual DiskにHFS+のパーティションを1つ作成するので、ディスクユーティリティーを起動します!
起動ボリュームなのでGUIDパーティションを作成します。間違わないと思いますけど。念のため。

04
05
06
07

パーティションができたらディスクユーティリティを終了させます。小細工はしてないですよ:)

08

インストールボリュームを選択して、インストール開始!

09
10
11

あっけなくインストール終了! :)

12

再起動!、さてさて。。。不安定なのでこの部分でcrash reportの出力が繰り返されるようならあきらめて、VMを強制終了後、再起動を繰り返すと、運が良ければ起動しますw

13

運良く起動したら、インストール後の初期設定などをコツコツと。

14

きた〜〜〜〜〜、Leopard 10.5 ! (Serverじゃないよ) 起動しました〜〜〜。

15_2

CPU*6で起動した時の記念スナップ :)

20121108_225948


ちなみに、ディスプレイの解像度が動的に変更できないので、

$ VBoxManage setextradata "your vm name" "VBoxInternal2/EfiGopMode" "4"

とかするといいと思います。ググればこのあたりはすぐ見つかるかと。
your vm nameの箇所には仮想マシン名を指定してね。

ね。ほんとうに小細工なしで起動するでしょ :) 10.5までのLeopardならね。

ではでは。

Enjoy Vitrual Machines! :)

あ、忘れてた、サウンドは出ないので、以下のスレッド参考にしてね。

https://forums.virtualbox.org/viewtopic.php?f=30&t=33358

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

2012年11月 7日 (水)

MacOS X Leopard on VirtualBox 4.2.4 for OS X #1

久々にMac De Oracleらしいネタです。

一応対GuestOSとして対応していることになっているMac OS X Leopard Server とSnow Leopard Serverなのですが、VirtualBoxでうまく起動したという記事は聞いたことないというか見たことないという方は多いかもしれませんね。

ということで、小細工とかしないで起動するのもあるよ。という例を。(不安定感があるのは仕方ないかもw)

ということで今日は、Leopard向けVMの設定だけ。

まずホストOSの情報から.

20121106_222927


ちなみに、VIrtualBox 4.2.4でサポートされているとされているOS X は以下の通り、Parallels Server for Mac 4.0と同じように見えます. これをみるとOS XでゲストOSが必要な場合、Parallels Desktop 8 for Macを使ったほうが楽だと思います。が楽しいですよね。サポートされているOS Xが多くて :)
20121107_63123

・Parallels Server for Mac 4.0のデータはこちら
・Parallels Desktop 8 for Macのデータはこちら

ParallelsのサポートゲストOSを確認してみてもLeopard/Snow LeopardについてはServer版のみにしか対応していないことが確認できますよね。ServerっていうくくりがなくなったLion以降は :) マーク。

でも、でもですね。今回私が試したのは、Leopard 10.5.0。 実はこのリリースだとVirtualBoxで起動しちゃうんです。不安定ですが...
結局のところ何の役にも立たないわけですが、ネタということで、よろしくお願いします。
(なにを、よろしくお願いするんだ〜〜w)

以下、Parallels Desktop 8 でVirtualBox 4.2.4の仮想マシンを変換して起動しようとすると仕様通りの結果になります。

20121107_65533


ホストマシンによっても影響はありそうで今回使用したMacProでは以下の通り。

VirtualBox 4.2.4 for OS X へ Extention Packを追加してあります。
20121106_230843
20121106_230851

Leopardの場合32bitにしてね。

20121106_225513

Leopardで64bitだと、以下のようにkernel panicで起動できない。

20121106_232818

メモリは2GB程度で、CPUは1CPUの時が一番安定して起動します。
メモリサイズを3GBにすると起動しなくなる場合が多くなりました。><
CPUは1CPU/2CPU/6CPUのいずれも起動実績がありますが安定感のあるのは1CPUという感じです。残念。
ちなみに、virtual diskサイズは20GB以上あればいいです。

20121106_225539
20121106_225547
20121106_225557
20121106_225606

LionServer.vdi なのは気にしないでね。
20121106_225614
20121106_225635
20121106_225642
20121106_225652
20121106_225704

そして最後は、これね。

20121106_235001


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

2012年10月21日 (日)

Unconference at db tech showcase 2012の資料公開 :)

db tech showcase 2012
Unconference at db tech showcase 2012

db tech showcaseの一角をJPOUGが占拠してUnconferenceを開催しました。 db tech showcase関係者の皆様、このような機会を与えて頂き大変感謝します。
そして、お疲れさまでした。



Index Only Access 3部作の最終回?! として 「Index Only Accessが実装されるたった一つの理由」と題したセッションを行いました。
実行計画を取得するために操作したデータベースの中には人生二度目のデータベース複数もあり、かなりの時間を裂いて調べた割にはセッション時間が少々短めになってしまいました。m(_ _)m

なぜ、このテーマを選んだか.

PostgreSQLがリリースされてから9.1まで実装されなかったIndex Only Accessでしたが、9.2でついに実装されました。

そして..db tech showcase 2012は...

SQL> select dbms_name from all_dbms where dbms_category like "%";

DBMS_NAME
------------------
Oracle
DB2
MySQL
PostgreSQL
SQL Server
Vecterwise
MongoDB
Symfoware
Clustrix
InfiniDB
.
.
.
.

的な雰囲気となっていることもあり、Index Only Accessの魚拓をあつめて比較、Index Only Accessが実装される理由について今一度、考えてみたいな..と。
タイトル見ただけで理由が想像できた方は、資料見なくても大丈夫だと思いますよ。:) 
 

H/Wの性能が急速に伸びてきている影響もあるように感じますが、無駄に広範囲な検索や、無駄にビッグなデータとなっていること気にしていないのではないか? というケースが多くなっていると感じています。
DBMSはアクセスするデータをより少なくするための工夫をしているのに...エンジニアがそれをうまく使っていない、使えていない、設計できてない...そんな"感じ"がするんです。

セッション資料を公開しました。
S1a


じゃ、like "%てない" 状況をどうすればいいか....答えは、小田さんのセッションの中にあった。。。。:)


#不慣れなDBMSもあり、こんなメトリックみたほうが分かりやすいよ〜、などのツッコミ歓迎します.


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

2012年9月24日 (月)

Unconferenceあります。

JPOUGが、再び、Unconference をやっちゃいます!  (タイムスケジュールなどはJPOUG(Japa Oracle User Group)のサイトをご覧ください

Unconference_dbts2012


今回はインサイトテクノロジー社主催のdb tech showcase 2012で...

Dts_2012e1346810339148300x54



db tech showcase2012 ハッシュタグ : #dbts2012
JPOUG(Japan Oracle User Group) ハッシュタグ : #jpoug

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

2012年9月23日 (日)

Oracle ACEに認定されました :)

私を推薦して頂いた方々、そしてJPOUGに"いいね"してくださった方々、そして家族に感謝します。ありがとうございました。

20120922_150906

これからも役に立つような、立たないようなネタを発信していけたら私らしいかな? と考えております。

Oracle ACEキャンペーンとしてOracle Kittyを頂きました
ノベルティ専門のブログをもってるのですが今回はこちらに載せますね。(そういえば最近更新していない)

X2_ea5a292

そして、今回はうれしい事に私以外にもう一人認定されています。渡部さん、おめでとうございます

なんと、JPOUGから2名同時認定です。同時です。(大切なので2度書きました)

ところで、

Oracle ACEに認定されるとOracle ACE Directoryに登録されるんですが、下記サイトAPEX (Oracle Application Express)で作られてるサイトなののをご存知でしたか? 
(最近ネタにしてAPEX関連のエントリは書いていませんが、かなりネタとして使っています:)

20120922_152158

Oracle ACE等の認定条件は以下のサイトから確認できます。
http://www.oracle.com/technetwork/jp/community/index-098108-ja.html

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

2012年9月22日 (土)

VirtualBox 4.2 Released !!!

VirtualBoxリリーススピードがすごいっす。。

VIrutalBox 4.2リリース! グループ化したVMは一括起動できるんで便利ですね〜

一括起動するとする際には、グループ内の並び順で順次起動されてます。この機能が追加されるとたぶん更なる要求があるかもしれませんね。順に起動するだけでなく任意に一括起動とか、各VMの起動間隔したいとか :)

20120922_141057


20120922_141104


2012/9/23更新(相変わらずミスが多い ><)

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

2012年7月22日 (日)

JPOUG SET EVENTS 20120721 - 「(続)いん!、イン!、Index 大人の事情縛りのSQLチューニング」資料公開

当日は、予想を大きく上回るご参加、ありがとうございました。m(_ _)m  エンジニアの笑顔っていいですよね。

※数日前まで風邪で体調を崩していたこもあり名刺切れしていた申し訳ありませんでした。(名刺印刷をアウトソースしないこだわり名刺というのもその理由なのですが。)

JPOUG> SET EVENTS 20120721 @ 日本オラクル青山センター
でのセッション「(続)いん!、イン!、Index 大人の事情縛りのSQLチューニング」の資料を公開します。

Safari以外のブラウザではアニメーション効果はありませんが、Safari (Mac/iPad/iPhone)ではKeynote風(但し、ページ間のトランジッションなし)に表示されます。

デモ環境は前回と同じです。
デモ内容は別途追加予定です。2012/7/24:デモを追加しました。)

いん!、イン!、Index どっぷり Inde Only Access生活w - Oracle OpenWorld Unconference presented by JPOUGのセッション資料はこちら

おまけの資料(OracleとNULL)

Oracle8i SQL Reference Release 8.1.5 - Nulls


Oracle® Database SQL言語リファレンス 11gリリース2(11.2) - NULL


20120722_92335


DEMO : 1回で2万行参照するバッチ処理

ほんとのバッチはこんなもんじゃないのは知ってますよねw.

SCOTT> @demo1_2
1 declare
2 type t_unique_id is table of tab1.unique_id%type index by pls_integer;
3 type t_non_unique_id is table of tab1.non_unique_id%type index by pls_integer;
4 unique_ids t_unique_id;
5 non_unique_ids t_non_unique_id;
6 begin
7 select
8 unique_id
9 ,non_unique_id
10 bulk collect into
11 unique_ids
12 ,non_unique_ids
13 from
14 tab1
15 where
16 unique_id between 1 and 20000
17 and is_delete = 0
18 and status_code = '00'
19 ;
20 dbms_output.put_line('rows:'||unique_ids.last);
21* end;
rows:20000

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

経過: 00:00:00.75

・・・中略・・・

Plan Statistics DB/Inst: DISCUS/discus Snaps: 208-209
-> % Total DB Time is the Elapsed Time of the SQL statement divided
into the Total Database Time multiplied by 100

Stat Name Statement Per Execution % Snap
---------------------------------------- ---------- -------------- -------
Elapsed Time (ms) 699 699.0 22.4
CPU Time (ms) 310 310.0 16.3
Executions 1 N/A N/A
Buffer Gets 1,648 1,648.0 7.9
Disk Reads 1,584 1,584.0 65.0
Parse Calls 1 1.0 0.1
Rows 20,000 20,000.0 N/A
User I/O Wait Time (ms) 585 N/A N/A
Cluster Wait Time (ms) 0 N/A N/A
Application Wait Time (ms) 0 N/A N/A
Concurrency Wait Time (ms) 0 N/A N/A
Invalidations 0 N/A N/A
Version Count 1 N/A N/A
Sharable Mem(KB) 14 N/A N/A
-------------------------------------------------------------

Execution Plan
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 1579 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| TAB1 | 20000 | 273K| 1579 (1)| 00:00:19 |
| 2 | INDEX RANGE SCAN | TAB1_PK | 20000 | | 39 (0)| 00:00:01 |
---------------------------------------------------------------------------------------

Full SQL Text

SQL ID SQL Text
------------ -----------------------------------------------------------------
cbmpgdpxr6vy SELECT UNIQUE_ID , NON_UNIQUE_ID FROM TAB1 WHERE UNIQUE_ID BETWEE
EN 1 AND 20000 AND IS_DELETE = 0 AND STATUS_CODE = '00'

Report written to demo1_2_awrsqrpt.txt
SCOTT>


DEMO : 1行の参照を2万回グルグル回すバッチ処理

単純な比較だからね。1回 vs. 2万回グルグル系の。
グルグル回す処理方式もいろいろな大人の事情で利用する必要があるのもこともわかっていますが、基本的に性能はでないので、それを想定した対処は必要ですよね :)

SCOTT> @demo1_3
1 declare
2 type t_unique_id is table of tab1.unique_id%type index by pls_integer;
3 type t_non_unique_id is table of tab1.non_unique_id%type index by pls_integer;
4 unique_ids t_unique_id;
5 non_unique_ids t_non_unique_id;
6 cursor c1(p_unique_id tab1.unique_id%TYPE) is
7 select
8 unique_id
9 ,non_unique_id
10 from
11 tab1
12 where
13 unique_id = p_unique_id
14 and is_delete = 0
15 and status_code = '00'
16 ;
17 begin
18 for i in 1..20000 loop
19 for c1_rec in c1(i) loop
20 unique_ids(i) := c1_rec.unique_id;
21 non_unique_ids(i) := c1_rec.non_unique_id;
22 end loop;
23 end loop;
24 dbms_output.put_line('rows:'||unique_ids.last);
25* end;
rows:20000

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

・・・中略・・・

Plan Statistics DB/Inst: DISCUS/discus Snaps: 210-211
-> % Total DB Time is the Elapsed Time of the SQL statement divided
into the Total Database Time multiplied by 100

Stat Name Statement Per Execution % Snap
---------------------------------------- ---------- -------------- -------
Elapsed Time (ms) 1,456 0.1 37.4
CPU Time (ms) 1,112 0.1 42.2
Executions 20,000 N/A N/A
Buffer Gets 60,047 3.0 95.3
Disk Reads 1,587 0.1 75.6
Parse Calls 1 0.0 0.7
Rows 20,000 1.0 N/A
User I/O Wait Time (ms) 866 N/A N/A
Cluster Wait Time (ms) 0 N/A N/A
Application Wait Time (ms) 0 N/A N/A
Concurrency Wait Time (ms) 0 N/A N/A
Invalidations 0 N/A N/A
Version Count 1 N/A N/A
Sharable Mem(KB) 18 N/A N/A
-------------------------------------------------------------

Execution Plan
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 2 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| TAB1 | 1 | 14 | 2 (0)| 00:00:01 |
| 2 | INDEX UNIQUE SCAN | TAB1_PK | 1 | | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------------

Full SQL Text

SQL ID SQL Text
------------ -----------------------------------------------------------------
dvpjay9p4csj SELECT UNIQUE_ID , NON_UNIQUE_ID FROM TAB1 WHERE UNIQUE_ID = :B1
AND IS_DELETE = 0 AND STATUS_CODE = '00'


Report written to demo1_3_awrsqrpt.txt
SCOTT>

DEMO : DEMO : 1行の参照を2万回グルグル回すバッチ処理をIndex Only Accessでチューニングしたみたよ:)

アクセスブロック数も減ったし、処理時間も短くなった。 グルグル系のはね、SQL文レベルのチューニング限界でもある。
Index Only Accessでチューニングしても、グルグル回ってる回数は同じだから。

SCOTT> @demo1_3_ix
create index tab1_ix_demo1_3 on tab1(unique_id,is_delete,status_code,non_unique_id) nologging;

索引が作成されました。

SCOTT> @demo1_3
1 declare
2 type t_unique_id is table of tab1.unique_id%type index by pls_integer;
3 type t_non_unique_id is table of tab1.non_unique_id%type index by pls_integer;
4 unique_ids t_unique_id;
5 non_unique_ids t_non_unique_id;
6 cursor c1(p_unique_id tab1.unique_id%TYPE) is
7 select
8 unique_id
9 ,non_unique_id
10 from
11 tab1
12 where
13 unique_id = p_unique_id
14 and is_delete = 0
15 and status_code = '00'
16 ;
17 begin
18 for i in 1..20000 loop
19 for c1_rec in c1(i) loop
20 unique_ids(i) := c1_rec.unique_id;
21 non_unique_ids(i) := c1_rec.non_unique_id;
22 end loop;
23 end loop;
24 dbms_output.put_line('rows:'||unique_ids.last);
25* end;
rows:20000

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

・・・中略・・・

Plan Statistics DB/Inst: DISCUS/discus Snaps: 212-213
-> % Total DB Time is the Elapsed Time of the SQL statement divided
into the Total Database Time multiplied by 100

Stat Name Statement Per Execution % Snap
---------------------------------------- ---------- -------------- -------
Elapsed Time (ms) 498 0.0 11.1
CPU Time (ms) 453 0.0 13.0
Executions 20,000 N/A N/A
Buffer Gets 40,088 2.0 74.9
Disk Reads 72 0.0 8.1
Parse Calls 1 0.0 0.2
Rows 20,000 1.0 N/A
User I/O Wait Time (ms) 37 N/A N/A
Cluster Wait Time (ms) 0 N/A N/A
Application Wait Time (ms) 0 N/A N/A
Concurrency Wait Time (ms) 0 N/A N/A
Invalidations 0 N/A N/A
Version Count 1 N/A N/A
Sharable Mem(KB) 18 N/A N/A
-------------------------------------------------------------

Execution Plan
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 1 (100)| |
| 1 | INDEX RANGE SCAN| TAB1_IX_DEMO1_3 | 1 | 14 | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------

Full SQL Text

SQL ID SQL Text
------------ -----------------------------------------------------------------
dvpjay9p4csj SELECT UNIQUE_ID , NON_UNIQUE_ID FROM TAB1 WHERE UNIQUE_ID = :B1
AND IS_DELETE = 0 AND STATUS_CODE = '00'


Report written to demo1_3_awrsqrpt.txt
SCOTT>
SCOTT> @drop_demo1_3_ix
drop index tab1_ix_demo1_3;

索引が削除されました。

DEMO : 暴走するスカラー副問合せ

SELECTリストにあるスカラー副問合せって、クエリ本体でヒットしたデータ件数分グルグル実行されるんだお。

SCOTT> @demo5
alter system flush buffer_cache;

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

経過: 00:00:00.26
1 select
2 t1.unique_id,
3 t1.item_code,
4 (
5 select
6 max(t3.unique_id)
7 from
8 tab31 t2 join tab311 t3
9 on
10 t3.sub_item_code = t2.sub_item_code
11 and t3.is_delete = 0
12 where
13 t2.item_code = t1.item_code
14 and t2.is_delete = 0
15 ) current_item
16 from
17 tab3 t1
18 where
19 t1.unique_id between 1 and 10000
20 and t1.is_delete = 0
21* and t1.status_code = '00'

10000行が選択されました。

経過: 00:00:17.54

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

-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 9998 | 253K| 424 (0)| 00:00:06 |
| 1 | SORT AGGREGATE | | 1 | 47 | | |
| 2 | NESTED LOOPS | | 2 | 94 | 8 (0)| 00:00:01 |
|* 3 | TABLE ACCESS BY INDEX ROWID| TAB31 | 1 | 29 | 3 (0)| 00:00:01 |
|* 4 | INDEX UNIQUE SCAN | TAB31_PK | 1 | | 2 (0)| 00:00:01 |
|* 5 | TABLE ACCESS BY INDEX ROWID| TAB311 | 2 | 36 | 5 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | TAB311_IX | 2 | | 2 (0)| 00:00:01 |
|* 7 | TABLE ACCESS BY INDEX ROWID | TAB3 | 9998 | 253K| 424 (0)| 00:00:06 |
|* 8 | INDEX RANGE SCAN | TAB3_PK | 10000 | | 23 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------

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

3 - filter("T2"."IS_DELETE"=0)
4 - access("T2"."ITEM_CODE"=:B1)
5 - filter("T3"."IS_DELETE"=0)
6 - access("T3"."SUB_ITEM_CODE"="T2"."SUB_ITEM_CODE")
7 - filter("T1"."IS_DELETE"=0 AND "T1"."STATUS_CODE"='00')
8 - access("T1"."UNIQUE_ID">=1 AND "T1"."UNIQUE_ID"<=10000)


統計
----------------------------------------------------------
1256 recursive calls
0 db block gets
52747 consistent gets
22557 physical reads
116 redo size
326816 bytes sent via SQL*Net to client
7742 bytes received via SQL*Net from client
668 SQL*Net roundtrips to/from client
35 sorts (memory)
0 sorts (disk)
10000 rows processed

SCOTT>

DEMO : 暴走するスカラー副問合せをIndex Only Accessでチューニング!

アクセスブロック数が明らかに減った :) 索引だけ参照させることでね :)

SCOTT> @demo5_ix
create index tab31_demo_ix on tab31(item_code, is_delete, sub_item_code) nologging;

索引が作成されました。

経過: 00:00:01.18
create index tab311_demo_ix on tab311(sub_item_code, is_delete, unique_id) nologging;

索引が作成されました。

SCOTT> @demo5
alter systen flush buffer_cache;

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

経過: 00:00:00.18
1 select
2 t1.unique_id,
3 t1.item_code,
4 (
5 select
6 max(t3.unique_id)
7 from
8 tab31 t2 join tab311 t3
9 on
10 t3.sub_item_code = t2.sub_item_code
11 and t3.is_delete = 0
12 where
13 t2.item_code = t1.item_code
14 and t2.is_delete = 0
15 ) current_item
16 from
17 tab3 t1
18 where
19 t1.unique_id between 1 and 10000
20 and t1.is_delete = 0
21* and t1.status_code = '00'

10000行が選択されました。

経過: 00:00:03.81

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

----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 9998 | 253K| 424 (0)| 00:00:06 |
| 1 | SORT AGGREGATE | | 1 | 47 | | |
| 2 | NESTED LOOPS | | 2 | 94 | 5 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | TAB31_DEMO_IX | 1 | 29 | 3 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | TAB311_DEMO_IX | 2 | 36 | 2 (0)| 00:00:01 |
|* 5 | TABLE ACCESS BY INDEX ROWID| TAB3 | 9998 | 253K| 424 (0)| 00:00:06 |
|* 6 | INDEX RANGE SCAN | TAB3_PK | 10000 | | 23 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------

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

3 - access("T2"."ITEM_CODE"=:B1 AND "T2"."IS_DELETE"=0)
4 - access("T3"."SUB_ITEM_CODE"="T2"."SUB_ITEM_CODE" AND "T3"."IS_DELETE"=0)
5 - filter("T1"."IS_DELETE"=0 AND "T1"."STATUS_CODE"='00')
6 - access("T1"."UNIQUE_ID">=1 AND "T1"."UNIQUE_ID"<=10000)


統計
----------------------------------------------------------
1 recursive calls
0 db block gets
32839 consistent gets
4655 physical reads
0 redo size
326816 bytes sent via SQL*Net to client
7742 bytes received via SQL*Net from client
668 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
10000 rows processed

SCOTT>
SCOTT> @drop_demo5_ix
drop index tab31_demo_ix;

索引が削除されました。

経過: 00:00:00.42
drop index tab311_demo_ix;

索引が削除されました。

DEMO : NULLとハサミは使いよう。(Index Only Accessがその効果を失う日?!)

※セッション中のデモ時間短縮のため”暴走するスカラー副問合せ”のチューニング後の状態を同一条件の別テーブルで再現してあります。

Index Only Accessでチューニングしたはずの、スカラー副問合せが...再び暴れだした。どゆこと?
処理時間も以前より遅くなってるし、かつ特定の部分のROWSが異常に増加していて、Buffer Getsがすごい事になってます。


Index Only Accessのための索引を作ったものの
SCOTT> @demo5_2_ix
create index tab31_bk_demo_ix on tab31_bk(item_code, is_delete, sub_item_code) nologging;

索引が作成されました。

経過: 00:00:01.25
create index tab311_bk_demo_ix on tab311_bk(sub_item_code, is_delete, unique_id) nologging;

索引が作成されました。

SCOTT> @demo5_2
alter system flush buffer_cache;

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

経過: 00:00:00.09
1 select
2 t1.unique_id,
3 t1.item_code,
4 (
5 select
6 max(t3.unique_id)
7 from
8 tab31_bk t2 join tab311_bk t3
9 on
10 t3.sub_item_code = t2.sub_item_code
11 and t3.is_delete = 0
12 where
13 t2.item_code = t1.item_code
14 and t2.is_delete = 0
15 ) current_item
16 from
17 tab3 t1
18 where
19 t1.unique_id between 1 and 10000
20 and t1.is_delete = 0
21* and t1.status_code = '00'

10000行が選択されました。

経過: 00:00:34.59

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

-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 9998 | 253K| 424 (0)| 00:00:06 |
| 1 | SORT AGGREGATE | | 1 | 47 | | |
| 2 | NESTED LOOPS | | 35974 | 1651K| 5 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | TAB31_BK_DEMO_IX | 1 | 29 | 3 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | TAB311_BK_DEMO_IX | 35978 | 632K| 2 (0)| 00:00:01 |
|* 5 | TABLE ACCESS BY INDEX ROWID| TAB3 | 9998 | 253K| 424 (0)| 00:00:06 |
|* 6 | INDEX RANGE SCAN | TAB3_PK | 10000 | | 23 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------

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

3 - access("T2"."ITEM_CODE"=:B1 AND "T2"."IS_DELETE"=0)
4 - access("T3"."SUB_ITEM_CODE"="T2"."SUB_ITEM_CODE" AND "T3"."IS_DELETE"=0)
5 - filter("T1"."IS_DELETE"=0 AND "T1"."STATUS_CODE"='00')
6 - access("T1"."UNIQUE_ID">=1 AND "T1"."UNIQUE_ID"<=10000)


統計
----------------------------------------------------------
1 recursive calls
0 db block gets
896697 consistent gets
5190 physical reads
0 redo size
326829 bytes sent via SQL*Net to client
7742 bytes received via SQL*Net from client
668 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
10000 rows processed

SCOTT>

DEMO : NULLとハサミは使いよう。(Index Only Accessがその効果を失う日?!)

なぜ、Index Only Accessが効果を失ってしまったのか。。。
スカラー副問合せのチューニングでは、Index Range ScanやIndex Unique ScanをNested Loop結合かつIndex Only Access化したのだが、
特定の値に大きな偏りがあり、広範囲のIndex Range ScanがNested Loop結合で繰り返されたのがその理由。

特定の値の意味を調査していくと、実はNULLでも問題ないという意味合いしかないことが発覚。

しか〜〜〜し、大人の事情で、該当列をNULLに更新してしまうことは許されない。さて、どのように対処するか!

閃いた!。 Oracle11g で登場した新機能を使え! (恐る恐るw でも事前にKROWNなど調べまくりましたよv)

特定の値をNULLに置換する仮想列を追加して、その列でIndex Only Accesssを実現する索引を作る。さらに、SQL文の結合条件だけは変更してもらう。
(影響範囲を最小にした対処だと思います. 仮想列が無かったら大変だったと思います)

以下の結果の通り、処理時間も以前チューニングした時間まで改善し、広範囲のIndex Range Scanも消えていることが実行計画からも確認できます。めでたしめでたし。

SCOTT> @demo5_2_virtual
alter
table tab311_bk add (sub_item_code_virtual CHAR(10) as (replace(sub_item_code,' ',null)) virtual);

表が変更されました。

経過: 00:00:00.57

SCOTT> @demo5_2_ix_2
create index tab311_bk_demo_vix on tab311_bk(sub_item_code_virtual, is_delete, unique_id) nologging;

索引が作成されました。

経過: 00:00:05.38

SCOTT> @demo5_2_2
alter system flush buffer_cache;

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

経過: 00:00:00.07
1 select
2 t1.unique_id,
3 t1.item_code,
4 (
5 select
6 max(t3.unique_id)
7 from
8 tab31_bk t2 join tab311_bk t3
9 on
10 t3.sub_item_code_virtual = t2.sub_item_code
11 and t3.is_delete = 0
12 where
13 t2.item_code = t1.item_code
14 and t2.is_delete = 0
15 ) current_item
16 from
17 tab3 t1
18 where
19 t1.unique_id between 1 and 10000
20 and t1.is_delete = 0
21* and t1.status_code = '00'

10000行が選択されました。

経過: 00:00:03.45

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

--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 9998 | 253K| 424 (0)| 00:00:06 |
| 1 | SORT AGGREGATE | | 1 | 48 | | |
| 2 | NESTED LOOPS | | 7 | 336 | 5 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | TAB31_BK_DEMO_IX | 1 | 29 | 3 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | TAB311_BK_DEMO_VIX | 7 | 133 | 2 (0)| 00:00:01 |
|* 5 | TABLE ACCESS BY INDEX ROWID| TAB3 | 9998 | 253K| 424 (0)| 00:00:06 |
|* 6 | INDEX RANGE SCAN | TAB3_PK | 10000 | | 23 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------

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

3 - access("T2"."ITEM_CODE"=:B1 AND "T2"."IS_DELETE"=0)
4 - access("T3"."SUB_ITEM_CODE_VIRTUAL"="T2"."SUB_ITEM_CODE" AND "T3"."IS_DELETE"=0)
5 - filter("T1"."IS_DELETE"=0 AND "T1"."STATUS_CODE"='00')
6 - access("T1"."UNIQUE_ID">=1 AND "T1"."UNIQUE_ID"<=10000)


統計
----------------------------------------------------------
22 recursive calls
0 db block gets
32843 consistent gets
4072 physical reads
0 redo size
322687 bytes sent via SQL*Net to client
7742 bytes received via SQL*Net from client
668 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
10000 rows processed

SCOTT>
SCOTT> @drop_demo5_2_ix_2
drop index tab311_bk_demo_vix;

索引が削除されました。

経過: 00:00:00.29
SCOTT> @drop_demo5_2_ix
drop index tab31_bk_demo_ix;

索引が削除されました。

経過: 00:00:00.37
drop index tab311_bk_demo_ix;

索引が削除されました。

経過: 00:00:00.05

SCOTT> @drop_demo5_2_virtual
alter table tab311_bk drop (sub_item_code_virtual);

表が変更されました。

経過: 00:00:00.48

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

2012年7月13日 (金)

Exadata はじめてなので、おもしろった。

20120713_01550

ちょっと、SQLチューニングしてみた。おもしろった。(おもしろった=すげ〜おもしろかった。一部の人にしか分からないw)


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

2012年6月 7日 (木)

7/21(土)に Japan Oracle User Group主催のイベント開催!

Japan Oracle User Groupが2012年7月21日(土)に日本オラクル青山センターにてイベントを開催します!

詳細は以下
↓↓↓↓↓
http://www.jpoug.org/2012/06/06/jpoug-set-events-20120721

なお、参加の申し込みはzussarからどうぞ :)
http://www.zusaar.com/event/311004


現在開催に先立ちオーガナイザー募集中
募集は終了しました。ご応募ありがとうございました。

5分だけのライトニングトーク、20分のアンカンファレンス、45分のセミナーで「我こそは ぜひ思いを共有したい」という方を募集中!



オーガナイザー募集の詳細も以下のURLからどうぞ
↓↓↓↓↓

http://www.jpoug.org/2012/06/06/jpoug-set-events-20120721

Logo20120721w1000


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

VirtualBox 4.1.16 released

https://www.virtualbox.org/wiki/Changelog

https://www.virtualbox.org/wiki/Downloads

Mac OS X hosts: addressed issues running on Mountain Lion Preview 3 と Moutain Lion対応も順調のようですな〜 :)


20120602_75941

とりあえず、MacOS X Lion阪はアップデートdone.

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

2012年6月 2日 (土)

2012年7月21日(土)


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

2012年5月 6日 (日)

VirtualBox 4.1.14 と OS X Mountain Lion対応? 4.1.15がリリースされてた

今気づいたけど、4.1.14だけだと思ったら、MacOS X Mountain Lion対応の VirtualBox 4.1.15も出てたのね。:)

https://www.virtualbox.org/wiki/Downloads

20120506_183804


20120506_190633


とりあえずアップデート

20120506_185041


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

オプティマイザをだましちゃお! の続き。

オプティマイザをだましちゃお! の続きです。

リテラル値指定、かつ、全件ヒットするからTABLE FULL SCANになるはずなのに、何故、INDEX RANGE SCANしたのか? の種明かしです


status列の値が全て 0 の状態で統計情報を取得した状態で、status列全てを 2 に更新、その後統計情報は再取得していません。
オプティマイザは、status列が全て 0 だと思い込んでいるわけですね、実際は、全て 2 なのに。

08:55:05 SCOTT> update deluding_tab set status = 2;

100000行が更新されました。

08:55:15 SCOTT> commit;

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

status列が全て、またはほぼ、 0 なのであれば、全データをINDEX RANGE SCANで取得するなんて通常はあり得ないですよね :) 

列値と統計情報の取得タイミングでオプティマイザを騙しているわけです。
(これは意図的ですが、意図せずオプティマイザが誤った実行計画を算出して夜中に電話が鳴った,なんて方は意外に多いかも…w)


念のため 10053 トレースで status = 1 と status = 2 で同じ実行計画を求めているかも確認してみると…

08:55:18 SCOTT> alter session set events '10053 trace name context forever, level 1';

セッションが変更されました。

08:55:24 SCOTT> select * from deluding_tab where status = 1;

08:55:37 SCOTT> select * from deluding_tab where status = 2;


リテラル値部分が異なる事以外は皆同じということが分かります。うまくオプティマイザを騙せてますね :)

discus$ diff status_1.txt status_2.txt
1c1
< *** 2012-05-06 08:55:37.663
---
> *** 2012-05-06 08:55:50.630
23,24c23,24
< ----- Current SQL Statement for this session (sql_id=2pa417mwk4crp) -----
< select * from deluding_tab where status = 1
---
> ----- Current SQL Statement for this session (sql_id=7sb6ypqd43bdr) -----
> select * from deluding_tab where status = 2
824c824
< CBQT: Validity checks failed for 2pa417mwk4crp.
---
> CBQT: Validity checks failed for 7sb6ypqd43bdr.
847c847
< CBQT: Validity checks failed for 2pa417mwk4crp.
---
> CBQT: Validity checks failed for 7sb6ypqd43bdr.
870c870
< "DELUDING_TAB"."STATUS"=1
---
> "DELUDING_TAB"."STATUS"=2
872c872
< finally: "DELUDING_TAB"."STATUS"=1
---
> finally: "DELUDING_TAB"."STATUS"=2
874c874
< apadrv-start sqlid=3073848844558152437
---
> apadrv-start sqlid=8947771396877036983
883c883
< SELECT "DELUDING_TAB"."ID" "ID","DELUDING_TAB"."STATUS" "STATUS","DELUDING_TAB"."DATA" "DATA" FROM "SCOTT"."DELUDING_TAB"
"DELUDING_TAB" WHERE "DELUDING_TAB"."STATUS"=1
---
> SELECT "DELUDING_TAB"."ID" "ID","DELUDING_TAB"."STATUS" "STATUS","DELUDING_TAB"."DATA" "DATA" FROM "SCOTT"."DELUDING_TAB"
"DELUDING_TAB" WHERE "DELUDING_TAB"."STATUS"=2
893c893
< select * from deluding_tab where status = 1
---
> select * from deluding_tab where status = 2
959,960c959,960
< id=0 frofkks[i] (index start key) predicate="DELUDING_TAB"."STATUS"=1
< id=0 frofkke[i] (index stop key) predicate="DELUDING_TAB"."STATUS"=1
---
> id=0 frofkks[i] (index start key) predicate="DELUDING_TAB"."STATUS"=2
> id=0 frofkke[i] (index stop key) predicate="DELUDING_TAB"."STATUS"=2
980,982c980,982
< sql_id=2pa417mwk4crp plan_hash_value=1226994206 problem_type=3
< ----- Current SQL Statement for this session (sql_id=2pa417mwk4crp) -----
< select * from deluding_tab where status = 1
---
> sql_id=7sb6ypqd43bdr plan_hash_value=1226994206 problem_type=3
> ----- Current SQL Statement for this session (sql_id=7sb6ypqd43bdr) -----
> select * from deluding_tab where status = 2
984c984
< sql=select * from deluding_tab where status = 1
---
> sql=select * from deluding_tab where status = 2
1000c1000
< 2 - access("STATUS"=1)
---
> 2 - access("STATUS"=2)
discus$


status列の値が全て 2 なのですが、統計情報は 0 で取得されているので status = 0 で検索すると、索引は使わず TABLE FULL SCANですよね。対象データは 0 件なんですけど。 (^^;;;

14:52:04 SCOTT> select * from deluding_tab where status = 0;

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

経過: 00:00:00.64

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

----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 99991 | 48M| 1947 (1)| 00:00:24 |
|* 1 | TABLE ACCESS FULL| DELUDING_TAB | 99991 | 48M| 1947 (1)| 00:00:24 |
----------------------------------------------------------------------------------

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

1 - filter("STATUS"=0)


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




ここまでのあらすじ

オプティマイザをだましちゃお! (マジック・ザ・ギャザリング風w かも)

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

2012年3月16日 (金)

VirtualBox 4.1.10 released - あれ前回は 4.1.8だったけど一つ飛んだね

https://www.virtualbox.org/

少々時間空いたな〜と思っていたら、4.1.8の次は、一つ飛ばして4.1.10ですね。:)

20120316_163833

Oracle OpenWorld Tokyo 2012 Unconference当日のデモ環境は VirtualBox4.1.10で、GuestOSはCentOS5.8、データベースはOracle11g R2で。もちろんホストOSはOS Xですよ:)

Unconference_jpoug

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

2012年2月25日 (土)

Oracle OpenWorld Tokyo 2012 Unconference


JPOUGのFacebookページはこちら :)


Unconference_jpoug


Oracle OpenWorldの3日目(4/6)に・・・・


なにかが・・・・・・


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

2012年1月30日 (月)

AWRレポート、AWR SQLレポート一括取得スクリプトを作ったよ。

Statspackレポートもそうなのですが、AWRレポート/AWR SQLレポートも個別に取得していると凄ーく辛いんですよね。一日分出力するとか、AWRレポートで処理時間の長いSQL文のAWR SQLレポートを個別に取得しようとなると...

ただでさえ忙しいのに、AWRレポート取得するのに時間掛けたくないですよね。

ということで、やっつけで作ったのですが、そのまま載せるのもあれなんで、やっつけで作った感じを多少減らしてgithubに公開しました。;)

https://github.com/discus/Oracle-AWR-batch-generation-script/blob/master/awrreport_batch.sql


もっといい感じに改造してくれるといいな〜とかとか... :)

Oracle11g R1/R2 Enterprise Edition、HTML形式で出力します。(RAC環境では試してないので多分だめかと。)。
AWRを利用するには追加ライセンスが必要なのでご注意を


使い方は・・

SQL*plusを起動し、select any dictionaryシステム権限、dbms_repositoryパッケージの実行権限が付与されたユーザで接続して実行するだけ。
SYSTEMユーザでやる事が多いけど、所変わればなんとやらなので・・・そこんとこよろしく。(w


一括取得なので実行当日を含めてn日分のAWRレポートを取得し、同時に処理時間の長いTop20のAWR SQLレポートも取得します。
レポートは各スナップショット間(今のところ固定)で取得します。

指定するパラメータは、以下の3つ。

Enter snap_id for starting AWR report generation. [NULL] : 
AWRレポートを取得する最初のSNAP_IDを指定します。 NULLがデフォルトでほとんどの場合デフォルトのままで事足りると思います。


Enter number of days for reporting period. [1] : 

一括取得する日数を指定します。当日を含みます。
当日分のAWRレポートを出力するのであれば、デフォルト値の1のままでOKです。


Enter suffix for AWR reports filename. [NULL] : test

保存するAWRレポートのファイル名に付加するsuffixを指定します。
試験名とか設定するといいですよね。

"test"と指定した場合

awrrpt_nnnn_nnnn_test.htmlや
awrsqrpt_nnnn_nnnn_test_sqlid.html

の形式で保存します。(nnnnはSNAP_ID)


実行例1)当日分の全レポートを取得する例

SYSTEM> 
SYSTEM> !ls -l awr*.sql
-rw-r--r-- 1 oracle oinstall 6065 1月 29 18:23 awrreport_batch.sql

SYSTEM> @awrreport_batch
--
-- Oracle AWR and AWR SQL report batch generation script
--
-- ***** This script always generate html format AWR reports. *****
--
Enter snap_id for starting AWR report generation. [NULL] :
Enter number of days for reporting period. [1] :
Enter suffix for AWR reports filename. [NULL] : test
--
--
--
clear break compute;
repfooter off;
ttitle off;
btitle off;

・・・中略・・・

<p />
<br /><a class="awr" href="#top">Back to Top</a><p />
</body></html>

SYSTEM>


実行例2)当日分かつsnap_id=291以降で一括取得。(事前にsnap_idを調べておいてね)

SYSTEM> 
SYSTEM> @awrreport_batch
--
-- Oracle AWR and AWR SQL report batch generation script
--
-- ***** This script always generate html format AWR reports. *****
--
Enter snap_id for starting AWR report generation. [NULL] : 291
Enter number of days for reporting period. [1] :
Enter suffix for AWR reports filename. [NULL] : test
--
--
--
clear break compute;
repfooter off;
ttitle off;
btitle off;

・・・中略・・・

<p />
<br /><a class="awr" href="#top">Back to Top</a><p />
</body></html>

SYSTEM>
SYSTEM>
SYSTEM> !ls -l *.html
-rw-r--r-- 1 oracle oinstall 379083 1月 29 22:23 awrrpt_291_292_test.html
-rw-r--r-- 1 oracle oinstall 11899 1月 29 22:23 awrsqrpt_291_292_test_0c83z9rqx45hu.html
-rw-r--r-- 1 oracle oinstall 11899 1月 29 22:23 awrsqrpt_291_292_test_0h3mfbzk6uyw0.html
-rw-r--r-- 1 oracle oinstall 11897 1月 29 22:23 awrsqrpt_291_292_test_2p7t0mw7zvu5z.html

・・・中略・・・

-rw-r--r-- 1 oracle oinstall 11899 1月 29 22:23 awrsqrpt_291_292_test_bhtycgwkxhfj9.html
-rw-r--r-- 1 oracle oinstall 11900 1月 29 22:23 awrsqrpt_291_292_test_bpaggvtfkar9k.html
-rw-r--r-- 1 oracle oinstall 11899 1月 29 22:23 awrsqrpt_291_292_test_c50hdbyuwhfn6.html
-rw-r--r-- 1 oracle oinstall 11892 1月 29 22:23 awrsqrpt_291_292_test_g3f3cw3zy5aat.html

SYSTEM>

なお、Oracleインスタンスが再起動された期間でawrrpt.sqlなどを実行すると、レポートが作成できずエラーでSQL*Plusも終了してしまいますが、本スクリプトでは該当部分のレポートはスキップするようにしてあります。:)

Instance     DB Name      Snap Id   Snap Started       Level
------------ ------------ --------- ------------------ -----
lampeye LAMPEYE 274 29 1月 2012 09:33 1
275 29 1月 2012 10:00 1
276 29 1月 2012 10:30 1
277 29 1月 2012 11:00 1
278 29 1月 2012 11:30 1
279 29 1月 2012 12:00 1
280 29 1月 2012 12:30 1
281 29 1月 2012 13:00 1
282 29 1月 2012 13:30 1
283 29 1月 2012 14:00 1
284 29 1月 2012 14:30 1
285 29 1月 2012 15:00 1
286 29 1月 2012 15:30 1
287 29 1月 2012 16:00 1
288 29 1月 2012 16:30 1
289 29 1月 2012 17:00 1
290 29 1月 2012 17:30 1

291 29 1月 2012 18:31 1 ←再起動されてる
292 29 1月 2012 18:34 1

293 29 1月 2012 22:26 1 ←再起動されてる


Enjoy!

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

2012年1月14日 (土)

shutdown immeidateしない、ほかの理由に遭遇! (FYI)


ず〜〜〜〜っと、追記しようと思ってたんだけど書いてなかったので、徹夜明けで早起きした次いでなんで書いておきます。

もう一年近く前のネタなんだけどね。「shutdown immeidateしない、ほかの理由に遭遇!」

> yoheia-a さんありがとう :)

私か書いた記事がキッカケで調べなきゃいけなくなったらしいんだけどね。 ;)

http://d.hatena.ne.jp/yohei-a/20110627/1309180675




shutdown immeidateしない、ほかの理由に遭遇!
shutdown immeidateしない、ほかの理由に遭遇! #2
shutdown immeidateしない、ほかの理由に遭遇! #3
shutdown immeidateしない、ほかの理由に遭遇! おまけ
shutdown immeidateしない、ほかの理由に遭遇! おまけのおまけ(でた〜最近、よくあるパターンw)

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

2012年1月 6日 (金)

Index Only Access (Index Only Scan) っていいよね(デメリットもあるけどさ) #2

Index Only Accessのいいとこ、紹介しちゃいますの続きです。

前回は、索引しかアクセスしない(Index Only Access)場合と、索引+表データもアクセスしちゃう場合の実行計画上の違いを確認しましたよね。


今回は、Index Only Accessで得られる改善効果の1つであるソート処理の回避について簡単な例で確認してみます。

※VISIBLE/INVISIBLEにしている索引の詳細は前回の記事を参照してくださいね。

Now Playing ♪ - ハイスクールララバイ / イモ欽トリオ - 1981

まず、最初は、悪い子の例から。

索引を全表走査した上で order by seq# でソート処理が実行されます。酷いですね。検索条件列に適切な索引を作れよ〜〜〜っ。という状態ですね。

SQL> alter index tab10_i01 invisible;

索引が変更されました。

SQL> alter index tab10_i02 invisible;

索引が変更されました。

SQL> select seq# from tab10 where non_unique_key = '0000000001' order by seq#;

10行が選択されました。

経過: 00:00:04.99

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

----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10 | 160 | 16788 (1)| 00:03:22 |
| 1 | SORT ORDER BY | | 10 | 160 | 16788 (1)| 00:03:22 |
|* 2 | TABLE ACCESS FULL | TAB10 | 10 | 160 | 16787 (1)| 00:03:22 |
----------------------------------------------------------------------------

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

2 - filter("NON_UNIQUE_KEY"='0000000001')


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

次に、普通の子の例。

検索条件であるnon_unique_keyに定義された索引を使いIndex range scanかつ、seq#をアクセスするために表データをrowidでアクセス。その後にソート処理が行われています。

普通の子らしい、よい実行計画ですね。 :)

SQL> alter index tab10_i01 visible;

索引が変更されました。

SQL> alter index tab10_i02 invisible;

索引が変更されました。

SQL> select seq# from tab10 where non_unique_key = '0000000001' order by seq#;

10行が選択されました。

経過: 00:00:00.08

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

------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10 | 160 | 5 (20)| 00:00:01 |
| 1 | SORT ORDER BY | | 10 | 160 | 5 (20)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID| TAB10 | 10 | 160 | 4 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | TAB10_I01 | 10 | | 3 (0)| 00:00:01 |
------------------------------------------------------------------------------------------

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

3 - access("NON_UNIQUE_KEY"='0000000001')


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

そして、最後は、良い子の登場。

この子、データ量が多くなった場合、ソート処理も足枷になると考えたようで Index only accessを利用したソート処理回避作戦を取ったようです。 
実行計画をみればわかりますが、索引しかアクセスしておらず、SORT ORDER BY というオペレーションも消えています!

検索条件である、non_unique_key列とソート対象のseq#列の2列からなる複合索引を利用するIndex only accessを狙ったようですね。

ただし、これには order by seq# [asc]であるという大前提があります。
order by seq# desc というソートも考慮する必要がある場合には、non_unique_key [asc]とseq# desc とした別の複合索引必要になります。(デメリットといえばデメリットですかね、)

order by seq# descというソート条件が仕様上無い事を事前に確認しておけば、なお完璧ですよね〜。 良い子。流石です。 :)

SQL> alter index tab10_i01 invisible;

索引が変更されました。

SQL> alter index tab10_i02 visible;

索引が変更されました。

SQL> select seq# from tab10 where non_unique_key = '0000000001' order by seq#;

10行が選択されました。

経過: 00:00:00.01

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

------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10 | 160 | 3 (0)| 00:00:01 |
|* 1 | INDEX RANGE SCAN| TAB10_I02 | 10 | 160 | 3 (0)| 00:00:01 |
------------------------------------------------------------------------------

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

1 - access("NON_UNIQUE_KEY"='0000000001')


統計
----------------------------------------------------------
0 recursive calls
0 db block gets
4 consistent gets
16 physical reads
0 redo size
655 bytes sent via SQL*Net to client
520 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
10 rows processed


次回へつづく。(他のソート回避の例も紹介しちゃうか、考えちう)


Index Only Access (Index Only Scan) っていいよね(デメリットもあるけどさ) #1

いん!、イン!、Index どっぷり Inde Only Access生活w - Oracle OpenWorld Unconference presented by JPOUG

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

2012年1月 4日 (水)

Index Only Access (Index Only Scan) っていいよね(デメリットもあるけどさ) #1

さて、OracleさんがAppleさんのOSで楽しいことしてくれないから最近つまんなくなりつつあるので、普通にSQL文のチューニングネタです。


鬼熱かった、鬼熱かった! :: Insight out 2011- DB tech showcaseでもかなり触れていた、Index Only Scan日本ではこの表現が割合的に多いようなのですが、Index Only Accessって言ってましたね、トムカイトさんも。英語では後者のほうが一般的なのかもしれません。ここではIndex Only Accessってことにしておきます。;)


デメリットもあるけど、Index Only Accessのいいとこばかりを中心に、書いちゃうよ〜w


まず、前提から。

TAB10表は以下のように定義してあります。意図的に表データが大きくなるようにしてあります :)

 名前                NULL?    型
------------------ -------- --------------
SEQ# NOT NULL NUMBER
NON_UNIQUE_KEY NOT NULL CHAR(10)
DATA VARCHAR2(500)

また、以下のような索引を事前に索引しているが、PK_TAB10という主キー索引以外はINVISIBLEとして作成してある。
INVISIBLEで作成しておくと、索引は通常通りメンテナンスされるが、オプティマイザは実行計画作成時にINVISIBLEな索引を利用しないというOracle11gから登場した便利な機能

また前述の表には以下のような主キー(PK_TAB10)と非ユニークな索引が2つ作成してあります。(あまり良い例ではないですがご勘弁を)
但し、TAB10_I01、TAB10_I02の2索引は、INVISIBLEで作成してあります。効果確認時など便利ですよね。
(不可視索引の詳細はマニュアル「Oracle Database 管理者ガイド 11gリリース1(11.1)不可視索引の作成」を参照のこと。


INDEX_NAME COLUMN_NAME
------------ -------------------
PK_TAB10 SEQ#

TAB10_I01 NON_UNIQUE_KEY

TAB10_I02 NON_UNIQUE_KEY
SEQ#

検証時の処理時間及び、実行統計は、各クエリを2回実行し2回目の処理時間及び、実行統計を載せてあります。
(2回目の実行前にバッファキャッシュをクリアしてあるので、ソフトパース+キャッシュミスほぼ100%という状況の処理時間及び実行統計情報です。)

テストデータは以下件数で、non_unique_keyは偏りはなく均一に分布させあります。
実際のはなし、均等になることの方が稀ではあると思いますけど、Index Only Accessの効果を見る事ができればそれでOKなので。

COUNT(1)
----------
800000


COUNT(DISTINCTNON_UNIQUE_KEY)
-----------------------------
80000


NON_UNIQUE COUNT(1)
---------- ----------
0000000001 10
0000000002 10
0000000003 10
0000000004 10
0000000005 10
0000000006 10
0000000007 10
0000000008 10
0000000009 10
0000000010 10
0000000011 10
0000000012 10
0000000013 10
0000000014 10
0000000015 10
0000000016 10
0000000017 10
0000000018 10
0000000019 10
0000000020 10

・・・以下略・・・

まず最初は一番分かりやすい、Index Only Accessの例から。

non_unique_key列に、TAB10_I01という非ユニーク索引を作成してありますが、現状、INVISIBLE状態にしてあるため索引が無く全表走査となっていますよね。

SQL> set autot trace exp stat
SQL>
SQL> select count(non_unique_key) from tab10 where non_unique_key between '0000000001' and '0000000010';

経過: 00:00:04.32

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

----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 11 | 16787 (1)| 00:03:22 |
| 1 | SORT AGGREGATE | | 1 | 11 | | |
|* 2 | TABLE ACCESS FULL| TAB10 | 100 | 1100 | 16787 (1)| 00:03:22 |
----------------------------------------------------------------------------

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

2 - filter("NON_UNIQUE_KEY"<='0000000010' AND
"NON_UNIQUE_KEY">='0000000001')


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

ここで、TAB10_I01索引をVISIBLEへ変更して同一クエリを実行してみると……
TAB10_I01索引を利用した実行計画はIndex Range Scanに変化します。しかも、表データはアクセスしていません。これがIndex Only Accessの典型的な例です。アクセスブロック数も処理時間も大きく改善していますよね。

ただ、Index only scanにはデメリットもあります。

それは、索引が多くなればなるほどDML文には足枷になり遅くなるため点です。参照と更新、挿入、削除のバランスを取るのが大切ですが、
とにかく参照を速くする、更新、挿入、削除の処理性能などは少々犠牲にしても問題ないのであれば、どんどんやっちゃいます!(ご利用は計画的にw)

SQL> alter index tab10_i01 visible;
SQL>
SQL> select count(non_unique_key) from tab10 where non_unique_key between '0000000001' and '0000000010';

経過: 00:00:00.02

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

-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU) | Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 11 | 3 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 11 | | |
|* 2 | INDEX RANGE SCAN| TAB10_I01 | 100 | 1100 | 3 (0)| 00:00:01 |
-------------------------------------------------------------------------------

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

2 - access("NON_UNIQUE_KEY">='0000000001' AND
"NON_UNIQUE_KEY"<='0000000010')


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

ちなみに、クエリは少々違うのですが、Index only accessで無い場合は、index range scan+table access by index rowidとなり以下のような実行計画になっちゃいます。
(こちらの方がよく目にする実行計画じゃないでしょうかね。私も性能的な問題等なければ以下のような実行計画であれば良しとしておくことが多いのも事実です。。)

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

------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100 | 51700 | 11 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID | TAB10 | 100 | 51700 | 11 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | TAB10_I02 | 100 | | 3 (0)| 00:00:01 |
------------------------------------------------------------------------------------------

次回へつづく。(忙しくてなかなか書けないかもw)

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

2011年12月20日 (火)

VIrtualBox 4.1.8 released :)

クリスマス前にリリース! されてたのでupdate. Linuxのは後で。

https://www.virtualbox.org/wiki/Downloads

20111220_12000


20111220_12335


特に問題なく起動したし:)

20111220_12543


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

2011年12月14日 (水)

PL/SQL de Conditional Compile #6

随分前にPL/SQL de Conditional Compile #5ってエントリ書いてたのを思い出して、11g R1/R2用のを見てみたらソースに丁寧なコメント(マニュアルよりいいんじゃね?w )が書かれていてうれしくなった。

オラクルさんとして見せたくない部分は見えないようになっているけど、見せても問題ないところはコードが見える(all_sourceビュー)わけで、それはそれでうれしいわけです。はい。マニュアルが意外に不親切だったりするので。

(マニュアル、良くなりましたよ。昔より。 > 褒めておかないとね、だれとなくw。)...文字サイズも多くしておきましたw

● Oracle11g R1 11.1 の DBMS_DB_VERSION

package dbms_db_version is
version constant pls_integer := 11; -- RDBMS version number
release constant pls_integer := 1; -- RDBMS release number

/* The following boolean constants follow a naming convention. Each
constant gives a name for a boolean expression. For example,
ver_le_9_1 represents version <= 9 and release <= 1
ver_le_10_2 represents version <= 10 and release <= 2
ver_le_10 represents version <= 10

A typical usage of these boolean constants is

$if dbms_db_version.ver_le_10 $then
version 10 and ealier code
$elsif dbms_db_version.ver_le_11 $then
version 11 code
$else
version 12 and later code
$end

This code structure will protect any reference to the code
for version 12. It also prevents the controlling package
constant dbms_db_version.ver_le_11 from being referenced
when the program is compiled under version 10. A similar
observation applies to version 11. This scheme works even
though the static constant ver_le_11 is not defined in
version 10 database because conditional compilation protects
the $elsif from evaluation if the dbms_db_version.ver_le_10 is
TRUE.
*/

ver_le_9_1 constant boolean := FALSE;
ver_le_9_2 constant boolean := FALSE;
ver_le_9 constant boolean := FALSE;
ver_le_10_1 constant boolean := FALSE;
ver_le_10_2 constant boolean := FALSE;
ver_le_10 constant boolean := FALSE;
ver_le_11_1 constant boolean := TRUE;
ver_le_11 constant boolean := TRUE;

end dbms_db_version;


● Oracle11g R2 11.2 の DBMS_DB_VERSION

package dbms_db_version is
version constant pls_integer := 11; -- RDBMS version number
release constant pls_integer := 2; -- RDBMS release number

/* The following boolean constants follow a naming convention. Each
constant gives a name for a boolean expression. For example,
ver_le_9_1 represents version <= 9 and release <= 1
ver_le_10_2 represents version <= 10 and release <= 2
ver_le_10 represents version <= 10

A typical usage of these boolean constants is

$if dbms_db_version.ver_le_10 $then
version 10 and ealier code
$elsif dbms_db_version.ver_le_11 $then
version 11 code
$else
version 12 and later code
$end

This code structure will protect any reference to the code
for version 12. It also prevents the controlling package
constant dbms_db_version.ver_le_11 from being referenced
when the program is compiled under version 10. A similar
observation applies to version 11. This scheme works even
though the static constant ver_le_11 is not defined in
version 10 database because conditional compilation protects
the $elsif from evaluation if the dbms_db_version.ver_le_10 is
TRUE.
*/

ver_le_9_1 constant boolean := FALSE;
ver_le_9_2 constant boolean := FALSE;
ver_le_9 constant boolean := FALSE;
ver_le_10_1 constant boolean := FALSE;
ver_le_10_2 constant boolean := FALSE;
ver_le_10 constant boolean := FALSE;
ver_le_11_1 constant boolean := FALSE;
ver_le_11_2 constant boolean := TRUE;
ver_le_11 constant boolean := TRUE;

end dbms_db_version;

version 12 and later codeなんて箇所、いいですね〜w


PL/SQL de Conditional Compile #1
PL/SQL de Conditional Compile #2
PL/SQL de Conditional Compile #3
PL/SQL de Conditional Compile #4
PL/SQL de Conditional Compile #5

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

2011年11月 9日 (水)

VirtualBox 4.1.6 released - Update done

https://www.virtualbox.org/wiki/Changelog

出かけるまえにVirtualBox4.1.6 へUpdate done


virtualbox416_inst


virtualbox416_splash


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

2011年11月 1日 (火)

鬼熱かった! :: Insight out 2011- DB tech showcase

書くのおそくなっちゃいました m(_ _)m

10月19日〜21日に開催されたInsight out 2011- DB tech showcase
に、つまみ食いながらなんとか参加し、インサイトテクノロジーさんの鬼熱い魂を感じてきた :)

無理矢理空き時間作って参加したセッションは以下の通り。

  • Deep Dive into Oracle Database Patch (Oracle) - 諸橋渉
  • Why Why is probably the right answer (Oracle) - Tom Kyte
  • Rac Buffer Sharingの仕組み (Oracle) - 山下正
  • Effective Indexing (Oracle) - Tom Kyte
  • New challenges Information security technologies are facing (others) - David Maman
  • Developer and Indexes (Oracle) - Anjo Kolk

MySQLとかPostgreSQLとかOSSなのはOSCとかでも聴けるかなーと思いきづいたらOracle中心だったw

Effective Indexing/Developer and Indexes というセッションは予想以上だった、Indexを理解してるのって重要だよなーと改めて感じたセッションだった。

Tom Kyteさんが紹介していた書籍、「Relational Database Index Design and the Optimizers」


鬼熱い語りの山下さんのセッション、前回のOOWのUnconferenceの続編か?と思わせるような諸橋さんのセッション、つい引き込まれちゃいましたよ:)

参加者やスピーカが鬼熱いエンジニアであることは間違いないが、何と言っても、世界中からデータベースに関わる凄い方々を集めてしまうインサイトテクノロジーさんが一番、鬼熱いんじゃないかと感じた3日間だった。

来年も開催して頂きたいイベントだ。


Img_2299


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

2011年10月31日 (月)

実習セミナー:DBにて発生しているスローダウンを調査しよう@日本オラクル青山センター

実習セミナー:DBにて発生しているスローダウンを調査しよう

さて、この実習セミナー、回を重ねて3回目です。パチパチ。:)

今回のお題は、

「社内システムにおいて、「2010年1月10日 23:21~1月11日 0:15」に、業務遅延(障害)が発生しました。該当時間帯のDB稼動状況を確認し、遅延の原因調査を行って下さい」

というものでした。

「iPhoneの予約が殺到したんでしょうかね?。。。」と、かってに予想してましたw (違うらしいですw)

X2_8ff8097

ちょっとまじめにレポートしますか(今回はw)

今回は前回とは異なりグループ単位ではなく個人で解析を進めるという形式で行われました。
普段からstatspackレポートを見て解析している人とそうでない人差が出てしまうかな?と思いましたが、statspack/AWRレポートから問題解決の糸口を見つけ出すという流れはしっかりと体験できたのではないかと思います。

ただ、初めてstatspack/AWRレポート見て困るのが、待機イベントが具体的に何を示すものなのか、オラクルのマニュアルにもわかりやすい解説がないという点ではないでしょうか。(わかり難いのはそれだけではないのですが。。。)

前々回、前回と、問題となっている待機イベントを図解していたという点からも、待機イベント重要!!ってことは十分伝わったのではないかな〜と感じました。

以下、待機イベントを言葉だけで解説しているオラクルのマニュアルですが、あの文章だけで内容を理解できちゃう人ってすげ〜と思います。というか、中の人(元、中の人含む)しかわからんのではないかと、今でも思っています。(昔のマニュアルよりかなり細かく書かれているとは思いますが)

Oracle® Databaseリファレンス 11g リリース2(11.2)待機イベントの説明

待機イベントを図解したマニュアルとかあればいいのにね〜〜〜、と、と〜〜〜〜くの空を見て思う(今、夜中なんで街灯の方が目立ちますがw)


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

2011年10月 5日 (水)

VIrtualBox 4.1.4 released

http://www.virtualbox.org/wiki/Changelog


LinuxとMacのVirtualBoxを4.1.4アップデートするなど。毎月一回のお約束:)

VirtualBox4.1.4 about


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

2011年9月13日 (火)

オラクルは会議室で動いてんじゃな〜〜イ。統計情報で動いてんだ!

と少々無理矢理なタイトル付けてみました:) 今回はチューニングはチューニングでも、statspack.snap()のチューニングのお話。

注)表現はそれなりに脚色してありますw


ある日のこと。

大トラブルなんです〜(こういう人、うまくいってないプロジェクトにはよくいます。全然”大”じゃないのに、もう口癖なんでしょうねw)

statspack.snap()がものすごく遅いんです! なんとかなりませんか?

ん〜〜〜、で、状況を教えてください。

PERFSTAT> 
PERFSTAT> exec statspack.snap(i_snap_level=>7,i_executions_th=>0);

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

経過: 00:00:31.46

・・・・中略・・・・

PERFSTAT> exec statspack.snap(i_snap_level=>7,i_executions_th=>0);

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

経過: 00:06:28.02

説明によると、こんな状況になっていたらしい。最初は30秒ぐらいで終わっていたのに、ここ最近は5分以上になり今日はついに6分超えたとか。

で……statspack.snap()の時間がかかってて試験の消化に支障でてんですよね…とか、

マシン貧弱だからな…とか、

と打ち合わせでも話題になることしばしば…


で、この状態になってからやっと、訪ねてきたわけね.

・・・・マシン環境について、しばし歓談タイム〜・・・・

話していて気づいた!

あ!、その試験環境って、統計情報の自動収集は止められてますよね。しかも試験する時に取得している統計情報って試験用のユーザだけ統計情報収集していますよね〜。

あれだ!、  感のいいかあなたなら気づきましたよね。ね。ね。:)

次のディクショナリを見れば一目瞭然! :)

PERFSTAT> select table_name,num_rows,stale_stats,last_analyzed from user_tab_statistics;

TABLE_NAME NUM_ROWS STA LAST_ANA
------------------------------ ---------- --- --------
STATS$BUFFERED_QUEUES
STATS$BUFFERED_SUBSCRIBERS
STATS$BUFFER_POOL_STATISTICS
STATS$CR_BLOCK_SERVER
STATS$CURRENT_BLOCK_SERVER
STATS$DATABASE_INSTANCE
STATS$DB_CACHE_ADVICE
STATS$DLM_MISC
STATS$DYNAMIC_REMASTER_STATS
STATS$ENQUEUE_STATISTICS
STATS$EVENT_HISTOGRAM

・・・・中略・・・・

STATS$STREAMS_POOL_ADVICE
STATS$SYSSTAT
STATS$SYSTEM_EVENT
STATS$SYS_TIME_MODEL
STATS$TEMPSTATXS
STATS$TEMP_SQLSTATS
STATS$THREAD
STATS$TIME_MODEL_STATNAME
STATS$UNDOSTAT
STATS$WAITSTAT

72行が選択されました。


じゃ〜、統計情報収集しましょーよー、ちゃんと! 

23:58:46 PERFSTAT> execdbms_stats.gather_schema_stats(ownname=>'PERFSTAT',no_invalidate=>false,cascade=>true);

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

経過: 00:00:04.02

試してみて!、 staspack.snap()を!  ね!  前よりすげー速くなった!

23:58:50 PERFSTAT> exec statspack.snap(i_snap_level=>7,i_executions_th=>0);

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

経過: 00:00:01.38


STATSPACK.SNAP()内で実行されてるSQL文にも統計情報は必要ですよーーーーーーーーーーーーと。

ごれぐらい”ー”付けてればわかってくれるかなー:)


一発ネタは、これにて FIN

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

2011年9月 8日 (木)

悩ませ過ぎは及ばざるがごとし #7 - おまけ

最近、おまけ付けるの好きなので:)

コストベースオプティマイザがどんだけ考えているか、チューニング前とチューニング後(案1と案2)でトレースファイルサイズで比較してみることに(本来それだけで比較できるものではないとは思いますが、あまりにも差があるのでw)


コストベースオプティマイザのトレースは次のようにして取得できますよね(詳細はググってね)

SCOTT> alter session set events='10053 trace name context forever, level 1';

セッションが変更されました。

経過: 00:00:00.00
SCOTT> SELECT …… 

・・・仲略・・・

5999行が選択されました。

経過: 00:00:53.48

SCOTT> alter session set events='10053 trace name context off';

セッションが変更されました。

経過: 00:00:00.00

SCOTT>

オプティマイザトレースファイルのサイズを比較すると以下のようになります:) 
オプティマイザを悩ますクエリ、ヒントで考えるな!感じろ!にしたクエリ(考えてますけど〜〜〜w)、最後が、相関副問合せに書き換えたクエリの順となっています。

[oracle@lampeye trace]$ ll
合計 59088
-rw-r----- 1 oracle oinstall 54431 9月 8 22:14 alert_lampeye2.log
-rw-r----- 1 oracle oinstall 54294792 9月 8 22:16 lampeye2_ora_5913.trc
-rw-r----- 1 oracle oinstall 5154103 9月 8 22:17 lampeye2_ora_5969.trc
-rw-r----- 1 oracle oinstall 898699 9月 8 22:19 lampeye2_ora_5981.trc
[oracle@lampeye trace]$

最初のトレースファイル(.trc),54294792バイトもありますよーw このオプティマイザトレースファイルがハードパースで48秒も考え込んでた時のものです。
(対象クエリも合わせて載せてみました)

-rw-r----- 1 oracle oinstall 54294792  9月  8 22:16 lampeye2_ora_5913.trc

こんなにオプティマイザを考えさせちゃうクエリ↓

SELECT
T1.surro_id,
T1.name,
T1.modified
FROM
test T1 JOIN (
SELECT DISTINCT
surro_id,
surro_bcd
FROM
test
) T2
ON
T1.surro_id = T2.surro_id AND
T1.surro_bcd = T2.surro_bcd
JOIN test T3
ON
T1.surro_id = T3.surro_id AND
T1.surro_acd= T3.surro_acd
WHERE
T1.surro_id IN (リテラル,....限界まで)
OR T1.surro_id IN (リテラル,....限界まで

.....以下....すきなだけ繰り返しw

/


2つめのトレースファイル、5154103バイトで、1/10程度になりましたーでも大きいですよね。これはヒントを付加して悩まないようにしてあげたものですが、それでもこんなサイズになってます!

-rw-r----- 1 oracle oinstall  5154103  9月  8 22:17 lampeye2_ora_5969.trc

案2、ヒントを付加したクエリでもこんなにでるのね。

SELECT   
/*+
LEADING(T1 T2 T3)
USE_HASH(T1 T2)
USE_HASH(T1 T3)
INDEX(T1 IX01_TEST)
INDEX(T3 IX01_TEST)
*/
T1.surro_id,
T1.name,
T1.modified
FROM
test T1 JOIN (
SELECT
/*+
MERGE
INDEX(test IX02_TEST)
*/
DISTINCT
surro_id,
surro_bcd
FROM
test
) T2
ON
T1.surro_id = T2.surro_id AND
T1.surro_bcd = T2.surro_bcd
JOIN test T3
ON
T1.surro_id = T3.surro_id AND
T1.surro_acd= T3.surro_acd
WHERE
T1.surro_id IN (リテラル,....限界まで)
OR T1.surro_id IN (リテラル,....限界まで)

.....以下....すきなだけ繰り返しw

/


最後のトレースファイルが(案1)、相関副問合せを利用して改善したクエリをハードパースした際に出力されたトレースファイルです。随分違うもんですねー

-rw-r----- 1 oracle oinstall   898699  9月  8 22:19 lampeye2_ora_5981.trc

案1、処理時間も短いけど、ほんと決断はやって感じですね。

SELECT
T1.surro_id
,T1.name
,T1.modified
FROM
test T1 JOIN (
SELECT DISTINCT
surro_id
,surro_bcd
FROM
test
) T2
ON
T1.surro_id = T2.surro_id AND
T1.surro_bcd = T2.surro_bcd
JOIN test T3
ON
T1.surro_id = T3.surro_id AND
T1.surro_acd= T3.surro_acd
WHERE
EXISTS (
SELECT
1
FROM
test_keys
WHERE
surro_id BETWEEN 1000000001 AND 1000005999
AND surro_id = T1.surro_id
)
/




ここまでのあらずじ

悩ませ過ぎは及ばざるがごとし #1
悩ませ過ぎは及ばざるがごとし #2
悩ませ過ぎは及ばざるがごとし #3
悩ませ過ぎは及ばざるがごとし #4
悩ませ過ぎは及ばざるがごとし #5
悩ませ過ぎは及ばざるがごとし #6

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

2011年9月 7日 (水)

悩ませ過ぎは及ばざるがごとし #6

前回のつづきです。

ヒントを使ってコストベースオプティマイザを悩ませないという方法で、40秒台っだった処理時間を1秒台にすることに成功しました。

これでも解決は解決なのですが、SQL文単体としては。ただ、インスタンスレベルて考えた場合無駄なメモリ使い過ぎだろという点が気がかり。コストベースオプティマイザが悩まなくなったことでCPUリソースの無駄遣いは回避できましたが、メモリリソースの無駄遣いが凄い。

ということで、どちらの無駄も減らす効果が期待できる案1を…

案1で書き換えるとヒントなしでも随分スッキリしちゃいます。
リテラル値が6000個近くありましたが、その部分を相関副問合せにしてあります。リテラル値の箇所はバインド変数化しちゃえばいいっすね。

SELECT
T1.surro_id
,T1.name
,T1.modified
FROM
test T1 JOIN (
SELECT DISTINCT
surro_id
,surro_bcd
FROM
test
) T2
ON
T1.surro_id = T2.surro_id AND
T1.surro_bcd = T2.surro_bcd
JOIN test T3
ON
T1.surro_id = T3.surro_id AND
T1.surro_acd= T3.surro_acd
WHERE
EXISTS (
SELECT
1
FROM
test_keys
WHERE
surro_id BETWEEN 1000000001 AND 1000005999
AND surro_id = T1.surro_id
)
/

実行してみると… (以下、SQL*Plusで set autot trace exp statコマンド叩いて実行した結果です。なお事前に共有プールはクリアしてあります。)

5999行が選択されました。

経過: 00:00:00.07

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

-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5997 | 468K| 144 (3)| 00:00:02 |
| 1 | VIEW | VM_NWVW_1 | 5997 | 468K| 144 (3)| 00:00:02 |
| 2 | HASH UNIQUE | | 5997 | 491K| 144 (3)| 00:00:02 |
|* 3 | HASH JOIN RIGHT SEMI | | 5997 | 491K| 143 (2)| 00:00:02 |
|* 4 | INDEX RANGE SCAN | PK_TEST_KEYS | 5999 | 41993 | 17 (0)| 00:00:01 |
|* 5 | HASH JOIN | | 5997 | 450K| 125 (1)| 00:00:02 |
|* 6 | INDEX RANGE SCAN | IX01_TEST | 5999 | 128K| 20 (0)| 00:00:01 |
|* 7 | HASH JOIN | | 5998 | 322K| 105 (1)| 00:00:02 |
|* 8 | INDEX RANGE SCAN | IX02_TEST | 5999 | 53991 | 19 (0)| 00:00:01 |
| 9 | TABLE ACCESS BY INDEX ROWID| TEST | 5999 | 269K| 85 (0)| 00:00:02 |
|* 10 | INDEX RANGE SCAN | IX02_TEST | 5999 | | 19 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------


SQL_ID        SHARABLE_MEM PERSISTENT_MEM RUNTIME_MEM SQL_TEXT
------------- ------------ -------------- ----------- ------------------------------
52gaz3tjd90dh 43446 15648 14504 SELECT T1.surro_id, T1.name,
T1.modified FROM test T1 jo
in ( SELECT DISTINCT s
urro_id, surro_bcd FROM
test ) T2 ON T1.surro_id
= T2.surro_id AND T1.surro
_bcd = T2.surro_bcd JO
IN test T3 ON T1.sur
ro_id = T3.surro_id AND T1.s
urro_acd= T3.surro_acd WHERE
EXISTS ( SELECT 1 FROM
test_keys WHERE surro_
id BETWEEN 1000000001 AND 1000
005999 AND T1.surro_id = su
rro_id )

きゃほーーーーーーっ。 最高〜〜〜〜っ! :) 言うことナッシングw  

処理時間が、48.83秒 から 0.07秒へ、SHARABLE_MEMが、3318142バイト から 43446バイトに。 でバインド変数化しちゃえば〜〜〜、

同時に多数のセッションから集中して発行されても耐えられそうな〜

最後に、ドヤ顔(キリっ

ほんとうにヤバそうなところはキッチリ潰しておきましょ!

OLTP系で実行計画はよいけど、コストベースオプティマイザを悩ませ過ぎて遅延してたら本末転倒ですよー。と。

ーーーー完ーーーーー


ここまでのあらずじ

悩ませ過ぎは及ばざるがごとし #1
悩ませ過ぎは及ばざるがごとし #2
悩ませ過ぎは及ばざるがごとし #3
悩ませ過ぎは及ばざるがごとし #4
悩ませ過ぎは及ばざるがごとし #5

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

2011年9月 6日 (火)

悩ませ過ぎは及ばざるがごとし #5

前回のつづきです。

前回は改善案を2つ提示しました。

まず、最初は案2のヒントを使って、コストベースオプティマイザに、「考えるな! 感じろ!」作戦w

元のクエリに次のようにヒントを書き加えちゃいます。
どの索引使えとか、結合方法はハッシュとか、とか、よく見かけるヒントだけです。

SELECT   
/*+
LEADING(T1 T2 T3)
USE_HASH(T1 T2)
USE_HASH(T1 T3)
INDEX(T1 IX01_TEST)
INDEX(T3 IX01_TEST)
*/
T1.surro_id,
T1.name,
T1.modified
FROM
test T1 JOIN (
SELECT
/*+
MERGE
INDEX(test IX02_TEST)
*/
DISTINCT
surro_id,
surro_bcd
FROM
test
) T2
ON
T1.surro_id = T2.surro_id AND
T1.surro_bcd = T2.surro_bcd
JOIN test T3
ON
T1.surro_id = T3.surro_id AND
T1.surro_acd= T3.surro_acd
WHERE
T1.surro_id IN (リテラル,....限界まで)
OR T1.surro_id IN (リテラル,....限界まで)

.....以下....すきなだけ繰り返しw

/

では実行してみると… (以下、SQL*Plusで set autot trace exp statコマンド叩いて実行した結果です。なお事前に共有プールはクリアしてあります。)

経過: 00:00:01.32

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

----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5999 | 456K| 5796 (1)| 00:01:10 |
| 1 | VIEW | VM_NWVW_1 | 5999 | 456K| 5796 (1)| 00:01:10 |
| 2 | HASH UNIQUE | | 5999 | 451K| 5796 (1)| 00:01:10 |
|* 3 | HASH JOIN | | 5999 | 451K| 5795 (1)| 00:01:10 |
| 4 | INLIST ITERATOR | | | | | |
|* 5 | INDEX RANGE SCAN | IX01_TEST | 5999 | 128K| 1935 (1)| 00:00:24 |
|* 6 | HASH JOIN | | 5999 | 322K| 3859 (1)| 00:00:47 |
| 7 | INLIST ITERATOR | | | | | |
| 8 | TABLE ACCESS BY INDEX ROWID| TEST | 5999 | 269K| 2001 (1)| 00:00:25 |
|* 9 | INDEX RANGE SCAN | IX01_TEST | 5999 | | 1935 (1)| 00:00:24 |
| 10 | INLIST ITERATOR | | | | | |
|* 11 | INDEX RANGE SCAN | IX02_TEST | 5999 | 53991 | 1857 (1)| 00:00:23 |
----------------------------------------------------------------------------------------------

やたーーーーーーーーーーーーっ! :) 目標の3秒を下回りましたよー :)

コストベースオプティマイザの悩みを解消してあげることができました…が、例のSHARABLE_MEMが3MB超えちゃってる問題はまだ抱えたままです。><

SQL_ID        SHARABLE_MEM PERSISTENT_MEM RUNTIME_MEM SQL_TEXT
------------- ------------ -------------- ----------- ------------------------------
fbjw4wjdma4f7 3318142 396808 395664 SELECT /*+ LEADING(T1 T2 T3
) USE_HASH(T1 T2) USE_HASH
(T1 T3) INDEX(T1 IX01_TEST)
INDEX(T3 IX01_TEST) */ T1.
surro_id, T1.name, T1.modifi
ed FROM test T1 join ( SELE
CT /*+ MERGE INDEX(
test IX02_TEST) */ DISTI
NCT surro_id, surro_bcd

今回はここまで、次回へつづく。


ここまでのあらずじ

悩ませ過ぎは及ばざるがごとし #1
悩ませ過ぎは及ばざるがごとし #2
悩ませ過ぎは及ばざるがごとし #3
悩ませ過ぎは及ばざるがごとし #4

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

2011年9月 4日 (日)

悩ませ過ぎは及ばざるがごとし #4

さて、さて、前回のつづきです。

意識的に間を取ったんですが、ちょいと長過ぎたかw


前回、ハードパースに時間がかかってるねーってとこまで確認しましたよね〜

20110824_23733


・1回目のSQLトレース

call     count       cpu    elapsed       disk      query    current        rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 45.18 45.15 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 401 0.04 0.05 0 1897 0 5999
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 403 45.23 45.21 0 1897 0 5999


・2回目のSQLトレース

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 401 0.04 0.04 0 1897 0 5999
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 403 0.04 0.04 0 1897 0 5999


ということでした。

じゃ、こんなにコストペースオプティマイザに考えさせちゃうSQL文でどんなのよー。と。 OLTPなのに分析系のすげー難しそーなSQL文なげてるとか??? 想像しているより現物見た方が早いですw

こんな感じのクエリだんたんよー。

SELECT
T1.surro_id,
T1.name,
T1.modified
FROM
test T1 JOIN (
SELECT DISTINCT
surro_id,
surro_bcd
FROM
test
) T2
ON
T1.surro_id = T2.surro_id AND
T1.surro_bcd = T2.surro_bcd
JOIN test T3
ON
T1.surro_id = T3.surro_id AND
T1.surro_acd= T3.surro_acd
WHERE
T1.surro_id IN (リテラル,....限界まで)
OR T1.surro_id IN (リテラル,....限界まで

.....以下....すきなだけ繰り返しw

/


え、え〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜っ、リテラル値が〜〜〜〜すげ〜〜〜〜〜〜〜ぞ〜〜〜〜〜〜。
クエリ自体はたいしたことないw あの人達がすきそーな自己結合だー。(失礼w)

このクエリを見て、解決策2つをすぐに思いついたあたなあなた!、 そう、あなた、凄いです!
INリストには上限あるからね:)- Oracle® Database SQL言語リファレンス11gリリース2(11.2)式のリスト

このクエリの解決策。

解決案1
一番効果のありそうなの、リテラルを排除して、その部分を副問合せにしちゃう!。

解決案2
コストベースオプティマイザを悩ませない方法って、ありますよね、そう、ヒントを使って、「こんな感じにうごいて!」作戦。

このクエリの一番の問題点は、リテラル値が山ほどあって、似たような索引が沢山あって、しかも、リテラル値だから毎回値が異なる。結果として毎回ハードパースになるので、毎回コストベースオプティマイザが考え過ぎちゃうこと。

解決策は前述の2案のうちいずれか一つなんだけど、おすすめは案1のほう。

その訳は、でかいんです、なにが?

以下を見れば、なに? が でかいかよーくわかりますw

23:49:06 SYS> SELECT SQL_ID,SHARABLE_MEM,PERSISTENT_MEM,RUNTIME_MEM,SQL_TEXT FROM v$sql WHERE SQL_TEXT LIKE '%/*TEST*/%';

SQL_ID SHARABLE_MEM PERSISTENT_MEM RUNTIME_MEM SQL_TEXT
------------- ------------ -------------- ----------- ------------------------------
82q8wxz56y357 3317991 396816 395672 SELECT /*TEST*/ T1.surro_id,
T1.name, T1.modified FROM t
est T1 join ( SELECT DISTIN
CT surro_id, surro_bcd
FROM test ) T2 ON T1.s
urro_id = T2.surro_id AND T
1.surro_bcd = T2.surro_bcd
JOIN test T3 ON

ね、でかいでしょ。SHARABLE_MEMなんて3MBいっちゃってるし、この程度のことするにしてはデカ過ぎw
これって、直ちには影響はないのですが…(どっかで聞いた事ある言い回し><  やはり同じ事するセッション多いとさーボディーブローよ。全部ハードパースだしw


今回はここまで、次回へつづく。


ここまでのあらずじ

悩ませ過ぎは及ばざるがごとし #1
悩ませ過ぎは及ばざるがごとし #2
悩ませ過ぎは及ばざるがごとし #3

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

2011年9月 3日 (土)

実習セミナー:RAC環境でのレスポンス遅延を調査・解決@日本オラクル青山センター

昨日は、「実習セミナー:RAC環境でのレスポンス遅延を調査・解決」に参加してきた。 ;)

前回、2日に渡って行われた、
「DBがなんか遅いんだけど!」「DBで何か起きてないか確認して!」こんな問い合わせへの対応 “一人”でできますか? - 勉強会@日本オラクル青山センター

「DBがなんか遅いんだけど!」「DBで何か起きてないか確認して!」こんな問い合わせへの対応 “一人”でできますか? - 勉強会@日本オラクル青山センター #2

という勉強会シリーズの続編です。今回はペアプロじゃなくてグループチューニング形式(3名1組というグループで解析しました)、2ノードRACで発生した処理遅延の解析の流れを勉強しました。



X2_80d2194


前回と違って、真剣にレポート見ちゃいましたよw。 

実はgc cr block grant 2-way あたりで、RACをRACとして使ってないのに遅延なんて、マニアックなネタになってるんじゃないかなー、なんて、ニヤニヤして見てたんですが、私の想像とは違う待機イベントでしたねー ;)(ネタばれ禁止なので内容はこの程度でご勘弁を)

で、皆さんいろいろと解析しつつ、最後のQAで、衝撃的(笑劇的)な解決案が提示されました!


なんと! 「RAC使わなければ、いいんじゃね?!」(核爆


普段解析やってる人、AP設計開発オンリーだけど怖いもの見たさでやってみたい方、女性エンジニアの方(私個人の意見ではございませんw)、どんどん参加してみてくだちい。(またGANTS語に....)


次回も楽しみにしております。


追伸)
ビールの勢いとはいえ、場の空気読みきれず、じゃんけん大会で勝って頂いちゃいました m(_ _)m


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

2011年8月25日 (木)

悩ませ過ぎは及ばざるがごとし #3

前回の続きだす。(まだGANTZ語が抜けてないw)


前回は状況証拠固めでした。

で、以下のような状況証拠は確認済み。

  • 1回目だけ処理時間が異常に長い。
  • 実行統計(consistent getsなど)から実行時のアクセスブロック数の差異はない。かつ、ブロック数も少ない。
  • 物理読み込みは処理時間には影響していない。

という状況証拠を踏まえ、SQL文の処理フェーズを頭に浮かべて、順序よく考えていくと……

consitents getsの量や処理時間からexecutionフェーズ以降で時間を要しているわけではない……
また、
1回目だけ処理時間が長いが、物理読み込みが影響しているわけではない…

20110824_23033


ということは、1回目だけ実行されるといえばハードパース……この部分の処理時間が長いってことですよね!

20110824_23733

だんだん近づいてきましたね。というこで、次はパース時間が長いという証拠固めを。

パース時間を比較する方法はいくつかあります…。 SQL*Plusのset autot trace exp を使って実行計画だけを確認する方法とか、Explain plan文使うとか、SQLトレースを取るとか。
ようするに、パース時間が確認できる方法であればどんな方法でもOKなんです。


今回は結果の比較もしやすい、SQLトレースで証拠固をしてみます。SQLトレースを取得する方法はdbms_monitorパッケージを利用する方法が簡単ですかね。(他にもありますが)

SCOTT> exec dbms_monitor.session_trace_enable(null,null,true,false,'ALL_EXECUTIONS');

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

SCOTT> SELECT

・・・中略・・・

5999行が選択されました。

経過: 00:00:48.37

SCOTT> exec dbms_monitor.session_trace_disable(null,null);

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

こんな感じでセッション単位でSQLトレースを取得してtkprofで整形するというおなじみのスタイル。


・1回目のSQLトレース

call     count       cpu    elapsed       disk      query    current        rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 45.18 45.15 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 401 0.04 0.05 0 1897 0 5999
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 403 45.23 45.21 0 1897 0 5999


・2回目のSQLトレース

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 401 0.04 0.04 0 1897 0 5999
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 403 0.04 0.04 0 1897 0 5999


予想的中!。 パースに時間を要しているのは明らかですね。

最近のコストベースオプティマイザは賢いです。賢いのでいろいろと考えさせすぎると時間がかかります。(将棋でも相手の手数を読み過ぎなんて…ことありますよね。そんな状況に近いです。)。DWH系のクエリならそんなこともあるでしょうけど、今回問題になっているのはOLTPです、しかも、3秒程度で結果を返してほしいらしいし…

で、どのようにしたらコストベースオプティマイザを"悩ませずに”済むか、そこが今回のポイント。

思い切って、ルールベースにしちゃおーか?。それはNG。確かにパース時間は短くなるでしょうけど…


解決方法を見つけるには問題のクエリがどんなものか見極める必要がありそうですね〜…。どんな方法があるだろー。


今日はここまで、次回へつづく。


ここまでのあらずじ

悩ませ過ぎは及ばざるがごとし #1
悩ませ過ぎは及ばざるがごとし #2

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

2011年8月24日 (水)

悩ませ過ぎは及ばざるがごとし #2

「一緒に考えてみてくらちい」タイポじゃないだすw GANTZ見てたんでつい。:)

ということで前回のつづきです。

1回目だけ異常に処理時間を要するクエリがあると相談を受けたという前振り覚えてますか?

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

--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5999 | 351K| 5718 (1)| 00:01:09 |
|* 1 | HASH JOIN | | 5999 | 351K| 5718 (1)| 00:01:09 |
| 2 | INLIST ITERATOR | | | | | |
|* 3 | INDEX RANGE SCAN | IX01_TEST | 5999 | 59990 | 1935 (1)| 00:00:24 |
|* 4 | HASH JOIN | | 5999 | 292K| 3782 (1)| 00:00:46 |
| 5 | VIEW | | 5999 | 95984 | 1858 (1)| 00:00:23 |
| 6 | HASH UNIQUE | | 5999 | 53991 | 1858 (1)| 00:00:23 |
| 7 | INLIST ITERATOR | | | | | |
|* 8 | INDEX RANGE SCAN | IX02_TEST | 5999 | 53991 | 1857 (1)| 00:00:23 |
| 9 | INLIST ITERATOR | | | | | |
| 10 | TABLE ACCESS BY INDEX ROWID| TEST | 5999 | 199K| 1923 (1)| 00:00:24 |
|* 11 | INDEX RANGE SCAN | IX02_TEST | 5999 | | 1857 (1)| 00:00:23 |
--------------------------------------------------------------------------------------------

・・・中略・・・

統計
----------------------------------------------------------
1 recursive calls
0 db block gets
1897 consistent gets
0 physical reads
0 redo size
247808 bytes sent via SQL*Net to client
69769 bytes received via SQL*Net from client
401 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
5999 rows processed


1回目とそれ以降で実行計画の差異はありません。実行統計上はrecursive callが1から0になっている程度の変化しかありません。

ですが……………………

1回目は、48.83秒なのに、2回目以降は、0.08秒…

1回目の処理時間 (SQL*Plusのset timiing on 及び、set autot trace exp statにて計測)


5999行が選択されました。

経過: 00:00:48.83

2回目以降〜


5999行が選択されました。

経過: 00:00:00.08

実行統計を見ると、consistent gets 1897もそれほど多くないのに1回目だけ処理時間が長い、その差はどこに……


SQL文がどのような順序で処理されるのか、よーーーーーく思い出してください;)

リンク先にあるオラクルのマニュアルの図をざっくりまとめちゃうと以下のような感じ(BINDや、FETCHフェーズが書かれている図もよく見かけますね)


20110823_142232



となっています。


この辺りも一緒に読むといいですよね。わかりやすいです。無料だし:)
門外不出のOracle現場ワザ - 第4章 Oracleデータベースの頭脳 「オプティマイザ」徹底研究
SQL文の処理におけるオプティマイザの役割
Part2 CBOは何を見てどう判断するのか

これまでに確認できた状況証拠

  • 1回目だけ処理時間が異常に長い。
  • 実行統計(consistent getsなど)から実行時のアクセスブロック数の差異はない。かつ、ブロック数も少ない。
  • 物理読み込みは処理時間には影響していない。

これらの状況証拠とSQL文の処理フェーズから導きだせますよね。どこに処理時間を要しているか…………

ほーら、ほら。見えてきました…………………よね? (ヒントだしてますから〜〜:)


ということで、今日はここまで、次回へつづく。




ここまでのあらずじ

悩ませ過ぎは及ばざるがごとし #1

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

2011年8月23日 (火)

悩ませ過ぎは及ばざるがごとし #1

あるクエリを実行していて、目標時間は3秒程度のオンライン処理向けクエリ、初回だけ異常に遅いんですけど……。 と再び、大人の事情に縛られて心が痣だらけ(どんなだ)な人達からお呼びがかかりました。

こんな感じの表(索引は似たようなのが数多くありそれもイケてないといえばイケテてないが)を自己結合しているだけ(イケてるクエリとか、イケてないクエリだというのは置いといて)で実行計画もそんなに悪くないし、実行統計を見る限り
初回とそれ以降で大きく乖離しているようにも見えません。

表は次のように定義されています。

SCOTT> desc test
名前 NULL? 型
----------------------------------------- -------- ----------------------------
ID NOT NULL NUMBER(10)
SURRO_ID NOT NULL NUMBER(10)
SURRO_ACD NOT NULL CHAR(2)
SURRO_BCD NOT NULL CHAR(1)
SURRO_CCD NOT NULL CHAR(3)
SURRO_DCD NOT NULL CHAR(5)
NAME NOT NULL VARCHAR2(100)
DESCRIPTION VARCHAR2(300)
CREATED NOT NULL TIMESTAMP(6)
MODIFIED NOT NULL TIMESTAMP(6)

索引は以下のようになってます。

INDEX_NAME                     COLUMN_NAME
------------------------------ ------------------------------
IX01_TEST SURRO_ID
SURRO_ACD

IX02_TEST SURRO_ID
SURRO_BCD

IX03_TEST SURRO_ID
SURRO_CCD

IX04_TEST SURRO_ID
SURRO_DCD

PK_TEST ID

なんだか、どれも似たような索引がならんでいます。

INDEX_NAME                     INDEX_TYPE                    NUM_ROWS DISTINCT_KEYS CLUSTERING_FACTOR
------------------------------ --------------------------- ---------- ------------- -----------------
IX04_TEST NORMAL 700000 700000 7653
IX03_TEST NORMAL 700000 700000 7653
IX02_TEST NORMAL 700000 700000 7653
IX01_TEST NORMAL 700000 700000 7653
PK_TEST NORMAL 700000 700000 7653





どーーーーーーーしてでしょう。

このつづきは次回のお楽しみ。

一緒に考えてみてくらちい ;)


1回目の処理行数、処理時間、実行計画及び実行統計。


・・・中略・・・

5999行が選択されました。

経過: 00:00:48.83

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

--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5999 | 351K| 5718 (1)| 00:01:09 |
|* 1 | HASH JOIN | | 5999 | 351K| 5718 (1)| 00:01:09 |
| 2 | INLIST ITERATOR | | | | | |
|* 3 | INDEX RANGE SCAN | IX01_TEST | 5999 | 59990 | 1935 (1)| 00:00:24 |
|* 4 | HASH JOIN | | 5999 | 292K| 3782 (1)| 00:00:46 |
| 5 | VIEW | | 5999 | 95984 | 1858 (1)| 00:00:23 |
| 6 | HASH UNIQUE | | 5999 | 53991 | 1858 (1)| 00:00:23 |
| 7 | INLIST ITERATOR | | | | | |
|* 8 | INDEX RANGE SCAN | IX02_TEST | 5999 | 53991 | 1857 (1)| 00:00:23 |
| 9 | INLIST ITERATOR | | | | | |
| 10 | TABLE ACCESS BY INDEX ROWID| TEST | 5999 | 199K| 1923 (1)| 00:00:24 |
|* 11 | INDEX RANGE SCAN | IX02_TEST | 5999 | | 1857 (1)| 00:00:23 |
--------------------------------------------------------------------------------------------

・・・中略・・・

統計
----------------------------------------------------------
1 recursive calls
0 db block gets
1897 consistent gets
0 physical reads
0 redo size
247808 bytes sent via SQL*Net to client
69769 bytes received via SQL*Net from client
401 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
5999 rows processed



2回目の処理行数、処理時間、実行計画及び実行統計。

1回目とくらべて随分処理時間が短いです。。。。

・・・中略・・・

5999行が選択されました。

経過: 00:00:00.08

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

--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5999 | 351K| 5718 (1)| 00:01:09 |
|* 1 | HASH JOIN | | 5999 | 351K| 5718 (1)| 00:01:09 |
| 2 | INLIST ITERATOR | | | | | |
|* 3 | INDEX RANGE SCAN | IX01_TEST | 5999 | 59990 | 1935 (1)| 00:00:24 |
|* 4 | HASH JOIN | | 5999 | 292K| 3782 (1)| 00:00:46 |
| 5 | VIEW | | 5999 | 95984 | 1858 (1)| 00:00:23 |
| 6 | HASH UNIQUE | | 5999 | 53991 | 1858 (1)| 00:00:23 |
| 7 | INLIST ITERATOR | | | | | |
|* 8 | INDEX RANGE SCAN | IX02_TEST | 5999 | 53991 | 1857 (1)| 00:00:23 |
| 9 | INLIST ITERATOR | | | | | |
| 10 | TABLE ACCESS BY INDEX ROWID| TEST | 5999 | 199K| 1923 (1)| 00:00:24 |
|* 11 | INDEX RANGE SCAN | IX02_TEST | 5999 | | 1857 (1)| 00:00:23 |
--------------------------------------------------------------------------------------------

・・・中略・・・

統計
----------------------------------------------------------
0 recursive calls
0 db block gets
1897 consistent gets
0 physical reads
0 redo size
247808 bytes sent via SQL*Net to client
69766 bytes received via SQL*Net from client
401 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
5999 rows processed


3回目の処理行数、処理時間、実行計画及び実行統計。

3回目も2回目と同じようです。1回目だけなぜか異常に遅い。ちなみに、Oracle11g R2 11.2.0.1.0 for Linux x86_64です。


・・・中略・・・

5999行が選択されました。

経過: 00:00:00.08

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

--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5999 | 351K| 5718 (1)| 00:01:09 |
|* 1 | HASH JOIN | | 5999 | 351K| 5718 (1)| 00:01:09 |
| 2 | INLIST ITERATOR | | | | | |
|* 3 | INDEX RANGE SCAN | IX01_TEST | 5999 | 59990 | 1935 (1)| 00:00:24 |
|* 4 | HASH JOIN | | 5999 | 292K| 3782 (1)| 00:00:46 |
| 5 | VIEW | | 5999 | 95984 | 1858 (1)| 00:00:23 |
| 6 | HASH UNIQUE | | 5999 | 53991 | 1858 (1)| 00:00:23 |
| 7 | INLIST ITERATOR | | | | | |
|* 8 | INDEX RANGE SCAN | IX02_TEST | 5999 | 53991 | 1857 (1)| 00:00:23 |
| 9 | INLIST ITERATOR | | | | | |
| 10 | TABLE ACCESS BY INDEX ROWID| TEST | 5999 | 199K| 1923 (1)| 00:00:24 |
|* 11 | INDEX RANGE SCAN | IX02_TEST | 5999 | | 1857 (1)| 00:00:23 |
--------------------------------------------------------------------------------------------

・・・中略・・・


統計
----------------------------------------------------------
0 recursive calls
0 db block gets
1897 consistent gets
0 physical reads
0 redo size
247808 bytes sent via SQL*Net to client
69766 bytes received via SQL*Net from client
401 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
5999 rows processed

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

2011年8月16日 (火)

VirtualBox 4.1.2 released! 月1ペースか。:)

http://www.virtualbox.org/wiki/Changelog

ほんと良いペースですねー。


20110816_230112


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

CentOS6.0 on VirtualBox4.1.0 for MacOS X(Snow Leopard) - Can't mount VBoxGuestAdditions.iso as Virtual CD/DVD-ROM

MacOS XのVirtualBoxでGuest Additionsがインストールできないというか VBoxGuestAdditions.isoファイルを仮想CD/DVDとしてマウントできないなー、なんて状態になったら、これから紹介する方法で解決できますよ。というお話です。:)

VirtualBox for MacOS Xは他のアプリケーションと同様にアプリケーション・パッケージ形式なのでVirtualBoxアプリケーションアイコンを右クリックし、「ボップアップメニュー」>「パッケージの内容を表示」を選択します。

Menu




でFinderに表示されたVirtualBoxパッケージないを開いていくと Contents/MacOS/VBoxGuestAdditions.iso というファイルが見つかります。これがGuest Additions用の.iso形式ファイル。これがなぜかマウントできないんだな。以前はマウントできてたのに…

Iso


この VBoxGuestAdditions.iso をDesktopなどアクセスしやすい場所にコピーしちゃいます。

あとはいつものように、CD/DVD-ROMとしてマウントして…


Mount


Mount2


コマンドを打つだけ。:)


[discus@angelfish VBOXADDITIONS_4.1.0_73009]$ su -
パスワード:
[root@angelfish &]# cd /media
[root@angelfish media]# ll
合計 2
dr-xr-xr-x. 4 discus root 2048 7月 19 19:43 2011 VBOXADDITIONS_4.1.0_73009
[root@angelfish media]# cd VBOXADDITIONS_4.1.0_73009
[root@angelfish VBOXADDITIONS_4.1.0_73009]# ll
合計 41440
dr-xr-xr-x. 3 discus root 2048 7月 19 19:43 2011 32Bit
dr-xr-xr-x. 2 discus root 2048 7月 19 19:43 2011 64Bit
-r-xr-xr-x. 1 discus root 647 8月 14 00:58 2010 AUTORUN.INF
-r-xr-xr-x. 1 discus root 7525824 7月 19 19:38 2011 VBoxLinuxAdditions.run
-r-xr-xr-x. 1 discus root 14235136 7月 19 19:42 2011 VBoxSolarisAdditions.pkg
-r-xr-xr-x. 1 discus root 13097968 7月 19 19:30 2011 VBoxWindowsAdditions-amd64.exe
-r-xr-xr-x. 1 discus root 7277968 7月 19 19:25 2011 VBoxWindowsAdditions-x86.exe
-r-xr-xr-x. 1 discus root 278832 7月 19 19:24 2011 VBoxWindowsAdditions.exe
-r-xr-xr-x. 1 discus root 6966 7月 19 19:36 2011 autorun.sh
-r-xr-xr-x. 1 discus root 5523 7月 19 19:36 2011 runasroot.sh
[root@angelfish VBOXADDITIONS_4.1.0_73009]# sh ./VBoxLinuxAdditions.run
Verifying archive integrity... All good.
Uncompressing VirtualBox 4.1.0 Guest Additions for Linux.........
VirtualBox Guest Additions installer
Removing existing VirtualBox DKMS kernel modules [ OK ]
Removing existing VirtualBox non-DKMS kernel modules [ OK ]
Building the VirtualBox Guest Additions kernel modules
Building the main Guest Additions module [ OK ]
Building the shared folder support module [ OK ]
Building the OpenGL support module [ OK ]
Doing non-kernel setup of the Guest Additions [ OK ]
Starting the VirtualBox Guest Additions [ OK ]
Installing the Window System drivers
Installing X.Org Server 1.7 modules [ OK ]
Setting up the Window System to use the Guest Additions [ OK ]
You may need to restart the hal service and the Window System (or just restart
the guest system) to enable the Guest Additions.

Installing graphics libraries and desktop services componen[ OK ]
[root@angelfish VBOXADDITIONS_4.1.0_73009]#

このお遊びの続きはいずれまた。

2011/8/16追記
実は、こんな事しなくてもちゃんとマウンドできた><。

VirtualBoxの「メニュー」>「デバイス」>「Guest Addtionsのインストール(I)...」でマウントできるじゃないですかー。失礼しました。 m(_ _)m

20110816_230742


これまでのあらずじ…

CentOS6.0 on VirtualBox4.1.0 for MacOS X(Snow Leopard)

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

2011年8月15日 (月)

CentOS6.0 on VirtualBox4.1.0 for MacOS X(Snow Leopard)

まだLionじゃなくて、Snow Leopardなのですが…

VirtualBox4.1.0でCentOS6.0をGuestOSとして構築してみた。この続きは暇を見て…。
20110815_03357

20110815_71248

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

2011年8月11日 (木)

いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング番外編 

おまけのおまけのおまけとは、さすがに書きづらかったのでw 番外編。

いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング8 おまけのおまけで次のように書いていたのを覚えてますか?

"アクセスするブロック数を減らすのはハッシュパーティションのままではもう、むーりーw 限界です。"

なぜ、限界としたか。という点、聞きたくないですか?

聞きたいですよね。:)

理由は次のクエリ(GROUP BYとCUBEでチューニングしたクエリの一番大切部分)と処理時間、実行計画、consistent getsを見ていただければお分かりいただけるかと……  


そーーーなんです。 以下のブロック数と処理時間って、前回、GROUP BY と CUBEだけでチューニングした結果とそっくりですよね。


  1   SELECT
2 SUBSTR(starting_date,1,6) AS month
3 ,shop_code
4 ,SUM(sales_figure) AS sales_figure
5 FROM
6 test2
7 WHERE
8 starting_date BETWEEN '20110101' AND '20110331'
9 GROUP BY
10 SUBSTR(starting_date,1,6)
11* ,shop_code

3900行が選択されました。

経過: 00:00:06.89

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

---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 8274 | 137K| 82839 (2)| 00:16:35 | | |
| 1 | HASH GROUP BY | | 8274 | 137K| 82839 (2)| 00:16:35 | | |
| 2 | PARTITION HASH ALL| | 1703K| 27M| 82788 (2)| 00:16:34 | 1 | 4 |
|* 3 | TABLE ACCESS FULL| TEST2 | 1703K| 27M| 82788 (2)| 00:16:34 | 1 | 4 |
---------------------------------------------------------------------------------------------

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

3 - filter("STARTING_DATE"<='20110331' AND "STARTING_DATE">='20110101')


統計
----------------------------------------------------------
0 recursive calls
0 db block gets
302341 consistent gets
302289 physical reads
0 redo size
87392 bytes sent via SQL*Net to client
3369 bytes received via SQL*Net from client
261 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
3900 rows processed

と、いうことで限界! と判断したわけなんです。 どうですか? スッキリしましたか? :) では、また。


これまでのあらずじ…

いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #1
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #2
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #3
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #4
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #5
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #6
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #7 おまけ
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング8 おまけのおまけ

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

2011年8月 6日 (土)

いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング8 おまけのおまけ

前回のつづきです。

GROUPING SETSを単純に使っただけでは可読性は向上するが内部で自動的に行われる一時表への展開(WITH句で書くのに似ている)とUNION ALLによる集計時オーバーヘッドが大きくなるようで、WITH句+UNION ALLに書き換えた場合ほど効果はありませんでした。残念。

だったら〜。

ということで、GROUPING SETSは使わず、やってる事がやってる事だから、素直にCUBEを使ってクロス集計しちゃえばオーバーヘッドとなっているWITH句+UNION ALLへの展開も行わないようにすればなって良いんジャマイカ! そうしよう!


で、パーティションは大人の事情でハッシュのままに、WITH句+UNION ALLへの書き換えや、GROUPING SETSで自動的にWITH句+UNION ALLへ展開する方法を止めたのが以下のクエリ。

え〜〜〜〜〜、え〜〜〜〜〜〜。シリアル実行としては最速の結果、6秒台キタ〜〜〜〜!。
可読性は副問合せなどの影響でちょいと落ちますがSQL文は短いのでそれほど影響はないですよね。いままで一番短いのではないでしょうか:)

  1  SELECT
2 CASE
3 WHEN grouping_id(month) = 1 THEN
4 CASE
5 WHEN SUBSTR(MIN(month),5,2)||SUBSTR(MAX(month),5,2) = '0406' THEN 'Q1'
6 WHEN SUBSTR(MIN(month),5,2)||SUBSTR(MAX(month),5,2) = '0709' THEN 'Q2'
7 WHEN SUBSTR(MIN(month),5,2)||SUBSTR(MAX(month),5,2) = '1012' THEN 'Q3'
8 WHEN SUBSTR(MIN(month),5,2)||SUBSTR(MAX(month),5,2) = '0103' THEN 'Q4'
9 END
10 ELSE month
11 END AS month
12 ,CASE
13 WHEN grouping_id(shop_code) = 1 THEN 'ALL'
14 ELSE shop_code
15 END AS shop_code
16 ,SUM(sales_figure) AS sales_figure
17 FROM
18 (
19 SELECT
20 SUBSTR(starting_date,1,6) AS month
21 ,shop_code
22 ,SUM(sales_figure) AS sales_figure
23 FROM
24 test2
25 WHERE
26 starting_date BETWEEN '20110101' AND '20110331'
27 GROUP BY
28 SUBSTR(starting_date,1,6)
29 ,shop_code
30 )
31 GROUP BY
32 CUBE(month,shop_code)
33 HAVING
34 shop_code = '1000'
35 OR grouping_id(shop_code) = 1
36 ORDER BY
37 month
38* ,shop_code

8行が選択されました。

経過: 00:00:06.86

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

---------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
---------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1381 | 34525 | 83173 (2)| 00:16:39 | | |
| 1 | SORT ORDER BY | | 1381 | 34525 | 83173 (2)| 00:16:39 | | |
|* 2 | FILTER | | | | | | | |
| 3 | SORT GROUP BY | | 1381 | 34525 | 83173 (2)| 00:16:39 | | |
| 4 | GENERATE CUBE | | 1381 | 34525 | 83173 (2)| 00:16:39 | | |
| 5 | SORT GROUP BY | | 1381 | 34525 | 83173 (2)| 00:16:39 | | |
| 6 | VIEW | | 2758 | 68950 | 83172 (2)| 00:16:39 | | |
| 7 | HASH GROUP BY | | 2758 | 66192 | 83172 (2)| 00:16:39 | | |
| 8 | PARTITION HASH ALL | | 394K| 9242K| 83161 (2)| 00:16:38 | 1 | 4 |
|* 9 | TABLE ACCESS FULL | TEST2 | 394K| 9242K| 83161 (2)| 00:16:38 | 1 | 4 |
---------------------------------------------------------------------------------------------------

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

2 - filter("SHOP_CODE"='1000' OR GROUPING_ID(BIN_TO_NUM(SYS_OP_GROUPING("SHOP_CODE",1,0,
SYS_OP_BITVEC)))=1)
9 - filter("STARTING_DATE"<='20110331' AND SUBSTR("STARTING_DATE",1,6)<='201103' AND
SUBSTR("STARTING_DATE",1,6)>='201101' AND "STARTING_DATE">='20110101')


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

お約束、オラクルお任せのパラレル実験。 

お〜〜〜〜〜〜〜〜〜〜〜〜っ。キター、1.2秒台〜〜〜〜〜。 パーティションタイプのミスという痛恨の設計ミスを帳消にしてあげたかも。(それは嘘。
そもそも、アクセスするブロック数を減らすのはハッシュパーティションのままではもう、むーりーw 限界です。

  1  SELECT /*+ PARALLEL */
2 CASE
3 WHEN grouping_id(month) = 1 THEN
4 CASE
5 WHEN SUBSTR(MIN(month),5,2)||SUBSTR(MAX(month),5,2) = '0406' THEN 'Q1'
6 WHEN SUBSTR(MIN(month),5,2)||SUBSTR(MAX(month),5,2) = '0709' THEN 'Q2'
7 WHEN SUBSTR(MIN(month),5,2)||SUBSTR(MAX(month),5,2) = '1012' THEN 'Q3'
8 WHEN SUBSTR(MIN(month),5,2)||SUBSTR(MAX(month),5,2) = '0103' THEN 'Q4'
9 END
10 ELSE month
11 END AS month
12 ,CASE
13 WHEN grouping_id(shop_code) = 1 THEN 'ALL'
14 ELSE shop_code
15 END AS shop_code
16 ,SUM(sales_figure) AS sales_figure
17 FROM
18 (
19 SELECT
20 SUBSTR(starting_date,1,6) AS month
21 ,shop_code
22 ,SUM(sales_figure) AS sales_figure
23 FROM
24 test2
25 WHERE
26 starting_date BETWEEN '20110101' AND '20110331'
27 GROUP BY
28 SUBSTR(starting_date,1,6)
29 ,shop_code
30 )
31 GROUP BY
32 CUBE(month,shop_code)
33 HAVING
34 shop_code = '1000'
35 OR grouping_id(shop_code) = 1
36 ORDER BY
37 month
38* ,shop_code

8行が選択されました。

経過: 00:00:01.24

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

-----------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop | TQ |IN-OUT| PQ Distrib |
-----------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1381 | 34525 | 5773 (2)| 00:01:10 | | | | | |
| 1 | PX COORDINATOR | | | | | | | | | | |
| 2 | PX SEND QC (ORDER) | :TQ10004 | 1381 | 34525 | 5773 (2)| 00:01:10 | | | Q1,04 | P->S | QC (ORDER) |
| 3 | SORT ORDER BY | | 1381 | 34525 | 5773 (2)| 00:01:10 | | | Q1,04 | PCWP | |
| 4 | PX RECEIVE | | 1381 | 34525 | 5773 (2)| 00:01:10 | | | Q1,04 | PCWP | |
| 5 | PX SEND RANGE | :TQ10003 | 1381 | 34525 | 5773 (2)| 00:01:10 | | | Q1,03 | P->P | RANGE |
|* 6 | FILTER | | | | | | | | Q1,03 | PCWC | |
| 7 | SORT GROUP BY | | 1381 | 34525 | 5773 (2)| 00:01:10 | | | Q1,03 | PCWP | |
| 8 | PX RECEIVE | | 1381 | 34525 | 5773 (2)| 00:01:10 | | | Q1,03 | PCWP | |
| 9 | PX SEND HASH | :TQ10002 | 1381 | 34525 | 5773 (2)| 00:01:10 | | | Q1,02 | P->P | HASH |
| 10 | GENERATE CUBE | | 1381 | 34525 | 5773 (2)| 00:01:10 | | | Q1,02 | PCWC | |
| 11 | SORT GROUP BY | | 1381 | 34525 | 5773 (2)| 00:01:10 | | | Q1,02 | PCWP | |
| 12 | PX RECEIVE | | 1381 | 34525 | 5773 (2)| 00:01:10 | | | Q1,02 | PCWP | |
| 13 | PX SEND HASH | :TQ10001 | 1381 | 34525 | 5773 (2)| 00:01:10 | | | Q1,01 | P->P | HASH |
| 14 | SORT GROUP BY | | 1381 | 34525 | 5773 (2)| 00:01:10 | | | Q1,01 | PCWP | |
| 15 | VIEW | | 2758 | 68950 | 5772 (2)| 00:01:10 | | | Q1,01 | PCWP | |
| 16 | HASH GROUP BY | | 2758 | 66192 | 5772 (2)| 00:01:10 | | | Q1,01 | PCWP | |
| 17 | PX RECEIVE | | 2758 | 66192 | 5772 (2)| 00:01:10 | | | Q1,01 | PCWP | |
| 18 | PX SEND HASH | :TQ10000 | 2758 | 66192 | 5772 (2)| 00:01:10 | | | Q1,00 | P->P | HASH |
| 19 | HASH GROUP BY | | 2758 | 66192 | 5772 (2)| 00:01:10 | | | Q1,00 | PCWP | |
| 20 | PX BLOCK ITERATOR | | 394K| 9242K| 5770 (2)| 00:01:10 | 1 | 4 | Q1,00 | PCWC | |
|* 21 | TABLE ACCESS FULL | TEST2 | 394K| 9242K| 5770 (2)| 00:01:10 | 1 | 4 | Q1,00 | PCWP | |
-----------------------------------------------------------------------------------------------------------------------------------------------

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

6 - filter("SHOP_CODE"='1000' OR GROUPING_ID(BIN_TO_NUM(SYS_OP_GROUPING("SHOP_CODE",1,0,SYS_OP_BITVEC)))=1)
21 - filter("STARTING_DATE"<='20110331' AND SUBSTR("STARTING_DATE",1,6)<='201103' AND SUBSTR("STARTING_DATE",1,6)>='201101' AND
"STARTING_DATE">='20110101')

Note
-----
- automatic DOP: Computed Degree of Parallelism is 16


統計
----------------------------------------------------------
96 recursive calls
0 db block gets
304109 consistent gets
302556 physical reads
0 redo size
871 bytes sent via SQL*Net to client
520 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
65 sorts (memory)
0 sorts (disk)
8 rows processed


今一度、最初のイケてないクエリと比較。あ〜、ダメダメだ〜。やっぱり無駄が多すぎですね!

  1  SELECT
2 month
3 ,shop_code
4 ,sales_figure
5 FROM
6 (
7 SELECT
8 SUBSTR(starting_date,1,6) AS month
9 ,shop_code
10 ,SUM(sales_figure) AS sales_figure
11 FROM
12 test2
13 WHERE
14 starting_date BETWEEN '20110101' AND '20110331'
15 AND shop_code = '1000'
16 GROUP BY
17 SUBSTR(starting_date,1,6)
18 ,shop_code
19 UNION ALL
20 SELECT
21 CASE
22 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
23 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
24 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
25 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
26 END AS month
27 ,shop_code
28 ,SUM(sales_figure) AS salles_figure
29 FROM
30 test2
31 WHERE
32 starting_date BETWEEN '20110101' AND '20110331'
33 AND shop_code = '1000'
34 GROUP BY
35 CASE
36 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
37 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
38 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
39 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
40 END
41 ,shop_code
42 UNION ALL
43 SELECT
44 SUBSTR(starting_date,1,6) AS month
45 ,'ALL ' AS shop_code
46 ,SUM(sales_figure) AS sales_figure
47 FROM
48 test2
49 WHERE
50 starting_date BETWEEN '20110101' AND '20110331'
51 GROUP BY
52 SUBSTR(starting_date,1,6)
53 UNION ALL
54 SELECT
55 CASE
56 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
57 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
58 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
59 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
60 END AS month
61 ,'ALL ' AS shop_code
62 ,SUM(sales_figure) AS salles_figure
63 FROM
64 test2
65 WHERE
66 starting_date BETWEEN '20110101' AND '20110331'
67 GROUP BY
68 CASE
69 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
70 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
71 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
72 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
73 END
74 )
75 ORDER BY
76 month
77* ,shop_code

8行が選択されました。

経過: 00:00:20.92

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

-------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |Pstart| Pstop|
-------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 22 | 726 | 166K (2)| 00:33:18 | | |
| 1 | SORT ORDER BY | | 22 | 726 | 166K (2)| 00:33:18 | | |
| 2 | VIEW | | 22 | 726 | 166K (2)| 00:33:18 | | |
| 3 | UNION-ALL | | | | | | | |
| 4 | HASH GROUP BY | | 3 | 72 | 43 (3)| 00:00:01 | | |
| 5 | PARTITION HASH ALL | | 303 | 7272 | 42 (0)| 00:00:01 | 1 | 4 |
| 6 | TABLE ACCESS BY LOCAL INDEX ROWID| TEST2 | 303 | 7272 | 42 (0)| 00:00:01 | 1 | 4 |
|* 7 | INDEX RANGE SCAN | PK_TEST2 | 303 | | 19 (0)| 00:00:01 | 1 | 4 |
| 8 | HASH GROUP BY | | 7 | 119 | 43 (3)| 00:00:01 | | |
| 9 | PARTITION HASH ALL | | 303 | 5151 | 42 (0)| 00:00:01 | 1 | 4 |
| 10 | TABLE ACCESS BY LOCAL INDEX ROWID| TEST2 | 303 | 5151 | 42 (0)| 00:00:01 | 1 | 4 |
|* 11 | INDEX RANGE SCAN | PK_TEST2 | 303 | | 19 (0)| 00:00:01 | 1 | 4 |
| 12 | HASH GROUP BY | | 3 | 57 | 83172 (2)| 00:16:39 | | |
| 13 | PARTITION HASH ALL | | 394K| 7316K| 83161 (2)| 00:16:38 | 1 | 4 |
|* 14 | TABLE ACCESS FULL | TEST2 | 394K| 7316K| 83161 (2)| 00:16:38 | 1 | 4 |
| 15 | HASH GROUP BY | | 9 | 108 | 83172 (2)| 00:16:39 | | |
| 16 | PARTITION HASH ALL | | 394K| 4621K| 83161 (2)| 00:16:38 | 1 | 4 |
|* 17 | TABLE ACCESS FULL | TEST2 | 394K| 4621K| 83161 (2)| 00:16:38 | 1 | 4 |
-------------------------------------------------------------------------------------------------------------------

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

7 - access("SHOP_CODE"='1000' AND "STARTING_DATE">='20110101' AND "STARTING_DATE"<='20110331')
filter(SUBSTR("STARTING_DATE",1,6)<='201103' AND SUBSTR("STARTING_DATE",1,6)>='201101')
11 - access("SHOP_CODE"='1000' AND "STARTING_DATE">='20110101' AND "STARTING_DATE"<='20110331')
filter(SUBSTR("STARTING_DATE",1,6)<='201103' AND SUBSTR("STARTING_DATE",1,6)>='201101')
14 - filter("STARTING_DATE"<='20110331' AND SUBSTR("STARTING_DATE",1,6)<='201103' AND
SUBSTR("STARTING_DATE",1,6)>='201101' AND "STARTING_DATE">='20110101')
17 - filter("STARTING_DATE"<='20110331' AND SUBSTR("STARTING_DATE",1,6)<='201103' AND
SUBSTR("STARTING_DATE",1,6)>='201101' AND "STARTING_DATE">='20110101')


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

前回の結果なのでここには載せませんが、GROUPSING SETSで書き換えた場合や、WITH句+UNION ALLで書き換えた場合でも一時表への書き込み及び一時表からの読み込みは複数回発生し、それがボディーブローのように効いていたんよね。:)

これもお約束、そもそもパーティションタイプが月単位のレンジパーティションだったら〜(タラレバw)どんな結果になるか。

キター、シリアル実行でも4秒台、4秒切りそうな勢いです:)
月単位のレンジパーティションにより無駄なブロックを読み込む必要がなくなったことが効いてますね。

  1  SELECT
2 CASE
3 WHEN grouping_id(month) = 1 THEN
4 CASE
5 WHEN SUBSTR(MIN(month),5,2)||SUBSTR(MAX(month),5,2) = '0406' THEN 'Q1'
6 WHEN SUBSTR(MIN(month),5,2)||SUBSTR(MAX(month),5,2) = '0709' THEN 'Q2'
7 WHEN SUBSTR(MIN(month),5,2)||SUBSTR(MAX(month),5,2) = '1012' THEN 'Q3'
8 WHEN SUBSTR(MIN(month),5,2)||SUBSTR(MAX(month),5,2) = '0103' THEN 'Q4'
9 END
10 ELSE month
11 END AS month
12 ,CASE
13 WHEN grouping_id(shop_code) = 1 THEN 'ALL'
14 ELSE shop_code
15 END AS shop_code
16 ,SUM(sales_figure) AS sales_figure
17 FROM
18 (
19 SELECT
20 SUBSTR(starting_date,1,6) AS month
21 ,shop_code
22 ,SUM(sales_figure) AS sales_figure
23 FROM
24 test3
25 WHERE
26 starting_date BETWEEN '20110101' AND '20110331'
27 GROUP BY
28 SUBSTR(starting_date,1,6)
29 ,shop_code
30 )
31 GROUP BY
32 CUBE(month,shop_code)
33 HAVING
34 shop_code = '1000' OR
35 grouping_id(shop_code) = 1
36 ORDER BY
37 month
38* ,shop_code

8行が選択されました。

経過: 00:00:04.04

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

-----------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 32 | | 52177 (2)| 00:10:27 | | |
| 1 | SORT ORDER BY | | 1 | 32 | | 52177 (2)| 00:10:27 | | |
|* 2 | FILTER | | | | | | | | |
| 3 | SORT GROUP BY | | 1 | 32 | | 52177 (2)| 00:10:27 | | |
| 4 | GENERATE CUBE | | 1 | 32 | | 52177 (2)| 00:10:27 | | |
| 5 | SORT GROUP BY | | 1 | 32 | | 52177 (2)| 00:10:27 | | |
| 6 | VIEW | | 80894 | 2527K| | 52171 (2)| 00:10:27 | | |
| 7 | HASH GROUP BY | | 80894 | 1342K| 428M| 52171 (2)| 00:10:27 | | |
| 8 | PARTITION RANGE ITERATOR | | 15M| 258M| | 20630 (2)| 00:04:08 | 1 | 3 |
|* 9 | TABLE ACCESS FULL | TEST3 | 15M| 258M| | 20630 (2)| 00:04:08 | 1 | 3 |
-----------------------------------------------------------------------------------------------------------------

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

2 - filter("SHOP_CODE"='1000' OR GROUPING_ID(BIN_TO_NUM(SYS_OP_GROUPING("SHOP_CODE",1,0,SYS_OP_BITVEC)
))=1)
9 - filter("STARTING_DATE"<='20110331' AND "STARTING_DATE">='20110101')


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

これまた恒例になった、オラクル任せのパラレル処理〜〜。ついにでました、1秒切りそうな処理時間w

  1  SELECT /*+ PARALLEL */
2 CASE
3 WHEN grouping_id(month) = 1 THEN
4 CASE
5 WHEN SUBSTR(MIN(month),5,2)||SUBSTR(MAX(month),5,2) = '0406' THEN 'Q1'
6 WHEN SUBSTR(MIN(month),5,2)||SUBSTR(MAX(month),5,2) = '0709' THEN 'Q2'
7 WHEN SUBSTR(MIN(month),5,2)||SUBSTR(MAX(month),5,2) = '1012' THEN 'Q3'
8 WHEN SUBSTR(MIN(month),5,2)||SUBSTR(MAX(month),5,2) = '0103' THEN 'Q4'
9 END
10 ELSE month
11 END AS month
12 ,CASE
13 WHEN grouping_id(shop_code) = 1 THEN 'ALL'
14 ELSE shop_code
15 END AS shop_code
16 ,SUM(sales_figure) AS sales_figure
17 FROM
18 (
19 SELECT
20 SUBSTR(starting_date,1,6) AS month
21 ,shop_code
22 ,SUM(sales_figure) AS sales_figure
23 FROM
24 test3
25 WHERE
26 starting_date BETWEEN '20110101' AND '20110331'
27 GROUP BY
28 SUBSTR(starting_date,1,6)
29 ,shop_code
30 )
31 GROUP BY
32 CUBE(month,shop_code)
33 HAVING
34 shop_code = '1000' OR
35 grouping_id(shop_code) = 1
36 ORDER BY
37 month
38* ,shop_code

8行が選択されました。

経過: 00:00:01.09

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

-------------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Pstart| Pstop | TQ |IN-OUT| PQ Distrib |
-------------------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 32 | | 13834 (2)| 00:02:47 | | | | | |
| 1 | PX COORDINATOR | | | | | | | | | | | |
| 2 | PX SEND QC (ORDER) | :TQ10004 | 1 | 32 | | 13834 (2)| 00:02:47 | | | Q1,04 | P->S | QC (ORDER) |
| 3 | SORT ORDER BY | | 1 | 32 | | 13834 (2)| 00:02:47 | | | Q1,04 | PCWP | |
| 4 | PX RECEIVE | | 1 | 32 | | 13834 (2)| 00:02:47 | | | Q1,04 | PCWP | |
| 5 | PX SEND RANGE | :TQ10003 | 1 | 32 | | 13834 (2)| 00:02:47 | | | Q1,03 | P->P | RANGE |
|* 6 | FILTER | | | | | | | | | Q1,03 | PCWC | |
| 7 | SORT GROUP BY | | 1 | 32 | | 13834 (2)| 00:02:47 | | | Q1,03 | PCWP | |
| 8 | PX RECEIVE | | 1 | 32 | | 13834 (2)| 00:02:47 | | | Q1,03 | PCWP | |
| 9 | PX SEND HASH | :TQ10002 | 1 | 32 | | 13834 (2)| 00:02:47 | | | Q1,02 | P->P | HASH |
| 10 | GENERATE CUBE | | 1 | 32 | | 13834 (2)| 00:02:47 | | | Q1,02 | PCWC | |
| 11 | SORT GROUP BY | | 1 | 32 | | 13834 (2)| 00:02:47 | | | Q1,02 | PCWP | |
| 12 | PX RECEIVE | | 1 | 32 | | 13834 (2)| 00:02:47 | | | Q1,02 | PCWP | |
| 13 | PX SEND HASH | :TQ10001 | 1 | 32 | | 13834 (2)| 00:02:47 | | | Q1,01 | P->P | HASH |
| 14 | SORT GROUP BY | | 1 | 32 | | 13834 (2)| 00:02:47 | | | Q1,01 | PCWP | |
| 15 | VIEW | | 80894 | 2527K| | 13831 (2)| 00:02:46 | | | Q1,01 | PCWP | |
| 16 | HASH GROUP BY | | 80894 | 1342K| 428M| 13831 (2)| 00:02:46 | | | Q1,01 | PCWP | |
| 17 | PX RECEIVE | | 80894 | 1342K| | 13831 (2)| 00:02:46 | | | Q1,01 | PCWP | |
| 18 | PX SEND HASH | :TQ10000 | 80894 | 1342K| | 13831 (2)| 00:02:46 | | | Q1,00 | P->P | HASH |
| 19 | HASH GROUP BY | | 80894 | 1342K| 428M| 13831 (2)| 00:02:46 | | | Q1,00 | PCWP | |
| 20 | PX BLOCK ITERATOR | | 15M| 258M| | 5726 (2)| 00:01:09 | 1 | 3 | Q1,00 | PCWC | |
|* 21 | TABLE ACCESS FULL | TEST3 | 15M| 258M| | 5726 (2)| 00:01:09 | 1 | 3 | Q1,00 | PCWP | |
-------------------------------------------------------------------------------------------------------------------------------------------------------

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

6 - filter("SHOP_CODE"='1000' OR GROUPING_ID(BIN_TO_NUM(SYS_OP_GROUPING("SHOP_CODE",1,0,SYS_OP_BITVEC)))=1)
21 - filter("STARTING_DATE"<='20110331' AND "STARTING_DATE">='20110101')

Note
-----
- automatic DOP: Computed Degree of Parallelism is 4


統計
----------------------------------------------------------
24 recursive calls
0 db block gets
74326 consistent gets
73923 physical reads
0 redo size
871 bytes sent via SQL*Net to client
520 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
17 sorts (memory)
0 sorts (disk)
8 rows processed


以上、いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング 無事に終了。(おわらすのか〜w)


おまけのおまけまでチューニングしハッシュパーティションのままでも6秒台までになることは確認しましたが、実際に提示したクエリは、UNION ALLで記述された無駄の多いダメダメクエリをWITH句とUNION ALLに書き換えた8秒台の結果となるクエリでした。

なぜ、今回のおまけのおまけで載せた最速クエリを提示しなかったか。。。。。その理由は・・・・・・、8秒台であればユーザと調整可能なレベルだったから、元クエリを書いた方に書き換え方を伝えやすかった(UNION ALLを残しつつチューニング)という、これまた大人の事情が絡んでいます;)。

いま以上にチューニングできるとはわかっていても、余力を残しつつ許容できるレベルでチューニングを止めるという”チューニングのゴール”は大切ですよ。(チューニングに割ける時間もお金も限りがありますから)

8秒台のクエリもデータ量や処理量が増加した際、再び問題視される時期が来るかもしれません。その時は、GROUP BY+CUBEにすれば喜ばれますよ。月単位のレンジパーティションに切り替えてあげるだけでも喜ばれますよ(より工数掛かりますが)。チューニング余地は見切れていたほうがいいですよね。


追記、なにか忘れてるー、と思ったらPIVOTの事すっかり忘れてたw。どうしようw

さて、次のネタのキーワードは、「悩ませ過ぎは及ばざるがごとし」w にしようかな。




これまでのあらずじ…

いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #1
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #2
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #3
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #4
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #5
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #6
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #7 おまけ

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

2011年8月 2日 (火)

いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング7 おまけ

前回、思いつきでGROUPING SETSに書き換えたら予想外に結果が悪かったわけですが、よーく考えたら事前にもうちょっとサマってしまえばいけるんじゃないかと気づいて少々書き換えてみました、
悲惨だった前回の結果に比べたら随分改善しましたが、それでも元に戻った程度ですね。(^^;;;;;;

やはり、いくない。

以下、前回、一番処理時間が長くなったケース。単純にGROUPING SETSに書き換えた場合、元のクエリより遅い。アクセスブロック数は3/4程度なんですがね。処理時間がねぇ〜

 1  SELECT
2 CASE
3 WHEN quarter IS NULL THEN month
4 ELSE quarter
5 END AS month
6 ,CASE
7 WHEN grouping_id = 1 THEN 'ALL'
8 ELSE shop_code
9 END AS shop_code
10 ,sales_figure
11 FROM (
12 SELECT
13 grouping_id(shop_code) as grouping_id
14 ,quarter
15 ,month
16 ,shop_code
17 ,SUM(sales_figure) AS sales_figure
18 FROM
19 (
20 SELECT
21 CASE
22 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
23 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
24 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
25 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
26 END AS quarter
27 ,SUBSTR(starting_date,1,6) AS month
28 ,shop_code
29 ,sales_figure
30 FROM
31 test2
32 WHERE
33 SUBSTR(starting_date,1,6) BETWEEN '201101' AND '201103'
34 )
35 GROUP BY GROUPING SETS (
36 (month, shop_code),
37 (quarter, shop_code),
38 (month),
39 (quarter)
40 )
41 )
42 WHERE
43 shop_code = '1000'
44 OR grouping_id = 1
45 ORDER BY
46 month
47* ,shop_code

8行が選択されました。

経過: 00:00:34.88

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

------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Pstart| Pstop |
------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 711K| 33M| | 93048 (3)| 00:18:37 | | |
| 1 | SORT ORDER BY | | 711K| 33M| 43M| 93048 (3)| 00:18:37 | | |
| 2 | VIEW | | 711K| 33M| | 84288 (3)| 00:16:52 | | |
| 3 | TEMP TABLE TRANSFORMATION | | | | | | | | |
| 4 | LOAD AS SELECT | SYS_TEMP_0FD9D6636_4A53D793 | | | | | | | |
| 5 | PARTITION HASH ALL | | 15M| 351M| | 84280 (3)| 00:16:52 | 1 | 4 |
|* 6 | TABLE ACCESS FULL | TEST2 | 15M| 351M| | 84280 (3)| 00:16:52 | 1 | 4 |
| 7 | LOAD AS SELECT | SYS_TEMP_0FD9D6637_4A53D793 | | | | | | | |
| 8 | SORT GROUP BY ROLLUP | | 1 | 33 | | 3 (34)| 00:00:01 | | |
| 9 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6636_4A53D793 | 1 | 33 | | 2 (0)| 00:00:01 | | |
| 10 | LOAD AS SELECT | SYS_TEMP_0FD9D6637_4A53D793 | | | | | | | |
| 11 | SORT GROUP BY ROLLUP | | 1 | 23 | | 3 (34)| 00:00:01 | | |
| 12 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6636_4A53D793 | 1 | 23 | | 2 (0)| 00:00:01 | | |
| 13 | VIEW | | 1 | 50 | | 2 (0)| 00:00:01 | | |
|* 14 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6637_4A53D793 | 1 | 50 | | 2 (0)| 00:00:01 | | |
------------------------------------------------------------------------------------------------------------------------------------

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

6 - filter(SUBSTR("STARTING_DATE",1,6)<='201103' AND SUBSTR("STARTING_DATE",1,6)>='201101')
14 - filter("SYS_TEMP_0FD9D6637_4A53D793"."C1"='1000' OR
BIN_TO_NUM(SYS_OP_VECBIT(SYS_OP_NUMTORAW("SYS_TEMP_0FD9D6637_4A53D793"."D0"),1))=1)


統計
----------------------------------------------------------
761 recursive calls
52432 db block gets
405879 consistent gets
405750 physical reads
1944 redo size
871 bytes sent via SQL*Net to client
520 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
3 sorts (memory)
0 sorts (disk)
8 rows processed

意外な結果になってしまったので、WITH句は利用していないのですが今回試したのは事前にある程度サマったケース。
コードサイズも一番小さくて可読性もよいと思うんだけどね〜。
アクセスしているブロック数は1/2でいい感じなのですが、処理時間は今回ネタにしたクエリ(後述)より少々良い程度。ブロック数は半減したのに処理時間がさほど改善していないのはCPUへの負荷が考えていた以上に高いのでしょうかね。(それはまた別途しらべておきますか)

  1  SELECT
2 CASE
3 WHEN quarter IS NULL THEN month
4 ELSE quarter
5 END AS month
6 ,CASE
7 WHEN grouping_id(shop_code) = 1 THEN 'ALL'
8 ELSE shop_code
9 END AS shop_code
10 ,SUM(sales_figure) AS sales_figure
11 FROM
12 (
13 SELECT
14 CASE
15 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
16 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
17 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
18 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
19 END AS quarter
20 ,SUBSTR(starting_date,1,6) AS month
21 ,shop_code
22 ,SUM(sales_figure) AS sales_figure
23 FROM
24 test2
25 WHERE
26 starting_date BETWEEN '20110101' AND '20110331'
27 GROUP BY
28 CASE
29 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
30 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
31 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
32 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
33 END
34 ,SUBSTR(starting_date,1,6)
35 ,shop_code
36 )
37 GROUP BY GROUPING SETS (
38 (month, shop_code),
39 (quarter, shop_code),
40 (month),
41 (quarter)
42 )
43 HAVING
44 shop_code = '1000'
45 OR grouping_id(shop_code) = 1
46 ORDER BY
47 month
48* ,shop_code

8行が選択されました。

経過: 00:00:19.69

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

--------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
--------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2758 | 134K| 83181 (2)| 00:16:39 | | |
| 1 | TEMP TABLE TRANSFORMATION | | | | | | | |
| 2 | LOAD AS SELECT | SYS_TEMP_0FD9D66D1_4A54AE09 | | | | | | |
| 3 | HASH GROUP BY | | 17550 | 411K| 83172 (2)| 00:16:39 | | |
| 4 | PARTITION HASH ALL | | 394K| 9242K| 83161 (2)| 00:16:38 | 1 | 4 |
|* 5 | TABLE ACCESS FULL | TEST2 | 394K| 9242K| 83161 (2)| 00:16:38 | 1 | 4 |
| 6 | LOAD AS SELECT | SYS_TEMP_0FD9D66D2_4A54AE09 | | | | | | |
| 7 | SORT GROUP BY ROLLUP | | 1 | 33 | 3 (34)| 00:00:01 | | |
| 8 | TABLE ACCESS FULL | SYS_TEMP_0FD9D66D1_4A54AE09 | 1 | 33 | 2 (0)| 00:00:01 | | |
| 9 | LOAD AS SELECT | SYS_TEMP_0FD9D66D2_4A54AE09 | | | | | | |
| 10 | SORT GROUP BY ROLLUP | | 1 | 23 | 3 (34)| 00:00:01 | | |
| 11 | TABLE ACCESS FULL | SYS_TEMP_0FD9D66D1_4A54AE09 | 1 | 23 | 2 (0)| 00:00:01 | | |
| 12 | SORT ORDER BY | | 2758 | 134K| 3 (34)| 00:00:01 | | |
| 13 | VIEW | | 1 | 50 | 2 (0)| 00:00:01 | | |
|* 14 | TABLE ACCESS FULL | SYS_TEMP_0FD9D66D2_4A54AE09 | 1 | 50 | 2 (0)| 00:00:01 | | |
--------------------------------------------------------------------------------------------------------------------------

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

5 - filter("SYS_TBL_$2$"."STARTING_DATE"<='20110331' AND SUBSTR("STARTING_DATE",1,6)<='201103' AND
SUBSTR("STARTING_DATE",1,6)>='201101' AND "SYS_TBL_$2$"."STARTING_DATE">='20110101')
14 - filter("SYS_TEMP_0FD9D66D2_4A54AE09"."C1"='1000' OR
BIN_TO_NUM(SYS_OP_VECBIT(SYS_OP_NUMTORAW("SYS_TEMP_0FD9D66D2_4A54AE09"."D0"),1))=1)


統計
----------------------------------------------------------
358 recursive calls
52 db block gets
302715 consistent gets
302587 physical reads
1764 redo size
871 bytes sent via SQL*Net to client
519 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
3 sorts (memory)
0 sorts (disk)
8 rows processed


前々回、WITH句とunion allだけでチューニングしたクエリと比べてみるとブロック数は同じ程度でもCPUコストの差は実行計画からもある程度読み取れますね(参考程度ですがw)。

  1  WITH
2 t01 AS
3 (
4 SELECT
5 SUBSTR(starting_date,1,6) AS month
6 ,CASE
7 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
8 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
9 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
10 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
11 END AS quarter
12 ,shop_code
13 ,SUM(sales_figure) as sales_figure
14 FROM
15 test2
16 WHERE
17 starting_date BETWEEN '20110101' AND '20110331'
18 GROUP BY
19 SUBSTR(starting_date,1,6)
20 ,SUBSTR(starting_date,5,2)
21 ,shop_code
22 ),
23 t02 AS
24 (
25 SELECT
26 month
27 ,quarter
28 ,shop_code
29 ,sales_figure
30 FROM
31 t01
32 WHERE
33 month BETWEEN '201101' AND '201103'
34 AND shop_code = '1000'
35 )
36 SELECT
37 month
38 ,shop_code
39 ,sales_figure
40 FROM
41 (
42 SELECT
43 month
44 ,shop_code
45 ,SUM(sales_figure) AS sales_figure
46 FROM
47 t02
48 GROUP BY
49 month
50 ,shop_code
51 UNION ALL
52 SELECT
53 quarter AS month
54 ,shop_code
55 ,SUM(sales_figure) AS sales_figure
56 FROM
57 t02
58 GROUP BY
59 quarter
60 ,shop_code
61 UNION ALL
62 SELECT
63 month
64 ,'ALL ' AS shop_code
65 ,SUM(sales_figure) AS sales_figure
66 FROM
67 t01
68 GROUP BY
69 month
70 UNION ALL
71 SELECT
72 quarter AS month
73 ,'ALL ' AS shop_code
74 ,SUM(sales_figure) AS salles_figure
75 FROM
76 t01
77 GROUP BY
78 quarter
79 )
80 ORDER BY
81 month
82* ,shop_code

8行が選択されました。

経過: 00:00:08.35

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

--------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
--------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3692 | 118K| 83282 (2)| 00:16:40 | | |
| 1 | TEMP TABLE TRANSFORMATION | | | | | | | |
| 2 | LOAD AS SELECT | SYS_TEMP_0FD9D660A_4A53712A | | | | | | |
| 3 | HASH GROUP BY | | 17550 | 411K| 83172 (2)| 00:16:39 | | |
| 4 | PARTITION HASH ALL | | 394K| 9242K| 83161 (2)| 00:16:38 | 1 | 4 |
|* 5 | TABLE ACCESS FULL | TEST2 | 394K| 9242K| 83161 (2)| 00:16:38 | 1 | 4 |
| 6 | LOAD AS SELECT | SYS_TEMP_0FD9D660B_4A53712A | | | | | | |
|* 7 | VIEW | | 17550 | 497K| 17 (0)| 00:00:01 | | |
| 8 | TABLE ACCESS FULL | SYS_TEMP_0FD9D660A_4A53712A | 17550 | 411K| 17 (0)| 00:00:01 | | |
| 9 | SORT ORDER BY | | 3692 | 118K| 93 (8)| 00:00:02 | | |
| 10 | VIEW | | 3692 | 118K| 92 (7)| 00:00:02 | | |
| 11 | UNION-ALL | | | | | | | |
| 12 | HASH GROUP BY | | 2758 | 68950 | 27 (4)| 00:00:01 | | |
| 13 | VIEW | | 17550 | 428K| 26 (0)| 00:00:01 | | |
| 14 | TABLE ACCESS FULL | SYS_TEMP_0FD9D660B_4A53712A | 17550 | 497K| 26 (0)| 00:00:01 | | |
| 15 | HASH GROUP BY | | 920 | 20240 | 27 (4)| 00:00:01 | | |
| 16 | VIEW | | 17550 | 377K| 26 (0)| 00:00:01 | | |
| 17 | TABLE ACCESS FULL | SYS_TEMP_0FD9D660B_4A53712A | 17550 | 497K| 26 (0)| 00:00:01 | | |
| 18 | HASH GROUP BY | | 13 | 260 | 18 (6)| 00:00:01 | | |
| 19 | VIEW | | 17550 | 342K| 17 (0)| 00:00:01 | | |
| 20 | TABLE ACCESS FULL | SYS_TEMP_0FD9D660A_4A53712A | 17550 | 411K| 17 (0)| 00:00:01 | | |
| 21 | HASH GROUP BY | | 1 | 17 | 18 (6)| 00:00:01 | | |
| 22 | VIEW | | 17550 | 291K| 17 (0)| 00:00:01 | | |
| 23 | TABLE ACCESS FULL | SYS_TEMP_0FD9D660A_4A53712A | 17550 | 411K| 17 (0)| 00:00:01 | | |
--------------------------------------------------------------------------------------------------------------------------

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

5 - filter("STARTING_DATE"<='20110331' AND SUBSTR("STARTING_DATE",1,6)<='201103' AND
SUBSTR("STARTING_DATE",1,6)>='201101' AND "STARTING_DATE">='20110101')
7 - filter("MONTH">='201101' AND "MONTH"<='201103' AND "SHOP_CODE"='1000')


統計
----------------------------------------------------------
4 recursive calls
28 db block gets
302642 consistent gets
302570 physical reads
1156 redo size
875 bytes sent via SQL*Net to client
519 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
8 rows processed


ということで、おまけはおしまい、次回へつづく。(どーやってまとめんるんだこれw)

追記(2011/8/3)
次回は、今回チューニングしたGROUPING SETSを利用した文をさらにチューニングするというおまけのおまけ。(ほんと、どーやってまとめんだこれ!)


これまでのあらずじ…

いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #1
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #2
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #3
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #4
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #5
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #6


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

2011年7月24日 (日)

いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #6

2011/7/28追記
GROUPING SETSで異常に遅かったのは使い方わるいなーと m(_ _)m WITH句で一時表1つ使うのと同じぐらいにはなるかもね。と(次回書きます)

前回のつづきでーす。

前回のは、元々がイケテない、かつ、大人の事情でパーティションタイプ変更とか、マテリアライズドビューとか、パラレルクエリーもだーめー。と言いつつ、なんとか、できる限り早くしてくれーという、匂いの元を絶たないで別の匂いでごまかしてくれー的なことだったのですが、いろいろと考えた末、WITH句を使えばなんとかいい感じになりました。でも、元が元だけにほぼ限界ですね。

大人の事情で男らしくずばーっとパーティションタイプ変更することができないとか言ってた方々も、この結果をもって、交渉のテーブルにつけるんじゃないかな。検討祈る :)
この結果で納得してもらえるかは感知しませんからねw 最後の手としてパーティションタイプ変更を残しておいてもいいいと思いますし。;)

ということで、今回は、他の方法との比較編。

前回、grouping setsでもいけそーな感じがすると書いたのですが、grouping setsの場合裏の動きはOracleにお任せなので…

まあ、見てみましょう。


最初は、シンプルにgrouping setsに置き換えて見ました。(深く考えてないので余計な部分はまだまだありそうですがw )

実行計画と処理時間、アクセスブロック数みて少々意外な結果に驚いたんですよ。 

ダメダメだった元のクエリより処理時間は長くなってる><。 アクセスしているブロック数は随分少なくなっているんですが。CPUへの負荷が高いのか(詳細は別途調べるとして)とにかく前より遅くなっちゃNGです。ただ、クエリ自体は随分読みやすい :)

grouping setsで置き換えた今回のクエリ、実行計画を見るとWITH句で書き換えたときのようものにかなり似ていますが、WITH句で書き換えた場合と大きく異なるのは内部で一時表に置き換えられるタイミングにありそうですね。WITH句は本体のクエリとは別で、しかも一番先に実行されますが、以下の実行計画をみると違いますよね。WITH句で書き換えた実行計画と比較するとその違いがよくわかります。

  1  SELECT
2 CASE
3 WHEN quarter IS NULL THEN month
4 ELSE quarter
5 END AS month
6 ,CASE
7 WHEN grouping_id = 1 THEN 'ALL'
8 ELSE shop_code
9 END AS shop_code
10 ,sales_figure
11 FROM (
12 SELECT
13 grouping_id(shop_code) as grouping_id
14 ,quarter
15 ,month
16 ,shop_code
17 ,SUM(sales_figure) AS sales_figure
18 FROM
19 (
20 SELECT
21 CASE
22 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
23 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
24 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
25 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
26 END AS quarter
27 ,SUBSTR(starting_date,1,6) AS month
28 ,shop_code
29 ,sales_figure
30 FROM
31 test2
32 WHERE
33 SUBSTR(starting_date,1,6) BETWEEN '201101' AND '201103'
34 )
35 GROUP BY GROUPING SETS (
36 (month, shop_code),
37 (quarter, shop_code),
38 (month),
39 (quarter)
40 )
41 )
42 WHERE
43 shop_code = '1000'
44 OR grouping_id = 1
45 ORDER BY
46 month
47* ,shop_code

8行が選択されました。

経過: 00:00:34.88

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

------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Pstart| Pstop |
------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 711K| 33M| | 93048 (3)| 00:18:37 | | |
| 1 | SORT ORDER BY | | 711K| 33M| 43M| 93048 (3)| 00:18:37 | | |
| 2 | VIEW | | 711K| 33M| | 84288 (3)| 00:16:52 | | |
| 3 | TEMP TABLE TRANSFORMATION | | | | | | | | |
| 4 | LOAD AS SELECT | SYS_TEMP_0FD9D6636_4A53D793 | | | | | | | |
| 5 | PARTITION HASH ALL | | 15M| 351M| | 84280 (3)| 00:16:52 | 1 | 4 |
|* 6 | TABLE ACCESS FULL | TEST2 | 15M| 351M| | 84280 (3)| 00:16:52 | 1 | 4 |
| 7 | LOAD AS SELECT | SYS_TEMP_0FD9D6637_4A53D793 | | | | | | | |
| 8 | SORT GROUP BY ROLLUP | | 1 | 33 | | 3 (34)| 00:00:01 | | |
| 9 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6636_4A53D793 | 1 | 33 | | 2 (0)| 00:00:01 | | |
| 10 | LOAD AS SELECT | SYS_TEMP_0FD9D6637_4A53D793 | | | | | | | |
| 11 | SORT GROUP BY ROLLUP | | 1 | 23 | | 3 (34)| 00:00:01 | | |
| 12 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6636_4A53D793 | 1 | 23 | | 2 (0)| 00:00:01 | | |
| 13 | VIEW | | 1 | 50 | | 2 (0)| 00:00:01 | | |
|* 14 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6637_4A53D793 | 1 | 50 | | 2 (0)| 00:00:01 | | |
------------------------------------------------------------------------------------------------------------------------------------

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

6 - filter(SUBSTR("STARTING_DATE",1,6)<='201103' AND SUBSTR("STARTING_DATE",1,6)>='201101')
14 - filter("SYS_TEMP_0FD9D6637_4A53D793"."C1"='1000' OR
BIN_TO_NUM(SYS_OP_VECBIT(SYS_OP_NUMTORAW("SYS_TEMP_0FD9D6637_4A53D793"."D0"),1))=1)


統計
----------------------------------------------------------
761 recursive calls
52432 db block gets
405879 consistent gets
405750 physical reads
1944 redo size
871 bytes sent via SQL*Net to client
520 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
3 sorts (memory)
0 sorts (disk)
8 rows processed

GROUPING SETSで内部的に行われる一時表作成の効率が今ひとつなのであれば、やはり明示的にWITH句で宣言してからやったほうがいいのかな〜ということで、WITH句で一番大きな一時表を作ってみます。

お〜〜〜〜〜〜〜〜〜〜っ。随分変りましたねー。

  1  WITH
2 t01 AS
3 (
4 SELECT
5 SUBSTR(starting_date,1,6) AS month
6 ,CASE
7 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
8 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
9 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
10 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
11 END AS quarter
12 ,shop_code
13 ,SUM(sales_figure) as sales_figure
14 FROM
15 test2
16 WHERE
17 starting_date BETWEEN '20110101' AND '20110331'
18 GROUP BY
19  SUBSTR(starting_date,1,6)
20 ,SUBSTR(starting_date,5,2)
21 ,shop_code
22 )
23 SELECT
24 CASE
25 WHEN quarter IS NULL THEN month
26 ELSE quarter
27 END AS month
28 ,CASE
29 WHEN grouping_id = 1 THEN 'ALL'
30 ELSE shop_code
31 END AS shop_code
32 ,sales_figure
33 FROM (
34 SELECT
35 grouping_id(shop_code) as grouping_id
36 ,quarter
37 ,month
38 ,shop_code
39 ,SUM(sales_figure) AS sales_figure
40 FROM
41 t01
42 GROUP BY GROUPING SETS (
43 (month, shop_code),
44 (quarter, shop_code),
45 (month),
46 (quarter)
47 )
48 )
49 WHERE
50 shop_code = '1000'
51 OR grouping_id = 1
52 ORDER BY
53 month
54* ,shop_code

8行が選択されました。

経過: 00:00:11.83

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

----------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
----------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2758 | 134K| 83181 (2)| 00:16:39 | | |
| 1 | SORT ORDER BY | | 2758 | 134K| 83181 (2)| 00:16:39 | | |
| 2 | VIEW | | 2758 | 134K| 83180 (2)| 00:16:39 | | |
| 3 | TEMP TABLE TRANSFORMATION | | | | | | | |
| 4 | LOAD AS SELECT | SYS_TEMP_0FD9D6650_4A53D793 | | | | | | |
| 5 | HASH GROUP BY | | 17550 | 411K| 83172 (2)| 00:16:39 | | |
| 6 | PARTITION HASH ALL | | 394K| 9242K| 83161 (2)| 00:16:38 | 1 | 4 |
|* 7 | TABLE ACCESS FULL | TEST2 | 394K| 9242K| 83161 (2)| 00:16:38 | 1 | 4 |
| 8 | LOAD AS SELECT | SYS_TEMP_0FD9D6651_4A53D793 | | | | | | |
| 9 | SORT GROUP BY ROLLUP | | 1 | 33 | 3 (34)| 00:00:01 | | |
| 10 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6650_4A53D793 | 1 | 33 | 2 (0)| 00:00:01 | | |
| 11 | LOAD AS SELECT | SYS_TEMP_0FD9D6651_4A53D793 | | | | | | |
| 12 | SORT GROUP BY ROLLUP | | 1 | 23 | 3 (34)| 00:00:01 | | |
| 13 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6650_4A53D793 | 1 | 23 | 2 (0)| 00:00:01 | | |
| 14 | VIEW | | 1 | 50 | 2 (0)| 00:00:01 | | |
|* 15 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6651_4A53D793 | 1 | 50 | 2 (0)| 00:00:01 | | |
----------------------------------------------------------------------------------------------------------------------------

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

7 - filter("SYS_TBL_$2$"."STARTING_DATE"<='20110331' AND SUBSTR("STARTING_DATE",1,6)<='201103' AND
SUBSTR("STARTING_DATE",1,6)>='201101' AND "SYS_TBL_$2$"."STARTING_DATE">='20110101')
15 - filter("SYS_TEMP_0FD9D66B3_4A54062A"."C1"='1000' OR
BIN_TO_NUM(SYS_OP_VECBIT(SYS_OP_NUMTORAW("SYS_TEMP_0FD9D66B3_4A54062A"."D0"),1))=1)


統計
----------------------------------------------------------
358 recursive calls
53 db block gets
302715 consistent gets
302589 physical reads
1712 redo size
871 bytes sent via SQL*Net to client
520 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
3 sorts (memory)
0 sorts (disk)
8 rows processed


じゃ、WITH句だけで行ったチューニング同様に2つの一時表を作ることにしましょー :)
WITH句で2つの一時表を準備(本体の表へは1度だけアクセス)、かつ、grouping sets を使いunion allの利用は最小限にしてみました。w

読みやすくなったし、速度的にも WITH句で2つの一時表を使う場合と変らないし、GROUPING SETSだけだと今回のような問題は解決しづらいかもしれませんね、今のところ。
可読性の向上にはかなり効果あるとおもいます、GROUPING SETSって。


  1  WITH
2 t01 AS
3 (
4 SELECT
5 SUBSTR(starting_date,1,6) AS month
6 ,CASE
7 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
8 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
9 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
10 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
11 END AS quarter
12 ,shop_code
13 ,SUM(sales_figure) as sales_figure
14 FROM
15 test2
16 WHERE
17 starting_date BETWEEN '20110101' AND '20110331'
18 GROUP BY
19  SUBSTR(starting_date,1,6)
20 ,SUBSTR(starting_date,5,2)
21 ,shop_code
22 ),
23 t02 AS
24 (
25 SELECT
26 month
27 ,quarter
28 ,shop_code
29 ,sales_figure
30 FROM
31 t01
32 WHERE
33 month BETWEEN '201101' AND '201103'
34 AND shop_code = '1000'
35 )
36 SELECT
37 CASE
38 WHEN quarter IS NULL THEN month
39 ELSE quarter
40 END AS month
41 ,shop_code
42 ,sales_figure
43 FROM (
44 SELECT
45 quarter
46 ,month
47 ,shop_code
48 ,SUM(sales_figure) AS sales_figure
49 FROM
50 t02
51 GROUP BY GROUPING SETS (
52 (month, shop_code),
53 (quarter, shop_code)
54 )
55 UNION ALL
56 SELECT
57 quarter
58 ,month
59 ,'ALL' AS shop_code
60 ,SUM(sales_figure) AS sales_figure
61 FROM
62 t01
63 GROUP BY GROUPING SETS (
64 (month),
65 (quarter)
66 )
67 )
68 ORDER BY
69 month
70* ,shop_code

8行が選択されました。

経過: 00:00:08.34

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

------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 17 | 595 | 83223 (2)| 00:16:39 | | |
| 1 | TEMP TABLE TRANSFORMATION | | | | | | | |
| 2 | LOAD AS SELECT | SYS_TEMP_0FD9D667D_4A53D793 | | | | | | |
| 3 | HASH GROUP BY | | 17550 | 411K| 83172 (2)| 00:16:39 | | |
| 4 | PARTITION HASH ALL | | 394K| 9242K| 83161 (2)| 00:16:38 | 1 | 4 |
|* 5 | TABLE ACCESS FULL | TEST2 | 394K| 9242K| 83161 (2)| 00:16:38 | 1 | 4 |
| 6 | SORT ORDER BY | | 17 | 595 | 51 (10)| 00:00:01 | | |
| 7 | VIEW | | 17 | 595 | 50 (8)| 00:00:01 | | |
| 8 | UNION-ALL | | | | | | | |
| 9 | TEMP TABLE TRANSFORMATION | | | | | | | |
| 10 | LOAD AS SELECT | SYS_TEMP_0FD9D667E_4A53D793 | | | | | | |
|* 11 | TABLE ACCESS FULL | SYS_TEMP_0FD9D667D_4A53D793 | 1 | 24 | 17 (0)| 00:00:01 | | |
| 12 | LOAD AS SELECT | SYS_TEMP_0FD9D667F_4A53D793 | | | | | | |
| 13 | HASH GROUP BY | | 1 | 33 | 3 (34)| 00:00:01 | | |
| 14 | TABLE ACCESS FULL | SYS_TEMP_0FD9D667E_4A53D793 | 1 | 33 | 2 (0)| 00:00:01 | | |
| 15 | LOAD AS SELECT | SYS_TEMP_0FD9D667F_4A53D793 | | | | | | |
| 16 | HASH GROUP BY | | 1 | 23 | 3 (34)| 00:00:01 | | |
| 17 | TABLE ACCESS FULL | SYS_TEMP_0FD9D667E_4A53D793 | 1 | 23 | 2 (0)| 00:00:01 | | |
| 18 | VIEW | | 1 | 37 | 2 (0)| 00:00:01 | | |
| 19 | TABLE ACCESS FULL | SYS_TEMP_0FD9D667F_4A53D793 | 1 | 37 | 2 (0)| 00:00:01 | | |
| 20 | TEMP TABLE TRANSFORMATION | | | | | | | |
| 21 | LOAD AS SELECT | SYS_TEMP_0FD9D6680_4A53D793 | | | | | | |
| 22 | TABLE ACCESS FULL | SYS_TEMP_0FD9D667D_4A53D793 | 17550 | 411K| 17 (0)| 00:00:01 | | |
| 23 | LOAD AS SELECT | SYS_TEMP_0FD9D6681_4A53D793 | | | | | | |
| 24 | HASH GROUP BY | | 1 | 27 | 3 (34)| 00:00:01 | | |
| 25 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6680_4A53D793 | 1 | 27 | 2 (0)| 00:00:01 | | |
| 26 | LOAD AS SELECT | SYS_TEMP_0FD9D6681_4A53D793 | | | | | | |
| 27 | HASH GROUP BY | | 1 | 17 | 3 (34)| 00:00:01 | | |
| 28 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6680_4A53D793 | 1 | 17 | 2 (0)| 00:00:01 | | |
| 29 | VIEW | | 1 | 31 | 2 (0)| 00:00:01 | | |
| 30 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6681_4A53D793 | 1 | 31 | 2 (0)| 00:00:01 | | |
------------------------------------------------------------------------------------------------------------------------------

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

5 - filter("STARTING_DATE"<='20110331' AND SUBSTR("STARTING_DATE",1,6)<='201103' AND
SUBSTR("STARTING_DATE",1,6)>='201101' AND "STARTING_DATE">='20110101')
11 - filter("SYS_TBL_$2$"."C0">='201101' AND "SYS_TBL_$2$"."C0"<='201103' AND "SYS_TBL_$2$"."C2"='1000')


統計
----------------------------------------------------------
790 recursive calls
82 db block gets
302843 consistent gets
302593 physical reads
3964 redo size
871 bytes sent via SQL*Net to client
520 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
8 rows processed

次回は、月毎のレンジパーティションで同じ対処をしたらとこまで改善するのか確認しておきましょう。




これまでのあらずじ…

いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #1
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #2
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #3
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #4
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #5

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

2011年7月22日 (金)

Lionがでたとおもったら、VirtualBox 4.1も出てたのね。

http://www.virtualbox.org/wiki/Changelog

20110722_13624

Lion入れるより先にVirtualBox 4.1にしてみたw

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

2011年7月19日 (火)

いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #5

前回のつづきです。

さて、前回登場したボスキャラなSQL。どう料理しましょーか。
前回は、まずは肩ならし程度のチューニングをしましたがそれほど改善していません。(^^;;;; 元が元ですからね。

このままでは多いく改善することはむーりー、なので構文変更を前提にしましょう。

で、このクエリをよーく見ていたら、店、品目毎の日単位の売上げデータから、店毎の月単位売上げ合計と四半期の売上げ合計、全店の月単位売上げ合計、四半期の売上げ合計を求めるクエリだとわかりました。

本来なら月単位のレンジパーティション表とか、マテリアライズドビューでも用意しておけばすげー簡単な話ではあるんですが、パーティションタイプの変更とかマテリアライズドビューの追加作成とかむーりーと言われてしまう大人の事情が因縁をつけてきますw

でも、じ〜〜〜〜〜〜っと、このSQL文を見ていると改善案が見えてきます。大きな無駄が……………………見えてきますよね。ほーら、ほーらw

  1  SELECT
2 month
3 ,shop_code
4 ,sales_figure
5 FROM
6 (
7 SELECT
8 SUBSTR(starting_date,1,6) AS month
9 ,shop_code
10 ,SUM(sales_figure) AS sales_figure
11 FROM
12 test2
13 WHERE
14 starting_date BETWEEN '20110101' AND '20110331'
15 AND shop_code = '1000'
16 GROUP BY
17 SUBSTR(starting_date,1,6)
18 ,shop_code
19 UNION ALL
20 SELECT
21 CASE
22 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
23 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
24 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
25 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
26 END AS month
27 ,shop_code
28 ,SUM(sales_figure) AS salles_figure
29 FROM
30 test2
31 WHERE
32 starting_date BETWEEN '20110101' AND '20110331'
33 AND shop_code = '1000'
34 GROUP BY
35 CASE
36 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
37 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
38 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
39 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
40 END
41 ,shop_code
42 UNION ALL
43 SELECT
44 SUBSTR(starting_date,1,6) AS month
45 ,'ALL ' AS shop_code
46 ,SUM(sales_figure) AS sales_figure
47 FROM
48 test2
49 WHERE
50 starting_date BETWEEN '20110101' AND '20110331'
51 GROUP BY
52 SUBSTR(starting_date,1,6)
53 UNION ALL
54 SELECT
55 CASE
56 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
57 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
58 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
59 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
60 END AS month
61 ,'ALL ' AS shop_code
62 ,SUM(sales_figure) AS salles_figure
63 FROM
64 test2
65 WHERE
66 starting_date BETWEEN '20110101' AND '20110331'
67 GROUP BY
68 CASE
69 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
70 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
71 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
72 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
73 END
74 )
75 ORDER BY
76 month
77* ,shop_code

8行が選択されました。

経過: 00:00:21.03

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

-------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |Pstart|Pstop |
-------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 22 | 726 | 166K (2)| 00:33:18 | | |
| 1 | SORT ORDER BY | | 22 | 726 | 166K (2)| 00:33:18 | | |
| 2 | VIEW | | 22 | 726 | 166K (2)| 00:33:18 | | |
| 3 | UNION-ALL | | | | | | | |
| 4 | HASH GROUP BY | | 3 | 72 | 43 (3)| 00:00:01 | | |
| 5 | PARTITION HASH ALL | | 303 | 7272 | 42 (0)| 00:00:01 | 1 | 4 |
| 6 | TABLE ACCESS BY LOCAL INDEX ROWID| TEST2 | 303 | 7272 | 42 (0)| 00:00:01 | 1 | 4 |
|* 7 | INDEX RANGE SCAN | PK_TEST2 | 303 | | 19 (0)| 00:00:01 | 1 | 4 |
| 8 | HASH GROUP BY | | 7 | 119 | 43 (3)| 00:00:01 | | |
| 9 | PARTITION HASH ALL | | 303 | 5151 | 42 (0)| 00:00:01 | 1 | 4 |
| 10 | TABLE ACCESS BY LOCAL INDEX ROWID| TEST2 | 303 | 5151 | 42 (0)| 00:00:01 | 1 | 4 |
|* 11 | INDEX RANGE SCAN | PK_TEST2 | 303 | | 19 (0)| 00:00:01 | 1 | 4 |
| 12 | HASH GROUP BY | | 3 | 57 | 83172 (2)| 00:16:39 | | |
| 13 | PARTITION HASH ALL | | 394K| 7316K| 83161 (2)| 00:16:38 | 1 | 4 |
|* 14 | TABLE ACCESS FULL | TEST2 | 394K| 7316K| 83161 (2)| 00:16:38 | 1 | 4 |
| 15 | HASH GROUP BY | | 9 | 108 | 83172 (2)| 00:16:39 | | |
| 16 | PARTITION HASH ALL | | 394K| 4621K| 83161 (2)| 00:16:38 | 1 | 4 |
|* 17 | TABLE ACCESS FULL | TEST2 | 394K| 4621K| 83161 (2)| 00:16:38 | 1 | 4 |
-------------------------------------------------------------------------------------------------------------------

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

7 - access("SHOP_CODE"='1000' AND "STARTING_DATE">='20110101' AND "STARTING_DATE"<='20110331')
filter(SUBSTR("STARTING_DATE",1,6)<='201103' AND SUBSTR("STARTING_DATE",1,6)>='201101')
11 - access("SHOP_CODE"='1000' AND "STARTING_DATE">='20110101' AND "STARTING_DATE"<='20110331')
filter(SUBSTR("STARTING_DATE",1,6)<='201103' AND SUBSTR("STARTING_DATE",1,6)>='201101')
14 - filter("STARTING_DATE"<='20110331' AND SUBSTR("STARTING_DATE",1,6)<='201103' AND
SUBSTR("STARTING_DATE",1,6)>='201101' AND "STARTING_DATE">='20110101')
17 - filter("STARTING_DATE"<='20110331' AND SUBSTR("STARTING_DATE",1,6)<='201103' AND
SUBSTR("STARTING_DATE",1,6)>='201101' AND "STARTING_DATE">='20110101')


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

03:43:16 SCOTT>

見えてきましたよね、無駄が。

このクエリ、同じ検索範囲のデータを対象に4回もTEST2表にアクセスしています。全表走査2回、改善したとはいえ2回の全ローカル索引のレンジスキャンを行っています。無駄なアクセスが多すぎます。
こんなタイプのUNION ALLやUNION繰り返しタイプのクエリを改善できる方法は……

まず浮かんだのは、WITH句を使って各クエリの共通部分をまとめてしまう方法です。

WITH句に記述したクエリは最初に1度実行され結果を一時セグメントに溜め込みます。その後、一時セグメントに溜め込んだ結果を使い回せばいいので巨大な表本体を何度も参照する必要がなくなります。
そうそう、うまく使えば、都度使い捨てのマテリアライズドビュー(例えが良くないですが気にしないでね)みたいな使い方ができます。


書き換えのポイントは一番大きな範囲で集計する共通部分を見つけ出してWITH句にまとめてしまうこと!
あとはその結果をうまく使い回すこと!  それだけです。 SQL文も共通部分をWITH句にまとめたことで随分読みやすくなりましたよね。 直感的に読みやすい訳ではないですがね。:)

以下、書き換えた結果…

  1  WITH
2 t01 AS
3 (
4 SELECT
5 SUBSTR(starting_date,1,6) AS month
6 ,CASE
7 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
8 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
9 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
10 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
11 END AS quarter
12 ,shop_code
13 ,SUM(sales_figure) as sales_figure
14 FROM
15 test2
16 WHERE
17 starting_date BETWEEN '20110101' AND '20110331'
18 GROUP BY
19 SUBSTR(starting_date,1,6)
20 ,SUBSTR(starting_date,5,2)
21 ,shop_code
22 ),
23 t02 AS
24 (
25 SELECT
26 month
27 ,quarter
28 ,shop_code
29 ,sales_figure
30 FROM
31 t01
32 WHERE
33 month BETWEEN '201101' AND '201103'
34 AND shop_code = '1000'
35 )
36 SELECT
37 month
38 ,shop_code
39 ,sales_figure
40 FROM
41 (
42 SELECT
43 month
44 ,shop_code
45 ,SUM(sales_figure) AS sales_figure
46 FROM
47 t02
48 GROUP BY
49 month
50 ,shop_code
51 UNION ALL
52 SELECT
53 quarter AS month
54 ,shop_code
55 ,SUM(sales_figure) AS sales_figure
56 FROM
57 t02
58 GROUP BY
59 quarter
60 ,shop_code
61 UNION ALL
62 SELECT
63 month
64 ,'ALL ' AS shop_code
65 ,SUM(sales_figure) AS sales_figure
66 FROM
67 t01
68 GROUP BY
69 month
70 UNION ALL
71 SELECT
72 quarter AS month
73 ,'ALL ' AS shop_code
74 ,SUM(sales_figure) AS salles_figure
75 FROM
76 t01
77 GROUP BY
78 quarter
79 )
80 ORDER BY
81 month
82* ,shop_code

8行が選択されました。

経過: 00:00:08.35

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

--------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
--------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3692 | 118K| 83282 (2)| 00:16:40 | | |
| 1 | TEMP TABLE TRANSFORMATION | | | | | | | |
| 2 | LOAD AS SELECT | SYS_TEMP_0FD9D660A_4A53712A | | | | | | |
| 3 | HASH GROUP BY | | 17550 | 411K| 83172 (2)| 00:16:39 | | |
| 4 | PARTITION HASH ALL | | 394K| 9242K| 83161 (2)| 00:16:38 | 1 | 4 |
|* 5 | TABLE ACCESS FULL | TEST2 | 394K| 9242K| 83161 (2)| 00:16:38 | 1 | 4 |
| 6 | LOAD AS SELECT | SYS_TEMP_0FD9D660B_4A53712A | | | | | | |
|* 7 | VIEW | | 17550 | 497K| 17 (0)| 00:00:01 | | |
| 8 | TABLE ACCESS FULL | SYS_TEMP_0FD9D660A_4A53712A | 17550 | 411K| 17 (0)| 00:00:01 | | |
| 9 | SORT ORDER BY | | 3692 | 118K| 93 (8)| 00:00:02 | | |
| 10 | VIEW | | 3692 | 118K| 92 (7)| 00:00:02 | | |
| 11 | UNION-ALL | | | | | | | |
| 12 | HASH GROUP BY | | 2758 | 68950 | 27 (4)| 00:00:01 | | |
| 13 | VIEW | | 17550 | 428K| 26 (0)| 00:00:01 | | |
| 14 | TABLE ACCESS FULL | SYS_TEMP_0FD9D660B_4A53712A | 17550 | 497K| 26 (0)| 00:00:01 | | |
| 15 | HASH GROUP BY | | 920 | 20240 | 27 (4)| 00:00:01 | | |
| 16 | VIEW | | 17550 | 377K| 26 (0)| 00:00:01 | | |
| 17 | TABLE ACCESS FULL | SYS_TEMP_0FD9D660B_4A53712A | 17550 | 497K| 26 (0)| 00:00:01 | | |
| 18 | HASH GROUP BY | | 13 | 260 | 18 (6)| 00:00:01 | | |
| 19 | VIEW | | 17550 | 342K| 17 (0)| 00:00:01 | | |
| 20 | TABLE ACCESS FULL | SYS_TEMP_0FD9D660A_4A53712A | 17550 | 411K| 17 (0)| 00:00:01 | | |
| 21 | HASH GROUP BY | | 1 | 17 | 18 (6)| 00:00:01 | | |
| 22 | VIEW | | 17550 | 291K| 17 (0)| 00:00:01 | | |
| 23 | TABLE ACCESS FULL | SYS_TEMP_0FD9D660A_4A53712A | 17550 | 411K| 17 (0)| 00:00:01 | | |
--------------------------------------------------------------------------------------------------------------------------

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

5 - filter("STARTING_DATE"<='20110331' AND SUBSTR("STARTING_DATE",1,6)<='201103' AND
SUBSTR("STARTING_DATE",1,6)>='201101' AND "STARTING_DATE">='20110101')
7 - filter("MONTH">='201101' AND "MONTH"<='201103' AND "SHOP_CODE"='1000')


統計
----------------------------------------------------------
4 recursive calls
28 db block gets
302642 consistent gets
302570 physical reads
1156 redo size
875 bytes sent via SQL*Net to client
519 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
8 rows processed

04:15:55 SCOTT>

アクセスしているブロック数は約1/2に減少し処理時間も20秒台から8秒台へ改善することができました。バンザーイ!w

ついでなので、オラクル任せのパラレルクエリだとどうなるか試してみました。(CPUたくさんあるんでw)
アクセスブロック数は少々増えますが、パラレルの良さが出ています。2秒台になりましたー。しかしーw 、影響が大きいのでこのときは参考出品で、ここまでできるよーって話のネタだけにしときました :)

  1  WITH
2 t01 AS
3 (
4 SELECT
5 SUBSTR(starting_date,1,6) AS month
6 ,CASE
7 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
8 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
9 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
10 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
11 END AS quarter
12 ,shop_code
13 ,SUM(sales_figure) as sales_figure
14 FROM
15 test2
16 WHERE
17 starting_date BETWEEN '20110101' AND '20110331'
18 GROUP BY
19 SUBSTR(starting_date,1,6)
20 ,SUBSTR(starting_date,5,2)
21 ,shop_code
22 ),
23 t02 AS
24 (
25 SELECT
26 month
27 ,quarter
28 ,shop_code
29 ,sales_figure
30 FROM
31 t01
32 WHERE
33 month BETWEEN '201101' AND '201103'
34 AND shop_code = '1000'
35 )
36 SELECT /*+ PARALLEL */
37 month
38 ,shop_code
39 ,sales_figure
40 FROM
41 (
42 SELECT
43 month
44 ,shop_code
45 ,SUM(sales_figure) AS sales_figure
46 FROM
47 t02
48 GROUP BY
49 month
50 ,shop_code
51 UNION ALL
52 SELECT
53 quarter AS month
54 ,shop_code
55 ,SUM(sales_figure) AS sales_figure
56 FROM
57 t02
58 GROUP BY
59 quarter
60 ,shop_code
61 UNION ALL
62 SELECT
63 month
64 ,'ALL ' AS shop_code
65 ,SUM(sales_figure) AS sales_figure
66 FROM
67 t01
68 GROUP BY
69 month
70 UNION ALL
71 SELECT
72 quarter AS month
73 ,'ALL ' AS shop_code
74 ,SUM(sales_figure) AS salles_figure
75 FROM
76 t01
77 GROUP BY
78 quarter
79 )
80 ORDER BY
81 month
82* ,shop_code

8行が選択されました。

経過: 00:00:02.98

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

-------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop | TQ |IN-OUT| PQ Distrib |
-------------------------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3692 | 118K| 5787 (2)| 00:01:10 | | | | | |
| 1 | TEMP TABLE TRANSFORMATION | | | | | | | | | | |
| 2 | PX COORDINATOR | | | | | | | | | | |
| 3 | PX SEND QC (RANDOM) | :TQ10001 | 17550 | 411K| 5772 (2)| 00:01:10 | | | Q1,01 | P->S | QC (RAND) |
| 4 | LOAD AS SELECT | SYS_TEMP_0FD9D6626_4A53712A | | | | | | | Q1,01 | PCWP | |
| 5 | HASH GROUP BY | | 17550 | 411K| 5772 (2)| 00:01:10 | | | Q1,01 | PCWP | |
| 6 | PX RECEIVE | | 17550 | 411K| 5772 (2)| 00:01:10 | | | Q1,01 | PCWP | |
| 7 | PX SEND HASH | :TQ10000 | 17550 | 411K| 5772 (2)| 00:01:10 | | | Q1,00 | P->P | HASH |
| 8 | HASH GROUP BY | | 17550 | 411K| 5772 (2)| 00:01:10 | | | Q1,00 | PCWP | |
| 9 | PX BLOCK ITERATOR | | 394K| 9242K| 5770 (2)| 00:01:10 | 1 | 4 | Q1,00 | PCWC | |
|* 10 | TABLE ACCESS FULL | TEST2 | 394K| 9242K| 5770 (2)| 00:01:10 | 1 | 4 | Q1,00 | PCWP | |
| 11 | PX COORDINATOR | | | | | | | | | | |
| 12 | PX SEND QC (RANDOM) | :TQ20000 | 17550 | 497K| 2 (0)| 00:00:01 | | | Q2,00 | P->S | QC (RAND) |
| 13 | LOAD AS SELECT | SYS_TEMP_0FD9D6627_4A53712A | | | | | | | Q2,00 | PCWP | |
|* 14 | VIEW | | 17550 | 497K| 2 (0)| 00:00:01 | | | Q2,00 | PCWP | |
| 15 | PX BLOCK ITERATOR | | 17550 | 411K| 2 (0)| 00:00:01 | | | Q2,00 | PCWC | |
| 16 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6626_4A53712A | 17550 | 411K| 2 (0)| 00:00:01 | | | Q2,00 | PCWP | |
| 17 | PX COORDINATOR | | | | | | | | | | |
| 18 | PX SEND QC (ORDER) | :TQ30005 | 3692 | 118K| 13 (39)| 00:00:01 | | | Q3,05 | P->S | QC (ORDER) |
| 19 | SORT ORDER BY | | 3692 | 118K| 13 (39)| 00:00:01 | | | Q3,05 | PCWP | |
| 20 | PX RECEIVE | | 3692 | 118K| 12 (34)| 00:00:01 | | | Q3,05 | PCWP | |
| 21 | PX SEND RANGE | :TQ30004 | 3692 | 118K| 12 (34)| 00:00:01 | | | Q3,04 | P->P | RANGE |
| 22 | BUFFER SORT | | 3692 | 118K| | | | | Q3,04 | PCWP | |
| 23 | VIEW | | 3692 | 118K| 12 (34)| 00:00:01 | | | Q3,04 | PCWP | |
| 24 | UNION-ALL | | | | | | | | Q3,04 | PCWP | |
| 25 | HASH GROUP BY | | 2758 | 68950 | 3 (34)| 00:00:01 | | | Q3,04 | PCWP | |
| 26 | PX RECEIVE | | 2758 | 68950 | 3 (34)| 00:00:01 | | | Q3,04 | PCWP | |
| 27 | PX SEND HASH | :TQ30000 | 2758 | 68950 | 3 (34)| 00:00:01 | | | Q3,00 | P->P | HASH |
| 28 | HASH GROUP BY | | 2758 | 68950 | 3 (34)| 00:00:01 | | | Q3,00 | PCWP | |
| 29 | VIEW | | 17550 | 428K| 2 (0)| 00:00:01 | | | Q3,00 | PCWP | |
| 30 | PX BLOCK ITERATOR | | 17550 | 497K| 2 (0)| 00:00:01 | | | Q3,00 | PCWC | |
| 31 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6627_4A53712A | 17550 | 497K| 2 (0)| 00:00:01 | | | Q3,00 | PCWP | |
| 32 | HASH GROUP BY | | 920 | 20240 | 3 (34)| 00:00:01 | | | Q3,04 | PCWP | |
| 33 | PX RECEIVE | | 920 | 20240 | 3 (34)| 00:00:01 | | | Q3,04 | PCWP | |
| 34 | PX SEND HASH | :TQ30001 | 920 | 20240 | 3 (34)| 00:00:01 | | | Q3,01 | P->P | HASH |
| 35 | HASH GROUP BY | | 920 | 20240 | 3 (34)| 00:00:01 | | | Q3,01 | PCWP | |
| 36 | VIEW | | 17550 | 377K| 2 (0)| 00:00:01 | | | Q3,01 | PCWP | |
| 37 | PX BLOCK ITERATOR | | 17550 | 497K| 2 (0)| 00:00:01 | | | Q3,01 | PCWC | |
| 38 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6627_4A53712A | 17550 | 497K| 2 (0)| 00:00:01 | | | Q3,01 | PCWP | |
| 39 | HASH GROUP BY | | 13 | 260 | 3 (34)| 00:00:01 | | | Q3,04 | PCWP | |
| 40 | PX RECEIVE | | 13 | 260 | 3 (34)| 00:00:01 | | | Q3,04 | PCWP | |
| 41 | PX SEND HASH | :TQ30002 | 13 | 260 | 3 (34)| 00:00:01 | | | Q3,02 | P->P | HASH |
| 42 | HASH GROUP BY | | 13 | 260 | 3 (34)| 00:00:01 | | | Q3,02 | PCWP | |
| 43 | VIEW | | 17550 | 342K| 2 (0)| 00:00:01 | | | Q3,02 | PCWP | |
| 44 | PX BLOCK ITERATOR | | 17550 | 411K| 2 (0)| 00:00:01 | | | Q3,02 | PCWC | |
| 45 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6626_4A53712A | 17550 | 411K| 2 (0)| 00:00:01 | | | Q3,02 | PCWP | |
| 46 | HASH GROUP BY | | 1 | 17 | 3 (34)| 00:00:01 | | | Q3,04 | PCWP | |
| 47 | PX RECEIVE | | 1 | 17 | 3 (34)| 00:00:01 | | | Q3,04 | PCWP | |
| 48 | PX SEND HASH | :TQ30003 | 1 | 17 | 3 (34)| 00:00:01 | | | Q3,03 | P->P | HASH |
| 49 | HASH GROUP BY | | 1 | 17 | 3 (34)| 00:00:01 | | | Q3,03 | PCWP | |
| 50 | VIEW | | 17550 | 291K| 2 (0)| 00:00:01 | | | Q3,03 | PCWP | |
| 51 | PX BLOCK ITERATOR | | 17550 | 411K| 2 (0)| 00:00:01 | | | Q3,03 | PCWC | |
| 52 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6626_4A53712A | 17550 | 411K| 2 (0)| 00:00:01 | | | Q3,03 | PCWP | |
-------------------------------------------------------------------------------------------------------------------------------------------------------------

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

10 - filter("STARTING_DATE"<='20110331' AND SUBSTR("STARTING_DATE",1,6)<='201103' AND SUBSTR("STARTING_DATE",1,6)>='201101' AND
"STARTING_DATE">='20110101')
14 - filter("MONTH">='201101' AND "MONTH"<='201103' AND "SHOP_CODE"='1000')

Note
-----
- automatic DOP: Computed Degree of Parallelism is 16


統計
----------------------------------------------------------
348 recursive calls
5435 db block gets
318993 consistent gets
309118 physical reads
310176 redo size
875 bytes sent via SQL*Net to client
519 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
33 sorts (memory)
0 sorts (disk)
8 rows processed

04:31:33 SCOTT>

パラレルクエリの話は一旦横に置いといて(月単位のレンジパーティションならもっと良い結果がでそうですがそれはまた別途ということで…


WITH句以外にもGROUPING SETも効果がありそうではあります。次回は、それらを試して結果を比較してみようかと…。

つづく。


これまでのあらずじ…

いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #1
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #2
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #3
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #4

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

2011年7月18日 (月)

いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #4

前回のつづきです。

このクエリ、前回、大人の事情縛りでチューニング対象だったクエリがアクセスしている表と同じ表を、またまた残念な方法でアクセスしています。ボスキャラ登場って感じですw

いくら最近のオプティマイザが賢くても、勿体ないお化けを絵に描いたようなクエリじゃ最適化なんてできねっす。しかもパーティションタイプは前回の通り、starting_date列でハッシュパーティションorz.
かつ、検索範囲は、全店舗(1300店)、全品目(140品目)の365日分の3ヶ月分、つまり、全データの1/4を検索するサブクエリが2つ、索引をうまく使えばよさげなタイプのサブクエリが2つunion allで統合されています。

前回のクエリのようにファンクション索引でなんとかできるような相手ではありません。しかも読みにくいw

  1  SELECT
2 month
3 ,shop_code
4 ,sales_figure
5 FROM
6 (
7 SELECT
8 SUBSTR(starting_date,1,6) AS month
9 ,shop_code
10 ,SUM(sales_figure) AS sales_figure
11 FROM
12 test2
13 WHERE
14 starting_date BETWEEN '20110101' AND '20110331'
15 AND shop_code = '1000'
16 GROUP BY
17 SUBSTR(starting_date,1,6)
18 ,shop_code
19 UNION ALL
20 SELECT
21 CASE
22 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
23 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
24 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
25 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
26 END AS month
27 ,shop_code
28 ,SUM(sales_figure) AS salles_figure
29 FROM
30 test2
31 WHERE
32 starting_date BETWEEN '20110101' AND '20110331'
33 AND shop_code = '1000'
34 GROUP BY
35 CASE
36 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
37 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
38 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
39 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
40 END
41 ,shop_code
42 UNION ALL
43 SELECT
44 SUBSTR(starting_date,1,6) AS month
45 ,'ALL ' AS shop_code
46 ,SUM(sales_figure) AS sales_figure
47 FROM
48 test2
49 WHERE
50 starting_date BETWEEN '20110101' AND '20110331'
51 GROUP BY
52 SUBSTR(starting_date,1,6)
53 UNION ALL
54 SELECT
55 CASE
56 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
57 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
58 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
59 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
60 END AS month
61 ,'ALL ' AS shop_code
62 ,SUM(sales_figure) AS salles_figure
63 FROM
64 test2
65 WHERE
66 starting_date BETWEEN '20110101' AND '20110331'
67 GROUP BY
68 CASE
69 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
70 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
71 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
72 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
73 END
74 )
75 ORDER BY
76 month
77* ,shop_code

8行が選択されました。

経過: 00:00:21.24

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

-------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
-------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 143 | 4719 | 205K (2)| 00:41:05 | | |
| 1 | SORT ORDER BY | | 143 | 4719 | 205K (2)| 00:41:05 | | |
| 2 | VIEW | | 143 | 4719 | 205K (2)| 00:41:05 | | |
| 3 | UNION-ALL | | | | | | | |
| 4 | HASH GROUP BY | | 3 | 72 | 19054 (1)| 00:03:49 | | |
| 5 | TABLE ACCESS BY GLOBAL INDEX ROWID| TEST2 | 2675 | 64200 | 19053 (1)| 00:03:49 | ROWID | ROWID |
|* 6 | INDEX SKIP SCAN | PK_TEST2 | 11266 | | 11197 (1)| 00:02:15 | | |
| 7 | HASH GROUP BY | | 57 | 969 | 19054 (1)| 00:03:49 | | |
| 8 | TABLE ACCESS BY GLOBAL INDEX ROWID| TEST2 | 2675 | 45475 | 19053 (1)| 00:03:49 | ROWID | ROWID |
|* 9 | INDEX SKIP SCAN | PK_TEST2 | 11266 | | 11197 (1)| 00:02:15 | | |
| 10 | HASH GROUP BY | | 3 | 57 | 83623 (2)| 00:16:44 | | |
| 11 | PARTITION HASH ALL | | 3477K| 63M| 83515 (2)| 00:16:43 | 1 | 4 |
|* 12 | TABLE ACCESS FULL | TEST2 | 3477K| 63M| 83515 (2)| 00:16:43 | 1 | 4 |
| 13 | HASH GROUP BY | | 80 | 960 | 83623 (2)| 00:16:44 | | |
| 14 | PARTITION HASH ALL | | 3477K| 39M| 83515 (2)| 00:16:43 | 1 | 4 |
|* 15 | TABLE ACCESS FULL | TEST2 | 3477K| 39M| 83515 (2)| 00:16:43 | 1 | 4 |
-------------------------------------------------------------------------------------------------------------------

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

6 - access("STARTING_DATE">='20110101' AND "SHOP_CODE"='1000' AND "STARTING_DATE"<='20110331')
filter("SHOP_CODE"='1000' AND SUBSTR("STARTING_DATE",1,6)<='201103' AND
SUBSTR("STARTING_DATE",1,6)>='201101')
9 - access("STARTING_DATE">='20110101' AND "SHOP_CODE"='1000' AND "STARTING_DATE"<='20110331')
filter("SHOP_CODE"='1000' AND SUBSTR("STARTING_DATE",1,6)<='201103' AND
SUBSTR("STARTING_DATE",1,6)>='201101')
12 - filter("STARTING_DATE"<='20110331' AND SUBSTR("STARTING_DATE",1,6)<='201103' AND
SUBSTR("STARTING_DATE",1,6)>='201101' AND "STARTING_DATE">='20110101')
15 - filter("STARTING_DATE"<='20110331' AND SUBSTR("STARTING_DATE",1,6)<='201103' AND
SUBSTR("STARTING_DATE",1,6)>='201101' AND "STARTING_DATE">='20110101')


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


12:51:25 SCOTT>

この結果をみたら残念な感じが確実になりましたよね.

まあ、あせらずじっくり、料理してみましょう。

PK_TEST2は主キー索引ですが、列の順番がイケてません。SHOP_CODE,ITEM_CODE,STARTING_DATEって変えたほうが良さげ

12:50:03 SCOTT> 
12:47:30 SCOTT> select index_name,column_name from user_ind_columns where table_name like '%TEST2%' order by index_name,column_position

INDEX_NAME COLUMN_NAME
------------------------------ ------------------------------
IX1_TEST2 SYS_NC00005$

INDEX_NAME COLUMN_NAME
------------------------------ ------------------------------
PK_TEST2 STARTING_DATE
ITEM_CODE
SHOP_CODE

12:24:03 SCOTT> select segment_name,partition_name,blocks from user_segments where segment_name like '%TEST2%'

SEGMENT_NAME PARTITION_NAME BLOCKS
------------------------------ ------------------------------ ----------
IX1_TEST2 TEST2IX11 37888
IX1_TEST2 TEST2IX12 45056
IX1_TEST2 TEST2IX13 41984
IX1_TEST2 TEST2IX14 46080
PK_TEST2 368640
TEST2 TEST201 66560
TEST2 TEST202 80896
TEST2 TEST203 74752
TEST2 TEST204 82944

9行が選択されました。

経過: 00:00:00.01

主キーをローカル索引かつ列順を変えて作り直し。
(検索パターンも考慮しましたが影響は少なかったので、カーディナリティの高い順というB*Tree索引設計の基本みたいな順序で作り直してます。。)

第一回目の冒頭でも書きましたが、shot_code(1300店)、starting_date(365日)、item_code(140品目)ってことなので (nologgingなのは気にしないでねー)

13:36:12 SCOTT> alter table test2 add constraint pk_test2 primary key (shop_code,starting_date,item_code) using index local
13:38:07 2 (
13:38:08 3 partition pk_test2_1 tablespace tsi001,
13:38:36 4 partition pk_test2_2 tablespace tsi002,
13:38:45 5 partition pk_test2_3 tablespace tsi003,
13:38:55 6 partition pk_test2_4 tablespace tsi004
13:39:06 7 ) nologging;

表が変更されました。

できあがあったセグメントサイズを見るとグローバル索引かつ、starting_date,item_code,shop_codeって並びの時より40,000ブロックですが、索引サイズは縮小しています。

13:53:29 SCOTT> select segment_name,partition_name,blocks from user_segments where segment_name like '%TEST2%'

SEGMENT_NAME PARTITION_NAME BLOCKS
------------------------------ ------------------------------ ----------
IX1_TEST2 TEST2IX11 37888
IX1_TEST2 TEST2IX12 45056
IX1_TEST2 TEST2IX13 41984
IX1_TEST2 TEST2IX14 6080
PK_TEST2 PK_TEST2_1 71680
PK_TEST2 PK_TEST2_2 87040
PK_TEST2 PK_TEST2_3 79872
PK_TEST2 PK_TEST2_4 89088
TEST2 TEST201 66560
TEST2 TEST202 80896
TEST2 TEST203 74752
TEST2 TEST204 82944

12行が選択されました。


主キーを作り直しただけですが、同じクエリの結果が変わるか確認してみます。索引の調整で約30,000ブロック程、節約できましたが処理時間、総アクセスブロック数とも、あまり改善していません。
(30,000ブロック程節約できたわけですが〜もとが大きいのでこの程度では焼け石に水状態です><)

経過: 00:00:21.02

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

-------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |Pstart|Pstop |
-------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 22 | 726 | 166K (2)| 00:33:18 | | |
| 1 | SORT ORDER BY | | 22 | 726 | 166K (2)| 00:33:18 | | |
| 2 | VIEW | | 22 | 726 | 166K (2)| 00:33:18 | | |
| 3 | UNION-ALL | | | | | | | |
| 4 | HASH GROUP BY | | 3 | 72 | 43 (3)| 00:00:01 | | |
| 5 | PARTITION HASH ALL | | 303 | 7272 | 42 (0)| 00:00:01 | 1 | 4 |
| 6 | TABLE ACCESS BY LOCAL INDEX ROWID| TEST2 | 303 | 7272 | 42 (0)| 00:00:01 | 1 | 4 |
|* 7 | INDEX RANGE SCAN | PK_TEST2 | 303 | | 19 (0)| 00:00:01 | 1 | 4 |
| 8 | HASH GROUP BY | | 7 | 119 | 43 (3)| 00:00:01 | | |
| 9 | PARTITION HASH ALL | | 303 | 5151 | 42 (0)| 00:00:01 | 1 | 4 |
| 10 | TABLE ACCESS BY LOCAL INDEX ROWID| TEST2 | 303 | 5151 | 42 (0)| 00:00:01 | 1 | 4 |
|* 11 | INDEX RANGE SCAN | PK_TEST2 | 303 | | 19 (0)| 00:00:01 | 1 | 4 |
| 12 | HASH GROUP BY | | 3 | 57 | 83172 (2)| 00:16:39 | | |
| 13 | PARTITION HASH ALL | | 394K| 7316K| 83161 (2)| 00:16:38 | 1 | 4 |
|* 14 | TABLE ACCESS FULL | TEST2 | 394K| 7316K| 83161 (2)| 00:16:38 | 1 | 4 |
| 15 | HASH GROUP BY | | 9 | 108 | 83172 (2)| 00:16:39 | | |
| 16 | PARTITION HASH ALL | | 394K| 4621K| 83161 (2)| 00:16:38 | 1 | 4 |
|* 17 | TABLE ACCESS FULL | TEST2 | 394K| 4621K| 83161 (2)| 00:16:38 | 1 | 4 |
-------------------------------------------------------------------------------------------------------------------

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

7 - access("SHOP_CODE"='1000' AND "STARTING_DATE">='20110101' AND "STARTING_DATE"<='20110331')
filter(SUBSTR("STARTING_DATE",1,6)<='201103' AND SUBSTR("STARTING_DATE",1,6)>='201101')
11 - access("SHOP_CODE"='1000' AND "STARTING_DATE">='20110101' AND "STARTING_DATE"<='20110331')
filter(SUBSTR("STARTING_DATE",1,6)<='201103' AND SUBSTR("STARTING_DATE",1,6)>='201101')
14 - filter("STARTING_DATE"<='20110331' AND SUBSTR("STARTING_DATE",1,6)<='201103' AND
SUBSTR("STARTING_DATE",1,6)>='201101' AND "STARTING_DATE">='20110101')
17 - filter("STARTING_DATE"<='20110331' AND SUBSTR("STARTING_DATE",1,6)<='201103' AND
SUBSTR("STARTING_DATE",1,6)>='201101' AND "STARTING_DATE">='20110101')


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

15:31:33 SCOTT>

ちなみに同じ対策を月単位のレンジパーティション表で行うとどうなるんでしょ。
比較のために確認しておきます。(大人の事情でパーティションタイプの変更は不可なのですが、悔しいので比較データだけは見せつけておきたいとwww)

まずは、主キーの並びはstarting_date,item_code,shop_codeという順の複合主キーかつローカル索引で作成した場合です。(オリジナルと同じ列順の複合主キーの場合)

14:30:16 SCOTT> select index_name,column_name from user_ind_columns where table_name like '%TEST3' order by index_name,column_position;

INDEX_NAME COLUMN_NAME
------------------------------ ------------------------------
PK_TEST3 STARTING_DATE
ITEM_CODE
SHOP_CODE

以下のクエリ、表が月単位のレンジパーティションかつローカル索引の主キー制約ががあるという点だけが異なりますが、アクセスしているブロック数は1/3以下になります。
まったく同じクエリなのに、1/3のアクセスブロック数ですむんですよ〜〜〜〜〜〜〜。 ;)

  1  SELECT
2 month
3 ,shop_code
4 ,sales_figure
5 FROM
6 (
7 SELECT
8 SUBSTR(starting_date,1,6) AS month
9 ,shop_code
10 ,SUM(sales_figure) AS sales_figure
11 FROM
12 test3
13 WHERE
14 starting_date BETWEEN '20110101' AND '20110331'
15 AND shop_code = '1000'
16 GROUP BY
17 SUBSTR(starting_date,1,6)
18 ,shop_code
19 UNION ALL
20 SELECT
21 CASE
22 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
23 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
24 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
25 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
26 END AS month
27 ,shop_code
28 ,SUM(sales_figure) AS salles_figure
29 FROM
30 test3
31 WHERE
32 starting_date BETWEEN '20110101' AND '20110331'
33 AND shop_code = '1000'
34 GROUP BY
35 CASE
36 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
37 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
38 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
39 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
40 END
41 ,shop_code
42 UNION ALL
43 SELECT
44 SUBSTR(starting_date,1,6) AS month
45 ,'ALL ' AS shop_code
46 ,SUM(sales_figure) AS sales_figure
47 FROM
48 test3
49 WHERE
50 starting_date BETWEEN '20110101' AND '20110331'
51 GROUP BY
52 SUBSTR(starting_date,1,6)
53 UNION ALL
54 SELECT
55 CASE
56    WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
57          WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
58 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
59 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
60 END AS month
61 ,'ALL ' AS shop_code
62 ,SUM(sales_figure) AS salles_figure
63 FROM
64 test3
65 WHERE
66 starting_date BETWEEN '20110101' AND '20110331'
67 GROUP BY
68 CASE
69  WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
70  WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
71  WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
72  WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
73 END
74 )
75 ORDER BY
76 month
77* ,shop_code

8行が選択されました。

経過: 00:00:15.76

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

-------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |Pstart|Pstop |
-------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 280 | 9240 | 82645 (3)| 00:16:32 | | |
| 1 | SORT ORDER BY | | 280 | 9240 | 82645 (3)| 00:16:32 | | |
| 2 | VIEW | | 280 | 9240 | 82644 (3)| 00:16:32 | | |
| 3 | UNION-ALL | | | | | | | |
| 4 | HASH GROUP BY | | 58 | 986 | 20192 (1)| 00:04:03 | | |
| 5 | PARTITION RANGE ITERATOR | | 11467 | 190K| 20191 (1)| 00:04:03 | 1 | 3 |
| 6 | TABLE ACCESS BY LOCAL INDEX ROWID| TEST3 | 11467 | 190K| 20191 (1)| 00:04:03 | 1 | 3 |
|* 7 | INDEX SKIP SCAN | PK_TEST3 | 2783 | | 11867 (1)| 00:02:23 | 1 | 3 |
| 8 | HASH GROUP BY | | 58 | 986 | 20192 (1)| 00:04:03 | | |
| 9 | PARTITION RANGE ITERATOR | | 11467 | 190K| 20191 (1)| 00:04:03 | 1 | 3 |
| 10 | TABLE ACCESS BY LOCAL INDEX ROWID| TEST3 | 11467 | 190K| 20191 (1)| 00:04:03 | 1 | 3 |
|* 11 | INDEX SKIP SCAN | PK_TEST3 | 2783 | | 11867 (1)| 00:02:23 | 1 | 3 |
| 12 | HASH GROUP BY | | 82 | 984 | 21130 (4)| 00:04:14 | | |
| 13 | PARTITION RANGE ITERATOR | | 14M| 170M| 20627 (2)| 00:04:08 | 1 | 3 |
|* 14 | TABLE ACCESS FULL | TEST3 | 14M| 170M| 20627 (2)| 00:04:08 | 1 | 3 |
| 15 | HASH GROUP BY | | 82 | 984 | 21130 (4)| 00:04:14 | | |
| 16 | PARTITION RANGE ITERATOR | | 14M| 170M| 20627 (2)| 00:04:08 | 1 | 3 |
|* 17 | TABLE ACCESS FULL | TEST3 | 14M| 170M| 20627 (2)| 00:04:08 | 1 | 3 |
-------------------------------------------------------------------------------------------------------------------

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

7 - access("STARTING_DATE">='20110101' AND "SHOP_CODE"='1000' AND "STARTING_DATE"<='20110331')
filter("SHOP_CODE"='1000')
11 - access("STARTING_DATE">='20110101' AND "SHOP_CODE"='1000' AND "STARTING_DATE"<='20110331')
filter("SHOP_CODE"='1000')
14 - filter("STARTING_DATE"<='20110331' AND "STARTING_DATE">='20110101')
17 - filter("STARTING_DATE"<='20110331' AND "STARTING_DATE">='20110101')


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

この月単位レンジパーティションの索引も列順を入れ替えて作り直してみます。consistent getsが索引を作り変えてRANGE SCANできるようにしただけで約25,000ブロック減りました。

経過: 00:00:15.56

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

-------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |Pstart|Pstop |
-------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 302 | 9966 | 44244 (4)| 00:08:51 | | |
| 1 | SORT ORDER BY | | 302 | 9966 | 44244 (4)| 00:08:51 | | |
| 2 | VIEW | | 302 | 9966 | 44243 (4)| 00:08:51 | | |
| 3 | UNION-ALL | | | | | | | |
| 4 | HASH GROUP BY | | 63 | 1071 | 951 (1)| 00:00:12 | | |
| 5 | PARTITION RANGE ITERATOR | | 12188 | 202K| 949 (0)| 00:00:12 | 1 | 3 |
| 6 | TABLE ACCESS BY LOCAL INDEX ROWID| TEST3 | 12188 | 202K| 949 (0)| 00:00:12 | 1 | 3 |
|* 7 | INDEX RANGE SCAN | PK_TEST3 | 2958 | | 66 (0)| 00:00:01 | 1 | 3 |
| 8 | HASH GROUP BY | | 63 | 1071 | 951 (1)| 00:00:12 | | |
| 9 | PARTITION RANGE ITERATOR | | 12188 | 202K| 949 (0)| 00:00:12 | 1 | 3 |
| 10 | TABLE ACCESS BY LOCAL INDEX ROWID| TEST3 | 12188 | 202K| 949 (0)| 00:00:12 | 1 | 3 |
|* 11 | INDEX RANGE SCAN | PK_TEST3 | 2958 | | 66 (0)| 00:00:01 | 1 | 3 |
| 12 | HASH GROUP BY | | 88 | 1056 | 21171 (4)| 00:04:15 | | |
| 13 | PARTITION RANGE ITERATOR | | 15M| 182M| 20630 (2)| 00:04:08 | 1 | 3 |
|* 14 | TABLE ACCESS FULL | TEST3 | 15M| 182M| 20630 (2)| 00:04:08 | 1 | 3 |
| 15 | HASH GROUP BY | | 88 | 1056 | 21171 (4)| 00:04:15 | | |
| 16 | PARTITION RANGE ITERATOR | | 15M| 182M| 20630 (2)| 00:04:08 | 1 | 3 |
|* 17 | TABLE ACCESS FULL | TEST3 | 15M| 182M| 20630 (2)| 00:04:08 | 1 | 3 |
-------------------------------------------------------------------------------------------------------------------

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

7 - access("SHOP_CODE"='1000' AND "STARTING_DATE">='20110101' AND "STARTING_DATE"<='20110331')
11 - access("SHOP_CODE"='1000' AND "STARTING_DATE">='20110101' AND "STARTING_DATE"<='20110331')
14 - filter("STARTING_DATE"<='20110331' AND "STARTING_DATE">='20110101')
17 - filter("STARTING_DATE"<='20110331' AND "STARTING_DATE">='20110101')


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


パーティションタイプの選択ミスってほんとに痛い結果になりますよね。初期段階のというかパーティションタイプ毎の得意技は把握しておくべきですよー。


ということで次回本格的なチューニングへと、つづく…




これまでのあらずじ…

いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #1
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #2
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #3

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

2011年7月17日 (日)

VirtualBox 4.0.12 リリース、活発だな〜 :)

気づいたら、4.0.12がリリースされてる。:) すげ〜活発な。  アップデートdone.

20110717_83523

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

いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #3

いろいろと面倒くさい大人の事情が登場してきたので、パーティションタイプの変更案はひとまず却下されてしまいました。(パーティションタイプの変更のほかに索引構成表なんて案もあるにはあったんですが、パーティションタイプの変更が却下されたので表ごと索引にしちゃえばーみたいな案も却下されるのは当然かもねw)

ようするに、影響範囲を最小にしたい、でも、遅いクエリーはなんとかしたいーということなんですよー。めんどくさいです。まあ、アイデアが無いではないですが限界もみえちゃいますからねー。元が元だからw


ということで、代替案を試してみることに…

非ユニークなファクンクション索引を作り、SQL文のWHERE句の構文を換えます。SQL文を書き換えるためプログラムにも多少影響がでますが、影響範囲はかなり限定されるので、大人の事情でチューニングに手枷足枷を付けてくる人たちもOK〜って言ってくれるか、くれないか、ギリギリってところです。(月単位のレンジパーティションに比べれば索引で余計にディスク容量食いますし、ファンクション索引で更新性能にも多少影響でますがね。)

ローカル索引にしたのは多少でも索引サイズ少なくしたかったからw グローバル索引にしてもほぼ同じことはできますが。

00:55:51 SCOTT> create index ix1_test2 on test2(substr(starting_date,1,6)) local (
00:56:43 2 partition test2ix11 tablespace tsi001,
00:57:13 3 partition test2ix12 tablespace tsi002,
00:57:22 4 partition test2ix13 tablespace tsi003,
00:57:33 5 partition test2ix14 tablespace tsi004
00:57:44 6 );

索引が作成されました。

ファンクション索引を作成したら、SQL文を書き換えます。

変更前

SELECT 
SUBSTR(starting_date,1,6) AS month
,shop_code
,SUM(sales_figure) AS sales_figure
FROM
test2
WHERE
starting_date BETWEEN '20110101' AND '20110331'
GROUP BY
SUBSTR(starting_date,1,6)
,shop_code
ORDER BY 1,2

変更後
WHERE句をBETWEENの範囲指定から、新たに作成したファンクション索引で検索できるよう書き換えます:) これがポイントね。

SELECT 
SUBSTR(starting_date,1,6) AS month
,shop_code
,SUM(sales_figure) AS sales_figure
FROM
test2
WHERE
SUBSTR(starting_date,1,6) = '201101'
GROUP BY
SUBSTR(starting_date,1,6)
,shop_code
ORDER BY 1,2


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

-----------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1300 | 28600 | 4688 (1)| 00:00:57 | | |
| 1 | SORT GROUP BY | | 1300 | 28600 | 4688 (1)| 00:00:57 | | |
| 2 | PARTITION HASH ALL | | 664K| 13M| 4669 (1)| 00:00:57 | 1 | 4 |
| 3 | TABLE ACCESS BY LOCAL INDEX ROWID| TEST2 | 664K| 13M| 4669 (1)| 00:00:57 | 1 | 4 |
|* 4 | INDEX RANGE SCAN | IX1_TEST2 | 265K| | 3458 (1)| 00:00:42 | 1 | 4 |
-----------------------------------------------------------------------------------------------------------------

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

4 - access(SUBSTR("STARTING_DATE",1,6)='201101')


統計
----------------------------------------------------------
1 recursive calls
0 db block gets
38550 consistent gets
0 physical reads
0 redo size
29487 bytes sent via SQL*Net to client
1465 bytes received via SQL*Net from client
88 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
1300 rows processed

随分改善できました〜:) アクセスするブロック数は多めですが、処理時間は月単位のレンジパーティションの1パーティションをフルスキャンするのと同程度。それなりにニッコリ。

前述の方法以外に、マテリアライズドビューを利用してちょうどいい感じに集計済みの状態を作っておけばもっと早くできるんですけど…なぜそれをしないかも大人の事情縛りなのです。はいw

レンジパーティションでシングルパーティションの全表走査よりブロック数は多いですが…ひとまずこの問題はクリア。でも次のもイライラもんでした。


ということで次回へつづく。




これまでのあらずじ…

いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #1
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #2

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

2011年7月10日 (日)

いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #2

前回のつづきです。

非パーティション表で主キーあり、ハッシュパーティション表で主キーはグローバル索引、月単位のレンジパーティション表で主キーはローカル索引の3表を作成してデータを登録したところまででした。

では早速つづきを。

(条件をそろえるため、バッファキャッシュクリア後2回実行しています。結果は2回目のものを載せています)

今回、残念だった一番のポイント。 それは、データ量が多いのでパーティション表を選んだ、パーティションキーも悪くない、でも〜。選んだパーティションタイプが残念だったんです。

まずは、残念な結果を見てください。SQL文の性能がでない〜と問題になっていたところでは、test2表のように、ハッシュパーティションかつ一意検索向けにグローバル索引として主キーあったのですが、集計目的のクエリでは全く効果なく見るも無惨な結果に…

検索キーにパーティションキー(starting_date)は利用していますが、BETWEENによる範囲検索であるため全パーティションをFULL SCANしちゃってます。残念です。おしいw
(実は主キーはグローバル索引で、starting_date,item_code,shop_codeの複合キーとなっていますが……………索引も残念な感じになっているので、索引には目もくれずに全パーティションの全表走査になっています。ほんと残念)

  1  SELECT
2 SUBSTR(starting_date,1,6) AS month
3 ,shop_code
4 ,SUM(sales_figure) AS sales_figure
5 FROM
6 test2
7 WHERE
8 starting_date BETWEEN '20110101' AND '20110131'
9 GROUP BY
10 SUBSTR(starting_date,1,6)
11 ,shop_code
12* ORDER BY 1,2

1300行が選択されました。

経過: 00:00:06.67

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

---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1839 | 31263 | 83128 (2)| 00:16:38 | | |
| 1 | SORT GROUP BY | | 1839 | 31263 | 83128 (2)| 00:16:38 | | |
| 2 | PARTITION HASH ALL| | 374K| 6220K| 83118 (2)| 00:16:38 | 1 | 4 |
|* 3 | TABLE ACCESS FULL| TEST2 | 374K| 6220K| 83118 (2)| 00:16:38 | 1 | 4 |
---------------------------------------------------------------------------------------------

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

3 - filter("STARTING_DATE"<='20110131' AND "STARTING_DATE">='20110101')


統計
----------------------------------------------------------
0 recursive calls
0 db block gets
302587 consistent gets
302556 physical reads
0 redo size
29487 bytes sent via SQL*Net to client
1466 bytes received via SQL*Net from client
88 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
1300 rows processed


この結果を見てすぐに浮かんだのは、月単位のパーティション表にしちゃうってことだったのですが、ここで大人の事情がじゃまをしますw


現状では、パーティションタイプの変更はむーりー。と。  ではどうするか。 他の案を提示しなければなりません…。

私が、考えている間、以下の例でもみて、どんな方法が残されているか………………考えてみてください:)

せっかくなので、非パーティション表のFULL SCANとどのくらい差がでる見てみます :) test1表は非パーティション表ですが、同じ複合主キーがあるのでやはり索引は利用せず全表走査になります。
若干アクセスしているブロック数が減っている程度の差ですが、ちょっとだけこちらのほうがいいですよねw でもパーティション化したメリットはほぼナッシングw

  1  SELECT
2 SUBSTR(starting_date,1,6) AS month
3 ,shop_code
4 ,SUM(sales_figure) AS sales_figure
5 FROM
6 test1
7 WHERE
8 starting_date BETWEEN '20110101' AND '20110131'
9 GROUP BY
10 SUBSTR(starting_date,1,6)
11 ,shop_code
12* ORDER BY 1,2

1300行が選択されました。

経過: 00:00:06.66

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

----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1839 | 31263 | 82796 (2)| 00:16:34 |
| 1 | SORT GROUP BY | | 1839 | 31263 | 82796 (2)| 00:16:34 |
|* 2 | TABLE ACCESS FULL| TEST1 | 374K| 6220K| 82785 (2)| 00:16:34 |
----------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("STARTING_DATE"<='20110131' AND
"STARTING_DATE">='20110101')

統計
----------------------------------------------------------
0 recursive calls
0 db block gets
302081 consistent gets
302068 physical reads
0 redo size
29487 bytes sent via SQL*Net to client
1466 bytes received via SQL*Net from client
88 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
1300 rows processed


月単位のレンジパーティションなら全表走査しても1パーティションで済むし、アクセスするブロック数も1/10程度に減少するのにね :)
初期段階のミスって、ほんとに痛いですよね〜 :)

  1  SELECT
2 SUBSTR(starting_date,1,6) AS month
3 ,shop_code
4 ,SUM(sales_figure) AS sales_figure
5 FROM
6 test3
7 WHERE
8 starting_date BETWEEN '20110101' AND '20110131'
9 GROUP BY
10 SUBSTR(starting_date,1,6)
11 ,shop_code
12* ORDER BY 1,2

1300行が選択されました。

経過: 00:00:03.25

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

---------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Pstart| Pstop |
---------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 27578 | 457K| | 17785 (2)| 00:03:34 | | |
| 1 | SORT GROUP BY | | 27578 | 457K| 146M| 17785 (2)| 00:03:34 | | |
| 2 | PARTITION RANGE SINGLE| | 5460K| 88M| | 7004 (2)| 00:01:25 | 1 | 1 |
|* 3 | TABLE ACCESS FULL | TEST3 | 5460K| 88M| | 7004 (2)| 00:01:25 | 1 | 1 |
---------------------------------------------------------------------------------------------------------


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

3 - filter("STARTING_DATE">='20110101' AND "STARTING_DATE"<='20110131')


統計
----------------------------------------------------------
0 recursive calls
0 db block gets
24910 consistent gets
24901 physical reads
0 redo size
29487 bytes sent via SQL*Net to client
1466 bytes received via SQL*Net from client
88 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
1300 rows processed

次回へつづく… とその前に、なぜ、索引を使ってくれなかったのでしょうか?

WHERE句の検索条件であるstarting_date列は、主キーの第一列ですよね…………


ちょっとだけ情報を見せておきましょう :)
おっとっと、索引の方が表本体より大きくなってるし〜w

SEGMENT_NAME		       SEGMENT_TYPE	  BLOCKS
------------------------------ ------------------ ----------
PK_TEST2 INDEX 368640
TEST2 TABLE PARTITION 305152

で、無理矢理索引を使わせてみると…w あららら……………なんかすげ〜ことになってますね〜10倍ぐらいアクセスブロック数増えてるし〜

15:33:04 SCOTT> r
1 SELECT /*+ index(test2 pk_test2) */
2 SUBSTR(starting_date,1,6) AS month
3 ,shop_code
4 ,SUM(sales_figure) AS sales_figure
5 FROM
6 test2
7 WHERE
8 starting_date BETWEEN '20110101' AND '20110131'
9 GROUP BY
10 SUBSTR(starting_date,1,6)
11 ,shop_code
12* ORDER BY 1,2

1300行が選択されました。

経過: 00:00:09.20

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

----------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1839 | 31263 | 266K (1)| 00:53:19 | | |
| 1 | SORT GROUP BY | | 1839 | 31263 | 266K (1)| 00:53:19 | | |
| 2 | TABLE ACCESS BY GLOBAL INDEX ROWID| TEST2 | 374K| 6220K| 266K (1)| 00:53:19 | ROWID | ROWID |
|* 3 | INDEX RANGE SCAN | PK_TEST2 | 374K| | 2043 (1)| 00:00:25 | | |
----------------------------------------------------------------------------------------------------------------

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

3 - access("STARTING_DATE">='20110101' AND "STARTING_DATE"<='20110131')


統計
----------------------------------------------------------
0 recursive calls
0 db block gets
3883208 consistent gets
0 physical reads
0 redo size
29487 bytes sent via SQL*Net to client
1465 bytes received via SQL*Net from client
88 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
1300 rows processed
15:33:32 SCOTT>

ということで次回、大人の事情でパーティショニングタイプの変更はできないので、他の案をへつづく…。

それにしても、すげ〜ブロック数だw


これまでのあらずじ…

いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #1

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

2011年7月 6日 (水)

いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #1

どこかで聞いたことありそうなタイトルを少々パクリつつ、まずは準備運動から…

ということで、今回は、そのパーティショニングタイプ選択ミスってないか? 
ってところから始めていくつかイケてなかったクエリを、パーティショニングタイプの選択ミスはミスだが、どーしても今は変更できな〜い。
という大人の事情(どんな大人だw) に金縛りな状況でSQLチューニングしてみましょ。というネタ :)

以下のような3表を作成してあります。 (nologgingなのは気にしないでね)

列定義は同じですが、非パーティション表で主キーあり、ハッシュパーティション表で主キーはグローバル索引、月単位のレンジパーティション表で主キーはローカル索引の3表。
各店舗(1300店)での売上げを品目(140品目)毎、かつ日毎に集計したデータが1年分(365日分)登録されていると思ってください。
(思わなくてもいいですけど、登録してありますw)

create table test1
(
starting_date char(8) not null,
shop_code char(4) not null,
sales_figure number not null,
item_code char(10) not null,
constraint pk_test1 primary key (starting_date,item_code,shop_code) using index tablespace tsimax nologging
)
tablespace tsmax
nologging
/

create table test2
(
starting_date char(8) not null,
shop_code char(4) not null,
sales_figure number not null,
item_code char(10) not null,
constraint pk_test2 primary key (starting_date,item_code,shop_code) using index global tablespace tsimax nologging
)
partition by hash(starting_date)
(
partition test201 tablespace ts001,
partition test202 tablespace ts002,
partition test203 tablespace ts003,
partition test204 tablespace ts004
)
nologging
/

create table test3
(
starting_date char(8) not null,
shop_code char(4) not null,
sales_figure number not null,
item_code char(10) not null,
constraint pk_test3 primary key (starting_date,item_code,shop_code) using index local
(
partition test301idx tablespace tsi001,
partition test302idx tablespace tsi002,
partition test303idx tablespace tsi003,
partition test304idx tablespace tsi004,
partition test305idx tablespace tsi001,
partition test306idx tablespace tsi002,
partition test307idx tablespace tsi003,
partition test308idx tablespace tsi004,
partition test309idx tablespace tsi001,
partition test310idx tablespace tsi002,
partition test311idx tablespace tsi003,
partition test312idx tablespace tsi004,
partition testmaxidx tablespace tsimax
)
nologging
)
partition by range(starting_date) (
partition test301 values less than ('20110201') tablespace ts001,
partition test302 values less than ('20110301') tablespace ts002,
partition test303 values less than ('20110401') tablespace ts003,
partition test304 values less than ('20110501') tablespace ts004,
partition test305 values less than ('20110601') tablespace ts001,
partition test306 values less than ('20110701') tablespace ts002,
partition test307 values less than ('20110801') tablespace ts003,
partition test308 values less than ('20110901') tablespace ts004,
partition test309 values less than ('20111001') tablespace ts001,
partition test310 values less than ('20111101') tablespace ts002,
partition test311 values less than ('20111201') tablespace ts003,
partition test312 values less than ('20120101') tablespace ts004,
partition testmax values less than (maxvalue) tablespace tsmax
)
nologging
/


次回へつづく。

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

2011年6月29日 (水)

VirtualBox 4.0.10リリース、MacOS X Lion向けbug fixもあるね

MacOS X Lion向けbug fixもあるね〜

20110628_235215

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

2011年6月19日 (日)

「DBがなんか遅いんだけど!」「DBで何か起きてないか確認して!」こんな問い合わせへの対応 “一人”でできますか? - 勉強会@日本オラクル青山センター #2

「DBがなんか遅いんだけど!」「DBで何か起きてないか確認して!」こんな問い合わせへの対応 一人でできますか? - 勉強会@日本オラクル青山センター

先週に引き続き、「DBがなんか遅いんだけど!」「DBで何か起きてないか確認して!」こんな問い合わせへの対応 “一人”でできますか? - 勉強会@日本オラクル青山センター を参観してきた :)

今回も満席で、チューニング系かつ、オープンな勉強会ってのは興味ある人にとってはすげー希少な機会なんじゃないか…と。

懇親会、二次会も楽しませていただきました。二次会の締めの挨拶をなぜ私がしたのかいまいち状況わかりませんw.

次回「RAC環境の1ノードで発生したレスポンス遅延を調査・解決」の開催は日時、会場ともまだ未定ということですが、うわさによると恵比寿の可能性もあるとか。楽しみです 。また、参観枠で参加することになると思いますが :)

http://atnd.org/events/16250
http://atnd.org/events/16251

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

2011年6月11日 (土)

「DBがなんか遅いんだけど!」「DBで何か起きてないか確認して!」こんな問い合わせへの対応 “一人”でできますか? - 勉強会@日本オラクル青山センター

ブログ更新、随分さぼってました〜 m(_ _)m

久々のブログは、これまた今年初の勉強会ネタで。 

「DBがなんか遅いんだけど!」「DBで何か起きてないか確認して!」こんな問い合わせへの対応 “一人”でできますか?

この勉強会 Oracle Database の性能問題をstatspackレポートやv$sessionのログを使って突き止めるという、その筋の人たちにはかな〜り、興味をそそる勉強会です。しかも2週連続 :)

http://atnd.org/events/16250

おもしろいのは(実験的な意味もあるのかもしれないですけど…)、参観者枠があることw 
http://atnd.org/events/16251

X2_68441a6

実習者枠で申し込もうかな〜 と @Guutara さんに呟いたら、参観者枠あるから、そっちにして〜と軽く強制w されたので参観者として参加。 

リアルには面識の無い、オラクル界隈の濃〜っ方々にも会えたこともあり無理矢理時間作って参加した価値ありでした。

来週も楽しみです。

今回の内容は、「特定の処理にてレスポンス遅延が発生している」原因をstatspackレポートとv$sesionのログから探るものでした。

………enq: TX - row lock contention


面白い試みですよね、ほんと。  

あ、そうそう、マニアックというか難しい性能問題を演習にして、オラクル界隈の濃〜っ方々が実習者で、そーゆーこと今後やっていきたいな〜と考えている方々が参観者というパターンも面白そうですね。 Oracle Database Turning Festa (勝手に命名w)

ほんとうの現場で起きた(起きているw)ような性能問題を一気に解決するようなのも面白いかもね。

インスタンスレベルの問題に限らず、SQL単体でもいいかもね。 SQL Turning Festa 。

実際のプロジェクトで起こった、絶賛SQLチューニング祭り開催中!的なネタを使って、ビール片手に、あーだーこーだ良いながらやるのも、面白いよね。きっと。:)

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

2011年4月 3日 (日)

VirtualBox de Oracle Linux 6

http://blogs.oracle.com/linux/2011/02/oracle_linux_6_dvds_now_available.html

ってことだったので、VirtualBox 使ってお試し環境作っておいた。問題もなくinstall done…

20110402_214835

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

2011年2月17日 (木)

shutdown immeidateしない、ほかの理由に遭遇! おまけのおまけ(でた〜最近、よくあるパターンw)

さて、前回のおまけで終了〜。 のつもりだったがリスナー経由の時は問題ないな〜と気づいて気が変わり。おまけのおまけとなりましたw


前回まではBEQで繋いでたな〜、と思ってリスナー経由の専用サーバー接続で同じことを試してみたらshutdown immeidateが待機させられない事に気づいた。

お〜〜〜〜っ。oracleのサーバープロセスがdefunctにならないっつーことは…

ということで、早速試してみた。

※1つめの端末でSQL*Plusを起動してOracleをスタートアップ(なお事前にリスナーは起動済みです)

[oracle@lampeye ˜]$ sqlplus /nolog

SQL*Plus: Release 11.1.0.7.0 - Production on 水 2月 16 23:10:11 2011

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

23:10:11 > conn / as sysdba
接続されました。
23:21:54 SYS> startup
ORACLEインスタンスが起動しました。

Total System Global Area 1603411968 bytes
Fixed Size 2160112 bytes
Variable Size 436210192 bytes
Database Buffers 1157627904 bytes
Redo Buffers 7413760 bytes
データベースがマウントされました。
データベースがオープンされました。
23:22:04 SYS>

※2つめの端末でSQL*Plusを起動してリスナー経由(この例では専用サーバー接続)でOracleへ接続後、host command(!)でshellへ入っておく。

[oracle@lampeye ˜]$ 
[oracle@lampeye ˜]$ sqlplus /nolog

SQL*Plus: Release 11.1.0.7.0 - Production on 水 2月 16 23:22:45 2011

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

23:22:45 > conn scott/tiger@lampeye
接続されました。
23:22:53 SCOTT> !
[oracle@lampeye ˜]$

※この状態で、プロセスツリーをチェックしてみると…Bequeath Protocol接続の時は、PID=5398の子プロセスとしてOracleのサーバープロセスがforkされていたのに、居ないのよ…

[oracle@lampeye ˜]$ ps -ef | grep gnome-terminal
oracle 4976 1 0 23:08 ? 00:00:01 gnome-terminal
oracle 5440 5031 0 23:23 pts/3 00:00:00 grep gnome-terminal
[oracle@lampeye ˜]$
[oracle@lampeye ˜]$ pstree -p 4976 -ual
gnome-terminal,4976,oracle
├─bash,4982
│ └─sqlplus,5154
│ └─oracle,5376 (DESCRIPTION=(LOCAL=YES)(ADDRESS=(PROTOCOL=beq)))
├─bash,5007
│ └─sqlplus,5398
│ └─bash,5413
├─bash,5031
│ └─pstree,5441 -p 4976 -ual
├─bash,5161
├─gnome-pty-helpe,4981
└─{gnome-terminal},4983
[oracle@lampeye ˜]$

※じゃ、どこに行ったのよ…と、調べてみると…リスナー経由だとサーバープロセスの親プロセスはINIT

23:29:17 SYS> select s.username,p.username,p.spid from v$process p join v$session s on p.addr = s.paddr where s.username='SCOTT'

USERNAME USERNAME SPID
------------------------------ --------------- ------------------------
SCOTT oracle 5400

経過: 00:00:00.01
23:29:18 SYS>

[oracle@lampeye ˜]$ ps -f -p 5400
UID PID PPID C STIME TTY TIME CMD
oracle 5400 1 0 23:22 ? 00:00:00 oraclelampeye (LOCAL=NO)
[oracle@lampeye ˜]$

※これならshutdown immeidateは待たされずに実行される!

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

ということは、SQL*PlusからBequeath Protocol接続の場合、host commandでshellへ入ったままにしているとshutdown immeidateが待機させられるのな…


じゃ、sqlnet.ora に BEQUEATH_DETACH=yes を設定すれば回避できそーな気がする
Oracle Database Net Servicesリファレンス 11g リリース1(11.1)- 5.2.1 BEQUEATH_DETACH

早速、検証…

sqlnet.oraにBEQUEATH_DETACH=yesを追記。

[oracle@lampeye ˜]$ 
[oracle@lampeye ˜]$ cat $ORACLE_HOME/network/admin/sqlnet.ora
# sqlnet.ora Network Configuration File: /opt/u01/app/oracle/product/11.1.0/db_1/network/admin/sqlnet.ora
# Generated by Oracle configuration tools.

NAMES.DIRECTORY_PATH= (TNSNAMES, EZCONNECT)
BEQUEATH_DETACH=yes

[oracle@lampeye ˜]$

※1つめの端末でSQL*Plusを起動し、Oracleをstartup…

[oracle@lampeye ˜]$ sqlplus /nolog

SQL*Plus: Release 11.1.0.7.0 - Production on 水 2月 16 23:39:14 2011

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

23:39:14 > conn / as sysdba
アイドル・インスタンスに接続しました。
23:39:17 SYS> startup
ORACLEインスタンスが起動しました。

Total System Global Area 1603411968 bytes
Fixed Size 2160112 bytes
Variable Size 436210192 bytes
Database Buffers 1157627904 bytes
Redo Buffers 7413760 bytes
データベースがマウントされました。
データベースがオープンされました。
23:39:26 SYS>


※2つめの端末でSQL*Plusを起動、BEQ接続でSCOTTユーザへ接続後、host command(!)でshellへ入っておく…

[oracle@lampeye ˜]$ sqlplus /nolog

SQL*Plus: Release 11.1.0.7.0 - Production on 水 2月 16 23:39:39 2011

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

23:39:39 > conn scott/tiger
接続されました。
23:39:45 SCOTT> !
[oracle@lampeye ˜]$


※この状態でプロセスツリーを見てみると…お〜〜〜、SQL*Plusの子プロセスではなくなっている…

[oracle@lampeye ˜]$ pstree -p 4976 -ual 
gnome-terminal,4976,oracle
├─bash,4982
│ └─sqlplus,5596
├─bash,5007
│ └─sqlplus,5664
│ └─bash,5686
├─bash,5031
│ └─pstree,5711 -p 4976 -ual
├─bash,5161
├─gnome-pty-helpe,4981
└─{gnome-terminal},4983
[oracle@lampeye ˜]$

※じゃ〜、どこの子になっちゃったのか調べてみましょう…

23:41:08 SYS> select s.username,p.username,p.spid from v$process p join v$session s on p.addr = s.paddr where s.username IN ('SYS','SCOTT');

USERNAME USERNAME SPID
------------------------------ --------------- ------------------------
SYS oracle 5685
SCOTT oracle 5666

経過: 00:00:00.03
23:41:40 SYS>

お〜、やはり、SQL*Plusの子じゃなくて、INITの養子となってしまったようですw

[oracle@lampeye ˜]$ ps -f -p 5685 5666
UID PID PPID C STIME TTY STAT TIME CMD
oracle 5666 1 0 23:39 ? Ss 0:00 oraclelampeye (DESCRIPTION=(LOCAL=YES)(ADDRESS=(PROTOCOL=beq)))
oracle 5685 1 0 23:40 ? Ss 0:00 oraclelampeye (DESCRIPTION=(LOCAL=YES)(ADDRESS=(PROTOCOL=beq)))
[oracle@lampeye ˜]$

これなら、shutdown immeidateも待たされないでしょうね :)

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

immeidateしない、ほかの理由に遭遇! ネタはこれで、ほんとにほんとのおしまい :)


shutdown immeidateしない、ほかの理由に遭遇!
shutdown immeidateしない、ほかの理由に遭遇! #2
shutdown immeidateしない、ほかの理由に遭遇! #3
shutdown immeidateしない、ほかの理由に遭遇! おまけ

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

2011年2月10日 (木)

shutdown immeidateしない、ほかの理由に遭遇! おまけ

前回までで状況も把握できたし、そうなった場合の回避策も見えたのですが、子プロセスの生成とかdefunctなゾンピ君の刈り取りってどーなってんだろーと気になり…

子プロセスの生成状況を以下の順で見てみる…

  1. shellに入っただけ
  2. SQL*Plus起動
  3. SCOTTユーザへ接続
  4. host command(!)でshellへ入る


shellに入っただけ

[oracle@pleco ˜]$ 
[oracle@pleco ˜]$

[oracle@pleco ˜]$ 
[oracle@pleco ˜]$ ps -e -u oracle | grep gnome-terminal
3337 ? 00:00:09 gnome-terminal
[oracle@pleco ˜]$
[oracle@pleco ˜]$ pstree -p 3337 -u
gnome-terminal(3337,oracle)─┬─bash(3377)
├─bash(3737)───pstree(4063)
├─gnome-pty-helpe(3342)
└─{gnome-terminal}(3344)


SQL*Plus起動

[oracle@pleco ˜]$ 
[oracle@pleco ˜]$
[oracle@pleco ˜]$ sqlplus /nolog

SQL*Plus: Release 11.1.0.7.0 - Production on 木 2月 10 14:54:44 2011

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

14:54:44 >

[oracle@pleco ˜]$ 
[oracle@pleco ˜]$ pstree -p 3337 -u
gnome-terminal(3337,oracle)─┬─bash(3377)───sqlplus(4064)
├─bash(3737)───pstree(4066)
├─gnome-pty-helpe(3342)
└─{gnome-terminal}(3344)


SCOTTユーザへ接続

[oracle@pleco ˜]$ 
[oracle@pleco ˜]$
[oracle@pleco ˜]$ sqlplus /nolog

SQL*Plus: Release 11.1.0.7.0 - Production on 木 2月 10 14:54:44 2011

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

14:54:44 > conn scott/tiger
接続されました。
14:54:57 SCOTT>

scottユーザに接続した状態…
このようにSQL*Plusの子プロセスが1つであればshutdown immeidateが待機させられることはないんだけど…ね。
[oracle@pleco ˜]$ 
[oracle@pleco ˜]$ pstree -p 3337 -u
gnome-terminal(3337,oracle)─┬─bash(3377)───sqlplus(4064)───oracle(4067)
├─bash(3737)───pstree(4068)
├─gnome-pty-helpe(3342)
└─{gnome-terminal}(3344)


host command(!)でshellに入った

[oracle@pleco ˜]$ 
[oracle@pleco ˜]$
[oracle@pleco ˜]$ sqlplus /nolog

SQL*Plus: Release 11.1.0.7.0 - Production on 木 2月 10 14:54:44 2011

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

14:54:44 > conn scott/tiger
接続されました。
14:54:57 SCOTT> !
[oracle@pleco ˜]$

SQL*Plusからホストコマンド(!)でshellに入った状態…
SQL*Plusの子プロセスが複数でoracle以外のがある状態で、他のSQL*Plusからshutdown immediateを実行されちゃうと、以下にあるpid=4067が<defunct>状態のまま刈り取られない。===>Oracleがshutdown immeidateで停止しない。ってことになるのな。
[oracle@pleco ˜]$ 
[oracle@pleco ˜]$ pstree -p 3337 -u
gnome-terminal(3337,oracle)─┬─bash(3377)───sqlplus(4064)─┬─bash(4069)
└─oracle(4067)
├─bash(3737)───pstree(4093)
├─gnome-pty-helpe(3342)
└─{gnome-terminal}(3344)
[oracle@pleco ˜]$
[oracle@pleco ˜]$

じゃ、実験!

以下のようにshutdown immeidateが待機させられている状態を作る。赤字(pid=3627)の子プロセスがdefunctつまりゾンビになるわけね。

shutdown immeidateを実行するSQL*Plusとは別にSQL*Plusを起動し、host command(!)でshellに入っておきます…

16:09:28 > conn scott/tiger
接続されました。
16:12:45 SCOTT> !
[oracle@pleco ˜]$

[oracle@pleco ˜]$ pstree -p 3372 -u
gnome-terminal(3372,oracle)─┬─bash(3378)───sqlplus(3493)─┬─bash(3628)
└─oracle(3627)
├─bash(3403)───sqlplus(3427)───oracle(3599)
├─bash(3520)───pstree(3672)
├─gnome-pty-helpe(3377)
└─{gnome-terminal}(3379)

この状態で、もう一つのSQL*Plusからsysユーザでshutdown immeidateを実行します…shutdown immeidateは待機させられます。
この時、pid=3627の子プロセスはdefunct状態になってますが、刈り取られていません。この影響でshutdown immediateが待機させられています。
SQL*Plusからshellに入っていない場合、つまり、SQL*Plusのプロセス(pid=3493)の子プロセスが1つしかない場合は、shutdown immeidateが待機させられることはない…この辺りに問題がありそうな気がします…

16:12:59 SYS> 
16:12:59 SYS>
16:13:00 SYS> shutdown immediate

[oracle@pleco ˜]$ pstree -p 3372 -u
gnome-terminal(3372,oracle)─┬─bash(3378)───sqlplus(3493)─┬─bash(3628)
└─oracle(3627)
├─bash(3403)───sqlplus(3427)───oracle(3599)
├─bash(3520)───pstree(3672)
├─gnome-pty-helpe(3377)
└─{gnome-terminal}(3379)

試しに親プロセスであるSQL*Plus(pid=3493)にSIGCHLDシグナルを送ってみましょう。
[oracle@pleco ˜]$ kill -SIGCHLD 3493
[oracle@pleco ˜]$ kill -SIGCHLD 3493
[oracle@pleco ˜]$ kill -SIGCHLD 3493
[oracle@pleco ˜]$ kill -SIGCHLD 3493
[oracle@pleco ˜]$ kill -SIGCHLD 3493
・・・・以下略w・・・・

ん〜〜〜〜〜。ゾンビを刈り取ってくれません><

試しにshellからexitしてみます…

16:09:28 > conn scott/tiger
接続されました。
16:12:45 SCOTT> !
[oracle@pleco ˜]$ exit
exit

16:18:24 SCOTT>
[oracle@pleco ˜]$ pstree -p 3372 -u
gnome-terminal(3372,oracle)─┬─bash(3378)───sqlplus(3493)───oracle(3627)
├─bash(3403)───sqlplus(3427)───oracle(3599)
├─bash(3520)───pstree(3673)
├─gnome-pty-helpe(3377)
└─{gnome-terminal}(3379)

shellからはexitしました。SQL*Plus(pid=3493)の子プロセスがoracleだけ(pid=3627)のゾンビだけになりました。oracleのゾンビ(pid=3627)が残っているうちはshutdown immeidateは待機させられたままです! 

では、もう一回、親プロセスであるSQL*PlusにSIGCHLDシグナルを送ってみましょう! 今度はうまく行きそうな気がします!

[oracle@pleco ˜]$ kill -SIGCHLD 3493
[oracle@pleco ˜]$ pstree -p 3372 -u
gnome-terminal(3372,oracle)─┬─bash(3378)───sqlplus(3493)
├─bash(3403)───sqlplus(3427)───oracle(3599)
├─bash(3520)───pstree(3675)
├─gnome-pty-helpe(3377)
└─{gnome-terminal}(3379)
[oracle@pleco ˜]$

やった〜〜〜〜〜〜 :) SQL*Plus(pid=3493)の子プロセス(pid=3627)のゾンビが見事に刈り取られました!

その瞬間……待機させられていたshutdown immeidateは見事終了!!!!

16:12:59 SYS> 
16:12:59 SYS>
16:13:00 SYS> shutdown immediate
データベースがクローズされました。
データベースがディスマウントされました。
ORACLEインスタンスがシャットダウンされました。
16:18:42 SYS>


shutdown immeidateしない、ほかの理由に遭遇! ネタはこれにて :)




shutdown immeidateしない、ほかの理由に遭遇!
shutdown immeidateしない、ほかの理由に遭遇! #2
shutdown immeidateしない、ほかの理由に遭遇! #3
shutdown immeidateしない、ほかの理由に遭遇! おまけ

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

2011年2月 9日 (水)

shutdown immeidateしない、ほかの理由に遭遇! #3

もう一つ検証し忘れてた!。

この現象に気づいたとき、SQL*Plusから接続していたユーザがsysユーザや、systemユーザだけだったので、それ以外のユーザで接続している場合は大丈夫なんじゃね?都市伝説の検証…

これまでと同様に2つのSQL*Plusを起動しておきます。

1つめのSQL*Plusでは、sysユーザに接続しておきます…

[oracle@leaffish ˜]$ sqlplus /nolog

SQL*Plus: Release 11.2.0.1.0 Production on 水 2月 9 07:26:10 2011

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

07:26:10 > conn / as sysdba
接続されました。


2つめのSQL*Plusでは、scottユーザ(sysユーザ、systemユーザ以外の一般ユーザ)に接続し、host command(!)でshellに入ったままにしておきます…

[oracle@leaffish ˜]$ sqlplus /nolog

SQL*Plus: Release 11.2.0.1.0 Production on 水 2月 9 07:26:23 2011

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

07:26:23 > conn scott/tiger
接続されました。
07:26:29 SCOTT> !
[oracle@leaffish ˜]$


では、sysユーザでshutdown immeidateを実行してみます…

07:26:10 > conn / as sysdba
接続されました。
07:26:16 SYS>
07:26:16 SYS>
07:26:17 SYS> shutdown immediate

お〜〜〜、shutdown immeidateが待機させられています。

「SQL*Plusから接続していたユーザがsysユーザや、systemユーザだけだったので、それ以外のユーザで接続している場合は大丈夫なんじゃね?都市伝説」は都市伝説
Oracleへ接続するユーザに関係なく、SQL*Plusからhost command(!)でshellに入ったままのセッションがある状態で他のセッションからshutdown immeidateを実行した場合、shutdown immeidateは、shutdown timeoutせずにshutdownを待機し続けるのは事実。

まとめると…

現象:

  • shutdown immeidate文を実行したがshutdown timeout(1時間)も発生せずshutdownが待機させれれたままになり停止できない。


発生条件(Updated 16-Feb-2011):

  • Linux/Unix系のOracle11g R2 11.2.0.1.0 (ちなみにそれ以外のリリースでは11.1.0.7.0で同様の現象を確認しています、他のリリースでは未検証。)
  • shutdown immeidateを発行するセッション以外に、SQL*Plusからhost command(! はたま host)でshellに入ったままになっているセッションがある場合。かつ、Bequeath ProtocolでOracleへ接続している。


確認ポイント:

  • アラートログに "SHUTDOWN: Active processes prevent shutdown operation"が繰り返される。
  • ps -ef | grep ora などでプロセスをみると <defunct>な子プロセスがあり、親プロセスがSQL*Plusである。
  • ls -lrt で最後にリストされるトレースファイル(shutdownを実行しているプロセスのトレースファイル)に"ksukia: Attempt <連番?> to re-kill process OS PID=<pid>"のメッセージが繰り返し出力され続ける


対処(Updated 16-Feb-2011):

  • shutdown aboartするしかない。<defunct>なプロセスの親であるSQL*Plusのプロセスをkill -SIGTERMとか-SIGKILLで終了させる。それでもshutdownが待機しているようなら、最後の手段、shutdown abortということで。
    SQL*Plus起動してる人が連絡取れる場所にいるのならその方にexitしてもらうのもありw
  • その他の方法として、sqlnet.oraに、BEQUEATH_DETACH=yes を設定して、サーバープロセスをSQL*Plusの子ではなく、INITの子になってもらう(これがおすすめかも)


現象と回避方法を確認したリリース(Updated 16-Feb-2011):

  • Oracle11g R1 11.1.0.7.0 for Linux (32bit/64bit)
  • Oracle11g R1 11.1.0.7.0 for HP-UX(itanium)
  • Oracle11g R2 11.2.0.1.0 for Linux (32bit/64bit)
  • Oracle11g R2 11.2.0.1.0 for Solaris x86-64

KROWNっぽいまとめ方にしてみました :) (他のUnix系リリースでも作りはおなじでしょうね)

でもさあ、SQL*Plusからホストコマンド、使うんだよね結構。shellに入って、SQL*Plusから入っていたこと忘れて、また、SQL*Plus起動しちゃって…さらにshellに入って…以下繰り返しw。 注意しましょうね。→ 自分。




shutdown immeidateしない、ほかの理由に遭遇!
shutdown immeidateしない、ほかの理由に遭遇! #2
shutdown immeidateしない、ほかの理由に遭遇! #3
shutdown immeidateしない、ほかの理由に遭遇! おまけ

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

shutdown immeidateしない、ほかの理由に遭遇! #2

さて、昨日のつづきです。

Oracle11g R2 11.2.0.1.0 for Linux x86 をVirtualBox4.0 for MacOS XのCentOS5.5にインストールしてあります。

昨日は、SQL*Plusの!(host command)でshellへ入っている状態、かつ、別端末のSQL*plusからshutdown immeidate文を発行してしまうとSQL*Plusのプロセスの子プロセスに<defunct>なプロセスが登場して、shutdown immeidateを待機させてしまうところまでを再現してみました。

今日は、shutdown immediate文を発行するsysユーザ以外のsysユーザがSQL*Plusから接続しているだけでもshutdown immediateを待機させるんじゃないか?疑惑の確認と、shutdown immeidateの待機させるだけじゃなく、shutdown timeoutも抑止しちゃってるよね疑惑などを検鏡検証してみようと思います。

短期遠征先から帰宅している予定だったのによそーーーーー外の1日延泊となってしまった次いでに、VirtuslBox4.0のCentOS5.5のOracle11g R2でたっぷり検証してみましょうw

では、最初に、shutdown immeidateを実行するsysユーザ以外のsysユーザがSQL*Plusから接続しているだけでも実行に他の端末のSQL*Plusでsysユーザが接続しているだけでもshutdown immeidateを待機させるんじゃないか?疑惑するんじゃね?疑惑の検証から。

まず、SQL*Plusでsysユーザに接続している端末を2つ用意して…

一つめのSQL*Plusで〜す。

[oracle@leaffish ˜]$ 
[oracle@leaffish ˜]$ sqlplus /nolog

SQL*Plus: Release 11.2.0.1.0 Production on 水 2月 9 00:09:00 2011

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

00:09:00 > conn / as sysdba
接続されました。
00:09:11 SYS>
00:09:12 SYS>
00:09:12 SYS>

2つめのSQL*Plusで〜す。

[oracle@leaffish ˜]$ 
[oracle@leaffish ˜]$ sqlplus /nolog

SQL*Plus: Release 11.2.0.1.0 Production on 水 2月 9 00:08:39 2011

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

00:08:39 > conn / as sysdba
接続されました。
00:08:44 SYS>
00:08:45 SYS>
00:08:45 SYS>

この状態で1つめのSQL*Plusのsysユーザでshutdown immeidate文を実行します…

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

待機させられることなく、shutdown immeidate文が実行されました。Oracle11g R2 11.2.0.1.0 for Linuxでは”疑惑”だけだったようですね。一安心w。他のバージョンだとどうなんだろ=>TODO.
(昨日の記事では、systemユーザがSQL*Plusで接続している場合、他の端末のSQL*Plusのsysユーザがshutdown immeidateを実行した場合の検証もしていましたよね。これでshutdown immeidate文を実行するセッション以外にsysユーザやsystemユーザのセッションが存在していてもshutdown immeidateが待機させられることは、すくなくともOracle11g R2 11.2.0.1.0 for Linuxでは発生しないことは確かなようです。)


つづいて、SQL*Plusのhost command(!)からshellへ入ったままのセッションが存在している状態でshutdown immeidate文を別セッションから実行してしまうとshutdown immediateを待機させるだけでなく、shutdown timeoutも妨害してしまい1時間を経過してもハングしたままじゃね疑惑を検証してみたいと思います。

同じように2つのSQL*Plusを起動し、ともにsysユーザで接続して、一方のセッションではhost command (!)でshellへ入ったままにしておきます。

1つめのSQL*Plusです…

[oracle@leaffish ˜]$ sqlplus /nolog

SQL*Plus: Release 11.2.0.1.0 Production on 水 2月 9 00:36:01 2011

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

00:36:01 > conn / as sysdba
接続されました。
00:36:04 SYS>
00:36:04 SYS>

2つめのSQL*Plusです〜、こちらはhost commandでshellへ入っておきます。

[oracle@leaffish ˜]$ sqlplus /nolog

SQL*Plus: Release 11.2.0.1.0 Production on 水 2月 9 00:35:51 2011

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

00:35:51 > conn / as sysdba
接続されました。
00:35:54 SYS>
00:35:56 SYS> !
[oracle@leaffish ˜]$
[oracle@leaffish ˜]$

この状態で1つめのSQL*Plusのセッションからshutdown immeidate文を実行してみます…

00:39:34 SYS> 
00:39:34 SYS> shutdown immediate

見事にshutdown immeidateは待機させられます。すばらし〜。
このまま放置してみます。Oracle10g以降の機能であるshutdown timeoutが働いて1時間程度でshutdownはキャンセルされるはずです。しばしの間、ご歓談くださいませ。:)

・・・・・・・・・中略・・・・・・・・・・・

ながらくお待たせいたしました〜、shutdown immeidateを実行してから2時間以上経過しましたが、shutdown timeoutでshutdownがキャンセルされるどころか、止まる気配すらありません…

以下、アラートログの内容です。

Wed Feb 09 00:39:41 2011
Shutting down instance (immediate)
Stopping background process SMCO
Shutting down instance: further logons disabled
Stopping background process QMNC
Stopping background process MMNL
Stopping background process MMON
License high water mark = 2
All dispatchers and shared servers shutdown
Wed Feb 09 00:44:51 2011
SHUTDOWN: Active processes prevent shutdown operation
Wed Feb 09 00:50:05 2011
SHUTDOWN: Active processes prevent shutdown operation
Wed Feb 09 00:55:20 2011
SHUTDOWN: Active processes prevent shutdown operation
Wed Feb 09 01:00:36 2011
SHUTDOWN: Active processes prevent shutdown operation
Wed Feb 09 01:05:48 2011
SHUTDOWN: Active processes prevent shutdown operation
Wed Feb 09 01:10:57 2011
SHUTDOWN: Active processes prevent shutdown operation
Wed Feb 09 01:16:12 2011
SHUTDOWN: Active processes prevent shutdown operation

・・・中略・・・

Wed Feb 09 02:28:17 2011
SHUTDOWN: Active processes prevent shutdown operation
Wed Feb 09 02:33:21 2011
SHUTDOWN: Active processes prevent shutdown operation
Wed Feb 09 02:38:25 2011
SHUTDOWN: Active processes prevent shutdown operation
Wed Feb 09 02:43:28 2011
SHUTDOWN: Active processes prevent shutdown operation
Wed Feb 09 02:48:32 2011
SHUTDOWN: Active processes prevent shutdown operation
Wed Feb 09 02:53:38 2011
SHUTDOWN: Active processes prevent shutdown operation


ということで、「shutdown immeidateを待機させるだけじゃなく、shutdown timeoutも抑止しちゃってるよね!疑惑」は、疑惑じゃなくて真実。


次回へつづく。


shutdown immeidateしない、ほかの理由に遭遇!
shutdown immeidateしない、ほかの理由に遭遇! #2
shutdown immeidateしない、ほかの理由に遭遇! #3
shutdown immeidateしない、ほかの理由に遭遇! おまけ

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

2011年1月29日 (土)

階層問合せか、再帰問合せか、それが問題だ #3 おまけ

階層問合せと再帰問合せを比較するため条件を合わせるという意味でWITH句を利用したが、階層問合せは、再帰問合せと異なりWITH句は必要ないのも有利ですね。

実行計画こそWITH句を利用した場合と変わりまりませんが、階層問合せではWITH句は必須ではないので、WITH句を利用しない階層問合せとWITH句が必須な再帰問合せでは、今のところ、階層問合せの方がPGAの利用サイズが決定的に少ないのは間違いないですね :)

WITH句伴わない階層問合せでPGAサイズを見てみると…

SCOTT> r
1 select
2 empno,
3 ename,
4 job,
5 mgr,
6 level
7 from
8 emp
9 start with
10 mgr is null
11 connect by
12* prior empno = mgr

EMPNO ENAME JOB MGR LEVEL
---------- ---------- --------- ---------- ----------
7839 KING PRESIDENT 1
7566 JONES MANAGER 7839 2
7788 SCOTT ANALYST 7566 3
7876 ADAMS CLERK 7788 4
7902 FORD ANALYST 7566 3
7369 SMITH CLERK 7902 4
7698 BLAKE MANAGER 7839 2
7499 ALLEN SALESMAN 7698 3
7521 WARD SALESMAN 7698 3
7654 MARTIN SALESMAN 7698 3
7844 TURNER SALESMAN 7698 3
7900 JAMES CLERK 7698 3
7782 CLARK MANAGER 7839 2
7934 MILLER CLERK 7782 3

14行が選択されました。

再帰問合せとの差がはっきりでましたね〜 Oracle11g R2では階層問合せでいいかな。再帰問合せはどーゆー場合に有利なんだろ。悩む。

SYS> /

SID NAME VALUE
---------- ------------------------------ ----------
66 session pga memory 672296
66 session pga memory max 672296
66 session uga memory 246552
66 session uga memory max 246552


SYS> /

SID NAME VALUE
---------- ------------------------------ ----------
66 session pga memory 999976
66 session pga memory max 999976
66 session uga memory 377528
66 session uga memory max 377528

SYS>




階層問合せか、再帰問合せか、それが問題だ
階層問合せか、再帰問合せか、それが問題だ #2

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

2011年1月27日 (木)

階層問合せか、再帰問合せか、それが問題だ #2

階層問合せか、再帰問合せか、それが問題だ」の続きです。
前回、Oracleにかな〜り昔からある階層問合せと11g R2 で登場したSQL99対応の再帰問合せの実行計画の違いを見てみたので、今回はPGAをどのように利用するか確認してみた。


セッションID=66で階層問合せ、セッションID=170で再帰問合せを実行する。
以下、SQL*Plusで接続直後の2セッションのPGA/UGAサイズ。(当然どちらも同じ値ね)

SYS> r
1 select
2 ses.sid,
3 sname.name,
4 sstat.value
5 from
6 v$sesstat sstat
7 join v$statname sname
8 on sstat.statistic# = sname.statistic#
9 join v$session ses
10 on sstat.sid = ses.sid
11 where
12 ses.username = 'SCOTT' and
13 (sname.name like '%uga%' or sname.name like '%pga%')
14 order by
15* ses.sid, sname.name

SID NAME VALUE
---------- ---------------------------------------- ----------
66 session pga memory 672296
66 session pga memory max 672296
66 session uga memory 246552
66 session uga memory max 246552
170 session pga memory 672296
170 session pga memory max 672296
170 session uga memory 246552
170 session uga memory max 246552

8行が選択されました。

 
最初に、セッションID=66のセッションで階層問合せを実行してみた。

SYS> /

SID NAME VALUE
---------- ---------------------------------------- ----------
66 session pga memory 1131048
66 session pga memory max 1262120
66 session uga memory 414280
66 session uga memory max 763016
170 session pga memory 672296
170 session pga memory max 672296
170 session uga memory 246552
170 session uga memory max 246552

8行が選択されました。

2011/01/29:訂正:wmo6hashさん、ありがとう。

次に、セッションID=170のセッションで階層再帰問合せを実行してみた。
結果は一目瞭然。PGAの利用サイズも階層問合せが有利みたい。

SYS> /

SID NAME VALUE
---------- ---------------------------------------- ----------
66 session pga memory 1131048
66 session pga memory max 1262120
66 session uga memory 414280
66 session uga memory max 763016
170 session pga memory 999976
170 session pga memory max 1983016
170 session uga memory 312040
170 session uga memory max 1257064

8行が選択されました。

SYS>


なんか、再帰問合せを使う理由を探してみたいが…いまのところみつかんない。Oracle以外のRDBでも使える構文だから覚えとくか!。ぐらいの感覚しか持ててない。再帰問合せのほうが有利だと思えることをしばらく探しまわりそーな悪寒…


階層問合せか、再帰問合せか、それが問題だ

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

2011年1月26日 (水)

階層問合せか、再帰問合せか、それが問題だ

Oracle11g R2から再帰問合せができるようになった。階層問合せのように利用できるわけだけど…気になっていたのでちょっと試してみた。

実行計画を見る限り、11g R2 11.2.0.1.0では、階層問合せが対象表を1度だけアクセスするのに対し、WITH句を利用した再帰問合せは対象表を2度アクセスしているし、ソート回数も再帰問合せの方が多いので、対象表大きい場合や、データ量が多い場合、今のところ階層問合せが有利のように見える。
(そのうち、階層問合せと同じ実行計画になるように仕様変更されるとかあるんですかねぇ〜)

#ほかにも違いがありそうだからあとで調べる=>TODO

文法も再帰問合せの方が面倒だけど、細かな操作は書きやすいのだろうかねぇ〜。

・Hierarchical Query - 階層問合せ(オラクルの方言だけど…この方言は好き)

SQL> r
1 with
2 employees as
3 (
4 select
5 empno,
6 ename,
7 job,
8 mgr,
9 level
10 from
11 emp
12 start with
13 mgr is null
14 connect by
15 prior empno = mgr
16 )
17 select *
18 from
19* employees

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

-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14 | 728 | 4 (25)| 00:00:01 |
| 1 | VIEW | | 14 | 728 | 4 (25)| 00:00:01 |
|* 2 | CONNECT BY NO FILTERING WITH START-WITH| | | | | |
| 3 | TABLE ACCESS FULL | EMP | 14 | 308 | 3 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------

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

2 - access("MGR"=PRIOR "EMPNO")
filter("MGR" IS NULL)

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


EMPNO ENAME JOB MGR LEVEL
---------- ---------- --------- ---------- ----------
7839 KING PRESIDENT 1
7566 JONES MANAGER 7839 2
7788 SCOTT ANALYST 7566 3
7876 ADAMS CLERK 7788 4
7902 FORD ANALYST 7566 3
7369 SMITH CLERK 7902 4
7698 BLAKE MANAGER 7839 2
7499 ALLEN SALESMAN 7698 3
7521 WARD SALESMAN 7698 3
7654 MARTIN SALESMAN 7698 3
7844 TURNER SALESMAN 7698 3
7900 JAMES CLERK 7698 3
7782 CLARK MANAGER 7839 2
7934 MILLER CLERK 7782 3

14行が選択されました。

経過: 00:00:00.00
SQL>

・Recursive Query - ANSI SQL99対応だけど、今のところ好きじゃない。長いんだもん。

SQL> 
SQL> r
1 with
2 employees (
3 empno,
4 ename,
5 job,
6 mgr,
7 lvl
8 ) as (
9 select
10 empno,
11 ename,
12 job,
13 mgr,
14 1 lvl
15 from
16 emp
17 where
18 mgr is null
19 union all
20 select
21 e1.empno,
22 e1.ename,
23 e1.job,
24 e1.mgr,
25 e2.lvl + 1
26 from
27 emp e1 join employees e2
28 on e2.empno = e1.mgr
29 )
30 search depth first by mgr, empno set order#
31 select
32 empno,
33 ename,
34 job,
35 mgr,
36 lvl
37 from
38 employees
39 order by
40* order#

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

--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3 | 156 | 10 (10)| 00:00:01 |
| 1 | VIEW | | 3 | 156 | 10 (10)| 00:00:01 |
| 2 | UNION ALL (RECURSIVE WITH) BREADTH FIRST| | | | | |
|* 3 | TABLE ACCESS FULL | EMP | 1 | 22 | 3 (0)| 00:00:01 |
|* 4 | HASH JOIN | | 2 | 96 | 7 (15)| 00:00:01 |
| 5 | RECURSIVE WITH PUMP | | | | | |
|* 6 | TABLE ACCESS FULL | EMP | 13 | 286 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------

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

3 - filter("MGR" IS NULL)
4 - access("E2"."EMPNO"="E1"."MGR")
6 - filter("E1"."MGR" IS NOT NULL)

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




EMPNO ENAME JOB MGR LVL
---------- ---------- --------- ---------- ----------
7839 KING PRESIDENT 1
7566 JONES MANAGER 7839 2
7788 SCOTT ANALYST 7566 3
7876 ADAMS CLERK 7788 4
7902 FORD ANALYST 7566 3
7369 SMITH CLERK 7902 4
7698 BLAKE MANAGER 7839 2
7499 ALLEN SALESMAN 7698 3
7521 WARD SALESMAN 7698 3
7654 MARTIN SALESMAN 7698 3
7844 TURNER SALESMAN 7698 3
7900 JAMES CLERK 7698 3
7782 CLARK MANAGER 7839 2
7934 MILLER CLERK 7782 3

14行が選択されました。

経過: 00:00:00.00
SQL>

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

2011年1月 9日 (日)

CentOS5.5のVirtualBoxも4.0にアップデートした

MacOS X版VirtualBoxにつづき、CentOS5.5のVirtualBoxも3.2から4.0にアップデートした。

ついでなので、CentOS5.5のVirtualBoxのSolaris10をMacOS XのTerminalからX11 forwadingを使ってMac側で起動してみた。(どんな次いでだw)

まず最初はCentOSのVirtualBoxを3.2から4.0へアップデート。MacOS Xの場合特に気にすることもないのだが、Linuxの場合、3.2がインストールされたままだと4.0でアップデートできないので、一旦、VirtualBox 3.2はアンインストールする必要がある。アンインストールがうまくいったら4.0をインストールがうまくいく。(新規で4.0をインストールする場合は気にする必要ないですよ…)

ちなみに、うちのは64bit版です ;)

[root@lampeye Desktop]# cat /etc/redhat-release
CentOS release 5.5 (Final)
[root@lampeye Desktop]#
[root@lampeye Desktop]# uname -r
2.6.18-194.26.1.el5
[root@lampeye Desktop]#
[root@lampeye Desktop]# rpm -e VirtualBox-3.2-3.2.12_68302_rhel5-1.x86_64
[root@lampeye Desktop]# rpm -ivh VirtualBox-4.0-4.0.0_69151_rhel5-1.x86_64.rpm
警告: VirtualBox-4.0-4.0.0_69151_rhel5-1.x86_64.rpm: ヘッダ V4 DSA signature: NOKEY, key ID 98ab5139
準備中... ########################################### [100%]
1:VirtualBox-4.0 ########################################### [100%]

Creating group 'vboxusers'. VM users must be member of that group!

No precompiled module for this kernel found -- trying to build one. Messages
emitted during module compilation will be logged to /var/log/vbox-install.log.

Stopping VirtualBox kernel modules [ OK ]
Uninstalling old VirtualBox DKMS kernel modules [ OK ]
Trying to register the VirtualBox kernel modules using DKMS [失敗]
(Failed, trying without DKMS)
Recompiling VirtualBox kernel modules [ OK ]
Starting VirtualBox kernel modules [ OK ]


[root@lampeye Desktop]#

ちゃんとやっとこうかな〜と思って、やり直しておいた(2011/1/10更新)

[root@lampeye Desktop]# cat /etc/redhat-release
CentOS release 5.5 (Final)
[root@lampeye Desktop]#
[root@lampeye Desktop]# uname -r
2.6.18-194.26.1.el5
[root@lampeye Desktop]#
[root@lampeye Desktop]# wget -q http://download.virtualbox.org/virtualbox/debian/oracle_vbox.asc
[root@lampeye Desktop]# rpm --import oracle_vbox.asc
[root@lampeye Desktop]# rpm --checksig VirtualBox-4.0-4.0.0_69151_rhel5-1.x86_64.rpm
VirtualBox-4.0-4.0.0_69151_rhel5-1.x86_64.rpm: (sha1) dsa sha1 md5 gpg OK
[root@lampeye Desktop]# rpm -e VirtualBox-3.2-3.2.12_68302_rhel5-1.x86_64
[root@lampeye Desktop]# rpm -ivh VirtualBox-4.0-4.0.0_69151_rhel5-1.x86_64.rpm
準備中... ########################################### [100%]
1:VirtualBox-4.0 ########################################### [100%]

Creating group 'vboxusers'. VM users must be member of that group!

No precompiled module for this kernel found -- trying to build one. Messages
emitted during module compilation will be logged to /var/log/vbox-install.log.

Stopping VirtualBox kernel modules [ OK ]
Uninstalling old VirtualBox DKMS kernel modules [ OK ]
Trying to register the VirtualBox kernel modules using DKMS [失敗]
(Failed, trying without DKMS)
Recompiling VirtualBox kernel modules [ OK ]
Starting VirtualBox kernel modules [ OK ]
[root@lampeye Desktop]#

これで3.2から4.0へ移行は完了! :)

では本題、MacOS XのTerminalからX11 forwordingを使ってCentOS5.5のVirtualBox4.0を起動し、GuestOSとしてSolaris10を起動してみます。(なお、このVM、以前、MacOS XのVirtualBox3.2で作成したものをCentOS5.5にコピーというか移動したもの…)

discus:˜ discus$
discus:˜ discus$
discus:˜ discus$ ssh -Y discus@lampeye
discus@lampeye's password:
Warning: No xauth data; using fake authentication data for X11 forwarding.
Last login: Sun Jan 9 11:19:09 2011 from 192.168.1.30
[discus@lampeye ˜]$
[discus@lampeye ˜]$ VirtualBox &
[1] 4796
[discus@lampeye &