行移行・行連鎖に関する検証 その2

<行移行・行連鎖に関する検証 その2>
ペンネーム: ミラニスタ

▼ 前回のおさらい

 行移行の発生メカニズムを確認するために、行移行・行連鎖が発生しやすい
環境を準備しました。

 具体的には、DB_BLOCK_SIZE=2KB(2048byte)の表領域を作成し、最大レ
コード長が4000byteを超えるテーブルを作成しました。

  SQL> SELECT TABLESPACE_NAME,BLOCK_SIZE,INITIAL_EXTENT,
    2  EXTENT_MANAGEMENT,ALLOCATION_TYPE,SEGMENT_SPACE_MANAGEMENT
    3  FROM DBA_TABLESPACES WHERE TABLESPACE_NAME = 'TS_2K';
  
  TABLESPACE_NAME  BLOCK_SIZE INITIAL_EXTENT EXTENT_MAN ALLOCATIO SEGMEN
  ---------------- ---------- -------------- ---------- --------- ------
  TS_2K                  2048          10240 LOCAL      UNIFORM   AUTO

  SQL> desc SMALL_TBL
   Name    Type
   ------- ---------------
   RNO     CHAR(16)
   COL0    VARCHAR2(400)
   COL1    VARCHAR2(400)
   COL2    VARCHAR2(400)
   COL3    VARCHAR2(400)
   COL4    VARCHAR2(400)
   COL5    VARCHAR2(400)
   COL6    VARCHAR2(400)
   COL7    VARCHAR2(400)
   COL8    VARCHAR2(400)
   COL9    VARCHAR2(400)

▼ 1ブロックにぎりぎり収まるように複数レコードをInsertする。

 それでは、実際にデータを作って行移行を発生させてみます。
 RNO列、COL0~9の11個のカラムにそれぞれ16byteの文字列を以下のSQL文で
10行Insertします。
 レコード長は 16*11=176byte+α(レコード・ヘッダ等の長さ)となるので
10行程度であれば、1ブロック(2048byte)にぎりぎり収まるという計算です。

  SQL> INSERT INTO SMALL_TBL (RNO,COL0,COL1,COL2,COL3,COL4,COL5,COL6,COL7,COL8,COL9)
    2  VALUES ('R'||lpad( '1',15,'0'),lpad('0',16,'0'),lpad('1',16,'1'),lpad('2',16,'2'),
    3                                 lpad('3',16,'3'),lpad('4',16,'4'),lpad('5',16,'5'),
    4                                 lpad('6',16,'6'),lpad('7',16,'7'),lpad('8',16,'8'),
    5                                 lpad('9',16,'9'));
    6  INSERT INTO SMALL_TBL (RNO,COL0,COL1,COL2,COL3,COL4,COL5,COL6,COL7,COL8,COL9)
    7  VALUES ('R'||lpad( '2',15,'0'),lpad('0',16,'0'),lpad('1',16,'1'),lpad('2',16,'2'),
    8                                 lpad('3',16,'3'),lpad('4',16,'4'),lpad('5',16,'5'),
    9                                 lpad('6',16,'6'),lpad('7',16,'7'),lpad('8',16,'8'),
   10                                 lpad('9',16,'9'));
   ......................................................................................
   46  INSERT INTO SMALL_TBL (RNO,COL0,COL1,COL2,COL3,COL4,COL5,COL6,COL7,COL8,COL9)
   47  VALUES ('R'||lpad('10',15,'0'),lpad('0',16,'0'),lpad('1',16,'1'),lpad('2',16,'2'),
   48                                 lpad('3',16,'3'),lpad('4',16,'4'),lpad('5',16,'5'),
   49                                 lpad('6',16,'6'),lpad('7',16,'7'),lpad('8',16,'8'),
   50                                 lpad('9',16,'9'));
   51  COMMIT;

 以下のような内容でデータが作成されました。

SQL> SELECT * FROM SMALL_TBL;

RNO              COL0             COL1             COL2             COL3
---------------- ---------------- ---------------- ---------------- ----------------
COL4             COL5             COL6             COL7             COL8
---------------- ---------------- ---------------- ---------------- ----------------
COL9
----------------
R000000000000001 0000000000000000 1111111111111111 2222222222222222 3333333333333333
4444444444444444 5555555555555555 6666666666666666 7777777777777777 8888888888888888
9999999999999999

