待ちイベントに関する検証 その6

<待ちイベントに関する検証 その6>
ペンネーム: ダーリン

【latch: cache buffer chains/cache buffer lru chain】

先日、久しぶりにオートバイで箱根に行ってきました。
結構寒かったので、ちょっと暖かいものでも食べようと食券を買おうとしまし
たが、先客がいます。横のお土産を見ながら待っていると、おっとっと、次の
お客さんに先を越されてしまいました。別に急ぐわけではないので、別のお土
産を眺めていると、あっ、また先に行かれてしまいました。温かいうどんにあ
りつくまでに時間がかかってしまいました。そのときふと、「もしやこれはま
さしく”ラッチ”待ちではないか」と思ったしだいです。

※ ただし、9i 以降ではラッチも FIFO で管理される様になっており、順番
どおりにラッチを取得します。

余談はさておき、、
ここまで 3 回にわたって共有プールの待ちイベントを見てきました。
Oracle が SQLを実行する際には、共有プール上にすでに同じSQLの解析結果が
存在するかどうか確認して必要であれば HARD PARSE するといった動きをしま
す。その際、shared pool ラッチや、library cache のラッチを取得しますが、
これらが競合すると “latch: shared pool” や “latch: library cache” の待
ちイベントが発生するというお話でした。

では、SQLが解析されると次はどんなアクションを起こすのでしょう。

当然データを取得するわけですが、それではそのデータはどこにあるのでしょ
う。メモリ上に存在すれば、わざわざ DISK にアクセスする必要はありません。

まずはメモリ上を探してデータを取得しましょう。

データベースのデータが存在するメモリ上の領域といえば、当然、バッファ・
キャッシュです。バッファ・キャッシュに関する検証は下記に詳しいので、ぜ
ひチェックしてください。

<Oracle 9i 関する検証 その1>
https://www.insight-tec.com/mailmagazine/ora3/vol097.html

さて、前々回の待ちイベントの中に、”latch: cache buffer chains” という
ラッチ待ちが発生していたのを覚えているでしょうか。

これは文字通り、バッファ・キャッシュに関するラッチ待ちです。

    event                        wait_class   total_wait  time_waited
    ---------------------------- ------------ ---------- ------------
>>  latch: cache buffers chains  Concurrency           9         4635
    latch: shared pool           Concurrency           4          564
    latch: library cache         Concurrency           2          389
    .......................

実はこれ以外にもバッファ・キャッシュのラッチがあります。
“latch: cache buffer lru chain” です。

こんな感じです。

    event                          wait_class   total_wait  time_waited
    ------------------------------ ------------ ---------- ------------
    latch: cache buffers lru chain Other                14            2
    latch: cache buffers chains    Concurrency           1            0
    ..........

ともに、バッファ・キャッシュの待ちが発生しているのですが、少しその意味
合いが違います。

チューニングの方法を定めるには、まず、その原因を知らなければなりません。

まずは、”latch: cache buffer chains”から。
バッファ・キャッシュはバケットと呼ばれる単位で管理されているということ
を理解してください。バッファ・キャッシュ上の各ブロックにアクセスする時
にはこのバケット単位でラッチを取得します。そのため、同一のバケットに存
在するブロックへのアクセスが競合すると、”latch: cache buffer chains”
のラッチ待ちが発生します。

ということは、、

対応案
1) バケットを増やせば管理されている各バケットに管理されるブロックが
減るので、ラッチ待ちが減る。

2) 不必要にブロックにアクセスしている場合は、処理そのものを見直す。
特に全件検索していると、このラッチが多く発生してしまいます。

3) ブロックサイズを小さくして同一ブロックへの競合自体を減らす。

とにかく、ブロックの競合を減らすことが効果的です。

次に、”latch: cache buffer lru chain”。
これは、バッファ・キャッシュ上のブロックをLRU管理しているリストに関す
る待ちイベントです。ここで発生するラッチ待ちは、とにかく、バッファ・
キャッシュ上のデータの組み換えが頻繁に発生することで誘発されます。
バッファ・キャッシュ上でデータの組み換えが発生するケースとして最も多い
のが全件検索、次に大量更新です。

全件検索は、容易にイメージできると思います。では大量更新の場合はどうで
しょうか。大量更新とは、大量ブロックの更新を意味しているわけではありま
せん。ブロック数が少ない場合でも、頻繁に更新されるブロックが存在すれば
これがダーティーブロックとなり、比較的容易に DISK へ書き戻されてしまい
ます。その代表選手が UNDO ブロックです。

対応案
業務上必要な更新処理あれば、これをやめることは難しいので、やはり、、
1) 無駄な全件検索は避ける。

ということになるでしょうか。

さて、上記はテーブルのブロックに対するラッチ待ちの抑制というお話でした。

もし、競合しているブロックがインデックスだったらどうでしょうか。
インデックスはアクセスされることが多いのでテーブルに比べるとバッファ・
キャッシュ上に存在することが多いと考えられます。

インデックスの競合

インデックスの競合は特に INSERT の際に問題になります。INSERT時の問題は、
特にシーケンス状のデータをキーにしている場合に発生します。これはキーに
格納する値が近傍配置されることで、同じブロックに格納されていることが多
くなるためです。検索においても同様の処理が行われれば、当然同一ブロック
に対するアクセスが発生すると考えられます。

この場合は、ずばり、リバースインデックスが対応策となるでしょう。
リバースインデックスは、キーの値を逆に入れ込むため、まったく同じ値でな
ければ、格納ブロックが分散される可能性が高くなります。

当然、検索処理がユニーク検索である場合に限ります。
なぜなら、レンジ検索を主目的としている場合は格納ブロックが分散されたイ
ンデックスでは検索効果がなくなってしまいますからね。

太鼓の達人で QUEEN にはまる!?
茅ヶ崎にて