Oracle10gのSQLチューニングについての検証 その2

投稿日: 2006年11月01日

<Oracle10gのSQLチューニングについての検証 その2>
~v$sql活用編~
ペンネーム: りん

先週に引き続き、v$sqlのWAITイベントをチェックしていきます。

先週はAPPLICATION_WAIT_TIMEの値を見てきました。
先週はTraceを仕掛けたのですが、もっとお手軽に見る方法がないものでしょ
うか?

前回と同じように…

1.セッションAで更新
SQL> update test_table set value = '500' where id = '500';

1行が更新されました。

2.セッションBで更新
SQL> update test_table set value = '500' where id = '500';
*****ロック解放待ちが発生します*****

セッションA、セッションBの順でCommitを発行し、v$sqlを参照します。

SQL> select sql_text,application_wait_time from v$sql where application_wait_time != '0';

SQL_TEXT                                             APPLICATION_WAIT_TIME
---------------------------------------------------- ---------------------
update test_table set value = '500' where id = '500'                   993

ここまでは先週と同じです。
APPLICATION_WAIT_TIMEが9.93秒であったことはわかります。
内訳を知る為に前回は、Event Traceを仕掛けることによりトレースファイル
から更新ロックであることがわかりましたが、今回は、10gから追加された
v$session_wait_classと、v$session_eventを参照します。

まず、待ちが発生したSIDをv$sessionから探します。

SQL> select sid,username from v$session where username = 'RIN';

       SID USERNAME
---------- ------------------------------
       251 RIN

SQL> select sid,event,total_waits,time_waited
  2  from v$session_event where sid = '251'
  3  order by total_waits desc;

       SID EVENT                         TOTAL_WAITS TIME_WAITED
---------- ----------------------------- ----------- -----------
       251 SQL*Net message to client              14           0
       251 SQL*Net message from client            13        8365
       251 db file sequential read                 4           3
       251 enq: TX - row lock contention           4         993
       251 log file sync                           2           0

RINユーザのセッション(SID:251)には、なにやら、いろんなWAITイベントが
発生しているようです。
これらをカテゴライズするために、EVENT_IDをキーに、v$event_nameを参照し
ます。

SQL> select name,wait_class from v$event_name where event_id in
  2  ( select event_id from v$session_event s where s.sid = '251' );

NAME                                     WAIT_CLASS
---------------------------------------- --------------------
log file sync                            Commit
db file sequential read                  User I/O
enq: TX - row lock contention            Application
SQL*Net message to client                Network
SQL*Net message from client              Idle

今回、Applicationにカテゴライズされているのは、enq: TX – row lock
contentionだけですね。
TraceやStatspackを取得しなくても1度のSQL文でAPPLICATION_WAIT_TIMEが
分かります。
順を追って書いてきましたが、一回のSQL文で見ると以下のようになります。

SQL> select en.wait_class,en.name,se.time_waited
 2      from
 3          v$event_name en, v$session_event se
 4      where
 5          en.event_id = se.event_id and
 6          en.wait_class != 'Idle' and      -- Idle イベントは除外
 7          se.sid = ( select sid from v$session where username = 'RIN' ) -- 指定のセッションID
 8      order by se.time_waited desc;

WAIT_CLASS           NAME                                     TIME_WAITED
-------------------- ---------------------------------------- -----------
Application          enq: TX - row lock contention                    993
User I/O             db file sequential read                            9
Commit               log file sync                                      0
Network              SQL*Net message to client                          0

このセッションでの、WAITイベントは、Applicationクラスの更新ロック待ち
に9.93秒。I/O処理に、0.09秒費やしたと判断できます。

では、この方法で、どんなイベントがキャッチアップできるのでしょうか?
TPC負荷プログラムを使用して、280セッションで負荷をかけてみた結果です。

v$session_eventでは、セッションが終了してしまうと最終的な結果を参照す
るのが難しそうなので、v$system_eventの差分を使用します。

実行前
SQL> select event,sum(se.total_waits),sum(se.total_timeouts),sum(se.time_waited),en.wait_class
  2 from
  3     v$system_event se, v$event_name en
  4 where
  5     se.event_id = en.event_id and
  6     en.wait_class = 'Application'
  8 group by event, wait_class
  9 order by sum(se.total_waits);

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

Applicationクラスのものは存在しないようです。

