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

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

前回はOracleのブロック内でトランザクション・エントリと各データとの関
係を見ることによって行単位のロックがどのように取得されるのかを見た。
今回は、ロックは一旦忘れて、トランザクション・エントリとUNDO情報の関
連を見ていく。

あるユーザがUPDATEを行うとする。更新対象のブロックに対して、すでに他
のセッションから更新が行われていたらどうするか?アクセスするデータの
バージョン(SCN)が欲しいバージョンと異なる(SCNの番号が必要なSCNより
大きい)ので、読取一貫性を保証する為に、CRブロックが作成する。当然、
CRブロックの作成には、UNDO情報が必要となるのでトランザクション・エン
トリから必要なUNDO情報を取得する。

では、どのようにトランザクション・エントリからUNDO情報を取得している
のか?

今回も以下の環境で検証を進める。

*************************************************************
◆環境
Linux 2.4.2-2
Oracle9i EE Release 9.2.0.1.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

今回の検証は以下の順番で進めていく。

(1)UPDATE処理を行ったブロックをX$BH表より確認
(2)更新ブロックのトランザクション・エントリ(Itl)から、該当するUNDO
     情報を保持しているブロックのアドレス(Uba)を確認
(3)ロールバック情報のダンプを取得してUNDOエントリを確認

まずは2行のUPDATE処理を行ってどのようなロールバック情報が作成されるの
かを見ていく。

以下が更新されたブロックのバッファ・ダンプのブロック・ヘッダ部である。

Block header dump:  0x00411502
 Object id on Block? Y
 seg/obj: 0x82da  csc: 0x00.8921cd5  itc: 2  flg: O  typ: 1 - DATA
     fsl: 0  fnx: 0x0 ver: 0x01
                     ↓【Wrap】
 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x0007.01c.00027fa1  0x00800a8d.0998.0f  C-U-    0  scn 0x0000.0892196f
0x02   0x0007.00d.00027faa  0x00800a94.0998.59  ----    2  fsc 0x0000.00000000
              ↑【スロット番号】            ↑最後のUNDOレコード番号(16進)
          ↑【ロールバック番号】 ↑UNDOブロックのDBA(16進)

更新を行ったトランザクションのエントリ番号は0x02である。Uba(Undo Block
Address)は最後のUNDOエントリのアドレスを示している。

Uba : 0x00800a8d.0998.0f

左から最後のUNDOエントリの【DBA】【シーケンス】【レコード番号】を表し
ている。全て16進数なので、10進数に変換したのが以下の番号である。

ファイル番号        : 2
ブロック番号        : 2708
レコード番号(最後): 89

では上記のレコードのデータベース・バッファ上での状態をX$BH表より
検索して見てみる。

*************************************************************
【データベース・バッファ上の各オブジェクトの状態】

       OBJ OBJECT_NAME          STATE    DBARFIL     DBABLK BA      
---------- -------------------- ----- ---------- ---------- --------
4294967295                      xcur           2       2708 550CE000
     33498 TEST                 xcur           1      70913 551E8000
     33498 TEST                 xcur           1      70914 551EC000
     33497 TEST_IDX             xcur           1      96394 551EA000

ファイル番号(DBARFIL)が2でブロック番号が2708のオブジェクトを見ると
オブジェクト番号(OBJ)が4294967295となっている。この番号はロールバッ
ク・セグメントを表す番号である。

このブロック(ロールバック・セグメント)のバッファ・ダンプを取得して
実際に見てみると、以下の構成になっている。

【バッファ・ヘッダ部】
・・
・・
【データ・ブロック部】
◇UNDOエントリ・マップ
◇UNDOエントリ

UNDOエントリ・マップはUNDOブロック内にあるレコードの索引のようなもの
である。では、先程調べた情報から89番目のレコード見てみよう。89は16進
数にすると59である。

*************************************************************
【UNDOエントリ58番(16進数)】

         ↓
