Direct path read

基本情報

 

direct path readイベントの待機は、Parallel Queryの実行時に、スレーブセッション(Slave Session)が実行されるdirect path I/Oによって引き起こされます。direct path I/Oは、SGA内のバッファキャッシュを経由せずにセッションのPGAに直接ブロックを読み込むことで、I/O方式(synchronous I/O、asynchronous I/O)に関係なく、実行されることがありますが、ハードウェアプラットフォームとDISK_ASYNCH_IOパラメータに影響を受けます。Direct read I/Oは、一般的に、ディスクの一時的(temporary)セグメントへのアクセス時に使用されます。これらの操作は、並べ替え(sort)、並列照会(parallel query)とハッシュ結合(hash join)の際に発生します。

 

注意:Oracle11gでは、並列処理ではなく、一般的なQueryでもTable Full ScanのDirect Path Readを実行することができます。したがって、既存のdb file scattered read待機イベントが観察されたQueryがOracle11gでdirect path readイベントを報告することになります。これについての詳細は、次のBlogポストを参照してください。

 

Disabling direct path read for the serial full table scan – 11g

 

スレーブセッションがdirect path readを実行中にコーディネータのセッション(Coordinator Session)は、スレーブセッションからの応答が来るのを待ってPX Deq:Excute Replyイベントを待機することが観察されます。Parallel Queryの実行時に発生するdirect path read待機は必然的なものです。もしdirect path readイベントの待機時間が過度に高く出た場合は、次のような観点から、チューニングポイントを見つけなければなりません。 このイベントの待機回数と待機時間は誤解を招く可能性があります。もし非同期I/Oが使用されていなかった場合、セッションは、I/Oが完了するまで待機します。しかし、I/O要求が開始された時点から待機時間を計算せずに、I/O要求が完了した後、データをアクセスするときにdirect path read待機イベントを待つことになります。したがって待ち時間はかなり短く表示されます。

 

もし非同期I/Oが使用可能で、現在使用している場合、セッションは多数のdirect read要求をした後、PGA内部にキャッシュされたブロックの処理を行います。セッションがPGAの内部にキャッシュされたブロックがなくて処理を進めていない時点でdirect path read待機イベントが発生しています。したがって、読み取り要求の回数は、待機回数と同じではありません。これらの不一致のために、V$ SYSTEM_EVENTおよびV$ SESSION_EVENTビューで表示されるdirect path read待機イベントの数値は信頼できないのです。

 

LOBセグメントを読み取るときに発生するdirect path read待機は、オラクル8.1.7からdirect path read(lob)待機イベントで別々に区分されます。 通常direct path read待機イベントは、一時的(temporary)または一般的な表スペースからdirect readオペレーションを実行するSQL文によって発生します。 ORDER BY、GROUP BY、UNION、DISTINCT、ROLLUPのようにソートが必要な関数を実行するSQL文は、PGA内で許可することができるよりも大きなデータを並べ替える必要がある場合、ソートラン(sort run)を一時テーブルスペースに記録します。一時テーブルスペースのソートランは最終的な結果を作成するために、順次PGAに読まれてマージされます。ソートランをPGAに読み込む間、そのセッションはdirect path read待機イベントを待機します。Hintまたはオプティマイザの判断によりMERGE結合を実行するSQL文も、並べ替え操作が必要となります。

 

ヒント(hint)またはオプティマイザの判断によってHASH結合を実行するSQL文は、PGA内で許可することができない大きさのハッシュパーティションを一時テーブルスペースに記録します。TEMPORARY表スペースに記録されたハッシュパーティションは、SQLステートメントの条件に一致するレコードを見つけるために戻ってPGAに読み込まれます。ハッシュパーティションをPGAに読み込む間、そのセッションはdirect path read待機イベントを待機します。

 

V$ SESSION_EVENTビューのTOTAL_WAITSまたはTIME_WAITED値を利用して、direct path read待機イベントを評価しない方が良いです。代わりに、次のクエリを使用して、大量のdirect readを実行しているセッションを見つけることができます。physical reads directはparentセッションによって開始されたdirect readsとparentセッションが管理するスレーブプロセスによって発生したすべてのdirect readsの合計で構成されています。

 

select a.name, b.sid, b.value, 
       round((sysdate - c.logon_time) * 24) hours_connected
from   v$statname a, v$sesstat b, v$session c
where  b.sid        = c.sid
and    a.statistic# = b.statistic#
and    b.value      > 0
and    a.name       = 'physical reads direct'
order by b.value;

NAME                            SID      VALUE   HOURS_CONNECTED
-------------------------     ----     ---------- ------------------------
physical reads direct        2               41             980
physical reads direct        4               41             980
physical reads direct        5        445186             980

 

