Oracle9iに関する検証 その10

<Oracle 9iに関する検証 その10>
ペンネーム ちょびひげ

— データベース・バッファ上のデータ 5 --

前回はインデックス検索時のデータベース・バッファ(以下バッファ)の状態
をX$BH表を検索してみた。

前回まで幾度か出てきた”LRUリスト”であるが、どうもピンとこないという
方も多いと思う。LRUリストは”Least Recently Used List”の略で、もっと
も最後にアクセスのあったブロックの順番リストである。今回は、バッファの
ダンプを取ってバッファ管理の様子、つまりLRUリスト管理を詳しく見てみた
い。

バッファのダンプは以下のコマンドで取得することが出来る。ダンプファイル
は初期化パラメータ”user_dump_dest”で指定されたディレクトリに作成され
る。

SQL> alter session set events 'immediate trace name buffers level 4';

セッションが変更されました。

上記の例では”level”を”4”に指定している。これで、バッファのヘッダと
各ブロックのヘッダなどが取得できる。ちなみに、このコマンドを実行すると
LRU_FLAGの値が1プラスされて表示されるようなので注意しよう。(前回まで
ホットな領域にあるデータの”LRU_FLAG”は”8”であったが、このコマンド
を実行すると”9″になる)

”level”を”6”にすることによってすべてのブロックの内容を取得可能だが
、これを行なうとバッファのサイズによっては多くのディスク領域が必要にな
るので注意する必要がある。というより普通は使用しないであろう。

では、”level 4”で取得したダンプの一部を見ていこう。ダンプの内容はvi
等のテキストエディタで直接見る。するとダンプの最初の方に以下のような
MAINリスト(MAIN RPL_LST)がある。

                                ↓【注目1】
MAIN RPL_LST Queue header (NEXT_DIRECTION)[843e78b8,843ee068]
843e7878=>83fe8a30=>83fe7728=>843fec90=>843f3cb0=>843ee370=>83fdb788=>843d73f0
843eb628=>83fecb28=>83fd9fe8=>83fe90c0=>83fdb1a0=>83fd7dc8=>83ff1358=>843f5ed0

<省略>

83ff23c0=>843fa508=>843f7bb0=>83ffdad8=>843e3ac8=>83fe3a20=>843ec3f0=>843ec690
843edc38=>843e4158=>83fdd7b0=>843e9210=>843e0b88=>843ee028
                                ↓【注目2】
MAIN RPL_LST Queue header (PREV_DIRECTION)[843e78b8,843ee068]
843ee028=>843e0b88=>843e9210=>83fdd7b0=>843e4158=>843edc38=>843ec690=>843ec3f0
83fe3a20=>843e3ac8=>83ffdad8=>843f7bb0=>843fa508=>83ff23c0=>843e6810=>843dfe68

<省略>

83fd7dc8=>83fdb1a0=>83fe90c0=>83fd9fe8=>83fecb28=>843eb628=>843d73f0=>83fdb788
843ee370=>843f3cb0=>843fec90=>83fe7728=>83fe8a30=>843e7878

なんだ、なんだ、これは!と思われるが実はこれがバッファ上のデータを管理
するLRUリストである。

上記の膨大な量の整然と並んだ16進数の値はブロックヘッダのアドレスを示し
ており、【注目1】の”NEXT_DIRECTION”と【注目2】の”PREV_DIRECTION”の
横にある数字を見ていただくと同じであることが分かる。

1つ目:MAIN RPL_LST Queue header (NEXT_DIRECTION)[843e78b8,843ee068]
2つ目:MAIN RPL_LST Queue header (PREV_DIRECTION)[843e78b8,843ee068]
ここが同じ↑

この2つがセットになっていおり、ダブルリンク構成になっている。

【ダブルリンク構成のイメージ図】ぐるぐる回っているのが分かる

(NEXT_DIRECTION)
843e7878=>83fe8a30=>・・・・・・・・=>843e0b88=>843ee028 →
  ↑                                                      |
   ------------------------       |
                                                   ↑     |
(NEXT_DIRECTION)                                 |     |
843ee028=>843e0b88=>・・・・・・・・=>83fe8a30=>843e7878  |
  ↑                                                      | 
   ---------------------------←

