환경설정

SCOTT/TIGER 스키마 설정

  • 스크립트 실행
    • $ORACLE_HOME/sqlplus/demo 에 존재
  • exp파일로 imp - ^scott.dmp

환경설정

  • 대부분의 예제는 SQLPlus 환경에서 수행되도록 설계.
  • DBMS_OUTPUT을 사용하기 위해 아래의 명령어를 수행.

SQL> set serveroutput on

SQPlus에서 Autotrace 설정

  • 9i 이하 PLAN_TABLE생성
    • cd $ORACLE_HOME/rdbms/admin
    • system으로 splplus 접속
    • @utlxplan 실행 (실행계획 테이블 생성)
    • create public synonym plan_table for plan_table;
    • grant all on plan_table to public;
  • 10g 이상
    • 오라클 인스톨시 sys계정에 global temporary table로 plan_table$ 생성되어 있다.
    • 또한 public에 synonym 생성 및 권한도 부여되어 있다.
  • PLUSTRACE(role) 생성 및 부여
    **cd $ORACLE_HOME/sqlplus/admin
    • sys로 sqlplus 접속
    • @@plustrce 실행
    • GRANT PLUSTRACE TO PUBLIC;

set echo on

drop role plustrace;
create role plustrace;

grant select on v_$sesstat to plustrace;
grant select on v_$statname to plustrace;
grant select on v_$mystat to plustrace;
grant plustrace to dba with admin option;

set echo off

실제수행여부수행결과실행계획실행통계
set autotrace onOOOO
set autotrace on explain             OOO
set autotrace on statistics          OOO
set autotrace on traceonlyOOO
set autotrace on traceonly explainO
set autotrace on traceonly statisticsOO

Statspack 설정

  • $ORACLE_HOME/rdbms/admin/spcreate.sql 실행
    • PERFSTAT 스키마 생성시 필요한 비밀번호, 기본 테이블 스페이스, 템포러리 테이블 스페이스 만 입력하면 됨.
    • 제거시에는 $ORACLE_HOME/rdbms/admin/spdrop.sql 실행.

사용자 정의 스크립트

Runstats

  • 같은 작업을 하는 두 개의 다른 방법을 비교.
  • 측정값
    • Wall clock or elapsed time
    • System statistics - 단계적으로 둘 간의 차이점과 각각 무언가를 액세스한 횟수
    • Latching
  • 한 유저만 사용하는 테이터베이스에서 유용.(다른 세션에 의해 발생한 시스템 로드 및 래치 때문에)

-- sys로 접속
-- view 생성
create or replace view stats
as select 'STAT...' || a.name name, b.value
      from v$statname a, v$mystat b
     where a.statistic# = b.statistic#
    union all
    select 'LATCH.' || name,  gets
      from v$latch
	union all
	select 'STAT...Elapsed Time', hsecs from v$timer;

-- 글로벌 템프 테이블 생성 (세션단위 데이터 유지로)
create global temporary table run_stats
( runid varchar2(15),
  name varchar2(80),
  value int )
on commit preserve rows;

-- 패키지 생성
create or replace package runstats_pkg
as
    procedure rs_start;
    procedure rs_middle;
    procedure rs_stop( p_difference_threshold in number default 0 );
end;
/

create or replace package body runstats_pkg
as

g_start number;
g_run1  number;
g_run2  number;

procedure rs_start
is 
begin
    delete from run_stats;

    insert into run_stats 
    select 'before', stats.* from stats;
        
    g_start := dbms_utility.get_time;
end;

procedure rs_middle
is
begin
    g_run1 := (dbms_utility.get_time-g_start);
 
    insert into run_stats 
    select 'after 1', stats.* from stats;
    g_start := dbms_utility.get_time;

end;

