by-nc-sa     개발자, DBA가 함께 만들어가는 구루비 지식창고!

벤치마크, 벤치마크, 벤치마크




  • 벤치마크 성격
    (1)아이디어 또는 접근법 테스트를 위한 벤치마크
    (2)디자인된 애플리케이션이 실제로 동작하는지를 확인하기 위하여 수행되는 벤치마크
    (3)프로젝트 초기부터 갖게 되는 구체적인 메트릭(사용자 정의 프로시저의 형태)
    (4)반드시 현실이 반영되어야 한다는 것을 이해하는 것이 중요하다.
    (5)반드시 대상 규격을 이용하여 디자인, 테스트 그리고 구현되어야 한다.
    (6)개인적인 것으로서 대상 애플리케이션마다 고유하다.

8.1 소규모 벤치마킹

  • 소규모 벤치마킹의 성격
    (1)특정 테크닉의 확장성이 일부 다른 테크닉에 비해 훨씬 우수하다는 것을 증명하는 테스트
    (접근법 A가 접근법 B에 비해 우수, 열등, 또는 동일하다는 것을 양적으로 증명)
    (2)고립된 환경, 즉 단일 사용자 데이터베이스 환경에서 가장 잘 동작한다.
    (3)테스트 도중 다른 세션이 시스템에 부하를 주지 않아야 하며 일이 진행되는 동안 차단되지도 않아야 한다.