大量のdirect readsを発生させるセッションを見つけることに加えて、セッションがデータを読み取る位置(一時テーブルスペース、データファイルなど)、待機を発生させるSQL文を把握しなければなりません。次のクエリでは、これに対する答えを与えることができます。一時テーブルスペースからデータを読み取るセッションは、ソートやハッシュセグメントを読んでいるでしょう。データファイルからデータを読んでいるセッションは、並列クエリスレーブ(parallel query slave)です。

 

select a.event,
       a.sid, 
       c.sql_hash_value hash_value,
       decode(d.ktssosegt,
              1,'SORT', 2,'HASH',    3,'DATA',
              4,'INDEX',5,'LOB_DATA',6,'LOB_INDEX',
              null) as segment_type,
       b.tablespace_name,
       b.file_name
from   v$session_wait a, dba_data_files b, v$session c, x$ktsso d
where  c.saddr      = d.ktssoses(+)
and    c.serial#    = d.ktssosno(+)
and    d.inst_id(+) = userenv('instance')
and    a.sid        = c.sid
and    a.p1         = b.file_id
and    a.event      = 'direct path read'
union all
select a.event,
       a.sid,
       d.sql_hash_value hash_value,
       decode(e.ktssosegt,
              1,'SORT', 2,'HASH',    3,'DATA',
              4,'INDEX',5,'LOB_DATA',6,'LOB_INDEX',
              null) as segment_type,
       b.tablespace_name,
       b.file_name
from   v$session_wait a, dba_temp_files b, v$parameter c, 
       v$session d, x$ktsso e
where  d.saddr      = e.ktssoses(+)
and    d.serial#    = e.ktssosno(+)
and    e.inst_id(+) = userenv('instance')
and    a.sid        = d.sid
and    b.file_id    = a.p1 - c.value
and    c.name       = 'db_files'
and    a.event      = 'direct path read'
order by 1,2;

EVENT                SID   HASH_VALUE    SEGMENT    TABLESPACE_N         FILE_NAME
------------------   ---- ------------  --------   -----------------  --------------------
direct path read     8     511952958       SORT      TEMP_BATCH         temp_batch_01.dbf
direct path read     9    3138787393                 ORDERS                 orders_01.dbf
direct path read    11    3138787393                 ORDERS                 orders_01.dbf
direct path read    12    3138787393                 ORDERS                 orders_01.dbf
direct path read    14    3138787393                 ORDERS                 orders_01.dbf

 

一時表スペースからソートセグメントを読んでいるセッションは、SORT_AREA_SIZE(または、Oracle 9iでPGA_AGGREGATE_TARGETを使用している場合、work area size)がメモリソートを実行するのに十分ではないことを意味します。しかし、これは問題にはなりません。すべてのソートがメモリでのみ実行されるのは、現実的に不可能だからです。しかし、多くのマルチパス(multi pass)ソートは可能な限り防止しなければなりません。なぜなら、マルチパスソートは、一時表スペースのかなりのI/Oを誘発し、非常に遅くなるからです。SQLステートメントがマルチパスソートをするかどのように確認できますか? Oracle 9iの以前のバージョンでは、容易ではありません。 10032トレースイベントを設定した後、トレースファイルを確認しなければならないのです。しかし、Oracle 9iのから、ソートを実行するSQL文のhash valueを使用して、V $ SQL_WORKAREAまたはV $ SQL_WORKAREA_ACTIVEビューを照会してみるだけでも、確認が可能となりました。ソートのさらなる詳細は、International Oracle Users Group(IOUG)2004 conference proceedings(www.ioug.org)の「If Your Memory Serves You Right」ホワイトペーパーを参照してください。

 

このような場合には、チューニングの目標は、ディスクソート回数を最小限にすることです。 SORT_AREA_SIZE(またはPGA_AGGREGATE_TARGET)のサイズを増加することにより、ディスクソートの回数を減らすことができます。しかし、これは非常にSORT_AREA_SIZEが小さく設定されている場合を除き、根本的な解決方法とは言えません。まず、アプリケーションでソートが必ず必要かを確認しなければならないのです。アプリケーションは、DISTINCTとUNION関数を濫用して使用する傾向があります。可能であればUNIONよりUNION ALLを使用して、SORT MERGEよりHASH結合、HASH結合ではなく、NESTED LOOPS結合を使用するようにしなければなりません。また、オプティマイザドライビングテーブルを正しく選択したかも確認する必要があります。結合インデックスのカラムをよく使われるORDER BY句とよく合うように設定しておけばソートを避けることもできます。 Oracle 9iのであれば、PGA_AGGREGATE_TARGETを使用してSQL work areaを自動的に割り当てして使うことができるように設定することも検討してのみましょう。統計的に見たときに、自動的にメモリ管理をするようにすると、メモリソートの割合をより高めてくれます。

 