procedure rs_stop(p_difference_threshold in number default 0)
is
begin
    g_run2 := (dbms_utility.get_time-g_start);

    dbms_output.put_line
    ( 'Run1 ran in ' || g_run1 || ' hsecs' );
    dbms_output.put_line
    ( 'Run2 ran in ' || g_run2 || ' hsecs' );
	if ( g_run2 <> 0 )
	then
    dbms_output.put_line
    ( 'run 1 ran in ' || round(g_run1/g_run2*100,2) || 
      '% of the time' );
	end if;
    dbms_output.put_line( chr(9) );

    insert into run_stats 
    select 'after 2', stats.* from stats;

    dbms_output.put_line
    ( rpad( 'Name', 30 ) || lpad( 'Run1', 12 ) || 
      lpad( 'Run2', 12 ) || lpad( 'Diff', 12 ) );

    for x in 
    ( select rpad( a.name, 30 ) || 
             to_char( b.value-a.value, '999,999,999' ) || 
             to_char( c.value-b.value, '999,999,999' ) || 
             to_char( ( (c.value-b.value)-(b.value-a.value)), '999,999,999' ) data
        from run_stats a, run_stats b, run_stats c
       where a.name = b.name
         and b.name = c.name
         and a.runid = 'before'
         and b.runid = 'after 1'
         and c.runid = 'after 2'
         -- and (c.value-a.value) > 0
         and abs( (c.value-b.value) - (b.value-a.value) ) 
               > p_difference_threshold
       order by abs( (c.value-b.value)-(b.value-a.value))
    ) loop
        dbms_output.put_line( x.data );
    end loop;

    dbms_output.put_line( chr(9) );
    dbms_output.put_line
    ( 'Run1 latches total versus runs -- difference and pct' );
    dbms_output.put_line
    ( lpad( 'Run1', 12 ) || lpad( 'Run2', 12 ) || 
      lpad( 'Diff', 12 ) || lpad( 'Pct', 10 ) );

    for x in 
    ( select to_char( run1, '999,999,999' ) ||
             to_char( run2, '999,999,999' ) ||
             to_char( diff, '999,999,999' ) ||
             to_char( round( run1/decode( run2, 0, to_number(0), run2) *100,2 ), '99,999.99' ) || '%' data
        from ( select sum(b.value-a.value) run1, sum(c.value-b.value) run2,
                      sum( (c.value-b.value)-(b.value-a.value)) diff
                 from run_stats a, run_stats b, run_stats c
                where a.name = b.name
                  and b.name = c.name
                  and a.runid = 'before'
                  and b.runid = 'after 1'
                  and c.runid = 'after 2'
                  and a.name like 'LATCH%'
                )
    ) loop
        dbms_output.put_line( x.data );
    end loop;
end;

end;
/

-- synonym 생성
create public synonym stats for stats;
create public synonym run_stats for run_stats;
create public synonym runstats_pkg for runstats_pkg;

-- 권한 부여
grant select on stats to public;
grant all on run_stats to public;
grant execute on runstats_pkg to public;

  • 사용방법

-- 테스트 테이블 생성
create table t1
as
select * from big_table
where 1=0;

create table t2
as
select * from big_table
where 1=0;

exec runstats_pkg.rs_start;

insert into t1
select * 
from big_table
where rownum <= 100000;

commit;

exec runstats_pkg.rs_middle;

begin
    for x in (select * from big_table where rownum <= 100000)
    loop
        insert into t2 values x;
    end loop;
    commit;
end;
/

exec runstats_pkg.rs_stop(100000);

Run1 ran in 450 hsecs
Run2 ran in 599 hsecs
run 1 ran in 75.13% of the time

Name                                  Run1        Run2        Diff
LATCH.shared pool                      350     100,365     100,015
STAT...recursive calls                 497     101,467     100,970
STAT...db block changes             11,980     209,367     197,387
STAT...physical read total byt   1,032,192     688,128    -344,064
LATCH.cache buffers chains          61,722     555,472     493,750
STAT...physical write total by   4,251,648   2,834,432  -1,417,216
STAT...cell physical IO interc   5,283,840   3,522,560  -1,761,280
STAT...undo change vector size     392,044   6,404,460   6,012,416
STAT...redo size                12,332,532  37,717,016  25,384,484
STAT...logical read bytes from 173,244,416 981,065,728 807,821,312

Run1 latches total versus runs -- difference and pct
Run1        Run2        Diff       Pct
73,672     676,091     602,419     10.90%

h2.Mystat

  • mystat.sql과 mystat2.sql은 연산 전후의 오라클 '통계정보' 증가세를 보여주는 데 사용.

-- mystat.sql
set echo off
set verify off
column value new_val V
define S="&1"

set autotrace off
select a.name, b.value
from v$statname a, v$mystat b
where a.statistic# = b.statistic#
and lower(a.name) like lower('&S')
/
set echo on

