Row Cache Lock

基本情報

 

Oracleは、ディクショナリの情報をSGA内のRow Cache(あるいはdictionary cache)領域に保存しています。Row CacheはShared Pool領域に存在し、次のようなクエリで確認することができます。

 

SQL>  select pool, name, bytes from v$sgastat
  2   where name = 'row cache';
              
                                             
POOL            NAME             BYTES   
--------------- --------------- ----------   
shared pool    row cache    7480368   

 

ディクショナリの内容を変更しようとするプロセスは、それに対応するrow cache objectに対してrow cache lockを獲得しなければなりません。row cache lockはRow Cache領域を保護するロックです。Row CacheあるいはDictionary Cache領域は、Oracleのディクショナリ情報を保持する共有メモリ領域には、多くのプロセスが同時にディクショナリ情報を参照するか、変更するRow Cacheでの競合が発生するのです。V$ ROWCACHEビューを参照するとどのような種類のデータがRow Cache領域で管理されているかどうかを確認することができます。

 

SQL> SELECT                                            
		CACHE#, TYPE, PARAMETER, COUNT, GETS, GETMISSES     
FROM V$ROWCACHE;
                                       
CACHE#	TYPE	PARAMETER		COUNT	GETS	GETMISSES       
----	----	-------------------		----	----	----------
1	PARENT	dc_free_extents		0	0	0                     
4	PARENT	dc_used_extents		0	0	0                     
2	PARENT	dc_segments		651	17000	1048              
0	PARENT	dc_tablespaces		10	1665199	11          
5	PARENT	dc_tablespace_quotas		1	2	1               
6	PARENT	dc_files			0	0	0                         
7	PARENT	dc_users			32	113166	36                
3	PARENT	dc_rollback_segments		26	45160	36        
8	PARENT	dc_objects			1033	58828	1861            
…                                                     
16	SUBORDINATE	dc_histogram_data	4765	38747	4639      
16	SUBORDINATE	dc_histogram_data	124	7592	130         
19	SUBORDINATE	dc_partition_scns	0	0	0                 
7	SUBORDINATE	dc_users		29	1211	29                
7	SUBORDINATE	dc_users		0	0	0                       
21	SUBORDINATE	rule_fast_operators	0	0	0               

 

Row Cache Object(以下RCO)は、個々のディクショナリオブジェクトを意味します。例えば一つのシーケンスAがあるとましょう。シーケンスAは、物理的にSYSTEM表領域のSYS.SEQ$テーブルに格納されています。ユーザーがシーケンスAを参照するとSYS.SEQ$テーブルからシーケンスAに対応するロー(Row)をbuffer cacheを経由してRow Cache領域に読み込み、この過程で、シーケンスAに対応するRCOが生成されます。

 

Shared_pool

 

通常のテーブルの場合、ブロック単位でキャッシュ領域に読み込む一方、ディクショナリ情報は、ロー単位でキャッシュ領域に読み込むため、Row Cacheという名前が付けられました。 RCOを変更しようとするプロセスは、必ずrow cache lockを獲得しなければならないのです。例えばSEQUENCE.NEXTVALを呼び出す過程でディクショナリ情報を変更する必要が生じた場合のシーケンスについてrow cache lockをSRX(5)モードで獲得しなければなりません。 ALTERステートメントを利用して、テーブルを変更するプロセスは、テーブルのrow cache lockを排他的(X)モードで獲得しなければならないのです。

 

Row cache lockはEnqueue構造を使用せず、row cache object情報の中に存在するロック保持リスト(Owner list)とロック待ちリスト(Waiter list)を介してブロックメカニズムを実装します。このような構造は、library cache lock、library cache pin、buffer lockと同様なのですが、バッファヘッダーにuser listとwaiter listが存在し、この二つのリストを使用してブロッキングメカニズムを実装します。よくrow cache lockをrow cache enqueueと呼ぶのですが、これは意味を正確に伝達するために使用される用語であるだけでTXロックのようにEnqueue構造を使用しないという点に注意してください。

 