R000000000000002 0000000000000000 1111111111111111 2222222222222222 3333333333333333
4444444444444444 5555555555555555 6666666666666666 7777777777777777 8888888888888888
9999999999999999

....................................................................................

R000000000000010 0000000000000000 1111111111111111 2222222222222222 3333333333333333
4444444444444444 5555555555555555 6666666666666666 7777777777777777 8888888888888888
9999999999999999


10 rows selected.

 データを作成したので、統計情報を取得して状況を確認してみましょう。

  SQL> ANALYZE TABLE SMALL_TBL COMPUTE STATISTICS;
  
  Table analyzed.
  
  SQL> ANALYZE TABLE SMALL_TBL LIST CHAINED ROWS;
  
  Table analyzed.

(以後、統計情報を取得するSQL文の実行と結果の表示は割愛します。)

  SQL> SELECT
    2   TABLE_NAME,NUM_ROWS,BLOCKS,EMPTY_BLOCKS
    3  ,CHAIN_CNT,AVG_ROW_LEN,TO_CHAR(LAST_ANALYZED,'HH24:MI') LAST_ANALYZED
    4  FROM USER_TABLES
    5  WHERE TABLE_NAME='SMALL_TBL'
  
  TABLE_NAME   NUM_ROWS  BLOCKS EMPTY_BLOCKS  CHAIN_CNT AVG_ROW_LEN LAST_
  ----------- --------- ------- ------------ ---------- ----------- -----
  SMALL_TBL          10       2            3          0         190 18:11

 10行作成されていて、行の平均長(全部同じ長さですが)は190byteである
ことがわかります。(もう1行Insertすれば確実に次のブロックに書き込まれ
るでしょう。)
 当然ながら、行移行は発生していません。

 さらに、以下のSQL文で各行のRowidとどのデータ・ブロックに格納されてい
るかを確認します。(後でブロック・ダンプを見る際に必要な情報です。)

  SQL> SELECT
    2   ROWID
    3  ,DBMS_ROWID.ROWID_TO_ABSOLUTE_FNO(ROWID,'RM','SMALL_TBL') "FILE#"
    4  ,DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) "BLOCK#"
    5* FROM RM.SMALL_TBL;
  
  ROWID                   FILE#     BLOCK#
  ------------------ ---------- ----------
  AAADkVAAFAAAAAHAAA          5          7
  AAADkVAAFAAAAAHAAB          5          7
  AAADkVAAFAAAAAHAAC          5          7
  AAADkVAAFAAAAAHAAD          5          7
  AAADkVAAFAAAAAHAAE          5          7
  AAADkVAAFAAAAAHAAF          5          7
  AAADkVAAFAAAAAHAAG          5          7
  AAADkVAAFAAAAAHAAH          5          7
  AAADkVAAFAAAAAHAAI          5          7
  AAADkVAAFAAAAAHAAJ          5          7
  
  10 rows selected.

 すべての行が同じブロックに格納されていることがわかります。

▼ ブロック・ダンプを取得する。

 それでは、いよいよブロック・ダンプを取得してブロックの内容を確認して
みましょう。

 ブロック・ダンプは次の2通りの方法で取得することができます。

1. ALTER SYSTEM DUMP DATAFILE  BLOCK ;

2. ALTER SYSTEM DUMP DATAFILE  BLOCK MIN 
                                    BLOCK MAX ;

 ここでは、前者で取得してみましょう。(ダンプはUSER_DUMP_DESTに出力さ
れます。)

  SQL> ALTER SYSTEM DUMP DATAFILE 5 BLOCK 7;
  
  System altered.

 以下が、ブロック・ダンプの内容になります。
 この検証環境では、前半がバイナリ(16進)・ダンプ、後半が論理ブロック・
ダンプとなります。(Oracleのバージョンにより異なる場合があります。)
 長いので極力必要部分を抜粋し、所々注釈を加えていますので、頑張ってお
付き合いください。

