Showing posts with label block dump. Show all posts
Showing posts with label block dump. Show all posts

Wednesday, July 15, 2009

UTL_RAW パッケージでブロックダンプをdecodingする

次のブロックダンプの結果を見てみましょう。

block_row_dump:
tab 0, row 0, @0x1f3d
tl: 9 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 02 <-- RawにEncodingされている!
col 1: [ 2] 58 31 <-- RawにEncodingされている!
tab 0, row 1, @0x1f46
tl: 9 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 03
col 1: [ 2] 58 32
...
tab 0, row 9, @0x1f8e
tl: 10 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 0b
col 1: [ 3] 58 31 30
end_of_block_dump

上のようにEncodingされた値はブロックダンプを解析する時に大変不便なことになります。でも、UTL_RAWパッケージを適当に使用すればEncodingされたColumnの値を手軽にDecodingできます。

下の例を御覧なさい。

UKJA@ukja102> create table t1(c1 number, c2 varchar2(10));

Table created.

Elapsed: 00:00:00.01
UKJA@ukja102>
UKJA@ukja102> insert into t1
2 select level, 'X'||level
3 from dual
4 connect by level <= 10
5 ;

10 rows created.

Elapsed: 00:00:00.01
UKJA@ukja102>
UKJA@ukja102> col f# new_value fno
UKJA@ukja102> col b# new_value bno
UKJA@ukja102>
UKJA@ukja102> select dbms_rowid.rowid_relative_fno(rowid) as f#,
2 dbms_rowid.rowid_block_number(rowid) as b#
3 from t1
4 ;

F# B#
---------- ----------
6 836
6 836
6 836
6 836
6 836
6 836
6 836
6 836
6 836
6 836

10 rows selected.

Elapsed: 00:00:00.03
UKJA@ukja102>
UKJA@ukja102> alter system dump datafile &fno block &bno;
old 1: alter system dump datafile &fno block &bno
new 1: alter system dump datafile 6 block 836

UKJA@ukja102> @decode_block_dump T1 <-- 自動化したスクリプト

block_row_dump:
tab 0, row 0, @0x1f3d
tl: 9 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 02 means C1 = 1
col 1: [ 2] 58 31 means C2 = X1
tab 0, row 1, @0x1f46
tl: 9 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 03 means C1 = 2
col 1: [ 2] 58 32 means C2 = X2
tab 0, row 2, @0x1f4f
tl: 9 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 04 means C1 = 3
col 1: [ 2] 58 33 means C2 = X3
tab 0, row 3, @0x1f58
tl: 9 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 05 means C1 = 4
col 1: [ 2] 58 34 means C2 = X4
tab 0, row 4, @0x1f61
tl: 9 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 06 means C1 = 5
col 1: [ 2] 58 35 means C2 = X5
tab 0, row 5, @0x1f6a
tl: 9 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 07 means C1 = 6
col 1: [ 2] 58 36 means C2 = X6
tab 0, row 6, @0x1f73
tl: 9 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 08 means C1 = 7
col 1: [ 2] 58 37 means C2 = X7
tab 0, row 7, @0x1f7c
tl: 9 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 09 means C1 = 8
col 1: [ 2] 58 38 means C2 = X8
tab 0, row 8, @0x1f85
tl: 9 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 0a means C1 = 9
col 1: [ 2] 58 39 means C2 = X9
tab 0, row 9, @0x1f8e
tl: 10 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 0b means C1 = 10
col 1: [ 3] 58 31 30 means C2 = X10
end_of_block_dump

DECODE_BLOCK_DUMP.SQLスクリプトは次のようです。UTL_RAWパッケージを呼び出す部分を注意して見てください。

define __TABLE_NAME = &1

set serveroutput on

declare
v_varchar2 varchar2(4000);
v_number number;
col_idx number;
col_type varchar2(200);
col_name varchar2(100);
col_value varchar2(4000);

begin
for r in (select column_value as txt from table(get_trace_file1)) loop
dbms_output.put(r.txt);
if regexp_like(r.txt, 'col[[:space:]]+[[:digit:]]+:') then

col_idx := regexp_replace(r.txt, 'col[[:space:]]+([[:digit:]])+: [[:print:]]+', '\1');

select column_name, data_type into col_name, col_type
from user_tab_cols
where table_name = upper('&__TABLE_NAME')
and column_id = col_idx+1
;

