続 X$BH に関する検証 その9

<続 X$BH に関する検証 ~その9~>
ペンネーム ちょびひげ

これまで、「単一サーバ」、「単一セッション」の環境で、データベース・
バッファ上のオブジェクトの状態(ステータス)を詳細に見てきた。

今回からは、RAC(Real Application Cluster)の環境で、オブジェクトの状態
(ステータス)がどのように変化していくのかを見ていく。検証を始める前
に、GCS(グローバル・キャッシュ・サービス)について簡単にご説明する。

GCSとは
複数ノードでデータベース・バッファ上のデータを共有する為には、自分が
使いたいデータ・ブロックが、「他のインスタンスに存在するか」、「更新
されているか」、というような確認を行う必要がある。この確認をマスター
ノードに対して行う。マスターノードはハッシングによって決定される。

マスターノードはGCSを提供している。そしてこのサービスを司るのがLMSプ
ロセスであり、GCSメッセージの処理やデータベース・バッファの操作を行っ
ている。つまり、データベース・バッファ上のデータを共有ために、マスタ
ーノードへの問い合わせが増えるほど、LMSプロセスへの負荷が高くなる。そ
のような環境では、マスタノードのLMSプロセス数を増やすことにより、パフ
ォーマンスの向上が期待出来る。ユーザからの問い合わせが多いので、対応
を行う窓口を増やすわけである。

このようなGCSへの問い合わせ処理の流れを考えた場合、RACのパフォーマン
ス向上の為に重要となるのは、いかにマスターノードへの問い合わせを減ら
してローカルのデータベース・バッファ内で処理を行うか、という点である。

ちなみにローカルのデータベース・バッファ内での処理は共有のカレント・
ブロック(SCUR)により実現されている。※SCURに関しては後述

では、実際に検証を行って見て行こう。まず、初めにRAC構成で発生する競合
の種類を考えて見ると以下の4つのパターンが考えられる。

1. Read/Read の競合
2. Read/Write の競合
3. Write/Readの競合
4. Write/Write の競合

今回は上記のWrite/Write競合を除いた、3つのケースを見ていく。

検証環境は以下の通りである。

*************************************************************
□環境(2ノードのRAC構成)
Linux 2.4.9-e.9.30ml
Oracle9i Release 9.2.0.3.0

□テーブル構成

SQL> desc TEST
 Name      Type
 --------- ------------------
 ID1       NUMBER
 ID2       NUMBER
 TEXT      VARCHAR2(2000)

□ID1にINDEX(TEST_IDX)を付与

□入っているデータは以下の3件

       ID1        ID2 TEXT
---------- ---------- --------------------
         1          1 insight
         2          2 insight
         3          3 insight

まずは、最も基本的なRead/Read の競合の場合である。

***********************************[Read/Read の競合その1]***
□NODE1でSELECTを実行

SQL> select * from test;

<<NODE1の状況>>

OBJECT_NAME                    STATE               DBABLK BA
------------------------------ --------------- ---------- --------
TEST                           scur                   257 5E5E4000
TEST                           scur                   258 5E5DA000

□データベース・バッファの状況を確認したSQL文

SQL> select
       o.object_name
       ,decode(state,0,'free',1,'xcur',2,'scur',3,'cr', 4,'read'
       ,5,'mrec',6,'irec',7,'write',8,'pi') STATE
       , dbablk , ba
     from x$bh b , dba_objects o
     where b.obj = o.data_object_id
       and o.object_name like 'TEST%'
     group by o.object_name, state , dbablk , ba
     order by dbablk;

[Read/Read の競合その1]
以下が、NODE1 でSELECTを行った場合の流れである。

1. 自ノードのデータベース・バッファ上に必要なブロックが載ってない事を
   確認
2. マスターノードのLMSに、他のノードのデータベース・バッファ上に欲し
   いブロックが載っているかどうかを問い合わせる
3. どのノードにも必要なブロックが載っていない事を確認
4.全件検索なので、最初に、セグメントヘッダ・ブロックをディスクからデ
   ータベース・バッファへ共有のカレント・ブロック(SCUR)として読込み
5. データ・ブロックをカレント・ブロック(SCUR)として読込み

