【Oracle】統計情報取得と確認方法
テーブルの統計情報の取得方法とその確認方法の基本めも
◆テーブルの統計情報の取得
「DBMS_STATS.GATHER_TABLE_STATS」というのを実行する。
実行コマンドは以下の通り
exec DBMS_STATS.GATHER_TABLE_STATS('TEST' , 'TEST_TABLE');
第一引数はテーブルの所有者(OWNER)、第二引数はテーブルの物理名。
この例だと「TESTってユーザのTEST_TABLEってテーブルの統計情報を取れ」という指示になる。
この2つが必須パラメータで、第三引数以降にいろいろ指定できるが、
比較的よく目にするのが第四引数に指定する「ESTIMATE_PERSENT」だろう。
テーブルが巨大になればなるほど、そのテーブルに対する統計情報取得には時間がかかるので、
100%(全体)の統計情報取得なんかやってられねえってことが往々にしてある。
(ちなみに↑の例だと(第四引数未指定なので)100%取得になる)
なのでテーブルの一部をサンプル的に見て統計情報を取ってみるということがよくある。
この「サンプル」の範囲を第四引数に指定する。
第四引数を指定する場合、
統計情報のパーセンテージを指定する。
つまり「1」だと1%取得の意味になる。
exec DBMS_STATS.GATHER_TABLE_STATS('TEST' , 'TEST_TABLE' , '' ,1);
とか。
ちなみに第三引数はパーテーション名である。
パーテショニングしてないテーブルの場合は空文字で良い。
他にももっといっぱい引数がある(ex.「INDEXも一緒に統計情報取る」、「統計情報取得処理自体を多重化する」)が、
基本としてはこれだけ知ってれば問題ない。
◆テーブルの統計情報の確認方法
DBA_TABLESとかに情報として記録される。
select
a.owner
,a.TABLE_NAME
,a.NUM_ROWS
,a.SAMPLE_SIZE
,to_char(a.LAST_ANALYZED,'yyyy/MM/dd HH24:mi:ss') as LAST_ANALYZED_CHAR
from DBA_tables a
where 0=0
and w.OWNER = 'TEST'
and a.TABLE_NAME = 'TEST_TABLE'
↑のSQLで取得する項目のうち
■NUM_ROWS:テーブル全体の件数
■SAMPLE_SIZE:統計情報を取得した件数
■LAST_ANALYZED:統計情報を最後に取得した日
SAMPLE_SIZEは、↑の統計情報取得時に第四引数ESTIMATE_PERSENTに指定した割合に応じた件数が入る。
例えば1500件のテーブルに1を指定して統計情報取得した場合、SAMPLE_SIZEに入る値は15(=1500*1/100)になる。
(要するに15件しか見てないってことになる)
この15件がテーブル全体のデータ傾向を縮図的にきちんとあらわしているなら問題ないが、
そうでない場合は実質この統計情報は役に立たないことになる。
なので出来うる限り大量のサンプルを取るように指示した方がいい(出来るのなら常に100%取得が望ましい)。
統計情報取得した件数が多ければ多いほどOracleが正確な実行計画をたててくれるようになるが、
大がかりな統計情報取得にはそれなりの時間がかかる。
統計情報取得処理それ自体も、サーバリソースも結構食う傾向にあるので、
巨大テーブルに対する統計情報取得は悩みどころである。
ただ「ないよりマシ」って程度で「1%の統計情報」なんぞ取ったところでたかが知れており、
経験的には15%とか20%とかが好んでよく使われるように思う。
個人的には「~%」という考え方自体が肌に合わず、
取るときは何も考えず100%で取りたい派である。
(まあそれも時と場合に寄る)
一方で、統計情報に頼りっきりな実装は経験上非常に危うい。
「統計情報があればこのSQLでもまともに動く」という考えはやめたほうが良い、という意味である。
期待通りに動いてほしいなら「hintでガチガチに固めて実行計画を固定する」ぐらいの気持ちでいるべきだ。
そういう意味で、個人的な感覚では、この「統計情報」ってのは正直気休め程度に過ぎない。
なので、基本的に上で書いたように大体15%とか20%で良いんだと思う。
(あろうがなかろうがあまり関係ない、というように個人的には思っている)
ちなみに、最近、統計情報が下手に「ある」せいで悩まされた実体験がある。
(1)とあるテーブルを新規作成(Create Table)し、
(2)統計情報を取得した後、
(3)移行データ50万件をいれて
バッチを回したところ、バッチが超牛歩になって全然進まなくなった。
(2)の統計情報はテーブル空っぽ状態でとっているので、NUM_ROWSもSAMPLE_SIZEも0件になっており、
つまりOracleとして「このテーブルはすっからかん」と認識していて
このテーブルを参照するバッチ処理が全然動かなくなってしまったのである。
統計情報をとるなら(3)⇒(2)の順番にしておく必要があった(考えてみれば当たり前ではある)
単純にこれだけ見れば、(2)の実施タイミングを誤ったという「手順のミス」ところになるが、
実装の観点ではこれも、↑に書いた「統計情報に頼っている実装」の一種に入るだろう。
もしくは、(2)が一切なければあるいは別の動きをしたかもしれない。
この件だけでいえば、本来、実行計画を精査する目的で取る統計情報が、逆に足かせになるあほくさい例であり、
然るべきタイミングで統計情報取ってればこんなことにならなかったのにねという感じではあるが、
見方を変えれば、「タイミング誤るだけでまともに動作しなくなる危うさ」を包含しているというわけで、
「ないよりマシ」というより「ないほうがマシ」にあたる。
以上のように、統計情報はうまく使わないとだめなのである。