col_value := replace(regexp_replace(r.txt, 'col[[:space:]]+[[:digit:]]+:[[:space:]]+\[[[:space:]]+[[:digit:]]\][[:space:]]+([[:print:]]+)', '\1'), ' ', '');
if col_type = 'NUMBER' then
--dbms_stats.convert_raw_value(col_value, v_number);
v_number := utl_raw.cast_to_number(col_value);
dbms_output.put(' means ' || col_name || ' = ' || v_number);
elsif col_type = 'VARCHAR2' then
--dbms_stats.convert_raw_value(col_value, v_varchar2);
v_varchar2 := utl_raw.cast_to_varchar2(col_value);
dbms_output.put(' means ' || col_name || ' = ' || v_varchar2);
end if;
end if;

dbms_output.new_line;
end loop;
end;
/

set serveroutput off

(GET_TRACE_FILE1の定義はここ

もう、もっと楽にブロックダンプを解析することができるでしょう。

Tuesday, July 7, 2009

ファイル番号とブロック番号からオブジェクト情報得る

ファイル番号とブロック番号からオブジェクト情報を得たい。

このような簡単な要求が実際には性能については大変なことになってしまいます。例えば、次のような待機イベントがあるとしてみます。

   1: WAIT #6: nam='db file scattered read' ela= 438472 file#=6 block#=2641 blocks=8
   2: WAIT #6: nam='db file scattered read' ela= 1039 file#=6 block#=833 blocks=8 obj#=90054 tim=878243950382
   3: WAIT #6: nam='db file scattered read' ela= 835 file#=10 block#=22961 blocks=8 obj#=90054 tim=878243957168
   4: WAIT #6: nam='db file scattered read' ela= 815 file#=11 block#=7409 blocks=8 obj#=90054 tim=878243966696
   5: ...

P1(ファイル番号)とP2(ブロック番号)からオブジェクトの名前を得たいとします。どうすればいいでしょうか。一番一般的な方法は次のようにDBA_EXTENTSビューを問い合わせて見るのです。でも、その性能はとても悪いです。

   1: UKJA@ukja102> ed which_obj
   2:  
   3: /*
   4: define __FILE = &1
   5: define __BLOCK = &2
   6: 
   7: select segment_name
   8: from dba_extents
   9: where file_id = &__FILE
  10:   and &__BLOCK between block_id and block_id + blocks - 1
  11:             and rownum = 1
  12: ;
  13: 
  14: set echo on
  15: 
  16: */
  17:  
  18: UKJA@ukja102> @which_obj 6 2641
  19:  
  20: SEGMENT_NAME
  21: --------------------
  22: T1_N1
  23:  
  24: Elapsed: 00:02:43.84
  25:  
  26: Statistics
  27: ----------------------------------------------------------
  28:        4676  recursive calls
  29:           2  db block gets
  30:     4077424  consistent gets
  31:        6492  physical reads
  32:           0  redo size
  33:         418  bytes sent via SQL*Net to client
  34:         400  bytes received via SQL*Net from client
  35:           2  SQL*Net roundtrips to/from client
  36:           5  sorts (memory)
  37:           0  sorts (disk)
  38:           1  rows processed

この性能問題を解決ために、いくつの代案が具現されています。 1)DBA_EXTENTSビューに対して集計表を作る、2)X$BHビューをとても速く問い合わせする、3)ブロックダンプをしてそれからオブジェクトIDを取る。