-- mystat2.sql
set echo off
set verify off
select a.name, b.value V, to_char(b.value-&V, '999,999,999,999') diff
from v$statname a, v$mystat b
where a.statistic# = b.statistic#
and lower(a.name) like lower('&S')
/
set echo on

  • 사용방법

@mystat "redo size"
NAME                                                                  VALUE
---------------------------------------------------------------- ----------
redo size                                                            156048

update big_table set owner = lower(owner)
where rownum <= 1000;

1000 행이 갱신되었습니다.

@mystat2
NAME                                                                      V DIFF
---------------------------------------------------------------- ---------- ----------------
redo size                                                            257348          101,300

rollback;

Show_Space

  • show_space 루틴은 데이터베이스 세그먼트의 공간 활용 정보를 자세히 출력

create or replace procedure show_space
( p_segname in varchar2,
  p_owner   in varchar2 default user,
  p_type    in varchar2 default 'TABLE',
  p_partition in varchar2 default NULL )
-- this procedure uses authid current user so it can query DBA_*
-- views using privileges from a ROLE and so it can be installed
-- once per database, instead of once per user that wanted to use it
-- authid current_user
as
    l_free_blks                 number;
    l_total_blocks              number;
    l_total_bytes               number;
    l_unused_blocks             number;
    l_unused_bytes              number;
    l_LastUsedExtFileId         number;
    l_LastUsedExtBlockId        number;
    l_LAST_USED_BLOCK           number;
    l_segment_space_mgmt        varchar2(255);
    l_unformatted_blocks number;
    l_unformatted_bytes number;
    l_fs1_blocks number; l_fs1_bytes number;
    l_fs2_blocks number; l_fs2_bytes number;
    l_fs3_blocks number; l_fs3_bytes number;
    l_fs4_blocks number; l_fs4_bytes number;
    l_full_blocks number; l_full_bytes number;

    -- inline procedure to print out numbers nicely formatted
    -- with a simple label
    procedure p( p_label in varchar2, p_num in number )
    is
    begin
        dbms_output.put_line( rpad(p_label,40,'.') ||
                              to_char(p_num,'999,999,999,999') );
    end;
begin
   -- this query is executed dynamically in order to allow this procedure
   -- to be created by a user who has access to DBA_SEGMENTS/TABLESPACES
   -- via a role as is customary.
   -- NOTE: at runtime, the invoker MUST have access to these two
   -- views!
   -- this query determines if the object is a ASSM object or not
   begin
      execute immediate 
          'select ts.segment_space_management
             from dba_segments seg, dba_tablespaces ts
            where seg.segment_name      = :p_segname
              and (:p_partition is null or 
                  seg.partition_name = :p_partition)
              and seg.owner = :p_owner
              and seg.tablespace_name = ts.tablespace_name'
             into l_segment_space_mgmt
            using p_segname, p_partition, p_partition, p_owner;
   exception
       when too_many_rows then
          dbms_output.put_line
          ( 'This must be a partitioned table, use p_partition => ');
          return;
   end;


   -- if the object is in an ASSM tablespace, we must use this API
   -- call to get space information, else we use the FREE_BLOCKS
   -- API for the user managed segments
   if l_segment_space_mgmt = 'AUTO'
   then
     dbms_space.space_usage 
     ( p_owner, p_segname, p_type, l_unformatted_blocks,
       l_unformatted_bytes, l_fs1_blocks, l_fs1_bytes,
       l_fs2_blocks, l_fs2_bytes, l_fs3_blocks, l_fs3_bytes,
       l_fs4_blocks, l_fs4_bytes, l_full_blocks, l_full_bytes, p_partition);
     
     p( 'Unformatted Blocks ', l_unformatted_blocks );
     p( 'FS1 Blocks (0-25)  ', l_fs1_blocks );
     p( 'FS2 Blocks (25-50) ', l_fs2_blocks );
     p( 'FS3 Blocks (50-75) ', l_fs3_blocks );
     p( 'FS4 Blocks (75-100)', l_fs4_blocks );
     p( 'Full Blocks        ', l_full_blocks );
  else
     dbms_space.free_blocks(
       segment_owner     => p_owner,
       segment_name      => p_segname,
       segment_type      => p_type,
       freelist_group_id => 0,
       free_blks         => l_free_blks);
     
     p( 'Free Blocks', l_free_blks );
  end if;

  -- and then the unused space API call to get the rest of the 
  -- information
  dbms_space.unused_space
  ( segment_owner     => p_owner,
    segment_name      => p_segname,
    segment_type      => p_type,
    partition_name    => p_partition,
    total_blocks      => l_total_blocks,
    total_bytes       => l_total_bytes,
    unused_blocks     => l_unused_blocks,
    unused_bytes      => l_unused_bytes,
    LAST_USED_EXTENT_FILE_ID => l_LastUsedExtFileId,
    LAST_USED_EXTENT_BLOCK_ID => l_LastUsedExtBlockId,
    LAST_USED_BLOCK => l_LAST_USED_BLOCK );

    p( 'Total Blocks', l_total_blocks );
    p( 'Total Bytes', l_total_bytes );
    p( 'Total MBytes', trunc(l_total_bytes/1024/1024) );
    p( 'Unused Blocks', l_unused_blocks );
    p( 'Unused Bytes', l_unused_bytes );
    p( 'Last Used Ext FileId', l_LastUsedExtFileId );
    p( 'Last Used Ext BlockId', l_LastUsedExtBlockId );
    p( 'Last Used Block', l_LAST_USED_BLOCK );
