[Oracle] テーブルの領域使用量の確認方法 | Archive Redo Blog

Archive Redo Blog

DBエンジニアのあれこれ備忘録

レコードの挿入・更新・削除を繰り返していくと、エクステントやブロックの使用効率が低下し、ディスク領域を圧迫したり、パフォーマンスに悪影響を及ぼしたりします。


そのため、無駄な領域を確保しているテーブルは再編成して領域使用効率を改善したほうがよいということになります。


テーブルがどれだけの領域を使っていて、どれだけ空いていて、本当はどれだけ必要なのかを調べるにはテーブルの統計情報を利用します。


テーブルの統計情報はUSER_TABLES、DBA_TABLES、ALL_TABLESのいずれかのテーブルを検索すると参照することができます。


以下にSCOTTが所有するテーブルの統計情報を取得するSQLの例を示します。
(権限の都合上、sysユーザーで実行)

SELECT
  TABLE_NAME,
  NUM_ROWS,
  AVG_ROW_LEN,
  BLOCKS,
  EMPTY_BLOCKS,
  CASE AVG_ROW_LEN
    WHEN null THEN null
    WHEN 0 THEN 0
    ELSE ( CEIL( NUM_ROWS / TRUNC( ( ( BLOCK_SIZE - KCBH - UB4 - KTBBH
      - ( INI_TRANS - 1 ) * KTBIT - KDBH )
      * ( 100 - PCT_FREE ) / 100 - KDBT ) / AVG_ROW_LEN ) ) + 1 )
  END EST_BLOCKS
  FROM DBA_TABLES
    ,(SELECT TYPE_SIZE KCBH FROM V$TYPE_SIZE WHERE TYPE = 'KCBH')
    ,(SELECT TYPE_SIZE UB4 FROM V$TYPE_SIZE WHERE TYPE = 'UB4')
    ,(SELECT TYPE_SIZE KTBBH FROM V$TYPE_SIZE WHERE TYPE = 'KTBBH')
    ,(SELECT TYPE_SIZE KTBIT FROM V$TYPE_SIZE WHERE TYPE = 'KTBIT')
    ,(SELECT TYPE_SIZE KDBH FROM V$TYPE_SIZE WHERE TYPE = 'KDBH')
    ,(SELECT TYPE_SIZE KDBT FROM V$TYPE_SIZE WHERE TYPE = 'KDBT')
    ,(SELECT VALUE BLOCK_SIZE FROM V$PARAMETER WHERE NAME = 'db_block_size')
  WHERE OWNER = 'SCOTT'
  ORDER BY BLOCKS DESC;

TABLE_NAME             NUM_ROWS AVG_ROW_LEN     BLOCKS EMPTY_BLOCKS EST_BLOCKS
-------------------- ---------- ----------- ---------- ------------ ----------
DEPT                          4          23         60           68          2
EMP                          12          41         60           68          2
SALGRADE                      5          13         60           68          2
BONUS                         0           0          0          128          0

NUM_ROWS
行数
AVG_ROW_LEN
平均行サイズ
BLOCKS
使用ブロック数
EMPTY_BLOCKS
空きブロック数
EST_BLOCKS
行数と平均行サイズから算出した推定必要ブロック数。 ※あくまでも推定なので多少の誤差は生じます。


なお、推定必要ブロック数を算出するには、V$TYPE_SIZEやV$PARAMETERから算出に必要な値を取得しなければなりませんが、上記のように実行時に直接取得すると検索に非常に時間がかかります。


そのため、プラットフォームが決まっている場合やブロックサイズを統一している場合は、以下のようにこれらの値を決め撃ちにして、さらに先に計算できるところはしておいたほうが検索が速くなっていいでしょう。

SELECT
  TABLE_NAME,
  NUM_ROWS,
  AVG_ROW_LEN,
  BLOCKS,
  EMPTY_BLOCKS,
  CASE AVG_ROW_LEN
    WHEN null THEN null
    WHEN 0 THEN 0
    ELSE ( CEIL( NUM_ROWS / TRUNC( ( ( 8106 - ( INI_TRANS - 1 ) * 24 )
      * ( 1 - PCT_FREE / 100 ) - 4 ) / AVG_ROW_LEN ) ) + 1 )
  END EST_BLOCKS
  FROM DBA_TABLES
  WHERE OWNER = 'SCOTT'
  ORDER BY BLOCKS DESC;

TABLE_NAME             NUM_ROWS AVG_ROW_LEN     BLOCKS EMPTY_BLOCKS EST_BLOCKS
-------------------- ---------- ----------- ---------- ------------ ----------
DEPT                          4          23         60           68          2
EMP                          12          41         60           68          2
SALGRADE                      5          13         60           68          2
BONUS                         0           0          0          128          0


このようにして算出した推定必要ブロック数が使用ブロック数よりも著しく小さい場合は、領域が無駄に使われている可能性が高いため、再編成の候補となります。


ただ、よほどシビアなシステムでない限りは、使用ブロック数が小さければそれほど気にしなくてもいいかもしれません。


システム特性に応じて再編成の目安とするしきい値を設定し、それを上回ったり、下回ったりしたら再編成するなどのルールを決めて運用するのがいいでしょう。