3番目の方法を応用すれば、次のように完全に自動的にオブジェクトIDを迅速に取ることができます。

   1: UKJA@ukja102> ed which_obj2
   2:  
   3: /*
   4: define __FILE = &1
   5: define __BLOCK = &2
   6: 
   7: alter system dump datafile &__FILE block &__BLOCK;
   8: 
   9: set serveroutput on
  10: 
  11: declare
  12:     v_dba        varchar2(100);
  13:     v_type    varchar2(100);
  14:     v_obj_id        number;
  15:     v_obj_name    varchar2(100);
  16: begin
  17:     for r in (select column_value as t from table(get_trace_file1)) loop
  18:         if regexp_like(r.t, 'buffer tsn:') then
  19:             dbms_output.put_line('------------------------------------------------');
  20:             v_dba := regexp_substr(r.t, '[[:digit:]]+/[[:digit:]]+');
  21:             dbms_output.put_line(rpad('dba = ',20)|| v_dba);
  22:         end if;
  23: 
  24:         if regexp_like(r.t, 'type: 0x([[:xdigit:]]+)=([[:print:]]+)') then
  25:             v_type := substr(regexp_substr(r.t, '=[[:print:]]+'), 2);
  26:             dbms_output.put_line(rpad('type = ',20)|| v_type);
  27:         end if;
  28: 
  29:         if regexp_like(r.t, 'seg/obj:') then
  30:             v_obj_id := to_dec(substr(regexp_substr(r.t,
  31:                             'seg/obj: 0x[[:xdigit:]]+'), 12));
  32:             select object_name into v_obj_name from all_objects
  33:                 where data_object_id = v_obj_id;
  34:             dbms_output.put_line(rpad('object_id = ',20)|| v_obj_id);
  35:             dbms_output.put_line(rpad('object_name = ',20)|| v_obj_name);
  36:         end if;
  37: 
  38:         if regexp_like(r.t, 'Objd: [[:digit:]]+') then
  39:             v_obj_id := substr(regexp_substr(r.t, 'Objd: [[:digit:]]+'), 7);
  40:             select object_name into v_obj_name from all_objects
  41:                 where data_object_id = v_obj_id;
  42:             dbms_output.put_line(rpad('object_id = ',20)|| v_obj_id);
  43:             dbms_output.put_line(rpad('object_name = ',20)|| v_obj_name);
  44:         end if;
  45: 
  46:     end loop;
  47: 
  48:     dbms_output.put_line('------------------------------------------------');
  49: 
  50: end;
  51: /
  52: 
  53: */
  54:  
  55: UKJA@ukja102> @which_obj2 6 2641
  56: old   1: alter system dump datafile &__FILE block &__BLOCK
  57: new   1: alter system dump datafile 6 block 2641
  58:  
  59: System altered.
  60:  
  61: Elapsed: 00:00:00.01
  62: ------------------------------------------------
  63: dba =               6/2641
  64: type =              FIRST LEVEL BITMAP BLOCK
  65: object_id =         90055
  66: object_name =       T1_N1
  67: ------------------------------------------------
  68: PL/SQL procedure successfully completed.
  69:  
  70: Elapsed: 00:00:00.04

2分以上の遅い作業が0.1秒以下の効率的な作業に変わったことをみています。

Wednesday, June 24, 2009

Index Tree Dumpをもっと便利に使用しよう。

索引の性能問題を分析する時はいつもリーフブロックをダンプすべき必要性を感じます。例えば、索引の3番目のリーフブロックをダンプして、この内容を分析しようと思うと仮定してみましょう。
UKJA@ukja116> select * from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 - Production
PL/SQL Release 11.1.0.6.0 - Production
CORE 11.1.0.6.0 Production
TNS for 32-bit Windows: Version 11.1.0.6.0 - Production
NLSRTL Version 11.1.0.6.0 - Production

Elapsed: 00:00:00.03
UKJA@ukja116>
UKJA@ukja116> create table t1(c1 int, c2 char(100));

Table created.

Elapsed: 00:00:00.01
UKJA@ukja116>
UKJA@ukja116> insert into t1
2 select level, 'dummy' from dual
3 connect by level <= 1000
4 ;

1000 rows created.

Elapsed: 00:00:00.01
UKJA@ukja116>
UKJA@ukja116> create index t1_n1 on t1(c1, c2);

Index created.

Elapsed: 00:00:00.01
UKJA@ukja116> -- do index tree dump
UKJA@ukja116> exec tree_dump('t1_n1');

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.01

Index Tree Dumpの結果は次のようです。
----- begin tree dump
branch: 0x1c00af4 29362932 (0: nrow: 17, level: 1)
leaf: 0x1c00af5 29362933 (-1: nrow: 62 rrow: 62)
leaf: 0x1c00af6 29362934 (0: nrow: 62 rrow: 62)
leaf: 0x1c00af7 29362935 (1: nrow: 61 rrow: 61)
leaf: 0x1c00af8 29362936 (2: nrow: 61 rrow: 61)
leaf: 0x2802ad9 41954009 (3: nrow: 61 rrow: 61)
leaf: 0x2802ada 41954010 (4: nrow: 61 rrow: 61)
leaf: 0x2802adb 41954011 (5: nrow: 61 rrow: 61)
leaf: 0x2802adc 41954012 (6: nrow: 61 rrow: 61)
leaf: 0x2802add 41954013 (7: nrow: 61 rrow: 61)
leaf: 0x2802ade 41954014 (8: nrow: 61 rrow: 61)
leaf: 0x2802adf 41954015 (9: nrow: 61 rrow: 61)
leaf: 0x2802ae0 41954016 (10: nrow: 61 rrow: 61)
leaf: 0x2c0268a 46147210 (11: nrow: 61 rrow: 61)
leaf: 0x2c0268b 46147211 (12: nrow: 61 rrow: 61)
leaf: 0x2c0268c 46147212 (13: nrow: 61 rrow: 61)
leaf: 0x2c0268d 46147213 (14: nrow: 61 rrow: 61)
leaf: 0x2c0268e 46147214 (15: nrow: 22 rrow: 22)
----- end tree dump