V $ ROWCACHEビューとV $ ROWCACHE_PARENTビューを使用すると、row cacheでの競合の分析が可能となります。 V $ ROWCACHEビューは、個々のrow cache objectではなく、row cache全体(種類)の活動性に関する統計情報を提供します。 V $ ROWCACHE_PARENTビューを使用すると、実際の個々のrow cache objectの活動性を把握することができます。 row cache lock待機イベントの一つ不便な点は、実際のオブジェクトの情報ではなく、オブジェクトの種類の情報だけを提供するものであることです。これにより、正確にどのようなrow cache objectが問題になるのかを把握するのは難しいことなのです。 row cacheダンプを使用すると、実際のオブジェクトに関する情報まで得ることができます。

 

パラメータと待機時間

 

待機パラメータ

RCOを使用するすべてのプロセスは、row cache lockを獲得しなければなりません。Row cache lockを獲得する過程で競合が発生した場合row cache lockイベントを待機します。row cache lock待機イベントのパラメータの定義は、以下の通りです。

 

1.P1:cache id : キャッシュの種類を意味します。V$ ROWCACHE.CACHE#と結合すると、
   キャッシュの種類を得ることができます。
   例えばdc_tablespaces、dc_sequencesと同じ値を持ちます。

2.P2:mode : Row cache lockを獲得したモードを意味します。モード値の意味は、
   一般的なロックモード値定義と一致します。
   1はNモードでは、2は、RSモードでは、3はRXモードでは、4は、Sモードでは、
   5はSRXモードでは、6は、Xモードを意味します。

3.P3:request : Row cache lockを要求されたモードを意味します。モード値の意味はP2と同様です。

 

待機時間

3秒間待機します。100回のタイムアウトが発生した場合、プロセスは中断され、alert logファイルに「WAITED TOO LONG FOR A ROW CACHE ENQUEUE LOCK」とエラーメッセージを記録した後、終了します。

 

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

 

NOCACHE属性のシーケンスを使用している場合

Row cache lockの競合が最も頻繁に発生する場合は、NOCACHE属性のシーケンスです。キャッシュを使用していないシーケンスは、次の値(NEXTVAL)を呼び出すたびにディクショナリ情報を変更する必要があり、この過程でRCOについてrow cache lockをSRX(5)モードで獲得しなければなりません。SRXモード間での互換性がない。したがってNOCACHE属性のシーケンスに複数のセッションが同時に次の値(NEXTVAL)を呼び出す場合には、row cache lockイベントの待機が広範囲に発生し、P2の値は「5」で観察されるのです。次の例は、NOCACHE属性のシーケンスが使用されている環境で発生するrow cache lock待機現象を監視したものでP3(request)の値が「5」の値を示すことを確認することができます。

 

WAIT #9: nam='row cache lock' ela= 82009 cache id=13 mode=0 request=5 obj#=-1 tim=187377640119
…                                                                                            
SQL> SELECT PARAMETER FROM V$ROWCACHE WHERE CACHE# = 13;
                                      
PARAMETER                                                                                     
----------                                                                                    
dc_sequences        
                                                        

 

NOCACHE属性のシーケンスを使用することによってrow cache lockが発生した場合、複数のセッションで同時に使用されるシーケンスは、キャッシュのサイズを大きくしてくれることが望ましい事になります。1000程度のキャッシュサイズをデフォルトとして使用しますが、必要であれば、10,000以上の大きな値を付与してもよいのです。

同時実行性の高いシステムでは、常にrow cache lock競合が生じる危険性が常に存在します。シーケンスを除いた大部分のrow cache lock競合現象は明確なチューニング方法がないのが現状です。適切なシステムのチューニングを介して同時実行を減らすことが最善の方法となります。

 

Event Tip

 

待機イベントを通じたrow cache rockの確認

Row cache lockの競合を観察するための最も簡単な方法は、待機イベントを利用するものである。しかし、待機イベントだけではロックホルダーセッションと待機セッションの間の競合関係を把握するのが難しい。 V $ ROWCACHE_PARENTビューを利用すれば、これらの競合関係を把握することができる。まずV $ ROWCACHE_PARENTビューの重要な列の意味を調べてみよう。

 