ダブルリンク構成になっているのは、各リストの値がなくなっても、無くなっ
たデータが指していた次のデータの位置が分かるからである。

ちなみに、上記のブロックヘッダの数を数えたところ、3932個存在した。
これは、X$bh表のカウント値のちょうど2倍になる。

SQL> select count(*) from x$bh;

  COUNT(*)
----------
      1966    (1966×2=3932)

このLRUリストのセットの数は、9iからは隠し初期化パラメータになった、
”_db_block_lru_latches”の数だけ存在する。

検証環境のデフォルトの値は”8”なので、LRUリストが8つあることになる。
実際にダンプの中のLRUリストのセット数を数えると8セットである。

# 以下のSQL文で隠しパラメータの値を検索

SQL> select i.ksppinm parameter , v.ksppstvl VAL
  2  from x$ksppi i, x$ksppcv v
  3  where i.indx = v.indx
  4  and i.ksppinm like '%lru%'

PARAMETER                VAL
------------------------ --------------------
_db_block_lru_latches    8

# ダンプファイルの”MAIN RPL_LST”が含まれる行を検索

 1  MAIN RPL_LST Queue header (NEXT_DIRECTION)[NULL]
 2  MAIN RPL_LST Queue header (PREV_DIRECTION)[NULL]
 3  MAIN RPL_LST Queue header (NEXT_DIRECTION)[NULL]
 4  MAIN RPL_LST Queue header (PREV_DIRECTION)[NULL]
 5  MAIN RPL_LST Queue header (NEXT_DIRECTION)[83ff6648,843d7430]
 6  MAIN RPL_LST Queue header (PREV_DIRECTION)[83ff6648,843d7430]
 7  MAIN RPL_LST Queue header (NEXT_DIRECTION)[NULL]
 8  MAIN RPL_LST Queue header (PREV_DIRECTION)[NULL]
 9  MAIN RPL_LST Queue header (NEXT_DIRECTION)[NULL]
10  MAIN RPL_LST Queue header (PREV_DIRECTION)[NULL]
11  MAIN RPL_LST Queue header (NEXT_DIRECTION)[NULL]
12  MAIN RPL_LST Queue header (PREV_DIRECTION)[NULL]
13  MAIN RPL_LST Queue header (NEXT_DIRECTION)[NULL]
14  MAIN RPL_LST Queue header (PREV_DIRECTION)[NULL]
15  MAIN RPL_LST Queue header (NEXT_DIRECTION)[NULL]
16  MAIN RPL_LST Queue header (PREV_DIRECTION)[NULL]

16/2=8 で8セット
unixなのでgrepコマンドで検索
左の数字は見やすいように追加

”NEXT_DIRECTION”や”PREV_DIRECTION”の値がNULLであるもの7セットを含
め、全部で8セットある。これは、KEEPバッファ、RECYCLEバッファ、各ブロッ
クサイズ(2,4,8,16,32,64K)用のLRUリストを事前に用意しているためであろ
う。実際にブロックサイズ4Kのテーブルスペースにテーブルを作成して、検索
を行なったところ、1セットのLRUリストが使用されることを確認した。

では、試しに隠し初期化パラメータ”_db_block_lru_latches”の値を10に設
定したらどうなるかを試してみよう。

# 隠しパラメータの値を検索

PARAMETER                VAL
------------------------ --------------------
_db_block_lru_latches    10

