Gc cr/current request

基本情報

ローカルキャッシュに存在しないデータブロックを読みたいプロセスは、そのデータブロックを管理するマスターノードにブロック転送を要求し、応答を受信するまで、gc cr requestイベントやgc current requestイベントにより待機します。イベントのパラメータ情報は、以下の通りです。

SQL> SELECT 
	name, parameter1, parameter2, parameter3 
FROM  v$event_name 
WHERE name = 'gc cr request';
NAME				PARAMETER1	PARAMETER2	PARAMETER3
-----------------------------------------------------------------------
gc cr request			file#		block#		class#

P1(file#)の値とP2(block#)の値は、データブロックの情報を表します。P3(class#)の値は、データブロックのクラス(Class)を意味します。オラクルは、全18種類のデータブロックを使用しており、以下のようにV$ WAITSTATビューで照会可能です。

SQL> SELECT ROWNUM AS class#, class FROM v$waitstat
    CLASS# CLASS
-------- ------------------
         1 data block
         2 sort block
         3 save undo block
         4 segment header
         5 save undo header
         6 free list
         7 extent map
         8 1st level bmb
         9 2nd level bmb
        10 3rd level bmb
        11 bitmap block
	12 bitmap index block
        13 file header block
        14 unused
        15 system undo header
        16 system undo block
        17 undo header 
        18 undo block

いくつのクラスのデータブロックの競合と待機が発生するかが非常に重要となります。ブロックの種類、すなわち、一般的なデータブロックであるか、セグメントヘッダブロックであるか、アンドゥブロックか、ビットマップブロックであるかなどにより、問題を解決する方法が全く異なる可能性があるからです。また、gc cr requestイベントとgc current requestイベントは、任意のモードでブロックを要求しますが、これらは、CRモードか、あるいはCurrentモードかの違いだけがあるだけです。したがって、イベントの発生理由と待機現象を解消する手法は同様になります。

クラス#15以上のデータ・ブロックは、アンドゥ領域のブロックを意味し、ロールバックセグメント番号によって決定されます。 クラス#と、ロールバックセグメント番号(r)の関係は次の通りです。

  •  ・アンドゥヘッダブロック:クラス#=2* r+15
  •  ・アンドゥブロック:クラス#=2* r+16

SYSTEMロールバック・セグメントは、rの値が0であるため、アンドゥヘッダブロックのクラス#=2*0+15=15となり、 アンドゥブロックのクラス#=2*0+16=16となります。 また、ロールバックセグメント番号に基づいて、クラス#は{17,18}、{19,20}、{21,22}…の順に増加します。

gc cr/current request待機イベントは、db file sequential readイベントとほぼ同じ属性を持ちます。db file sequential read待機イベントがディスク・レベルでシングルブロックI/O操作を処理する過程で発生する場合は、gc cr/current request待機イベントは、相互接続レベルでシングルブロックI/O操作を処理する過程で発生するという違いがあるだけです。したがって、2つの待機イベントは発生理由もほぼ類似しており、待機現象を解消する手法も、以下の表に示すように、ほぼ同じとなります。

gc cr/current request・db file sequential read イベント

発生理由

– 非効率なSQLによる過度なブロックリクエスト

– ホット・ブロック

– 遅いインターコネクト

– 非効率的なネットワーク設定

– 非効率的なバッファキャッシュ

– LMSプロセスの負荷

– 非効率なSQLによる過度なブロックリクエスト

– ホット・ブロック(この場合は、db file sequential readイベントではなく、buffer busy waitsイベントに近い)

– 遅いI / Oシステム

– 非効率なI / O設定

– 非効率的なバッファキャッシュ

– I / Oプロセスの負荷(例えば、非同期I / Oプロセス)

解決策

– SQLチューニングによるブロック要求回数の減少化

– ホット・ブロックの解消

– より高い帯域幅(Bandwidth)の相互接続を使用

– ネットワーク設定(UDPバッファサイズなど)の最適化

– バッファキャッシュの効率的な使用

– LMSプロセスの効率性の向上

– より高いスループットのI / O(Disk)システムを使用

– I / O設定(ディスク間の競合、ストライプサイズなど)を最適化

– I / Oプロセスの効率性の向上

パラメータと待機時間

待機パラメータ

gc cr/current request待機イベントの待機パラメータは以下の通りです。

  • ・P1:File#
  • ・P2:Block#
  • ・P3:class#(gc cr requestの場合)
  • ・P3:id(gc current requestの場合)

P3= idである場合には、下位2 Byteの値がClass#を示す。

gc current requestのパラメータが以下のような場合

gc current request  file#= 717   block#= 2  id#= 33554445 
gc buffer busy      file#= 1058  block#= 2  id#= 65549    

id#=33554445は、16進数表現では、200000Dのであり、下位2 Byteの0D=13(10進)がこのClass#になります。

id#=65549は、16進数表現では、1000Dので、やはりClass#は13となります。

13のClass#の意味は、V$ WAITSTATビューで知ることができます。

 select rownum, class from v$waitstat;

    ROWNUM CLASS
---------- ------------------
         1 data block
         2 sort block
         3 save undo block
         4 segment header
         5 save undo header
         6 free list
         7 extent map
         8 1st level bmb
         9 2nd level bmb
        10 3rd level bmb
        11 bitmap block
        12 bitmap index block
        13 file header block
        14 unused
        15 system undo header
        16 system undo block
        17 undo header
        18 undo block

つまりClass#=13は、File Header(純粋File HeaderやLMTでのBitmap Block)を意味します。

待機時間

1秒

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

非効率的なSQL文に起因する過度なブロックリクエスト

非効率なSQL文(すなわち余分なLogical I/ Oを実行するSQL文)がgc cr/current requestイベント待ちの主たる原因です。非効率的なSQL文をチューニングしないまま、他のチューニング方法を適用しようとしてもほとんどうまくいきません。非効率なSQLによる問題は、その原因が単純なだけに、解決策も単純です。SQL文の非効率要素がないかを確認し、最適な実行パスに従うようにする必要があります。

RACでは、並列処理(Parallel Execution)が複数のノード間で実行されます。大量の作業を複数のノードを経て分散実行することは、クラスタの利点を最大化することで、RACの存在理由の一つと判断できます。問題は、個々の並列処理の結果が相互接続を介して通信される過程で、インターコネクトのリソースの不足を招くことになり、OLTP性一般クエリのパフォーマンスに影響を与えることあるということです。一つのシステムでDSS性クエリとOLTP性クエリを使用することは、いくつかのパフォーマンスの問題を引き起こす可能性があり、RAC環境でも例外ではありません。RACでの並列処理を実行する場合には、ノード間の負荷をどのように調整するかを事前に検討する必要があります。INSTANCE_GROUPSパラメータとPARALLEL_INSTANCE_GROUPパラメータを活用することで、並列処理をノード間でインテリジェントに分配することができます。

ホットブロック

ホット・ブロックとは、複数のプロセス、あるいは複数のインスタンスによって同時多発的にアクセス、あるいは変更されたブロックのことです。ホット・ブロックは、シングル・インスタンス環境では、cache buffers chainsラッチの競合やバッファロック競合を引き起こし、パフォーマンスの低下の主たる原因となります。RAC環境でのホット・ブロックは、ラッチやバッファロック競合だけでなく、過度のインターコネクト通信を発生させ、RACシステム全体の性能を低下させる要因となります。

ホット・ブロックによる性能低下現象は、ホット・ブロックを物理的に分散させることで解決可能となります。ホット・ブロックを分散させる方法には、次のようなものがあります。

  • ・セグメント分割の使用。ハッシュパーティショニングが代表的な方法となります。
  • ・小さいサイズのブロックを使用
  • ・高い値のPCTFREE属性を使用
  • ・セグメントデータの再作成。削除後、再挿入やテーブルの再作成など
  • ・ホット・ブロックによって競合が発生した場合には、Fixed-upイベントにgc cr/current block busyイベントが観察されている場合が多く、gc buffer busyイベントも多く観察されます。

同時DML実行

複数のノードで同じセグメントの大量のDMLを同時に実行すると、大規模なバッファロック競合が発生し、これにより、gc cr/current requestイベントの待機時間が増加します。大量のDML操作は、ノード間で適切に処理を分散するのが効果的です。複数ノードのうち、特定のノードを配置作業ノード、すなわち大量のDMLを実行するように指定することも現実的な方法となります。

遅いインターコネクト

インターコネクトを介して送受信データの絶対量が多い場合は、SQLチューニングだけでは問題を解決することができません。例えばインターコネクトの帯域幅が1G Bit(Gigabit Ethernet)であれば、理論上、同時に交換可能なデータの量は125Mバイトとなります。したがって125Mバイト以上のデータを相互接続で交換するとなると、ブロック転送の過程で、応答時間が遅延される現象が発生することになります。インターコネクトを介したデータ交換が多すぎるとLMSプロセスの負荷が増加し、応答処理過程で遅延が発生することになります。

インターコネクトでの混雑による遅延は、まるで高速道路での車両が過度に集中し渋滞が発生するのと同じ原理で発生します。道路が狭く、すなわち相互接続の帯域幅が低く、輻輳が発生するのであれば、道路を広げ、すなわち相互接続の帯域幅を向上させる方法によって解決可能となります。しかし、道路を広げることが莫大な費用を必要とするように、相互接続の帯域幅を向上させることも付加的な費用を必要とするという事実を覚えておかなければなりません。いつも最善の選択は、SQLステートメントを最適にチューニングして、不必要な競合によるブロック転送を最小限にする必要があるということです。

現在インターコネクトとして最も多く使用される方式はGigabit Ethernetであり、最近になっては、10 Gigabit Ethernetが採用されています。したがって、帯域幅を広げるための選択の幅が広くないことも留意する必要があります。

非効率的なネットワークの設定

インターコネクトが使用するネットワークプロトコルに基づいて、適切なネットワーク設定が必要となります。Gigabit Ethernetインターコネクトでは、UDP(User Datagram Protocol)が使用されます。oradebugツールを使用すると、インターコネクトでどのプロトコルを使用しているかどうかを確認することができます。

SQL> oradebug setmypid
SQL> oradebug ipc
SQL> oradebug tracefile_name
/oracle/ORA10g/admin/LAS10/udump/ora102_ora_7040.trc
SSKGXPT 0xc03f844 flags SSKGXPT_READPENDING     info for network 0
socket no 8     IP 192.168.2.202        UDP 41210
        sflags SSKGXPT_UP

ネットワーク設定の中でRACのインターコネクト性能と最も関連が深いのは、UDPバッファのサイズです。OSによってUDPバッファのサイズの推奨値は多少異なります。Oracleが正式に勧告するUDPバッファのサイズは256Kバイトです。ほとんどのシステムでは、256Kバイトの大きさで、適切な性能を確保することができます。パフォーマンステストのためのBMT環境のような状況や、同時にインターコネクトを使用するセッションの数が非常に多いシステムであれば、より大きなサイズのUDPバッファを使用することが必要があるかもしれません。通常、最大1M〜2Mバイト程度の大きさのUDPバッファを使用することもあります。OSごとにUDPバッファサイズを照会して設定する方法については、「5.3ネットワークの監視」で詳しく議論します。

UDPバッファサイズが過度に小さい場合には、パケット損失(Packet Loss)現象が頻繁に発生します。UDPパケットの損失が発生した場合には、Oracleでは、Fixed-upイベントgc cr block lostイベントやgc current block lostイベントに観察されます。もしこのイベントのために待機現象がしばしば発生する場合、ネットワークの監視を介してパケットの損失が頻繁に発生していないかを監視する必要があります。さまざまなツールを使用してパケットの損失現象を観察することができますが、Linuxの環境でnetstatツールを利用する例は、次のとおりとなります。

-- netstat -i オプションネットワークインターフェイスごとにパケット損失(RX-ERR)を観察することができます。
prompt> netstat -i
Iface	MTU	Met	RX-OK	RX-ERR	RX-DRP	RX-OVR	TX-OK	TX-ERR	TX-DRP	...
eth0	1500	0	8664	0	0	0	7708	0	0	... 
eth1	1500	0	8772	0	0	199	3541	0	0	...
lo	16436	0	2262	0	0	0	2262	0	0	...

-- nestat -s オプションネットワーク関連統計を介してパケット損失(packet receive errors)を
       照会することができます。
prompt> netstat -su
Udp:
    495820735 packets received
    50018 packets to unknown port received.
    114 packet receive errors
    584466009 packets sent

gc cr block lostイベントやgc current block lostイベントの待機現象がUDPパケットの損失のように発生する場合には、UDPバッファサイズが過度に小さく設定されていないことを点検する必要があります。UDPバッファのサイズを段階的に最大2Mバイト程度まで増やしながら、問題が解決されることを確認していきます。もしUDPバッファのサイズがすでに十分大きいのに問題が引き続き再現する場合、物理的なネットワーク設定に問題がないか確認する必要があります。

インターコネクトの性能を向上させる有効なもう一つのネットワーク設定は、Jumbo Frameを使用するものとなります。これは、Jumbo FrameとNIC(Network Interface Card)の設定時に1500バイトサイズのフレームサイズ(またはMTU。Maximum Transmission Unit)を使用することを意味します。 MTUが1500バイトという言葉の意味は、ハードウェア的に一度にネットワークを介して送信することができるパケットの最大サイズが1500バイトということです。一般的なネットワーク環境では、ほとんどデフォルトの1500バイトのMTUに十分です。しかし、インターコネクトのように大量のデータが非常に頻繁に交換される場合には、より大きなサイズのMTUを使用することにより、性能をさらに向上させることができます。MTUサイズに理論的な制限はありませんが、一般的に9000バイトのサイズのMTUを使用します。現在使用中のMTUのサイズは、netstatツールやifconfigツールを使用して確認することができます。

Prompt> netstat -i
Kernel Interface table
Iface      MTU Met    RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flg
eth0       1500   0 88277330      0      0      0 79745731      0      0      0 BMRU
eth1       1500   0 1385955750      0      0    199 3879777348      0      0      0 BMRU
lo        16436   0 30702314      0      0      0 30702314      0      0      0 LRU

Prompt> ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 00:11:11:EB:BE:7E
          inet addr:210.122.227.204  Bcast:210.122.227.255  Mask:255.255.255.0
          inet6 addr: fe80::211:11ff:feeb:be7e/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:88277786 errors:0 dropped:0 overruns:0 frame:0
          TX packets:79746070 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:3981177070 (3.7 GiB)  TX bytes:2529189640 (2.3 GiB)

Jumbo Frameを使用するには、OS、ネットワークカード(NIC)、スイッチなど関連するすべてのソフトウェアとハードウェアがこれをサポートしなければなりません。

非効率的なbuffer cache使用

バッファ・キャッシュを効率的に使用することは、シングルインスタンス環境だけでなく、RAC環境でも非常に重要な項目となります。バッファキャッシュが非効率的に使用される場合には、せっかくのローカルキャッシュに取り込んだブロックが定期的にバッファキャッシュから押し出されることになります。これらのブロックを再読み込み過程で再びグローバルキャッシュの同期が発生することになり、gc cr/current requestイベントの待機が不必要に増加することになるのです。

バッファ・キャッシュを効率的に使用する方法は、特定の方法があるわけではなく、アプリケーションのデータアクセスの種類に応じて適切な手法を試してみる必要があります。一般的に適用可能な手法は、以下の通りとなります。

  • ・ 複数のバッファプールの使用:セグメントの用途に応じてKeepバッファ、Defaultバッファ、Recycleバッファを適切に使用することにより、バッファキャッシュを効率的に使用することができます。
  • ・グローバル一時表の使用:データが一時的に必要な場合、可能なグローバル一時テーブル(Global Temporary Table)を使用します。グローバル一時テーブルは、セッションレベルでデータを管理するため、グローバルキャッシュの同期作業が不要となります。
  • ・読取り専用表領域の使用:もし特定の表スペースが読み取り専用の性格を持っている場合、テーブルスペースの属性自体を読み取り専用(Read only)に変更することが望まれます。Oracleは、読取り専用表領域に属するデータブロックは、グローバルキャッシュの同期を実行していません。
  • ・十分な大きさのバッファキャッシュサイズ:最後に、可能であれば、常に十分な大きさのバッファキャッシュを使用するようにすることです。バッファ・キャッシュのサイズが小さく、バッファキャッシュからよく使われるデータブロックの入れ変えが発生しないようにする必要があります。

LMSプロセスの負荷

リクエストノードのブロック転送要求は、常にリモート・ノードのLMSプロセスによって処理されます。非同期I/O処理がディスクI/O操作を処理するのに対し、LMSプロセスはインターコネクトI/O操作を処理します。非同期I/O操作のために、非同期I/Oの処理を使用しているシステムの場合には、非同期I/Oプロセスが使用できる資源を最大限に保証することで、I/Oの競合をある程度改善させることができます。同様にLMSプロセスが使用するリソースを最大限に保証ことによって相互接続での遅延(Delay)を向上させることができます。LMSプロセスがよりスムーズに作業を処理する分、gc cr requestイベントの待機現象も改善されます。LMSプロセスの作業は、ほとんどの適切なCPUリソースを必要とします。したがってLMSプロセスがCPUリソースを効率的に使用できるように保証することが必要となります。

CPUリソースが十分な場合には、LMSプロセスの数を増加させることによって、個々のLMSプロセスのパフォーマンスを向上させることができます。Oracle 10gのからは、GCS_SERVER_PROCESSESパラメータの値を用いてLMSプロセスの数を変更します。Oracleの勧告値は4つのCPUごとに1つのLMSプロセスを割り当てるものであり、最小値は2つです。グローバルキャッシュの同期処理が極端に多いシステムの場合、1〜2つのCPUごとに1つのLMSプロセスを割り当てることを考慮してみる価値はあります。

LMSプロセスのパフォーマンスを最大化するために、OSレベルでLMSプロセスがより多くのCPUリソースを使用できる方法を使用することができます。最も簡単な方法は、reniceコマンドを使用して、LMSプロセスの優先順位(Priority)が高くなるように調整するものであり、もう一つの方法は、LMSプロセスの優先順位スケジューリング方式をリアルタイム(Real-Time)に変更するものです。

この問題を理解するには、OSが、個々のプロセスにCPUを割り当てる方法についての理解が必要となります。OSは、基本的に時分割(Time Sharing)スケジュール方法を使用して、個々のプロセスにCPUリソースを割り当てています。時分割の方法を使用している場合は、OSはCPUを使用するプロセスの優先順位を参照して、優先順位が最も高い(実際にはPriority属性の値が小さいほど、優先順位は高い)プロセスを最初に実行させます。一定時間CPUを使用したプロセスは、再び待機状態に入り、次の回のプロセスが再びCPUを割り当てられます。プロセスの優先順位は、プロセスのCPU使用率の程度とCPU使用時間などを考慮して計算されます。もしCPUを使用する程度が似てい場合は、長時間CPUを使用するプロセスの優先順位が低くなります。時分割の方法は、CPUリソースを多くのプロセスが公平に分けて使うという利点がありますが、逆に、特定のプロセスがCPUリソースを最大限に使用することを妨げる副作用も持っています。インターコネクトを介したデータ交換が非常に頻繁な状況では、全体的なCPU使用率が増加します。この時、時分割技法の影響で、最も優先的にCPUを割り当てる必要があるLMSプロセスが頻繁に割り込みを受け、だんだん優先順位が低くなります。その結果、LMSプロセスが必要に応じてCPUを割り当てられない現象が発生することになるのです。

この問題を解決する1つの方法は、reniceコマンドを利用してLMSプロセスの優先順位が常に高い状態を維持するようにするものとなります。ほとんどのOSではNICE属性を利用して、プロセスの優先順位を調整することができます。個々のプロセスは、優先順位を示すPriority属性と一緒にNICE属性を提供します。 NICE属性は、文字通り、特定のプロセスが他のプロセスにどのように ”親切なプロセスか”、すなわち、CPUを他のプロセスに譲る優しさがどの程度かを決定します。NICE値が高ければ、すなわち、より親切、CPUを他のプロセスに、より多くの譲歩できるようになるため、優先順位が低くなるます。逆にNICE値が低いと、すなわち、より不親切と、CPUをなるべく他のプロセスに譲歩しなくなります。 NICE値は+19(最も親切)〜-20(最も不親切)との間の値を有しおり、Oracleなどの一般的なユーザプロセスは “0”の値を持ちます。以下のようにLMSプロセスの優先順位とNICE値を確認することができます。(Priority属性の値が低いほど優先順位が高いことを思い出してください)。

[root@rac02 ~]# ps -efl | grep lms
F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
0 S oracle    7991     1  1  65   0 - 428439 -     Sep11 ?        05:26:48 ora_lms0_ORA102
0 S oracle    7993     1  1  75   0 - 428439 -     Sep11 ?        05:23:51 ora_lms1_ORA102

インターコネクトを介したデータ交換が非常に頻繁に発生して、CPUの競合が激しいシステムであれば、次のようにreniceコマンドを利用してLMSプロセスのNICE値を最大-20まで下げるのが良い方法です。次のコマンドは、必ずrootユーザで実行しなければならなりません。

[root@rac02 ~]# renice -20 -p 7991 7993   # 7991, 7993 프로세스의 NICE 값을 -20으로 변경
7991: old priority 0, new priority -20
7993: old priority 0, new priority -20
[root@rac02 ~]#  ps -efl | grep lms
F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
0 S oracle    7991     1  1  60 -20 - 428439 -     Sep11 ?        05:26:51 ora_lms0_ORA102
0 S oracle    7993     1  1  60 -20 - 428439 -     Sep11 ?        05:23:54 ora_lms1_ORA102

時分割技法によるLMS性能低下現象を解決するもう一つの方法は、リアルタイムスケジューリング手法を使用しているものです。リアルタイムスケジューリング手法の厳密な意味では、特定のプロセスが特定のタスクを実行するために、常に「一定」の応答時間が保証されることが、リアルタイムOS(Real-Time OS)では非常に重要な概念となります。リアルタイムOS以外のほとんどのOSでは、完全に一定の応答時間を保証していません。しかし、リアルタイムのスケジューリングが可能なように、追加のスケジューリング手法を提供しています。一般的に、リアルタイムスケジューリングを使用するプロセスは、固定された優先順位つけ、時分割技法に比べて高い優先順位を与えられます。高い優先順位にあり、その優先順位が変更されないため、CPUを最大限に利用することができます。したがってLMSプロセスがリアルタイム技法を使用するように変更することを検討してみる必要があります。Oracle10g R2以前のバージョンまではOS固有の技法を使用して、LMSプロセスがリアルタイムスケジューリングを使用する必要があります。Oracle10g R2からLMSプロセスは、基本的にリアルタイムスケジューリングを使用するように変更されました。このメカニズムは、_OS_SCHED_HIGH_PRIORITYパラメータ(デフォルトは1)によって制御されます。このパラメータの値が「0」であれば、以前のバージョンと同様に時分割スケジューリングを使用します。一方、「1」以上の値を付与すると、リアルタイムスケジューリング手法を使用して、高い値を付与するほど、より高い優先順位を与えられることになります。しかし、1よりも大きな値を指定する場合には、LMSプロセスがCPUリソースを過度に多く占めることができるという事実に留意しなければなりません。

LGWR/DBWRプロセスの負荷

LGWRプロセスとDBWRプロセスはシングルインスタンス環境だけでなく、RAC環境でも非常に重要な仕事を担当します。

LGWRプロセスは、ブロック転送の過程で「REDOフラッシュ」を担当します。ローカルキャッシュのダーティブロックをリモート・ノードに送信する前に、ダーティブロックに対応する、REDOデータをREDOログに保存する必要があります。これをしばしば「REDOフラッシュ」と呼びます。もしLGWRプロセスの作業が円滑でない場合、REDOフラッシュが遅延され、これにより、gc cr/current requestイベントの待機時間が増加することになるのです。

DBWRプロセスは、「Fusion Write」機能を担当します。ローカルキャッシュのダーティブロックをディスクに記録(Checkpoint)する作業は、必ずグローバルに行われます。クラスタ内の同じブロックに複数のダーティブロックが存在することができるからです。特定のインスタンスでダーティブロックの記録要求が発生した場合、GRDを参照して、ブロックのマスターノードへ要求が渡され、マスターノードは、ブロックの「最新」バージョンのノードにディスクに記録要求を転送します。ディスクの記録が完了すると、他のインスタンスのPI(過去イメージ)のブロックは、キャッシュからフラッシュされます。このような一連のメカニズムをFusion Writeと呼びます。 DBWRプロセスの作業が円滑でない場合は、Fusion Write作業時間が遅延されることになります。DBWRプロセスによって記録されているブロックを受信するプロセスは、書き込みが終了するまで待機する必要があるため、gc cr/current block requestイベントの待機時間が増加することになるのです。

REDOフラッシュとFusion Writeのパフォーマンスの問題は、V $ CURRENT_BLOCK_SERVERビューからも間接的に確認することができます。

もし、REDOフラッシュとFusion Writeの性能低下によりgc cr/current requestイベント待機が増加すると判断されると、LGWRプロセスとDBWRプロセスのパフォーマンスを向上させなければならないことになります。

Event Tip

PlaceHolderイベントとFixed-upイベント

Oracleは、RACに関連する機能を絶えず改善しています。RACと関連待機イベントの定義も例外ではありません。Oracle 9iのとOracle 10gのクラスタ関連待機イベントは名前からは新たに定義され、何よりも、特定のイベントが互いに従属的な関係を持つようになりました。つまり、特定のイベントは、「Placeholder」(代用)の役割をして、別のイベントは、「Fixed-up」(事後調整)の役割をします。

Oracle 10gののクラスタ待機イベントは、PlaceholderイベントとFixed-upイベントという2つのカテゴリーに分類されます。 Placeholderイベントとは、特定のプロセスがグローバル・リソース、すなわちデータブロックを「獲得する」過程で待機するイベントを言います。一方、Fixed-upイベントは、特定のプロセスがデータブロックを最終的に “獲得した”時点で待機したと記録されているイベントを言います。 「獲得する」と「獲得した」の視点の違いがPlaceholderイベントとFixed-upイベントの役割を決定することに注目しなければなりません。例えば、一貫性のある読み取り(CR)モードで特定のブロックをリモート・ノードに要求されたプロセスは、そのブロックが送信されるまで、gc cr requestイベント(Placeholder)を待機します。要求の結果として、実際のブロックを受信した場合、結果的にはgc cr block 2-wayイベント(Fixed-up)を待機していたことで最終報告をすることになるのです。すなわち、Placeholderイベントは、プロセスがどのような結果を得たかによって、特定のFixed-upイベントに変わることになります。

PlaceholderイベントとFixed-upイベントシステムでもう一つの注意点は、監視方法にあります。 Placeholderイベントは、「獲得する」中で観察されるので、現在の待機情報を提供するV$SESSION_WAITビューで観察されます。また、まだFixed-upイベントに変更されていない場合、V$SESSION_EVENTビューでも観察されます。一方、Fixed-upイベントは「獲得した」後に観察されるため、V$SESSION_WAITビューでは観察されず、V$SESSION_EVENTビューまたはV$SYSTEM_EVENTビューでのみ観察されます。したがって、システム監視のために、V$SYSTEM_EVENTビューを照会している場合は、gc cr requestイベントのようなPlaceholderイベントではなく、gc cr block 2-wayのイベントなどのFixed-upイベントだけが観察されることを理解しなければなりません。

Placeholder / Fixed-upイベントシステムは、クラスタのクラスの待機イベントでのみ使用され、Oracle 10gの以前のバージョンでは全く使用されない概念です。 Placeholder / Fixed-upイベントのシステムに従わないイベントは、既存の待機イベントと同じように動作します。

Pracleが提供するRAC管理者ガイドでは、クラスタのイベントを、その属性に応じて、次のように分類しています。

    ・Block-oriented

    ・gc current block 2-way
    ・gc current block 3-way
    ・gc cr block 2-way
    ・gc cr block 3-way

 
    ・Message-oriented
        ・gc current grant 2-way
        ・gc cr grant 2-way

    ・Contention-oriented
        ・gc current block busy
        ・gc cr block busy
        ・gc buffer busy

    ・Load-oriented
        ・gc current block congested
        ・gc cr block congested

上記のイベントの分類は、イベントの発生理由に基づいてもので、各イベントの意味を概括的に把握する目的で使用することができます。Block-orientedイベントは、実際のブロックイメージを相互接続を介して交換したことを意味します。Message-orientedイベントはブロックイメージではなく、ブロックを読み取る権限のみ付与したことを意味します。

Contention-orientedイベントはブロックを送信される過程で、競合(Contention)が発生したことを意味します。Load-orientedイベントはインターコネクトで過度の混雑(Congestion)が発生して転送操作が遅れたことを意味します。 しかし、この分類法では、Placeholder / Fixed-upイベントの正確な区分を説明することはできません。例えば、上記のイベントのリストからgc buffer busyイベントは、Fixed-upイベントでなく、一般的な独立のイベントに分類されます。また、gc cr requestイベントやgc current requestイベントのようなPlaceholderイベントは最初からリストから抜けています。この文書では、上記の分類とPlaceholder / Fixed-upイベントシステムの特徴をすべて生かした下記のような分類体系を使用します。


    ・Placeholderイベント

    ・gc cr request
    ・gc current request

 

    ・Fixed-upイベント
        ・Block-oriented

 

    ・gc cr block 2-way
    ・gc cr block 3-way
    ・gc current block 2-way
    ・gc current block 3-way

 

    ・Message-oriented
       ・ gc cr grant 2-way
       ・ gc current grant 2-way

    ・Contention-oriented
        ・gc cr block busy
        ・gc cr grant busy
        ・gc current block busy
        ・gc current grant busy

    ・Load-oriented
        ・gc cr block congested
        ・gc cr grant congested
        ・gc current block congested
        ・gc current grant congested

    ・Miscellaneous
        ・gc cr block lost
        ・gc current block lost

    ・独立イベント
        ・gc cr multi block request
        ・gc current multi block request
        ・gc buffer busy

Oracle 10g RACには、大きく2つのPlaceholderイベントが存在します。 gc cr requestイベントとgc current requestイベントがそれです。この二つの待機イベントは、それぞれ「一貫性のある読み取りモード(以下CR)のI/O」と「現在のモード(以下Current)のI/O」の作業で発生する待機現象を示すもので、RACで最も一般的に発生します。 gc cr requestイベントとgc current requestイベントは、それぞれ多数のFixed-upイベントを従えています。一方gc cr / current multi block requestイベントとgc buffer busyイベントはPlaceholder / Fixed-upシステムを使用していない一般的な待機イベントです。

Placeholder / Fixed-upイベントシステムでの一つの留意点は、Fixed-upイベントはPlaceholderイベントが何であったかに関係がないということです。 Fixed-upイベントは、要求の種類とは無関係に任意の “結果”を受けた情報だけを提供します。したがってgc cr requestイベントのFixed-upイベントにgc cr block 2-wayイベントだけでなく、gc current block 2-wayイベントも使用され、Fixed-upイベントに記述されたすべてのイベントが使用されることができるという事実に注意しなければなりません。

gc cr/ current requestのfixed-upイベント

gc cr/ current requestイベントはPlaceholderイベントであり、ブロック要求の結果としてどのような応答を受けたかに応じて、次のようにいくつかの種類のFixed-upイベントに変更されます。

応答タイプ    Fixed-upイベント

 マスターノードよりブロックイメージを送信された場合(つまり、マスターノードがホルダーノードである場合)   gc cr block2-way

gc current block 2-way

マスターノードではなく、第3のホルダーノードからブロックイメージを送信された場合、gc cr block 3-way

gc current block 3-way

マスターノードからブロックを読み取る権限を付与された場合、gc cr grant 2-way

gc current grant 2-way

ブロックイメージを送信される過程で、競合が発生した場合、gc cr block busy

gc current block busy

ブロックを読み取る権限を付与される過程で、競合が発生した場合、gc cr grant busy

gc current grant busy

ブロックイメージを送信される過程で、混雑が発生した場合gc cr block congested

gc current block congested

ブロックを読み取る権限を付与される過程で、混雑が発生した場合gc cr grant congested

gc current grant congested

ブロック転送要求に対する応答が失わ(Lost)された場合(10g R2)gc cr block lost

gc current block lost

これらのFixed-upイベントは待機現象の発生原因をより詳細に分析することができるように助けてくれます。同じgc cr requestイベントであってもFixed-upイベントがgc cr block 2-wayイベントなのか、gc cr block busyイベントであるかによって、待機現象が発生する原因と解決策が全く異なる可能性があるからです。

並列実行(Parallel Execution、PX)

Oracle 10gのから並列実行(Parallel Execution、以下PX)エンジンについての重要な変更が試みられました。既存のPXエンジンが持っていた利点を補完し、グリッド環境に最適化されたパフォーマンスを確保するためにPSC(Parallel Single Cursor)モデルという名前の新しいアーキテクチャを導入したのです。PSCとは、文字通り、並列実行コーディネーター(Parallel Execution Coordinator、以下PEC)と並列実行スレーブ(Parallel Execution Slave。以下PES)が一つのカーソル(またはプラン)を共有するという意味です。次の例は、Oracle9iとOracle10gで、同じ種類の並列処理を実行した場合、各プロセスが実行するSQL文をキャプチャしたもので、PSCを使用することにより、生じた変化を確認することができます。

- Oracle 9iのではPECとPESが全く異なるカーソルを使用することを確認することができます。
Oracle 9i:
PEC:PXタスクを実行する

SQL> SELECT/*+ PARALLEL(A20)*/ COUNT(*)FROM big_table a;

PES(s):次のように変形されたSQL文がPESによって実行

SQL> SELECT/*+ PIV_SSF*/
       sys_op_msr(COUNT(*))
FROM(
        SELECT/*+ NO_EXPAND ROWID(A2)*/
               0
        FROM"MAXGAUGE"。 "BIG_TABLE」px_granule(0、block_range、dynamic)a2
       )a1;

- Oracle 10gでは、PECとPESが同じカーソルを使用することを確認することができます。
Oracle 10g
PEC:PXタスクを実行する
SQL> SELECT/*+ PARALLEL(A20)*/ COUNT(*)FROM big_table a;

PES(s):PECが実行されたSQLステートメントと同じ形のSQL文が実行

SQL> SELECT/*+ PARALLEL(A20)*/ COUNT(*)FROM big_table a;

Oracle 10gの以前のバージョンでは、PECが実行されたPX作業をPESに分配するために、特別な形のSQL文をPESが実行できるようにする方法を使用しました。上記の例では、確認できる暗号のようなSQL文に、各PESがこの特別な形のSQL文を使用して、それぞれの領域を分けて作業を行うことになります。これらの処理方法は、並列処理をSQL文に変換する複雑なプロセスを経る欠点を内包しているのです。

これらの問題点を克服するために、Oracle 10gでは、PECが生成されたカーソル(またはプラン)をすべてのPESが共有するモデルを使用します。これにより、複雑な変換プロセスが不要となり、PXと関連するすべてのプロセスが同じSQL文を使用するための監視と管理が容易になりました。PSCモデルが採用された方法の核心は、実行計画は共有するが、各PESが実行計画の特定の部分だけを実行するようにPECが追加のメッセージを介して制御するものとなります。次の例に、これらの概念が簡潔に表現されています。

- 2つのテーブルをHASH JOINにPXで実行します。

SQL> SELECT/*+ USE_HASH(A B)PARALLEL(A)PARALLEL(B)*/ COUNT(*)
FROM BIG_OBJECT A、BIG_OBJECT B;

- 実行計画は、以下の通りです。

SELECT STATEMENT ALL_ROWS-Cost:40564
  SORT AGGREGATE
   PX COORDINATOR
    PX SEND QC(RANDOM)
     SORT AGGREGATE
      HASH JOIN
       PX RECEIVE
        PX SEND HASH
         PX BLOCK ITERATOR
          TABLE ACCESS FULL BIG_OBJECT(1)
       PX RECEIVE
        PX SEND HASH
         PX BLOCK ITERATOR
          TABLE ACCESS FULL BIG_OBJECT(2)

PECとPESは、同じ実行計画を共有し、PECは、各PESに実行計画の中でどの部分を担当するかどうかを制御します。

シングルインスタンス環境では、PECが生成されたカーソルをPESが自然に共有することができますが、RACなどのマルチインスタンス環境では、PECは、他のインスタンスの特定のPESにSQLテキストを送信し、このPESが生成されたカーソルを他のPESが共有する方法でPSCモデルを実装します。

RACのようなグリッド環境は、並列処理におけるもう一つの利点を提供します。高いCPUパワーを持つシングルインスタンス環境でも、並列処理の利点を最大化することができますが、RACシステムでは、複数のインスタンス間での並列処理を分割することができますので、本当の意味での並列実行が可能となります。しかし、インスタンス間の並列処理を分割して使用することが常に有利ではないという点が重要です。同じインスタンス内で並列プロセス間の通信は、共有メモリ(Shared Memory)を介して行われます。これに対し、インスタンス間の並列プロセス間の通信は、インターコネクト(Interconnect)を通じたIPCを介して行われます。たとえ1Gbpsや10Gbps級の高速インターコネクトで通信していても、同じマシン内の共有メモリを介したデータ交換に比べても非効率的であることは自明です。したがって、過度のノード間の並列化は、相互接続の負荷を誘発して、むしろ性能を低下させることになるのです。

同じインスタンス内で行われる並列処理をインスタンス内の並列化(Intra-Instance Parallelism)と呼び、複数のインスタンス間で行われる並列処理をインスタンス間の並列化(Inter-Instance Parallelism)と呼びます。それぞれの方法は、互いに長所と短所を持っているので、それぞれの特徴を考慮し使用する必要があります。Oracleの基本的な方針は、インスタンス内の並列化を先に試してみて、1つのインスタンスで処理するのが難しい作業の場合には、インスタンス間並列化をしようとするものです。つまり、ユーザーに要求された並列処理が一つのインスタンスが保有するリソースパワーで処理可能な場合には、Oracleは可能なインスタンス内並列処理を試みます。しかし、複数のインスタンスが仕事を分けて実行することが有利であると判断される場合には、クラスタ内の他のインスタンスにも作業を分配します。Oracleは基本的に合理的な判断をするため、ほとんどの場合、これをそのまま従うのが良いことになります。

しかし、いくつかの状況では、特定のインスタンスまたはインスタンスグループに特定の並列処理を実行するように割り当てる必要がある場合があります。例えば5つのノードからなるRACシステムで、2つのノードはバッチ処理のためだけに使用し、残りの3つのノードはオンラインで作業のためだけに使用する、と仮定してみましょう。大容量のデータを処理するための並列処理は、2つのノードでのみ実行するように制御することができない場合、クラスタを管理することは非常に難しいことになります。幸いなことに、Oracleは、ユーザーが直接インスタンス内並列化とインスタンス間並列化を制御することができるインテリジェントな方法を提供します。INSTANCE_GROUPSとPARALLEL_INSTANCCE_GROUP二つのパラメータを使用すると、これらの目的を達成することができます。

INSTANCE_GROUPSパラメータは、特定のインスタンスを特定のグループに指定する役割を持ちます。PARALLEL_INSTANCE_GROUPSパラメータは、並列処理の実行時にどのようなインスタンスグループにPESを割り当てるかを決定する役割を持ちます。この二つのパラメータが持つ値はすべて、論理的な値であることに注意しなければなりません。パラメータ名のため、このパラメータの値が実際のインスタンス名を使用すると勘違いするケースが多くあります。以下に使用例を示します。

- インスタンス1の場合:インスタンス1:ソウル、釜山作業の両方で使用します。
SQL> ALTER SYSTEM SET INSTANCE_GROUPS = SEOUL、BUSAN SCOPE = SPFILE;

- インスタンス2の場合:インスタンス2:釜山作業のみ使用します。
SQL> ALTER SYSTEM SET INSTANCE_GROUPS = BUSAN SCOPE = SPFILE;

- 両方のインスタンスを再起動する。

- ソウルと関連するタスクを実行しようとするときは、「SEOUL」のグループを指定します。
  以下のような処理を実行すると、いくつかのインスタンスで作業を行うかどうか、
 PESは、常にインスタンス1回のみ実行されます。
SQL> ALTER SESSION SET PARALLEL_INSTANCE_GROUP = SEOUL;
SQL> SELECT / * + PARALLEL(A)PARALLEL(B)* /
COUNT(*)
FROM BIG_SEOUL1 A、BIG_SEOUL2 B
WHERE A.ID = B.ID;

- 釜山と関連するタスクを実行しようとする場合には、「BUSAN」グループを指定する。
 以下のように処理を実行すると、いくつかのインスタンスで作業を行うかどうか、
 PESはインスタンス1とインスタンス2回の両方を使用します。
SQL> ALTER SESSION SET PARALLEL_INSTANCE_GROUP = BUSAN;
SQL> SELECT / * + PARALLEL(A)PARALLEL(B)* /
COUNT(*)
FROM BIG_BUSAN1 A、BIG_BUSAN2 B
WHERE A.ID = B.ID;

上記の例でわかるように、並列ワークグループをうまく活用すれば、並列処理を効率的にインスタンスに分配することができるのです。業務設計とよく連動して使用すると、不必要なインターコネクトの無駄を事前に遮断することができるので、性能面でも大きなメリットを提供することができます。

Oracleは、並列処理を最適化するためのパーティションを積極的に活用します。例えば、パーティションに分割されているテーブルのDML操作はパラレルDML(Parallel DML、PDML)に切り替えることができます。並列に実行されるDML操作は、各パーティションごとに独立したDMLが実行されるため、大量DMLのパフォーマンスを最大化することができます。

並列処理とパーティションの関係で必ず言及する必要がある機能は、パーティション指向結合(Partition-wise Join)です。パーティション指向結合とは、パーティションごとに並列に結合操作を実行することにより、大量のデータの結合のパフォーマンスを最適化する機能を言います。パーティション指向結合は、結合対象となる表のパーティション構成が同じかどうかに応じて、パーティション全体指向結合(Full Partition-wise Join)と部分パーティション指向結合(Partial Partition-wise Join)に区分されます。

 ・パーティション全体指向結合:結合対象となる2つのテーブルが同じパーティション(Equi-partitioned)
  で構成されている場合。同じパーティションと同じパーティション方式(ハッシュ/リスト/範囲)
  と同じパーティション数、同じパーティションキーを持つことを意味します。この場合には、
  各テーブルのパーティションが1:1でマッチングされ、各パーティションのペアが個別に結合が行われます。
  したがって、並列結合で最適なパフォーマンスを出すことができるのです。一つの欠点は、
  DOP(Degree Of Parallelism)がパーティションの数によって制限されるというものです。
  例えば、パーティションの数が4つのテーブルに対して、パーティション全体指向結合が発生した場合、
  最大DOPは4になります。

 ・部分パーティション指向結合:結合対象となる表のパーティション構成が異なる場合。
  この場合、Oracleは、結合の先頭となるテーブルに基づいて、残りのテーブルを仮想パーティションを分けた後、
  各パーティションごとに個別に結合を実行します。フルパーティション結合ではなく、性能面で不利ですが、
  並列処理の利点を最大化することができる方法の一つです。部分のパーティション指向結合でも、
  パーティション全体指向結合と同様に、最適なパフォーマンスを得るためDOPを先頭のテーブルの
  パーティション数に制限されます。

次の例は、結合対象となる表のパーティション構成に応じて、全体/部分のパーティション指向結合がどのように行われ、その実行計画はどのように表示されるかをテストした結果です。

- PX_TESTテーブル:パーティションの数が4であるハッシュパーティション
SQL> CREATE TABLE PX_TEST(ID NUMBER、NAME VARCHAR2(100))
PARTITION BY HASH(ID)PARTITIONS 4;
- PX_TEST2テーブル:パーティションの数が4であるハッシュパーティション
SQL> CREATE TABLE PX_TEST2(ID NUMBER、NAME VARCHAR2(100))
PARTITION BY HASH(ID)PARTITIONS 4;
- PX_TEST3テーブル:パーティションのないテーブル
SQL> CREATE TABLE PX_TEST3(ID NUMBER、NAME VARCHAR2(100));

-------------------------------------------------- -------------
- Case1:全パーティション指向結合します。 PX_TESTテーブルとPX_TEST2テーブル
SELECT / * + PARALLEL(A)PARALLEL(B)* / COUNT(*)
FROM PX_TEST A、PX_TEST2 B
WHERE A.ID = B.ID

---> 2つのテーブルのパーティション構成が同一であるので、
   全体のパーティション指向結合でロック解除。
 SELECT STATEMENT ALL_ROWS-Cost:33882
  SORT AGGREGATE
   PX COORDINATOR
    PX SEND QC(RANDOM)
     SORT AGGREGATE
      PX PARTITION HASH ALL
       HASH JOIN
        TABLE ACCESS FULL OWI.PX_TEST2(2)
        TABLE ACCESS FULL OWI.PX_TEST(1)

-------------------------------------------------- -------------
- Case 2:部分のパーティション指向結合します。 PX_TESTテーブルとPX_TEST3テーブル
SELECT / * + PARALLEL(A)PARALLEL(B)* / COUNT(*)
FROM PX_TEST A、PX_TEST3 B
WHERE A.ID = B.ID

---> 2つのテーブルのパーティション構成が異なりますので部分の
   パーティション指向結合でロック解除。
 SELECT STATEMENT ALL_ROWS-Cost:41720
  SORT AGGREGATE
   PX COORDINATOR
    PX SEND QC(RANDOM)
     SORT AGGREGATE
      HASH JOIN
       PX RECEIVE
        PX SEND PARTITION(KEY)
         PX BLOCK ITERATOR
          TABLE ACCESS FULL OWI.PX_TEST3(2)
       PX PARTITION HASH ALL
        TABLE ACCESS FULL OWI.PX_TEST(1)

-------------------------------------------------- -------------
- Case 3:一般的な結合。 PX_TEST3テーブルとPX_TEST3テーブル(セルフ結合)
SELECT / * + PARALLEL(A)PARALLEL(B)* / COUNT(*)
FROM PX_TEST3 A、PX_TEST3 B
WHERE A.ID = B.ID

--->両方のテーブルは、パーティションがないため、一般的な結合でロック解除。
 SELECT STATEMENT ALL_ROWS-Cost:40564
  SORT AGGREGATE
   PX COORDINATOR
    PX SEND QC(RANDOM)
     SORT AGGREGATE
      HASH JOIN
       PX RECEIVE
        PX SEND
         PX BLOCK ITERATOR
          TABLE ACCESS FULL OWI.PX_TEST3(1)
       PX RECEIVE
        PX SEND
         PX BLOCK ITERATOR
          TABLE ACCESS FULL OWI.PX_TEST3(2)

パーティション指向結合では、結合のパフォーマンスの最適化のためにDOPをパーティション数に制限します。強制的にDOPを指定しない場合、Oracleは、パーティション指向の結合を実行する実行計画を生成しDOPも自然パーティションの数によって決定されます。もし強制的にDOPを指定すると、Oracleは、パーティション指向の結合をせずに、一般的な並列結合を実行します。もしパーティションの数とCPUの数に大きな違いがない場合は、パーティション指向結合が断然有利です。一方、CPUの数がパーティションの数に比べて十分に大きい場合に、すべてのCPUのリソースを活用することができる一般的な並列結合がより有利です。

並列タスクの実行において、パーティションだけ重要な要素がクラスタです。パーティションがセグメントの分割を担当すると、クラスタは、リソース(CPU、メモリ)の分割を担当することができます。Oracleは、並列処理の実行時、クラスタに関する情報を参照し、最適な実行計画を作成します。

並列実行に関与するPECとPESはタスクの実行時にメッセージやデータを送信受信のために待機している場合が多く、この場合PX:XXX型のイベントを待機することが観察されます。並列実行に関連する待機イベントに対する正確な定義と説明は、メタリンクの文書番号191103.1を参照すると得ることができます。ここではすべての待機イベントをいちいち列挙する代わりに、簡単な例を使って、各待機イベントの意味を簡単に調べることにします。次のように2つのテーブルに対して並列ハッシュ結合を実行する作業をしましょう。この作業は、SQL文と実行計画は、次のとおりです。

- 2つのテーブルをHASH JOINにPXで実行します。
SQL> SELECT/*+ USE_HASH(A B)PARALLEL(A)PARALLEL(B)*/
COUNT(*)
FROM BIG_OBJECT A、BIG_OBJECT B;
- 実行計画は、以下の通りです。
SELECT STATEMENT ALL_ROWS-Cost:40564
  SORT AGGREGATE
   PX COORDINATOR
    PX SEND QC(RANDOM)
     SORT AGGREGATE
      HASH JOIN
       PX RECEIVE
        PX SEND HASH
         PX BLOCK ITERATOR
          TABLE ACCESS FULL BIG_OBJECT(1)
       PX RECEIVE
        PX SEND HASH
         PX BLOCK ITERATOR
          TABLE ACCESS FULL BIG_OBJECT(2)

セグメント分割

Segment Partitioningを参照してください。

複数のバッファプールとLRU

Oracleは、バッファ・キャッシュを効率的に使用するために、二種類のLRU(Least Recently Used)リストを使用します。 LRUリストは、最も最近に使用されたバッファ、未使用のバッファ、使用中であるか、使用されたバッファ、まだLRUWリスト(Dirty List)に置き換えられていないダーティ(Dirty、変更された)バッファなどを含みます。いくつかの記事では、LRUリストを代替リスト(Replacement List)と呼びます。 LRUWリストはまだディスクに書き込まれていない変更されたバッファのリストを管理します。バッファキャッシュのすべてのバッファは、必ずLRUリストまたはLRUWリストのどちらかに属します。 LRUWリストはダーティリスト(Dirty List)、または記録リスト(Write List)とも呼びます。

Oracleは、リストスキャンの効率化のためLRUリストやLRUWリストを、メインリスト(Main List)と補助リスト(Auxiliary List)に分けて管理します。これを整理すると、次の通りになります。

LRUリスト(代替リスト)
- メインリスト:使用されたバッファのリスト。ホット領域とコールド領域に区分管理される。
- 補助リスト:フリーバッファのリスト。より正確に表現すると、未使用されたバッファや、
 DBWRによって記録されたバッファのリスト

LRUWリスト(記録リスト)
- メインリスト:変更されたバッファのリスト
- 補助リスト:現在DBWRによって記録されているバッファのリスト

oracleは、フリーバッファ探索時には、まずLRUリストの補助リストからフリーバッファを探します。補助リストのバッファがすべて使用されている場合には、メインリストのコールド領域でフリーバッファを探します。インスタンスが最初に駆動されたときは、すべてのバッファは、補助リストで管理されます。また、変更されたバッファがDBWRによって記録された後は、再びフリーバッファに変わり、LRUリストの補助リストに追加されます。

LRUリストとLRUWリストは、常にペア(Pair)で存在し、このペアをWorking Setと呼びます(すなわちWorking Set = LRU + LRUW)。オラクルは、複数のWorking Setを使用します。一つのWorking Setを一つのcache buffers lru chainラッチが管理します。 LRUリストやLRUWリストを閲覧しようとするプロセスは、必ずcache buffers lru chainラッチを獲得しなければなりません。したがって、同時に多くのプロセスがLRUリストやLRUWリストを参照したい場合にcache buffers lru chainラッチを獲得するために競争することになり、この過程でlatch:cache buffers lru chainイベントを待機します。

_DB_BLOCK_LRU_LATCHES隠しパラメータの値を照会したり、次のステートメントを利用してcache buffers lru chainラッチの最大数を求めることができます。

SQL> select count(*) from v$latch_children where name = 
                'cache buffers lru chain'; 
  COUNT(*)
----------
        16

しかし、上記のラッチをすべて使用することはありません。Oracleには、さまざまな種類のバッファプールが存在し、それぞれのバッファプールが、これらのラッチをまんべんなく使用します。

複数のバッファプールの概要この情報は、以下の通りです。

まず、Defaultバッファは、頻繁に使用されるオブジェクトのために使用します。

第二に、Keepバッファは「比較的」頻繁に使用されるオブジェクトのために使用します。非常に頻繁に使用されるオブジェクトは、Defaultバッファのホット(Hot)の領域に存在する可能性が大きいので、あえてKeepバッファに常駐させる必要がありません。しかし、使用頻度が比較的低いオブジェクトの場合には、Defaultバッファのコールド(Cold)の領域にあるが、バッファキャッシュから追い出される可能性が高いため、Keepバッファに常駐させることが望ましいことになります。

第三に、Recycleバッファは使用頻度が低いオブジェクトに対して使用します。 KeepバッファとRecycleバッファは、基本的に、ホット領域(使用頻度の高いブロックが滞在領域)とコールド領域(使用頻度が低いブロックが滞在領域)の区分がありません。正確に言えば、ホット領域を持たないのです。この点を除けば、Recycleバッファの動作は、Defaultバッファと全く同じであると言えます。しかし、Keepバッファの場合には、若干の違いがあります。FTS方法で読まれるブロックは、基本的にLRUリストの最後に位置します。しかし、Keepバッファの場合には、FTSの方法で読まれることを前提とするため、FTSの方法で読まれたブロックであってもLRUの一番前に位置するのです。

Oracle 10gの以前のバージョンでは、FTSの方法で読まれたブロックには、Touch Countをすぐに増加させませんでした。これにより、FTSの方法で非常に頻繁に読まれるテーブルがバッファキャッシュから押し出されていく現象が起こることがあります。したがって、このような形態のテーブルには、できればKeepバッファを使用することが望ましいということになります。

Keepバッファの使用において、もう一つ注意すべき点は、Keepバッファのサイズに関するものです。特定のオブジェクトをKeepバッファに完全に常駐させるためには、CRブロックが占める大きさまで考慮する必要があります。したがってKeepバッファのサイズは、Keepバッファに入れるオブジェクトのフルサイズよりも大きくとることが望ましくなります。

バッファとラッチとの関係は次のように整理が可能です。

まず、バッファは大きくDefaultバッファプール、Keepバッファプール、Recycleバッファプールに分割されます。第二に、Defaultバッファー・プールは、再ブロックサイズごとの標準ブロック・サイズ、2K、4K、8K、16K、32Kバッファプールに分割されます。個々のバッファー・プールは、それぞれ独立したcache buffers lru chainラッチを使用します。したがって、ラッチの最小数は8個となります。次のステートメントを使用すると、いくつかのラッチがどのような種類のバッファに使用していることを確認することができます。

SQL> 
-- x$kcbwds=Working Set, x$kcbwbpd=Buffer pool, v$latch_children=Latch
select d.blk_size, c.child#, p.bp_name, c.gets, c.sleeps
from x$kcbwds d, v$latch_children c, x$kcbwbpd p
where
 d.set_latch = c.addr
 and d.set_id between p.bp_lo_sid and p.bp_hi_sid
order by c.child#
;

  BLK_SIZE     CHILD# BP_NAME                GETS     SLEEPS
---------- ---------- -------------------- ---------- ----------
      8192          1 KEEP                     42          0
      8192          2 KEEP                     42          0
      8192          3 RECYCLE                  42          0
      8192          4 RECYCLE                  42          0
      8192          5 DEFAULT                2337          0     
      8192          6 DEFAULT                2322          0     
      2048          7 DEFAULT                  33          0
      2048          8 DEFAULT                  33          0
      4096          9 DEFAULT                  32          0
      4096         10 DEFAULT                  32          0
      8192         11 DEFAULT                  32          0
      8192         12 DEFAULT                  32          0
     16384         13 DEFAULT                  32          0
     16384         14 DEFAULT                  32          0
     32768         15 DEFAULT                  32          0
     32768         16 DEFAULT                  32          0

上記の結果を解釈するとKeepバッファプールの2つ、Recycleバッファプールの2つ、そしてDefaultバッファプールのブロックサイズごとそれぞれ2つずつのラッチを使用することを知ることができます。もしDefaultバッファプールに8Kの標準サイズのバッファプルマン使用する場合、2つのlruラッチのみ使うようになるでしょう。最大16個のラッチの数はCPUの数から由来したものです。Oracleは、DBWRの数が4よりも小さい場合、4 * CPU_COUNTだけlruラッチを作成し、DBWRの数が4以上であればDB_WRITER_PROCESSES * CPU_COUNTだけlruラッチを生成します。筆者のシステムでは、CPUの数は、4つのであるため、16個のラッチが生成され、その中で実質的に使われているのは、8Kのバッファプールに割り当てられている二つのラッチであることを知ることができます。ただし、前述したように、バッファプールの最小数が8個であるため、lruラッチの最小数も8に注意してください。 サーバプロセスがスキャンするすべてのバッファがLRUリストに登録されるため、LRUリストを効率的に管理することが非常に重要です。特に不必要に多くの量のブロックをスキャンするプロセスによって重要なバッファがバッファキャッシュから押し出さはを最小限に抑えることができなければなりません。 Oracleは8i以降のバージョンからLRUリストを効率的に管理するために、Touch countに基づいLRUアルゴリズムを使用します。このアルゴリズムは、LRUリストのメインリストを管理するために使用されます。

グローバル一時テーブル

グローバル一時テーブル(Global Temporary Table)は、セッションレベルの一時的なデータを格納する目的で使用されます。Oracleの実行計画を保存するためのPLANテーブルがグローバル一時テーブルの代表的な事例です。グローバル一時テーブルは、セッションレベルの一時データを格納するため、RACシステムでのグローバル同期が不要です。したがって、Oracleは、グローバル一時テーブルには、グローバル同期を実行していません。したがって、セッション・レベルでのみ使用するデータを操作する場合には、可能なグローバル一時テーブルを使用するようにします。

読取り専用表領域

読取り専用表領域(Read-only Tablespace)に属するデータにアクセスする過程では、グローバル同期が不要です。読取り専用表領域のデータは、絶対ユーザーのDMLによって変更されないので、Oracleは、不必要なグローバル同期過程を経ません。したがって、読み取り専用のデータは、必ず読取り専用表領域に位置させることが望ましいことになります。

V$CURRENT_BLOCK_SERVER

V$ CR_BLOCK_SERVERビューがCRブロック転送の統計値を提供するのに対し、このビューは、Currentブロック転送の統計値を提供します。このビューを使用してCurrentブロック転送に関するピン(Pin)、フラッシュ(Flush)、フュージョン記録(Fusion Write)作業の実行性能を確認することができます。ピン(Pin)に多くの時間がかかる場合、同じブロックの競合(Contention)が激しいことを意味し、REDOフラッシュ(Redo Flush)に多くの時間がかかる場合は、LGWRプロセスの性能に問題があることを意味します。フュージョン記録(Fusion Write)に多くの時間がかかる場合、DBWRプロセスのパフォーマンスに問題があることを意味します。各列の意味は次のとおりです。

カラム名         説明
PIN1            1ミリ秒以内にピン(Pin)を獲得した回数
PIN10           10ミリ秒でピン(Pin)を獲得した回数
PIN100         100ミリ秒以内にピン(Pin)を獲得した回数
PIN1000       1000ミリ秒でピン(Pin)を獲得した回数
PIN10000     10000ミリ秒以内にピン(Pin)を獲得した回数
FLUSH1           1ミリ秒以内に、REDOフラッシュ(Re​​do Flush)を実行した回数
FLUSH10         10ミリ秒で、REDOフラッシュ(Re​​do Flush)を実行した回数
FLUSH100       100ミリ秒で、REDOフラッシュ(Re​​do Flush)を実行した回数
FLUSH1000     1000ミリ秒で、REDOフラッシュ(Re​​do Flush)を実行した回数
FLUSH10000   10000ミリ秒以内に、REDOフラッシュ(Re​​do Flush)を実行した回数
WRITE1           1ミリ秒以内に融合記録(Fusion Write)を実行した回数
WRITE10         10ミリ秒でフュージョン記録(Fusion Write)を実行した回数
WRITE100       100ミリ秒でフュージョン記録(Fusion Write)を実行した回数
WRITE1000     1000ミリ秒でフュージョン記録(Fusion Write)を実行した回数
WRITE10000   10000ミリ秒以内に融合記録(Fusion Write)を実行した回数

下のスクリプトは、筆者のテストシステムでV$ CURRENT_BLOCK_SERVERビューを照会したものです。REDOフラッシュ(Redo Flush)とフュージョン記録(Fusion Write)に多くの時間が消費されることを確認することができ、このことから、I / Oシステムの性能が全体的に遅いという推論が可能となります。

SELECT * FROM V$CURRENT_BLOCK_SERVER
-------------------------------------		
PIN1			: 53642652
PIN10			: 8079
PIN100			: 126715
PIN1000		        : 2276
PIN10000		: 643
FLUSH1			: 283
FLUSH10		        : 5485
FLUSH100		: 67842
FLUSH1000               : 12403
FLUSH10000	        : 311
WRITE1			: 0
WRITE10		        : 18715
WRITE100		: 947335
WRITE1000	        : 3607630
WRITE10000		: 863429
-----------------------------------

分析事例

インターコネクト設定によるglobal cache cr reques待機現象

active sessionのpeak視点を確認してみると、global cache関連イベントを待機するセッションが多数発生しています。

7_5_1

global cache cr requestイベントを待機するセッションはLogical reads、physical readsなどのセッション仕事量が0であり、全く仕事をしていない。連続して遅延されていることを知ることができます。

システムレベルのsession logical reads / physical readsのグラフを確認してみても、全く仕事量が発生しないことを確認することができます。 また、global cache cr requestのグラフの数値を使用して、300秒以上待機していることを知ることができます。

global cache cr requestイベントは、ローカルキャッシュに存在しないデータブロックを読みたいセッションがそのデータブロックを管理するマスターノードにブロック転送を要求し、応答を受信するまで待機するイベントです。 つまり、RACノード間のブロックの交換が円滑に行われないことを知ることができます。

global cache cr requestのパラメータ値を使用してFile#、Block#を知ることができ、上記のセッションは他のすべてのブロックを要求しているので、ブロックの競合ではなく、ネットワーク環境設定に起因する遅延の原因を推測することができます。 つまり、遅いインターコネクト、非効率的なネットワークの設定、非効率的なバッファキャッシュなどを考慮することができるのです。

上記の事例は、相互接続の帯域幅を規定通りに設定したため、発生しました。一般的には、1Gigabit Ethernetのインターコネクトを使用しますが、この場合に、同時に交換可能なデータの量は125MByteです。したがって125MByte以上のデータを相互接続で交換になると、ブロック転送の過程で、応答時間が遅延される現象が発生します。 問題のインスタンスは、同時に交換可能なデータの量を10MByteに設定されていて、遅延されている現象が発生しました。

インターコネクトの帯域幅を高めた後は、次のようにブロックの交換が円滑問題が解決されました。

7_5_2

Active Sessionの推移が安定され、global cache cr requestのグラフの数値も0.1秒以下に減少しました。