CACHE#     :キャッシュの種類を表します。 V $ ROWCACHE.CACHE#カラムの意味です。
CACHE_NAME  :キャッシュの種類を表します。 V $ ROWCACHE.PARAMETERカラムの意味です。
LOCK_MODE   :このRCOのrow cache lockを獲得したモードです。 1〜6の間の値を持ちます。 
        row cache lock待機イベントのP2と同じ意味です。
LOCK_REQUEST:このRCOのrow cache lockを要求されたモードです。 1〜6の間の角度を持つ。 
              row cache lock待機イベントのP3と同じ意味である。
SADDR       :このRCOを、現在使用中のセッションのアドレス。 「使用中」というのは、
              row cache lockを獲得したか、要求しているということを意味する。
              V $ SESSION.SADDRカラムと結合可能である。

次のスクリプトは、NOCACHE属性のシーケンスを使用で発生するrow cache lockの競合をV $ ROWCACHE_PARNETビューを介して観察する例である。

 

SQL> SELECT                                          
cache_name, lock_mode, lock_request, saddr           
FROM v$rowcache_parent                               
WHERE address = ‘[特定の RCO address]’;
              
CACHE_NAME		LOCK_MODE	LOCK_REQUEST	SADDR           
----------		--------	----------	-------------     
dc_sequences		0		5		C00000009E0923E0              
dc_sequences		0		5               C00000009E0A92A0
dc_sequences		0		5               C00000009E0C15C0
dc_sequences		0		5               C00000009E133F80
dc_sequences		0		5               C00000009E0CCD20
dc_sequences		0		5               C00000009E07C980
dc_sequences		0		5               C00000009E053D80
dc_sequences		5		0               C00000009E089540

 

セッションアドレス値”C00000009E089540」に該当するセッションが5(SRX)番モードでrow cache lockを獲得しており、残りのセッションは5(SRX)番モードでrow cache lockを獲得するために待機しています。キャッシュの種類はすべてのシーケンス(dc_sequences)です。このように、V$ ROWCACHE_PARENTビューとV$ SESSIONビューを適切に活用すれば、row cache lockのホルダーセッション/スタンバイセッションの関係を明確に把握することができます。

 

row cacheダンプとV$ ROWCACHE_PARENTビューから正確なオブジェクトの情報を確認する方法

 

SQL> create sequence seq_seq nocache;
- NOCACHE属性のシーケンスを生成します。
                                                     
- シーケンスのObject IDを取得し、これを16進数に変換する。 16進数に変換された値を用いなければならない
row cacheダンプ内でシーケンスの情報を参照することができます。
select object_id from DBA_OBJECTS where object_name = 'SEQ_SEQ';
                                              
 OBJECT_ID
----------
    107886
                                                                           
SQL> select to_hex(107886)from dual;
      
TO_HEX(107886)
------------------------------------------
1A56E
                                                                                                 
- 次のコマンドを使用して、row cacheをダンプします。
alter session set events」immediate trace name row_cache level 12 '
                                                                               
                                                                                
- Row Cacheダンプファイルの内容は、次のとおりです。
BUCKET 104:
  row cache parent object:address = 201D46CC cid = 13(dc_sequences)
  hash = ba7abee7 typ = 9 transaction = 00000000 flags = 00000002
  own = 201D4740【201D4740,201D4740] wat = 201D4748【201D4748,201D4748] mode = N
  status = VALID / - / - / - / - / - / - / - / - 
  data =
  0001a56e 00020004 000f0002 00020001 000002c1 00000000 00000000 00000000
  02c10000 00000000 00000000 00000000 00000000 64640ace 64646464 64646464
  00646464 00800000 00000000 00000000 00000000 00000000 000002c1 00000000
  00000000 00000000 2d2d0000 2d2d2d2d 2d2d2d2d 2d2d2d2d 2d2d2d2d 2d2d2d2d
  2d2d2d2d 2d2d2d2d 2d2d2d2d 2d2d2d2d

 

上記のダンプファイルから意味のある情報は、以下の通りです。

Bucket:row cacheまた、一般的な他のオブジェクトのようにハッシュ値によるバケット構造で管理されることを
        知ることができます。