# ダンプファイルの”MAIN RPL_LST”が含まれる行を検索

     1  MAIN RPL_LST Queue header (NEXT_DIRECTION)[NULL]
     2  MAIN RPL_LST Queue header (PREV_DIRECTION)[NULL]
     3  MAIN RPL_LST Queue header (NEXT_DIRECTION)[NULL]
     4  MAIN RPL_LST Queue header (PREV_DIRECTION)[NULL]
     5  MAIN RPL_LST Queue header (NEXT_DIRECTION)[83ff6c30,843d7b68]
     6  MAIN RPL_LST Queue header (PREV_DIRECTION)[83ff6c30,843d7b68]
     7  MAIN RPL_LST Queue header (NEXT_DIRECTION)[83ff6ed0,843d7e08]
     8  MAIN RPL_LST Queue header (PREV_DIRECTION)[83ff6ed0,843d7e08]
     9  MAIN RPL_LST Queue header (NEXT_DIRECTION)[83ff6f78,843d7eb0]
    10  MAIN RPL_LST Queue header (PREV_DIRECTION)[83ff6f78,843d7eb0]
    11  MAIN RPL_LST Queue header (NEXT_DIRECTION)[NULL]
    12  MAIN RPL_LST Queue header (PREV_DIRECTION)[NULL]
    13  MAIN RPL_LST Queue header (NEXT_DIRECTION)[NULL]
    14  MAIN RPL_LST Queue header (PREV_DIRECTION)[NULL]
    15  MAIN RPL_LST Queue header (NEXT_DIRECTION)[NULL]
    16  MAIN RPL_LST Queue header (PREV_DIRECTION)[NULL]
    17  MAIN RPL_LST Queue header (NEXT_DIRECTION)[NULL]
    18  MAIN RPL_LST Queue header (PREV_DIRECTION)[NULL]
    19  MAIN RPL_LST Queue header (NEXT_DIRECTION)[NULL]
    20  MAIN RPL_LST Queue header (PREV_DIRECTION)[NULL]

20/2=10 で10セット

今度はLRUリストが10セットあるのが分かる。また、使用されているLRUリスト
の数も3つある。この利用されてるLRUリストの数と、存在しているLRUリスト
の数の関係は、おそらくはOracleが自動的に最適な数にしていると思われるが
、今回は検証を行なわなかった。

LRUリストの数がパフォーマンスに影響する点として、ラッチがLRUリスト単位
に取得されることである。ラッチとはメモリ上で取得される高速なロックのこ
とである。バッファ上に必要とするデータがない場合に、ディスクからバッフ
ァにデータを読み込む。その際にLRUリストの組換えが発生するが、この時に取
得されるのが”cache buffers lru chain”というラッチである。

このラッチを取得する回数が多いようだと、LRUリストの組換えが頻繁に発生
している状態、つまり、ディスクのI/Oが頻繁に発生しており、バッファのサ
イズが小さいか、もしくは、効率の悪いSQL文があると言えよう。

ちなみに、CPUが複数の環境では同時にラッチを取得しに行くため、タイムア
ウトの回数が多くなる(CPUが1つの環境ではほとんど発生しない)。システム
の環境を把握するには、CPU数、ラッチの取得数、ラッチのタイムアウトの数
を見る必要がある。

以下のSQL文でラッチが取得された回数とタイムアウトした回数を調べること
が出来る。

SQL> select  name,to_char(gets,'99999999999999990') GET_TOTAL
  2  ,to_char(misses,'99999999999999990') GET_MISS
  3  from v$latch
  4  where name='cache buffers lru chain';

NAME                      GET_TOTAL          GET_MISS
------------------------- ------------------ ------------------
cache buffers lru chain             42962162             114595

少し横道にそれてしまったが、、今回紹介したLRUリストとは別に、ダーティ
リスト(WRT_LST)や(XOBJ_LST)、(XRNG_LST)などが存在する。

それぞれのリストにはAUXILIARYリスト(AUXILIARY RPL_LST)というものが存
在する。これにリストされているブロックが存在する場合としない場合がある
が、データベースバッファ起動直後や、全件検索などを行なった時に、ブロッ
クがリストされることがある。”AUXILIARY”は”補助”という意味で、MAIN
リストを変更をする負荷を避ける為、すぐに再利用可能なブロックをAUXILIARY
リストにリストしていると思われる。

バッファ上のブロックは上記のいずれか1つのリストに存在する。

(参考)

<AUXILIARYのLRUリスト>

AUXILIARY RPL_LST Queue header (NEXT_DIRECTION)[843da568,83fef370]
843da528=>83fd83b0=>83fe58f8=>83fea668=>83fed110=>83fe1fe0=>843da3d8=>83fdbf68
・
・

<MAINのダーティリスト>

MAIN WRT_LST Queue header (PREV_DIRECTION)[843e7228,843ef178]
843ef138=>83fe7140=>83ff4490=>843f9fc8=>843f32d8=>83fdc208=>843eada0=>83fe3978
・
・

以上、茅ヶ崎にて