end;
/

-- synonym 생성
create public synonym show_space for show_space;

-- 권한 부여
grant execute on show_space to public;

  • 사용방법

exec show_space('BIG_TABLE');

Unformatted Blocks .....................               0
FS1 Blocks (0-25)  .....................               0
FS2 Blocks (25-50) .....................               0
FS3 Blocks (50-75) .....................               0
FS4 Blocks (75-100).....................               0
Full Blocks        .....................           7,565
Total Blocks............................           7,808
Total Bytes.............................      63,963,136
Total MBytes............................              61
Unused Blocks...........................             113
Unused Bytes............................         925,696
Last Used Ext FileId....................               4
Last Used Ext BlockId...................           8,320
Last Used Block.........................              15

  • Unformatted Blocks - 테이블의 하이 워터 마크 아래에 할당 된 블록 수로, 사용된 적이 없는 블록.
    unformatted와 unused 블록을 더하면 ASSM 객체 내에서 데이터를 저장하는 데 사용된 적이 없는 테이블에 할당된 블록 수의 합니다.
  • FS1 Blocks-FS4 Blocks - 데이터를 가진 포맷된 블록으로, 각 블록의 "빈 공간"의 범위를 나타내는 것. (0-25)는 0에서 25%가 비어 있는 블록 수.
  • Full Blocks - 더 이상 insert 할 수 없는 가득 찬 블록 수
  • Total Blocks, Total Bytes, Total MBytes - 데이터베이스 블록, 바이트, 메가바이트로 측정한 할당된 세그먼트의 총 공간
  • Unused Blocks, Unused Bytes - 사용된 적이 없는 공간을 나타낸다, 이 블록은 세그먼트에 할당은 되었으나 세그먼트의 하이 워터 마크 위에 있는 공간이다.
  • Last Used Ext FileId - 데이터가 있는 마지막 익스텐트 파일의 파일 ID
  • Last Used Ext BlockId - 마지막 익스텐트의 시작 블록 ID. 마지막으로 사용된 파일 내의 블록 ID.
  • Last Used Block - 마지막 익스텐트에 사용된 마지막 블록의 블록 ID 오프셋

Big_Table


-- 테이블 생성
create table big_table nologging
as
select rownum id, a.*
from all_objects a
where 1 = 0;

-- 데이터 입력
declare
    l_cnt number;
    l_rows number := &1;
begin
    insert /*+ append */
    into big_table
    select rownum, a.*
    from all_objects a
    where rownum <= &1;
    
    l_cnt := sql%rowcount;
    
    commit;
    
    while (l_cnt < l_rows)
    loop
        insert /*+ append */ into big_table
        select rownum + l_cnt, a.*
        from all_objects a
        where rownum <= l_rows - l_cnt;
        
        l_cnt := l_cnt + sql%rowcount;
        commit;
    end loop;
end;
/

-- pk 생성
alter table big_table add constraint
big_table_pk primary key(id);

-- 통계 생성
exec dbms_stats.gather_table_stats(user, 'BIG_TABLE', estimate_percent=> 1);

코딩 규칙

  • 글로벌 패키지 변수 : G_
  • 매개 변수 : P_
  • 로컬 변수 : L_