セグメントを読んでいるセッションを発見した場合は、HASH_AREA_SIZE(Oracle 9iのでPGA_AGGREGATE_TARGETを使用している場合は、work area size)が小さく、メモリにハッシュテーブルを収容していない場合となります。解決方法は、既に述べなったものと類似しています。もしHASH_AREA_SIZEが小さすぎる場合でなければ、HASH_AREA_SIZE(またはPGA_AGGREGATE_TARGET)を調整する前に、まずアプリケーションとSQLステートメントをチューニングする必要があります。

 

もし並列クエリ(parallel query)スレーブでdirect readsが発生した場合、並列スキャン(parallel scan)がparent SQL文に適しているとスレーブの数が適しているかを確認しなければなりません。また、クエリスレーブがシステムのCPUとディスク・リソースをすべて使い果たしていないことを確認しなければなりません。Parent SQL問い合わせhash valueとクエリスレーブが実行するSQLのhash valueが同じではないので、parent SQL文を見つけることは容易ではないことでありません。オラクル8.1.5でV$ PX_SESSIONビューが導入されるまでは、さらに大変なことでした。下の2つの例では、、並列クエリが実行されるとparent SQL文を見つける方法を、Oracle8.1.5以前のバージョンとそれ以降のバージョンについて、それぞれ説明します。

- オラクル8.1.5以前のバージョン
--Note:下のクエリは、SYSユーザによって実行される並列クエリ文を区別することができません。
- なぜなら、SYSユーザーは同じAUDSIDを共有するからです。
select decode(ownerid、2147483644、「PARENT」、「CHILD」)stmt_level、
         audsid、
         sid、
         serial#、
         username、
         osuser、
         process、
         sql_hash_value hash_value、
         sql_address
from v $ session
where type <> 'BACKGROUND」
and audsid in(select audsid
                        from v $ session
                       group by audsid
                      having count(*)> 1)
order by audsid、stmt_level desc、sid、username、osuser;

STMT_L  AUDSID  SID    SERIAL    #USERNAME    OSUSER    PROCESS    HASH_VALUE   SQL_ADDR
------ -------- ---- ---------- ------------- --------- --------- ------------- ----------
PARENT 3086501   20     779       INTREPID     cdh8455    16537     3663187692   A0938E54
CHILD  3086501   12     841       INTREPID     cdh8455    16544      817802256   A092E1CC
CHILD  3086501   14    2241       INTREPID     cdh8455    16546      817802256   A092E1CC
CHILD  3086501   17    3617       INTREPID     cdh8455    16540      817802256   A092E1CC
CHILD  3086501   21     370       INTREPID     cdh8455    16542      817802256   A092E1CC