SCUR(共有カレント・ブロック)はRACの構成で初めて出てきたステータスで
ある。SCURは、文字通り複数のノードでカレント・ブロックを共有できる状
態を現す。

全てのノードでブロックへのアクセスが集中(SELECTの競合)したとしても
ノード間でのブロックの転送は発生しない。なぜなら、ローカルのデータベ
ース・バッファにカレント・ブロックを保持している為、マスターノードに
問い合わせを行わずとも、データへのアクセスが可能だからである。

***********************************[Read/Read の競合その2]***
□NODE2でSELECTを実行

SQL> select * from test;

<<NODE2の状況>>

OBJECT_NAME                    STATE               DBABLK BA      
------------------------------ --------------- ---------- --------
TEST                           scur                   257 5B5CE000
TEST                           scur                   258 5B5C8000

<<NODE1の状況>>

OBJECT_NAME                    STATE               DBABLK BA
------------------------------ --------------- ---------- --------
TEST                           scur                   257 5E5E4000
TEST                           scur                   258 5E5DA000

[Read/Read の競合その2]
まず、マスターノードに対して、いずれかのノードのデータベース・バッフ
ァ上に必要なブロックが載っているかどうかの確認を行う。すでにNODE1のデ
ータベース・バッファ上に必要なブロックが載っている為、NODE1からNODE2
にSCURブロックの転送がインターコネクト経由で行われる。当然、ディスク
I/Oはゼロ(0)である。

では最後にRead/Write の競合をみる。

**********************************[Read/Write の競合その1]***
□NODE1でSELECTを実行

SQL> select * from test;

<<NODE1の状況>>

OBJECT_NAME   STATE               DBABLK BA        X_TO_NULL
------------- --------------- ---------- -------- ----------
TEST          scur                   257 5E5DA000          0
TEST          scur                   258 5E5D6000          0

[Read/Write の競合その1]
最初のNODE1でのSELECT(Read)は先程と同様の状態である。

**********************************[Read/Write の競合その2]***
□NODE2でUPDATEを実行

SQL> update test set id2=1 where id2=2;

<<NODE2の状況>>

OBJECT_NAME   STATE               DBABLK BA        X_TO_NULL
------------- --------------- ---------- -------- ----------
TEST          scur                   257 5E3A4000          0
TEST          xcur                   258 5E3A0000          0
TEST          cr                     258 5E3A2000          0

<<NODE1の状況>>

OBJECT_NAME   STATE               DBABLK BA        X_TO_NULL
------------- --------------- ---------- -------- ----------
TEST          scur                   257 5E5DA000          0
TEST          cr                     258 5E5D6000          1

[Read/Write の競合その2]
次にNODE2からのUPDATE(Write)を行うと、以下のようにステータスが変化
している。

NODE1   |    NODE2
        |
     [転送]
SCUR  -→  XCUR
 ↓     |
CR      |

[ロックダウン]

まず、NODE1 のデータ・ブロックでステータスがSCURからCRへのロックダウ
ンが行われる。次にNODE1 からNODE2にSCURのセグメント・ヘッダとXCURのデ
ータ・ブロックが転送される。最後にNODE2で「単一サーバ」、「単一セッシ
ョン」の場合と同様にCRブロックを作成した後、UPDATEを行う。

ロックダウンとは?
上記の結果としてX_TO_NULL列を追加している。この列はブロックのステータ
スがカレントモードから別のステータスに変化した場合にカウントアップさ
れる。上記の例では、共有のカレントモード(SCUR)から読取一貫性のモー
ドに変化している為に、X_TO_NULL列の値がカウントアップされ1となってい
る。この動きをロックダウンという。

ただし、Oracleのマニュアルには以下のような記載があるので、この列の値
自体が正しいという保証はない。

<Oracleのマニュアルより抜粋>---------------------------------------
X_TO_NULL:
別のインスタンスとの競合による排他モードからNULL モードへのPCM ロック
変換の回数。この列は廃止されたが、旧バージョンとの互換性を保つために
残されている
---------------------------------------------------------------------

次はWrite/Read の競合をみる。これも順次動きを追って、見ていきたい。