この情報をもとに特定のリーフブロックをダンプするためには、1)DBA(Data Block Address)をCopy&Pasteし、2)そのDBAをファイル番号とブロック番号で変化し、3)SQL*Plusで"alter system dump datafile f# block b#"命令文を実行しなければなりません。

このような退屈な作業は私が一番きらいなものです。いい知らせは、下のように自動化したスクリプトを使ってもう少し便利に作業をすることが可能だということです。
UKJA@ukja116>
UKJA@ukja116> select
2 prefix||
3 type ||
4 ' max_rows=' || nrow ||', '||
5 'cur_rows=' || rrow ||', '||
6 'dump=alter system dump datafile ' ||
7 dbms_utility.data_block_address_file(to_dec(dba)) || ' block ' ||
8 dbms_utility.data_block_address_block(to_dec(dba))
9 from (
10 select
11 regexp_substr(column_value, '^[[:space:]]+') as prefix,
12 regexp_substr(column_value, '(branch|leaf)') as type,
13 regexp_replace(regexp_substr(column_value, '(branch:|leaf:) [^ ]+'),
14 '(branch:|leaf:) 0x', '') as dba,
15 substr(regexp_substr(column_value, 'nrow: [[:digit:]]+'), 7) as nrow,
16 substr(regexp_substr(column_value, 'rrow: [[:digit:]]+'), 7) as rrow
17 from table(get_trace_file1)
18 where regexp_like(column_value, '(branch:|leaf:)')
19 )
20 ;

PREFIX||TYPE||'MAX_ROWS='||NROW||','||'CUR_ROWS='||RROW||','||'DUMP=ALTERSYSTEMD
--------------------------------------------------------------------------------
branch max_rows=17, cur_rows=, dump=alter system dump datafile 7 block 2804
leaf max_rows=62, cur_rows=62, dump=alter system dump datafile 7 block 2805
leaf max_rows=62, cur_rows=62, dump=alter system dump datafile 7 block 2806
leaf max_rows=61, cur_rows=61, dump=alter system dump datafile 7 block 2807
leaf max_rows=61, cur_rows=61, dump=alter system dump datafile 7 block 2808
leaf max_rows=61, cur_rows=61, dump=alter system dump datafile 10 block 10969
leaf max_rows=61, cur_rows=61, dump=alter system dump datafile 10 block 10970
leaf max_rows=61, cur_rows=61, dump=alter system dump datafile 10 block 10971
leaf max_rows=61, cur_rows=61, dump=alter system dump datafile 10 block 10972
leaf max_rows=61, cur_rows=61, dump=alter system dump datafile 10 block 10973
leaf max_rows=61, cur_rows=61, dump=alter system dump datafile 10 block 10974
leaf max_rows=61, cur_rows=61, dump=alter system dump datafile 10 block 10975
leaf max_rows=61, cur_rows=61, dump=alter system dump datafile 10 block 10976
leaf max_rows=61, cur_rows=61, dump=alter system dump datafile 11 block 9866
leaf max_rows=61, cur_rows=61, dump=alter system dump datafile 11 block 9867
leaf max_rows=61, cur_rows=61, dump=alter system dump datafile 11 block 9868
leaf max_rows=61, cur_rows=61, dump=alter system dump datafile 11 block 9869
leaf max_rows=22, cur_rows=22, dump=alter system dump datafile 11 block 9870

18 rows selected.

Elapsed: 00:00:00.06

上の結果を使えば、ただダンプ命令文(alter system dump datafile 11 block 9870)をSQL*PlusにCopy&Pasteするだけで必要な作業が終わります。

簡単なトリックだけで退屈に見える作業が知能的で面白い作業に変わるいい例です。