*-----------------------------
* Rec #0x58  slt: 0x0d  objn: 33468(0x000082bc)  objd: 33498  tblspc: 0(0x00000000)
*       Layer:  11 (Row)   opc: 1   rci 0x00          ←【※注目1※】
Undo type:  Regular undo    Begin trans    Last buffer split:  No
Temp Object:  No
Tablespace Undo:  No
rdba: 0x00000000
*-----------------------------
uba: 0x00800a94.0998.57 ctl max scn: 0x0000.0892196f prv tx scn: 0x0000.0892197e
KDO undo record:
KTB Redo
op: 0x03  ver: 0x01
op: Z
KDO Op code: URP row dependencies Disabled
  xtype: XA  bdba: 0x00411502  hdba: 0x00411501
itli: 2  ispac: 0  maxfr: 4863
tabn: 0 slot: 1(0x1) flag: 0x2c lock: 0 ckix: 1
ncol: 3 nnew: 1 size: 0
col  0: [ 2]  c1 03            ←【ID1 = 2 (更新前情報)】

         ↓【UNDOエントリ59番(16進数)】
*-----------------------------
* Rec #0x59  slt: 0x0d  objn: 33468(0x000082bc)  objd: 33498  tblspc: 0(0x00000000)
*       Layer:  11 (Row)   opc: 1   rci 0x58         ←【※注目2※】
Undo type:  Regular undo   Last buffer split:  No
Temp Object:  No                ↑
Tablespace Undo:  No          【最後のUNDOエントリを表す】
rdba: 0x00000000
*-----------------------------
KDO undo record:
KTB Redo
op: 0x02  ver: 0x01
op: C  uba: 0x00800a94.0998.58
KDO Op code: URP row dependencies Disabled
  xtype: XA  bdba: 0x00411502  hdba: 0x00411501
itli: 2  ispac: 0  maxfr: 4863
tabn: 0 slot: 2(0x2) flag: 0x2c lock: 0 ckix: 64
ncol: 3 nnew: 1 size: 0
col  0: [ 2]  c1 04             ←【ID1 = 2 (更新前情報)】

よーく見ても分かりづらいので、簡単にまとめると以下のような内容である。

*************************************************************
Rec #0x58  slt: 0x0d                rci 0x00[UNDOエントリ58番(16進数)]

                                        ↑

Rec #0x59  slt: 0x0d                rci 0x58[UNDOエントリ59番(16進数)]
*************************************************************

最後のUNDOエントリ(Rec #0x59)から元の状態に戻し始めて、rci が指す前
のUNDOエントリを適用していく。一番前のUNDOエントリ(Rec #0x58)まで来
ると、前を指すrci がないためにCRブロックの作成が終了すると考えられる。

実際は以下の様にインデックスのUNDOエントリも続けて入っている

*************************************************************
Rec #0x5a  slt: 0x0d                rci 0x59[UNDOエントリ59番(16進数)]
index undo for leaf key operations

                                        ↑

Rec #0x5b  slt: 0x0d                rci 0x5a[UNDOエントリ59番(16進数)]
index undo for leaf key operations
*************************************************************

つまり、ビデオテープの巻き戻しのように、UNDOエントリを当てつつ、ブロ
ックを過去のバージョンに戻していっているわけである。CRブロックが作成
されるイメージを持って頂けたであろうか?

ちなみ、過去の検証(その5)では、全件検索でのUPDATE、SELECTではCRが
作成されないことから、インデックスを使用しない場合はインデックスのCR
ブロックまでは作成されないような仕組みになっていると考えられる。

CRブロックが作成されるケース(複数セッション)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                         SELECT  UPDATE  INSERT 
------------------------ ------- ------- -------
全件検索         - TABLE ○      ○      ―
                 - INDEX ×      ×      ―
インデックス検索 - TABLE ○      ×      ―
                 - INDEX ○      ○      ―

以上、秋晴れの茅ヶ崎にて