**********************************[Write/Read の競合その1]***
□NODE1でUPDATE(Write)を実行

SQL> update test set id2=1 where id2=2;

<<NODE1の状況>>

OBJECT_NAME   STATE               DBABLK BA        X_TO_NULL
------------- --------------- ---------- -------- ----------
TEST          scur                   257 5E5CA000          0
TEST          xcur                   258 5E5C0000          0
TEST          cr                     258 5E5C2000          0

[Write/Read の競合その1]
まず、CRブロックは、「単一サーバ」、「単一セッション」の場合でも作成
された通り、全件検索でのUPDATEで作られるブロックである。SCURはセグメ
ントヘッダで、このヘッダの更新は行っていないので、共有のカレント・ブ
ロックとなっている。それに対して、データ・ブロックは更新をおこなって
いるので、排他のカレント・ブロック(XCUR)となっている。

ではもう一方のノード(NODE2)からSELECT(Read)を行う。

**********************************[Write/Read の競合その2]***
□NODE2でSELECT(Read)を実行

SQL> select * from test;

<<NODE2の状況>>

OBJECT_NAME   STATE               DBABLK BA        X_TO_NULL
------------- --------------- ---------- -------- ----------
TEST          scur                   257 5B5CE000          0
TEST          cr                     258 5B5C8000          0

セグメントヘッダはSCURでread/read の競合
cr はxcur から情報をもらって一時的に使用

<<NODE1の状況>>

OBJECT_NAME   STATE               DBABLK BA        X_TO_NULL
------------- --------------- ---------- -------- ----------
TEST          scur                   257 5E5CA000          0
TEST          xcur                   258 5E5C0000          0
TEST          cr                     258 5E5BE000          0
TEST          cr                     258 5E5C2000          0

[Write/Read の競合その2]
まず、両ノードでのSCURブロックを見て欲しい。セグメントヘッダへの更新
は行われていないので、共有のカレント・ブロックを両ノードで保持してい
る状態である。

次に両ノードでCRブロックがあるのが分かる。初めにNODE1でCRブロックが作
成され作成されたCRブロックがNODE2へ転送されている状態である。このあと
は何度、NODE2からSELECTを行っても、上記と同様に以下のCRブロックの転送
を繰り返す。

[NODE1 でデータ・ブロックのCRブロックを作成]

[NODE1からNODE2へのCRブロックを転送]

【競合発生時のレスポンスダウン】

最後に、それぞれの競合が発生した場合のEPS(Executions Per Second)を
表示する。

1ブロックに対してアクセスを行う検証を行っているので、100%競合が発生
している状態である。あまり、正確な値とは言えないが、どの程度パフォー
マンスが低下するかの目安として欲しい。トランザクション数は各ノードと
も1セッションである。

※EPS値・・・Execution Per Secondの略称で、単位時間(秒)あたりのSQL実
             行数を現す。処理性能の相対評価の指針として使用している。

◇同一の位置ブロックに対して以下の処理を行う。

[Read処理]         [Write処理]
^^^^^^^^^^          ^^^^^^^^^
select処理         update処理 ←
                     ↓        ↑
                   commit処理 →

評価の基準となる、EPS値を取得した所以下の様になった。

******************************************
   NODE1               EPS
 ---------------------------------------
 Read(SELECT) 処理     10000
 Write(UPDATE)処理       416
******************************************

競合させた場合のEPS値である。
※カッコ内は規準を1とした場合の比較値
******************************************

 NODE1 / NODE2         EPS(NODE1)       EPS(NODE2)
 -------------------------------------------------
 Read/Read  処理        10000[1.00]    10000[1.00]
 Write/Read 処理          250[0.60]      714[0.07]
 Write/Write処理          175[0.40]      192[0.46]
******************************************

Read/Readでの競合では全くレスポンスの悪化はなく、やはり、書込み処理が
入った競合で、レスポンスの悪化が見られる。ロックダウンやCRブロックの
作成ブロックの転送の負荷が大きく影響していると言える

次回はWrite/Writeの競合を見ていきたい。

以上、自転車の事故にも気をつけてね!茅ヶ崎にて