=====================================================================
/opt/app/oracle/admin/alcgen/udump/xxxxxx_ora_8652.trc
Oracle Database 10g Release 10.2.0.4.0 - 64bit Production
ORACLE_HOME = /opt/app/oracle/product/10.2.0/db
System name:    Linux
............... (中略) ............................................
*** SESSION ID:(446.1507) 2010-03-18 11:18:00.905
Start dump data blocks tsn: 5 file#: 5 minblk 7 maxblk 7
buffer tsn: 5 rdba: 0x01400007 (5/7)
scn: 0x0000.0376e690 seq: 0x01 flg: 0x06 tail: 0xe6900601
frmt: 0x02 chkval: 0x1f4b type: 0x06=trans data
Hex dump of block: st=0, typ_found=1 ←しばらくバイナリ・ダンプが続く
Dump of memory from 0x00000000125CE400 to 0x00000000125CEC00
0125CE400 00006206 01400007 0376E690 06010000  [.b....@...v.....]
0125CE410 00001F4B 00000001 00003915 0376E68F  [K........9....v.]
............... (中略) ............................................
0125CE480 02660324 00EA01A8 0000002C 00000000  [$.f.....,.......]
0125CE490 100B012C 30303052 30303030 30303030  [,...R00000000000] 10行目
0125CE4A0 30313030 30303010 30303030 30303030  [0010.00000000000]
0125CE4B0 30303030 31311030 31313131 31313131  [00000.1111111111]
............... (中略) ............................................
0125CEB40 3052100B 30303030 30303030 30303030  [..R0000000000000] 1行目
0125CEB50 30103130 30303030 30303030 30303030  [01.0000000000000]
0125CEB60 10303030 31313131 31313131 31313131  [000.111111111111]
0125CEB70 31313131 32323210 32323232 32323232  [1111.22222222222]
0125CEB80 32323232 33331032 33333333 33333333  [22222.3333333333]
0125CEB90 33333333 34103333 34343434 34343434  [333333.444444444]
0125CEBA0 34343434 10343434 35353535 35353535  [4444444.55555555]
0125CEBB0 35353535 35353535 36363610 36363636  [55555555.6666666]
0125CEBC0 36363636 36363636 37371036 37373737  [666666666.777777]
0125CEBD0 37373737 37373737 38103737 38383838  [7777777777.88888]
0125CEBE0 38383838 38383838 10383838 39393939  [88888888888.9999]
0125CEBF0 39393939 39393939 39393939 E6900601  [999999999999....]
(行データはブロック内で下から順に格納されるイメージ)
............... (中略) ............................................
data_block_dump,data header at 0x125ce464
===============
tsiz: 0x798
hsiz: 0x26
pbl: 0x125ce464
bdba: 0x01400007
     76543210
flag=--------
ntab=1        ←ブロックに格納されている表数
nrow=10       ←ブロックに格納されている行数
frre=-1
fsbo=0x26
fseo=0x2c
avsp=0x6
tosp=0x6
0xe:pti[0]      nrow=10 offs=0
0x12:pri[0]     offs=0x6da      ←行情報(ブロック内の行位置)
0x14:pri[1]     offs=0x61c        pri:pointer record index?
0x16:pri[2]     offs=0x55e        1行目(pri[0])~10行目(pri[9])
0x18:pri[3]     offs=0x4a0        行内の相対位置(オフセット)を表示
0x1a:pri[4]     offs=0x3e2
0x1c:pri[5]     offs=0x324
0x1e:pri[6]     offs=0x266
0x20:pri[7]     offs=0x1a8
0x22:pri[8]     offs=0xea
0x24:pri[9]     offs=0x2c
block_row_dump:                          ←論理ダンプの始まり
tab 0, row 0, @0x6da
tl: 190 fb: --H-FL-- lb: 0x1  cc: 11     ←1行目(長さ190byte)
col  0: [16]  52 30 30 30 30 30 30 30 30 30 30 30 30 30 30 31 ←RNO列
col  1: [16]  30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 ←COL0列
col  2: [16]  31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31
col  3: [16]  32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32
col  4: [16]  33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33
col  5: [16]  34 34 34 34 34 34 34 34 34 34 34 34 34 34 34 34
col  6: [16]  35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35
col  7: [16]  36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36
col  8: [16]  37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37
col  9: [16]  38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38
col 10: [16]  39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39
............... (後略) ............................................
=====================================================================

 1行目のRNO列
「R 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1」という値は
ASCIIコード(16進数)で
「52 30 30 30 30 30 30 30 30 30 30 30 30 30 30 31」という形として格納
されていることがおわかりでしょうか?

 長くなってしまいましたので、今回はここまでです。

 次回は、ブロックにぎっしり書き込まれたレコードのちょうど真ん中あたり
の行(RNO=R000000000000005)を長くするような更新をさせてみて、どのよう
に行移行するかを見てみます。

 桜が咲き始めたのにやたらと寒い。。。

恵比寿より