実行後
SQL> select event,sum(se.total_waits) total_w,
 2  sum(se.total_timeouts) total_t,
 3  sum(se.time_waited) time_w,en.wait_class
 2  from
 3      v$system_event se, v$event_name en
 4  where
 5      se.event_id = en.event_id and
 6      en.wait_class = 'Application'
 7  group by event, wait_class
 8  order by sum(se.time_waited);

EVENT                          TOTAL_W TOTAL_T TIME_W WAIT_CLASS
------------------------------ ------- ------- ------ --------------------
enq: TX - row lock contention        8       0      6 Application

SQL> select sql_text,application_wait_time,concurrency_wait_time,user_io_wait_time,plsql_exec_time
  2  from
  3      v$sql
  4  where
  5      application_wait_time > 0;

   SQL_TEXT
   --------------------------------------------------------------------------------
#1 BEGIN tpc_engine.tpc_stress(:1,:2); END;
#2 UPDATE WAREHOUSE SET W_YTD = W_YTD + :B2 WHERE W_ID = :B1
#3 UPDATE DISTRICT SET D_YTD = D_YTD + :B3 WHERE D_W_ID = :B2 AND D_ID = :B1

  APPLICATION_WAIT_TIME CONCURRENCY_WAIT_TIME USER_IO_WAIT_TIME PLSQL_EXEC_TIME
  --------------------- --------------------- ----------------- ---------------
#1                    7                   254              2847        17705118
#2                    4                    11                 0               0
#3                    3                    40                 0               0

tpc_engineパッケージ内のUpdate文が2件APPLICATION_WAIT_TIMEを検出しトー
タルで、0.07秒であることがわかります。
しかし、v$system_eventを参照すると、0.06秒となっています。
若干異なるのはなぜでしょう?

今回作成されたTraceファイルを確認してみます。

WAIT #17: nam='enq: TX - row lock contention' ela= 5041 p1=1415053318 p2=393218 p3=1061
WAIT #17: nam='enq: TX - row lock contention' ela= 830 p1=1415053318 p2=393218 p3=1103
WAIT #17: nam='enq: TX - row lock contention' ela= 654 p1=1415053318 p2=262182 p3=1625
WAIT #29: nam='enq: TX - row lock contention' ela= 5944 p1=1415053318 p2=393245 p3=1044
WAIT #29: nam='enq: TX - row lock contention' ela= 18263 p1=1415053318 p2=393218 p3=1061
WAIT #32: nam='enq: TX - row lock contention' ela= 26111 p1=1415053318 p2=393236 p3=1079
WAIT #29: nam='enq: TX - row lock contention' ela= 5031 p1=1415053318 p2=393234 p3=1083
WAIT #22: nam='enq: TX - row lock contention' ela= 2105 p1=1415053318 p2=131081 p3=1172

8件存在するので、v$system_eventのTotal_Waits=8と一致します。
elaの値を合計してみると、63979マイクロ秒、つまり0.06秒であることが判ります。
しかし、v$sqlビューは値が若干違います。

WAITの後に番号がついています。#??っていう部分です。
WAIT #17:←この部分です。

それぞれの番号が、なんのSQL文なのかを確認してみると

17…UPDATE WAREHOUSE SET W_YTD = W_YTD + :B2 WHERE W_ID = :B1
29…UPDATE WAREHOUSE SET W_YTD = W_YTD + :B2 WHERE W_ID = :B1
32…UPDATE DISTRICT SET D_YTD = D_YTD + :B3 WHERE D_W_ID = :B2 AND D_ID = :B1
22…UPDATE DISTRICT SET D_YTD = D_YTD + :B3 WHERE D_W_ID = :B2 AND D_ID = :B1

という結果でした。

それぞれのelaを合計すると

17…6525(0.01)
29…29238(0.03)
32…26111(0.03)
22…2105(0.00)

となります。
全てマイクロ秒単位なので、これを秒に変換し、小数点第3位を四捨五入して
100分の1秒以下は切り捨てたのが()の中の数字です。

それぞれのSQL文毎に集計し、突き合わせてみると

UPDATE WAREHOUSE SET W_YTD = W_YTD + :B2 WHERE W_ID = :B1→0.04秒
UPDATE DISTRICT SET D_YTD = D_YTD + :B3 WHERE D_W_ID = :B2 AND D_ID = :B1→0.03秒

となるので、v$sqlとv$system_eventの計算方法の違いによる誤差みたいですね

次回は、Application以外のWAITについても調べたいと思います。

最近、自炊にこだわる茅ヶ崎にて