8.1.1 간단한 테스트 도구를 사용하라

  • Runstats : 수행(경과) 시간, 파스 호출과 같은 시스템 통계, 그리고 래칭을 측정한다.
    (1)시스템 통계 : 각 접근법이 뭔가를 수행한 횟수와 두 접근법의 차이를 나란히 보여 준다.
    (2)래칭 정보 : 일종의 상호 배제 또는 잠금 메커니즘(직렬화 장치, 동시성을 금지)
  • 테이블에 데이터를 삽입하는 데 사용되는 두 개의 서로 다른 접근법의 차이를 테스트
    첫번째 접근법에서는 SQL 문에 개체 틀의 역할을 하는 바인드 변수를 사용함으로써 SQL 문이 반복적으로 사용될 수 있도록 허용
    두번째 접근법에서는 바인드 변수를 사용하지 않을 것이다.
    (1)V$ 테이블 액세스를 위한 뷰의 생성
    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;
    


    (2)통계 수집용 임시 테이블 생성

    create global temporary table run_stats(
      runid varchar2(15)
    , name  varchar2(80)
    , value int
    ) on commit preserve rows;
    


    (3)Runstats 패키지 생성

    create or replace package runstats_pkg
    as
        procedure rs_start;                                               --Runstats가 시작되면서 호출된다.
        procedure rs_middle;                                              -- 예상대로 도중에 호출된다.
        procedure rs_stop( p_difference_threshold in number default 0 );  -- 작업을 마치고 보고서를 인쇄한다.
    end;
    /
    


    (4)Runstats 패키지 본문 생성

    
    -- 실행의 경과 시간을 기록하는 데 사용되는 몇 가지 글로벌 변수로 시작
    create or replace package body runstats_pkg
    as
    
    g_start number;
    g_run1  number;
    g_run2  number;
    
    -- RS_START 루틴은 단순히 테이블을 차지하고 있는 통계를 지우고 "사용 전" 통계와 래치로 이 테이블을 채운다
    -- 그런 다음 현재 시간 값(경과된 시간을 1/100초 단위로 계산하기 위하여 사용되는 정렬 시계)을 갈무리한다.
    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;
    -- RS_MIDDLE 루틴은 테스트의 첫 번째 실행에 대한 경과 시간을 G_RUN1에 기록한 후 현재의 통계 및 래치의 집합을 삽입
    -- 이전에 RS_START에서 저장된 값에서 이들 값을 빼면 첫 번째 방법이 사용한 래치와 커서(통계)의 수 등을 알 수 있다.
    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;
    -- RS_STOP 루틴은 다음 실행의 시작 시각을 기록한다.
    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' );
        dbms_output.put_line
        ( 'run 1 ran in ' || round(g_run1/g_run2*100,2) ||
          '% of the time' );
        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', 10 ) || lpad( 'Run2', 10 ) ||
          lpad( 'Diff', 10 ) || lpad( 'Pct', 8 ) );
    
        for x in
        ( select to_char( run1, '9,999,999' ) ||
                 to_char( run2, '9,999,999' ) ||
                 to_char( diff, '9,999,999' ) ||
                 to_char( round( run1/run2*100,2 ), '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;
    /
    




    (5)실습에 사용할 테이블 생성

    create table t ( x varchar2(30) );
    


    (6)두가지 접근법을 구현하는 두 개의 루틴(프로시저) 작성

    create or replace procedure method1( p_data in varchar2 )
    is
    begin
       execute immediate
       'insert into t(x) values(:x)'
       using p_data;
    end method1;
    /
    create or replace procedure method2( p_data in varchar2 )
    is
    begin
       execute immediate
       'insert into t(x) values( ''' || replace( p_data,'''','''''' ) || ''' )';
    end method2;
    /
    


    (7)두가지 접근법의 성능을 테스트

    set serveroutput on size 1000000;
    -- dbms_output.put_line 확인
    -- ORA-2000: buffer overflow, limit of 2000 bytes 오류 해결
    
    begin
       runstats_pkg.rs_start;
       for i in 1 .. 10000
       loop
          method1( 'row ' || I );
       end loop;
       runstats_pkg.rs_middle;
       for i in 1 .. 10000
       loop
          method2( 'row ' || I );
       end loop;
       runstats_pkg.rs_stop;
    end;
    /
    


  • 이 보고서를 통해 바인드 변수를 사용하는 method1이 method2가 소비한 시간의 58%에 수행됨을 알 수 있다.
  • 래치 부문에서도 바인드 변수를 사용하지 않은 방법이 훨씬 많은 래치(확장성을 저해)를 사용하였다.
      method1 mothod2 차이
    래치 합계 365471 797749 432278
  • 바인드 변수를 사용하는 것이 빠를 뿐만 아니라 확장성이 훨씬 우수하다(동시 사용자수면에서 유리)

8.1.2 기타 벤치마크 툴

  • 소규모 벤치마킹에 활용될 수 있는 툴들
    (1)TKPROF, TIMED_STATISTICS, SQL_TRACE 프로그램이 무엇을 어떻게 수행하는지를 정확하게 볼 수 있는 훌륭한 방법
    (2)DBMS_PROFILER 정밀 PL/SQL 코드 튜닝용
    (3)Explain Plan 쿼리가 무엇을 할 것인지를 볼 수 있음
    (4)Autotrace 쿼리가 이미 수행한 것들을 볼 수 있음

8.2 대규모 벤치마킹

  • "대규모 벤치마킹"은 전체적으로 확장성을 고려하여 시스템을 테스트한다는 것을 의미한다.
  • 벤치마킹을 하지 않아 시스템이 제대로 동작하지 않는 사례
    (1)시스템을 전혀 테스트하지 않았다.
    (2)비현실적인 수의 사용자 환경에서 시스템을 테스트하였다.
    (3)소규모 데이터베이스를 가지고 시스템을 테스트하였다.

8.2.1 전체를 대표할 수 있는 양의 데이터로 테스트하라

  • 사이트의 DB에는 40,000개 이상의 계정이 있었지만, 개발자는 10개 계정으로 테스트
    -> 실세계 상황을 고려한 현실적인 테스트의 중요성

8.2.2 실질적인 입력으로 테스트하라

  • 중간 계층의 코드가 다른 소프트웨어로부터 생성된 자바 기반의 3계층 애플리케이션 벤치마크 사례
    SELECT * FROM USER_TABLE WHERE USERNAME = :X FOR UPDAT
    -> 컨테이너가 사용자 수준에서 트랜잭션을 직렬화함에 따라 시스템을 (심하게) 사용하는 한 사용자가
    트랜잭션을 실행하기 위하여 굉장히 긴 줄에서 기다리게 되었다.
    -> 벤치마킹에 영향을 줄 수 있는 많은 요소를 충분히 고려하고 현실적인 테스트 환경을 설정한 후에 목표를 달성했음.

8.2.3 반드시 결과를 검증하라

  • 벤치마크를 통해 10,000명 이상의 사용자에게 서비스를 원할히 제공하는 것으로 판명된 프로덕션 시스템에서
    동시 사용자 200명 이하에서 심각한 성능 장애가 발생한 사례
    -> 메트릭을 전혀 기록하지 않았고, 코드에 디버그용 코드를 삽입하지 않아 트랜잭션이 제대로 실행되는지 알수 없었음.
    바인드 변수를 사용하지 않아 사용자와 세션을 추가함에 따라 경합이 증가하고 응답시간이 증가했음.

8.2.4 벤치마킹을 허드렛일로 여기지 말라

  • 벤치마킹은 개발에 있어서 가장 중요한 단계 중 하나이다.
    고성능을 실현하려는 점에서 보면 올바른 디자인보다도 중요할 수 있다.
  • 벤치마킹을 하게 되면 곤란한 상황을 피할 수 있을 뿐만 아니라 시간과 비용을 절감할 수 있다.

문서에 대하여

문서정보

Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.