address:Row Cache objectのメモリアドレスです。V$ ROWCACHE_PARENTビューと結合することができます。
cid:Category idです。V$ ROWCACHE.CACHE#と結合することができます。
own:row cache lockを保有しているプロセスのリストです。
wat:row cache lockを待機しているプロセスのリストです。
mode:現在row cache lock保有モード(N= Null)です。
data:最初の16進数が1a56eでSEQ_SEQのobject_idの16進数の値と同一であることを知ることができます。

 

- nextvalを大量に呼び出しながらrow cache lockがどのように獲得されるかを観察してみましょう。
- 以下のように10000回の間seq_seq.nextvalを呼び出します。
- row cache lockを獲得してオフにする時間は非常に短いので、多くのsselect回数を実行することで観察が可能
  となります。

declare
  v_seq number;
begin
  for idx in 1 .. 10000 loop
    select seq_seq.nextval into v_seq from dual;
  end loop;
end;
/

- 上記のPL/SQLが実行される間、ダンプを使用して得られたaddress = 201D46CC値を利用して
V $ ROWCACHE_PARENTビュー情報を照会すると、以下の結果のようにSSX(Shared Sub-Exclusive)モードで
 row cache lockを獲得することを知ることができます。
    
SQL> exec print_table( 'select * from v $ rowcache_parent
where address = 201D46CC ');
INDX:2237
HASH:103
ADDRESS:201D46CC
CACHE#:13
CACHE_NAME:dc_sequences
EXISTENT:Y
LOCK_MODE:5

 

Shared Sub-Exclusiveモードの意味は、オブジェクト全体には、Sharedモードで、オブジェクトの一部については、 Exclusiveモードでロックを獲得することです。Seqneuce.nextval呼び出しによってシーケンスディクショナリ情報が変更されている場合には、 シーケンス自体を変更させるのではなく、シーケンスの「次の値」だけを変更させるので、 SSXモードでrow cache lockを獲得することになります。

 

分析事例

 

Remote Query実行時に発生したRow Cache競合分析

 

対象DBの8i – >10gの移行作業後監視中latch:row cache objectsイベントの待機が周期的に発生しました。DBをオープンした7月12日12時以降のデータを対象に、Performace AnalyzerのSession List機能を利用してlatch:row cache objectsイベントの発生状況を照会てみます。

 

8_1_1

 

SELECT * FROM”DUAL”とSQLを実行するセッションがlatch:row cache objectsイベントを待機しており、SQL文がすべて大文字でテーブル名に””(二重引用符)がされていることを見てリモートで実行されたクエリであることを知ることができます。

 

latch:row cache objectsイベント発生する状況で、V $ SESSIONビューを照会して、latchのaddressを確認することができます。

 

8_1_2

 

EVENT#205番latch:row cache objectsを待機するセッションは同じラッチaddressを持っていることをP1RAWカラムを介して知ることができます。(C0000001B7C03BC8)

 

latch:row cache objectsイベントを待機と同じ視点にrow cache lockイベントを待機するセッションも確認することができます。

 

8_1_3

 

特に、latch:row cache objectsイベントを待機するセッションが実行されたSQLが同一です。 また、row cache lockのrow cache id=16回であることを知ることができます。

 

row cache lockが発生したセッションのcache idをV$ ROWCACHEビューを介して照会すると、結果は次の通りです。

 

8_1_4

 

16番row cacheの使用回数(USAGE)とリクエストすることができ(GETS)は、要求が失敗することができ(GETMISSES)が高くなっています。Row Cacheは、Oracleのdictionary meta情報を提供するため、大気や競合が発生しないことがいいので、dc_histogram_defsディクショナリオブジェクトについての任意の部分で使用されるオブジェクトであることを確認しなければなりません。

 

オラクルメタリンクを探してみると、Remote Queryを実行するとき、local DBのテーブル、カラムのヒストグラムを参照するためにdc_histogram_defsディクショナリにアクセスするため、この時shared poolの負荷が発生する不具合があります。(Oracleの8バージョンまで存在していました。) このような状況を考慮して10gにも再発されていないことを、Oracleにお問い合わせすることが望ましいものと考えます。