下のクエリは、Oracle 8.1.5以降のバージョンで使用される。
select decode(a.qcserial#、null、「PARENT」、「CHILD」)stmt_level、
         a.sid、
         a.serial#、
         b.username、
         b.osuser、
         b.sql_hash_value、
         b.sql_address、
         a.degree、
         a.req_degree
  from v $ px_session a、v $ session b
where a.sid = b.sid
 order by a.qcsid、stmt_level desc;

STMT_L     SID   SERIAL     #USERNAME   OSUSER   HASH_VALUE   SQL_ADDR   DEG   REQ_DEG
------- ----- -------- ------------- --------- ------------- ----------- ------ -------
PARENT   20     779      INTREPID     cdh8455    3663187692    A0938E54
CHILD    17    3617      INTREPID     cdh8455     817802256    A092E1CC    4      4
CHILD    21     370      INTREPID     cdh8455     817802256    A092E1CC    4      4
CHILD    12     841      INTREPID     cdh8455     817802256    A092E1CC    4      4
CHILD    14    2241      INTREPID     cdh8455     817802256    A092E1CC    4      4

 

パラメータと待機時間

 

待機パラメータ

 

Direct path read待機イベントの待機パラメータは以下の通りです。

・P1:Absolute File#
・P2:Starting Block#
・P3:ブロック数

 

待機時間

 

I/O関連のイベントなので、タイムアウトが発生せず、セッションは、I/Oが完了するまで待機します。

 

チェックポイントとソリューション

 

Parallel Queryの性能を高める

 

Parallel Queryを実行する過程でのdirect path read待機は必然的なもので、待機自体をチューニングすることは不可能です。むしろSQLチューニングを介してParallel Query自体の性能を改善させることが正しいアプローチとなります。システムの容量に比べて不必要にParallel Queryを実行することは、むしろ性能を低下させる要因となります。データファイルに直接読み取り操作を実行する前に、読み取りの対象となるオブジェクトのダーティブロックがデータ・ファイルに記録されなければならないというものがあります。つまりチェックポイントが発生するようになるのです。このタスクを実行する間、コーディネータのセッションは、enq:TC – contention待機が発生します。

 

I/Oシステムの性能を高める

 

I/Oの概要

 

I / Oは、Oracleで最も重要な資源です。Oracleが提供するすべての機能は、最終的にどのようにすれば、ファイルに対してよりよくデータをread/Writeするかに関するものです。したがって、自然にOracleのパフォーマンスの問題の多くは、I/Oと関連があります。I/OとOracleのパフォーマンスの問題を理解するには、オラクルでのI/O操作は、複数の層(Layer)で構成されているという事実を理解することです。オラクルでのI/O処理層を次のように分けて説明してみることにします。

 

1.アプリケーション層:select/ insert/ update/ delete/ truncate…

 

2. Oracleのメモリ層:Buffer cache|PGA

 

3. Oracleのセグメント層:Datafile、tempfile、Tablespace、Segment

 

4. OS/デバイス層:Asynch I/ O、Direct I/ O、Raw device、RAID、…

 

I/Oパフォーマンスの問題の原因を特定することと解決策を見つけるプロセスは、常に1 – >2 – >3 – >4の順に従ってください。論理的な面だけでなく、経済的な面でさらに有効となります。それぞれの層に必要な予備知識とメカニズムについて記述します。

 

アプリケーション層(Application Layer)

 

アプリケーションを効果的に実装して、不必要なI/Oを最小限に抑える必要があります。非効率的なアプリケーションをそのままにして、システムをチューニングすることはほとんど不可能です。Oracleは、I/Oを効率的に処理する様々なテクニックを提供します。Parallel Query、Parallel DML、Nologging、Direct load、Direct readなどが代表的な例となります。Oracleのバージョンが上がるほど、より効率的に作業を処理することができる強力なSQLの機能が追加されます。解析関数(Analytical Function)が代表的な例ですが、この関数をうまく利用すれば、I/Oを大幅に削減しながら、さまざまな集計機能を実装することができます。これらの最新のSQL文を使用して、I/O負荷を減らすようにしなければなりません。データの性質に応じて、クラスタ(Cluster)、IOT、パーティショニング(Partitioning)、ビットマップインデックス(Bitmap Index)などの機能を適切に使用して、I/Oを効果的に使用することも、アプリケーションで実装するべき機能となります。

 

Oracleのメモリ層(Oracle Memory Layer)

 

バッファキャッシュは、OracleのI/O管理の核心です。頻繁に使用するブロックをメモリにキャッシュすることにより、物理的なI/Oを減らすことができます。Oracleのバージョンが上がるにつれてバッファキャッシュを処理するアルゴリズムが絶えず改善され、加えて、新しい管理方法が提供されています。バッファキャッシュを効果的に使用すると、物理I/Oが減り、自然にI/Oパフォーマンスの問題が解決される場合が多くあります。Oracleが提供する機能には、次のようなものがあります。

 

1.Touch countベースの効率的なLRUアルゴリズムを提供します。

2.Buffer Pinning技法を使用して、不必要なラッチの競合を減らし、現在の読み取り操作に使用される確率が高いブロックを
 メモリから押し出さないようにオフします。

3.複数のバッファプール(Multiple buffer pool)機能を使用すると、揮発性ブロックとメモリ常駐ブロックを区分して
 効果的に管理することができます。システムで一般的によく使用されるオブジェクトは、Defaultバッファを使用します。
 比較的定期的に使用される小さなサイズのオブジェクトは、Keepバッファに常駐させることをお勧めします。逆に、非常に
 少ない頻度で使用される大きいサイズのオブジェクトは、Recycleバッファを使用することにより、重要なメモリ領域を
 無駄にすることを防止することができます。

4.Oracle 9iのからブロックサイズを2K〜32Kまで使用可能となりました。オブジェクトの属性を考慮して、大きいサイズの
 ブロックを使用することが有利な場合(例えばローのサイズが大きく、フルテーブルスキャンでデータを読み取ることが
 多い場合)には、大きいサイズのブロックを使用することにより、パフォーマンスの改善効果を得ることができます。

5.メモリに上げる必要がない大容量のデータを処理する際は、バッファキャッシュをバイパスする方法を使用することができます。
 これらの機能をdirect path I/Oと呼びます。 Direct path I/Oを使用すると、SGA領域を経ていないため、メモリを共有
 するための同期メカニズムが不要で、その分性能が改善されます。 Direct path I/Oの反対はconventional path I/Oで
 SGAつまり、バッファキャッシュを経由することをさします。Oracleは、永続的なセグメント(Permanent Segment)と
 一時セグメント(Temporary Segment)の両方にdirect path I/Oをサポートします。Parallel QueryやParallel 
 DMLなどは永久セグメントのdirect path I/Oを使用します。並べ替え操作は、一時セグメントのdirect path I/Oを使用し
 ます。LOBセグメントは、若干のユニークな処理機構を持っていますが、LOB列の作成時に付与されるストレージ(Storage)
 属性に基づいてdirect path I/Oを使用することも、conventional path I/Oを使用することもできます。

 

Oracleのセグメント層(Oracle Segment Layer)

 

一般的なデータは、データファイルに格納されます。一時テーブルスペース(Temporary tablespace)を使用する場合は、基本的に、データファイルではなく、一時ファイル(Temp file)にデータを保存します。オラクル7.3以前のバージョンでは、ソート操作のために永続表スペース(Permanent Tablespace)を使用する必要でしたが、この場合、過剰なエクステントの割り当てと解放のために多くのパフォーマンスの問題が発生し、特にSTロック競合による性能低下現象が生じる場合が多くありました。オラクル8iから使用可能な一時テーブルスペース(Temporary tablespace)と一時ファイル(Tempfile)機能を使用すると、STロック競合は、もはや問題になりません。オラクル8iから提供されているLMT(Locally Managed Tablespace)とOracle 9iから提供されているASSM(Automatic Segment Space Management)を使用すると、エクステントとセグメント領域の不適切管理から来るパフォーマンスの問題のほとんどを解決することができます。大容量のテーブルは、パーティション(Partition)を利用して管理することが有利な場合が多くあります。管理的な側面だけでなく、大量のデータを処理する場合は、必要範囲のみをスキャンすることが可能ですので、必要なI/Oの範囲を減らす効果があります。

 

OS/デバイス層(Device Layer)

 

Oracleは、可能な場合は、非同期I/O(Asynchronous IO)を使用することを推奨します。非同期I/Oは、読み取り操作だけでなく、特にDBWRやLGWRなどの書き込み操作を実行するプロセスが非同期的にタスクを処理できるように処理してくれることでI/O操作の速度を全体的に改善させてくれます。残念ながら、多くのOSにおいて、真の非同期I/Oは、ローデバイス(Raw device)でのみ使用したことが知られています。非同期I/Oを使用することが不可能であればOSレベルでDirect I/Oを使用することが望ましいい方法です。Direct I/Oを使用する場合は、OSのバッファキャッシュをバイパスすることにより、不必要なI/O操作を最小限に抑えている。DBWRプロセスを複数個に使用することもひとつの方法です。 コントロールファイル(control file)の数やREDOログ・ファイルの数が不必要に多い場合は復旧が可能な分だけ維持することも有用となります。

 

Direct I/Oを使用する場合には、ローデバイスを使用する必要がないとの見方が多くあります。 Direct I/Oを使用する場合はOSのバッファキャッシュを経由しないため、その動作はローデバイスとほぼ同じだからです。しかし、ローデバイスがI/Oパフォーマンスの改善の重要な方法であるという事実自体は疑いの余地がないと思われます。 一つ留意することは、ローデバイスやDirect I/Oを非効率的に過剰に実行するアプリケーションの無条件的な解決策はないという事実です。例えば、非常に非効率的なI/Oを実行するアプリケーションの速度を改善するために、既存のファイルシステムをローデバイスに修正したと仮定しましょう。アプリケーションのパフォーマンスが向上するでしょうか?残念ながら、そうではない可能性があります。ローデバイスを使用することにより、I/O操作自体は速くなりますが、OSが提供するバッファキャッシュを使用していないからです。例えば、ファイルシステムを使用する場合には、100万回のPhysical Readのは実際には10万回のみ、実際のディスクの読み取りを誘発し、残りは90万回は、OSのバッファキャッシュから行うことができます。このような場合にローデバイスを使用すると、100万回のPhysical Readがすべてディスクの読み取りにつながるため、むしろアプリケーションケーションのパフォーマンスが低下することもあります。アプリケーションの適切なチューニングが常に優先であり、I/Oシステムのパフォーマンスの向上は、I/Oシステムが「実際に」遅い場合にのみ必要なのです。 RAIDで構成されたI/Oシステムを使用している場合には、RAIDレベル(level)を慎重に選択しなければなりません。 REDOログ・ファイルのように書き込み操作が豊富な資源のRAID-5を使用することは、パフォーマンスに大きな問題を起こします。データファイルの場合でも、RAID-5は、問題を引き起こす場合が多くあります。可能な場合はRAID1 + 0やRAID0 + 1を使用することが基本的な方法です。 ファイルを物理的に分離させることで、ディスクとの間の競合を避けることも重要となります。SANのように統合されたストレージシステムを利用する場合は、そのエンジニアとの協議を通じて、ファイル間での適切な分散が行われるように設定する必要があります。また、アーカイブモードでデータベースを操作する場合には、REDOログとアーカイブ・ログの間の競合が発生しないように構成してなければなりません。 I/Oシステムを変更することは多くの時間とお金を必要とする場合が多いため、常に最後の選択肢で残しておくなりません。 1〜4ステップで問題が解決されない場合にのみ、5段階目のチューニングとして考慮することが望ましい方法です。

 

RAIDの定義にのみ簡単に調べてみましょう。RAIDはRedundant Arrays of Inexpensive(Independent)Disksの略で、複数のディスクを組み合わせて、I/Oシステムを構成する技術をさします。ソフトウェア的に実装されることがありますが、性能面では、ハードウェア的に実装された方がはるかに有利です。

 

・RAID0:ストライピング(Striping)。データを複数のディスクに分割保存するように設定することを言います。
 データの負荷が自動的に分散されるため、性能面で有利だが、一つのディスクのみ故障しても、全体のI/Oが
 不可能になるという短所があります。
・RAID1:ミラーリング(Mirroing)。複数のディスクに同じデータを格納するための手法を言います。
 一つのディスクが故障しても、I/Oには障害がないという点で信頼性があります。しかし、常に利用容量の2倍に
 相当するディスクが必要であるという短所があります。
・RAID5:少なくとも3つのディスクを使用して分散保存する方式で、データの保存時にパリティ(Parity)ビット
 を格納し、一つのディスクが故障してもパリティビットを利用して復旧が可能な手法を言います。
 ディスクの使用率が最も高いとすることができますが、パリティ保存による書き込み作業の負荷が旺盛な場合、
 パフォーマンスが低下するという問題があります。

 

Oracleは、データファイルや制御ファイル、REDOログ・ファイルに対してRAID5よりRAID0+1またはRAID1+0を使用することを推奨します。RAID0+1は、物理的なストライピングに論理的なミラーリングを実装することをいい、RAID1+0は、物理的なミラーに論理的なストライピングを実装することを言います。

 

Direct Path I/O

 

OracleのI/Oは、基本的にSGA(バッファキャッシュ)を経由します。しかし、特殊な状況では、SGAを迂回してPGAにデータを上げます。データを共有する必要がないときは、バッファキャッシュにデータをロードする過程で発生するオーバーヘッドを回避することによって性能を改善することが可能です。バッファ・キャッシュ内の変更されたブロックをデータファイルに記録することはDBWR固有の作業です。一方、バッファキャッシュをバイパスする書き込み操作は、個々のプロセスが直接実行することになります。このように、バッファキャッシュをバイパスするI/O操作をdirect path I/Oと呼びます。Oracleは、次のような場合にdirect path I/Oを使用します。

 

1.並べ替え操作のために整列セグメント(Sort segment)を読み書きする場合。
  direct path read temp、direct path write tempイベントを待機します。
2.Parallel Queryのために、データファイルを読み取る場合。
 direct path readイベントを待機します。
3.PDMLやCTASのためにデータファイルを書き込む場合。
 direct path writeイベントを待機します。
4.NOCACHE属性で生成されたLOBセグメントを読み書き場合。
 direct path read(lob)、direct path write(lob)イベントを待機します。
5.I/Oシステムがデータを読むことによって、Oracleに返す速度よりもはるかに速い速度でバッファを要求する場合。
 この場合、Oracleのパフォーマンスを向上させるためにreadahead I/O(後に読むと判断されるデータを事前に
 一度読んでI/O操作)を使用します。この場合、direct path readイベントを待機します。

 

Direct path I/Oに関連する統計値について整理すると、以下の通りとなります。

 

・physical reads:ディスクから読み取られたブロック数。 Direct path I/Oかどうかとは無関係に、
          物理的な読み取り操作が発生した場合には、常に増加します。

・physical reads direct:Direct path I/Oを介して読んだブロック数。 
              LOBデータのdirect path I/Oは含みません。

・physical reads direct(lob):LOBデータをdirect path I/Oを介して読むブロック数

・physical writes:ディスクに記録されたブロック数。 
          Direct path I/Oかどうかとは無関係に、
          物理的な書き込みが発生した場合には、常に増加します。

・physical writes direct:Direct path I/Oを介して記録したブロックの数。 
               LOBデータのdirect path I/Oは含みません。

・physical writes direct(lob):LOBデータをdirect path I/Oを介して記録したブロック数

・sort(disk):ディスクを利用した並べ替え操作回数。ディスクを利用した並べ替え操作が発生した場合には、
               ソートセグメントに対してdirect path I / Oを使用します。

・sort(memory):メモリを利用した並べ替え操作回数

 

物理的な読み取り操作中にバッファキャッシュを経由した(conventional path I/O)読み取り操作は、次の式で計算することができます。

 

conventional physical reads = physical reads–(physical reads direct+physical reads direct(lob))

 

Direct path I/Oのパフォーマンスの問題は、ほとんどのI/Oシステムの性能と直接関連があります。Direct path I/Oは、バッファキャッシュを経由しないため、同期に伴うオーバーヘッドがありません。したがって、競合による性能低下現象が発生しません。並べ替え操作などの場合を除いては、チューニング作業を通じてdirect path I/Oでの待機回数と待機時間を減らすことは不可能です。もしdirect path I/Oの実行パフォーマンスに問題が生じると判断されると、I/O自体の性能を改善することに焦点を合わせることが正しい判断となります。

 

Oracleのdirect path I/Oは、OSのdirect I/Oとは別の概念であることに注意してください。Oracleのdirect path I/Oは、SGAのバッファ・キャッシュを経由していないものであり、OSのdirect I/Oは、OSのバッファキャッシュを経由しないでしょう。OracleのキャッシュとOSのキャッシュを併用することダブルバッファリング(double-buffering)と呼び、普通の性能によく無いないことが知られています。 しかし、ダブルバッファリングがパフォーマンスに及ぼす影響の程度は、システムの特性やアプリケーションの特性に応じて異なる場合があります。

 

_DB_FILE_DIRECT_IO_COUNTの調整

 

_DB_FILE_DIRECT_IO_COUNT隠しパラメータの値がdirect path I/Oでの最大I/Oバッファサイズを決定します。Oracle 9iのから、この値は、基本的に1Mの値を持ちます。しかし、実際にはO/Sやハードウェアの設定に応じて、最大値が決定されます。この値を大きくすると、Parallel Queryの性能が高くなる可能性がありますが、ほとんど実際の使用可能な値は、1Mよりも小さい値であるため、実際には変更する必要がありません。

 

Event Tip

 

irect path readとundo

 

direct path readにもかかわらず、データファイルからデータを直接読み取ることと、UNDOを参照するためのメカニズムは同じです。つまり、direct path readはSGAを経由していないだけで、読取り一貫性(Read consistency)を保証する方法は同じです。これを証明する方法は、サイズが小さい、UNDO表領域(Undo tablespace)を生成した後、Parallel Queryを実行しながら、別のセッションでDMLを過剰に行うと、ORA-01555(Snapshot too old)エラーを観察するものです。

 

ERROR at line 1:
ORA-12801: error signaled in parallel query server P002
ORA-01555: snapshot too old: rollback segment number 68 with name "_SYSSMU68$" too small

 

上記のエラーはすぐにPQスレーブセッションがデータファイルのdirect readを実行しながら、変更されたブロックを検出すると、UNDOデータを参照するものと解釈することができます。

 

データファイルのdirect path readの証明

 

PQの実行時に、スレーブセッションでのdirect path readが一時領域ではなく、データファイルのdirect path readであることをどのように証明することができますか?一つのセッションでPQを実行した後、PQが実行される間に、他のセッションでV$ SESSION_WAITビューを照会してP1の値を取得するいくつかのファイルのdirect path readのか知ることができます。次のスクリプトを見てみましょう。

 

Session A:Degreeが4pq_testテーブルに対してPQを何度も実行してdirect path read誘発。

declare
v_count number;
begin
for idx in 1 .. 100 loop
 select count(*)into v_count from pq_test;
end loop;
end;
/

Session B:Session Aで発生したPQのスレーブセッションについてdirect path readイベントをキャプチャ。
(Session AのSID = 162)

set serveroutput on size 100000
declare
begin
           for px in(select * from v $ px_session where qcsid = 162)loop
                for wait in(select * from v $ session_wait where
                   sid = px.sid and event like '%direct path read%')loop
                  dbms_output.put_line(「SID = '|| wait.sid ||
'、P1 =' || wait.P1);
                 end loop;
           end loop;
end;
/

Session Bの実行結果は以下の通りです。

SID = 138、P1 = 1
SID = 152、P1 = 1
SID = 144、P1 = 1
...
SID = 142、P1 = 1
SID = 144、P1 = 1
SID = 138、P1 = 1
direct path read待機イベントのP1 = file#ので、該当するファイルが、実際のデータファイルであるか確認する
ことができます。

SQL> exec print_table( 'select * from v $ datafile where file#= 1');

FILE#:1
...
BLOCK_SIZE:8192
NAME:
C:\ ORACLE \ PRODUCT \ 10.1.0 \ ORADATA \ UKJADB \ SYSTEM01.DBF
PLUGGED_IN:0
BLOCK1_OFFSET:8192
AUX_NAME:NONE

上記のようにsystem01.dbfというデータファイルのdirect path readであることを知ることができます。

 

DB_FILE_DIRECT_IO_COUNT

 

DB_FILE_DIRECT_IO_COUNTパラメータはdirect path readパフォーマンスに影響を与えることができます。このパラメータは、direct reads、direct writesの最大I/Oバッファサイズに設定する必要があります。Oracle 8iまでは、ほとんどのプラットフォームで、デフォルトの設定値は、64ブロックでした。したがってDB_BLOCK_SIZEが8Kの場合direct reads、direct writesの最大I/Oバッファサイズは512K(8K*64)です。最大I/Oバッファサイズは、ハードウェアの限界値によっても制限されます。 Oracle 9iのではDB_FILE_DIRECT_IO_COUNTパラメータはhiddenパラメータで変更され、ブロック数ではなくバイト(byte)単位で変更されました。Oracle 9iのデフォルト設定値は1MBです。実質的なdirect I/Oサイズは、ハードウェアの設定(configuration)と限界値にも影響を受けます。

 

Direct Read I/Oサイズを知る

 

3つの方法で実際のdirect read I/ Oサイズを知ることができます。

 

・direct readを実行するセッションの10046トレースイベントをレベル8に設定します。
 P3パラメータは読んだブロックの数を示します。以下の例の場合には、ブロックサイズが8Kであるため、
 direct path read I/Oサイズは64K(8K*8ブロック)です。さらに、V$ SESSION_WAITビュー
 を照会して、direct path read待機イベントのP3値を確認することができます。

WAIT #1: nam='direct path read' ela= 4 p1=4 p2=86919 p3=8
WAIT #1: nam='direct path read' ela= 5 p1=4 p2=86927 p3=8
WAIT #1: nam='direct path read' ela= 10 p1=4 p2=86935 p3=8
WAIT #1: nam='direct path read' ela= 39 p1=4 p2=86943 p3=8
WAIT #1: nam='direct path read' ela= 5 p1=4 p2=86951 p3=8
WAIT #1: nam='direct path read' ela= 38 p1=4 p2=86959 p3=8
...

 

・O/ Sのtruss、tusc、trace、またはstraceを利用して、direct readsあるいはdirect writesを実行する
 UNIXプロセスをトレースします。Oracle 9iのでtrussレポートの一部としてdirect I/ Oサイズが64Kであることを
 知ることがでます。

9218/1:         kaio(AIOWAIT, 0xFFBECE98)                       = 1
9218/1:         lwp_cond_signal(0xFEB7BFA0)                     = 0
9218/3:         pread64(256, "0602\0\001\0 ~13C19AEE }".., 65536, 0x0FC26000) = 65536
9218/1:         lwp_cond_signal(0xFEB69FA0)                     = 0
9218/4:         pread64(256, "0602\0\001\0 ~1BC19AEFE7".., 65536, 0x0FC36000) = 65536

 

・10357トレースイベントを設定して(例えば、alter session set events」10357 
 trace name context forever、level1')direct I/ Oを実行するセッションのデバッグ情報を確認します。

Unix process pid: 4375, image: oracle@kccdeds73 (P000)
*** SESSION ID:(9.18) 2004-02-08 21:47:01.908
DBA Range Initialized: length is 1570, start dba is 0100602b
kcblin: lbs=fc86c1cc  flag=8 slot_cnt=32 slot_size=65536 state obj=24321224
kcblin: state objects are: Call=243a2210,Current Call=243a2210, Session=24321224
kdContigDbaDrCbk:starting from tsn 5
kdContigDbaDrCbk:starting from rdba 0100602b
kdContigDbaDrCbk:returning 1570 blocks
kcblrs:issuing read on slot : 0
kcbldio:lbs=fc86c1cc slt=fc86408c typ=0 async=1 afn=4 blk=602b cnt=8 buf=fc87fe00
kcblrs:issuing read on slot : 1
kcbldio:lbs=fc86c1cc slt=fc864210 typ=0 async=1 afn=4 blk=6033 cnt=8 buf=fc89fe00
kcblcio: lbs=fc86c1cc slt=fc86408c type=0 afn=4 blk=602b cnt=8 buf=fc87fe00
... 

 

前の例では、トレースファイルは、クエリスレーブ(query slave)#0の実行履歴です。direct pathオペレーションのために、32個のI/Oスロットが利用可能です(slot_cnt = 32)。一つのスロットが、I/Oユニットであり、それぞれのスロットは65536 bytesです(slot_size = 65536)。読み込みオペレーション時非同期I/Oが使用されています(async = 1)。クエリスレーブは、データファイルの#4を読みます(afn = 4)。読み込むブロック数は8個です(cnt = 8)。ブロックサイズは8Kであるため、65536 bytesを送信します。

 

この場合には、direct I/Oスロットのサイズは、プロセスが1MB全部を使用しないようにします。 _DB_FILE_DIRECT_IO_COUNTパラメータのデフォルト設定値は1MBです。スロットサイズは10351イベントを使用して変更することができます。また、スロットの数も10353イベントを使用して変更することができます。 (注意):前述の情報を利用して、自分が使用しているシステムのdirect I/Oのスループットを把握することができます。スロットサイズとdirect I/Oスロットの数のデフォルト設定値を簡単に変更しないでください。変更前に、自分が使用しているハードウェアの限界を把握する必要があり、アプリケーションとSQLステートメントを最適化させることに焦点を当てる必要があります。