오라클 성능 고도화 원리와 해법 II (2012년)
통계정보 II 0 0 58,455

by 구루비스터디 통계정보 DBMS_STATS [2018.04.01]


  1. 전략적인 통계수집 정책의 중요성
  2. DBMS_STATS
  3. 컬럼 히스토그램 수집
  4. 데이터 샘플링
  5. 파티션 테이블 통계 수집
  6. 인덱스 통계 수집
  7. 캐싱된 커서 Invalidation
  8. 자동 통계 수집
  9. 자동 통계 수집 기능 활용 가이드
  10. Statistics Preference


전략적인 통계수집 정책의 중요성

  • 실제 통계 정보를 수집 하고 , DB 관리자들이 효과적인 통계 정보 수집전략을 세우는 것을 목표


1) CBO 능력을 최대한 끌어 올리는 핵심요소
  • 부정확한 통계정보는 잘못된 실행 게획을 실행 하게 된다.
  • EX) 엉둥한 인덱스를 탄다, NL 조인이 HASH JOIN으로 실행..


2) DB관리자의 핵심 역할은 통계정보 관리
  • 중요 거래 테이블일 경우 통계 정보 수집일자가 대부분 오래된 상태라면 성능 문제가 발생할 우려가 있다.
  • DBA는 단순 백업&복구, 오브젝트 생성관리, 트러블 슈팅 및 그이 상으로 중요한 역활은 통계정보 수집 정책을 세우고 그에 따라 통계 정보를 안정적으로 운영 관리하는데 있다.
  • EX) 문제없던 쿼리가 갑자기 악성 SQL로 돌변했할수 있음


3) 통계정보 수집시 고려사항
  1. 시간 : 부하가 없는 시간대에 가능한 빠르게 수집을 완료해야 함
  2. 샘플 크기 : 가능한 적은 양의 데이터를 읽어햐 함
  3. 정확성 : 전수 검사할 때의 통계치에 근접해야 함
  4. 안정성 : 데이터에 큰 변화가 없을 경우 매번 통계치가 바뀌지 않아야 함
  • 만약 샘플 크기를 최소화하더라도 정확성, 안정성을 확보 할 수 있다면 시간을 크게 줄일 수 있지만 그렇지 않다면 시간이 오래 걸리더라도 전수 검사를 해야함


4) 주기적으로 통계 수집하면서 안정적이어야 최적
  • 통계정보의 중요성은 무엇보다 좋은 실행계획을 통해 쿼리 성능을 높이는데 있음
  • CBO를 사용하는 한 통계정보를 수집하지 않을 수 없다.
  • 통계정보를 주기적으로 수집하면서도 안정적으로 운영되는 시스템이야말로 최선


DBMS_STATS

  • Analyze 명령어 대신 DBMS_STATS 패키지를 사용하며 특히 파티션 테이블/ 인덱스 일때는 반드시 사용
  • 446~447 참조


컬럼 히스토그램 수집

  • 나은 실행 계획을 수립 하는데 도움이 되지만 수집*관리 비용이 만만치 않으므로 필요한 컬럼에 대해서만 수집해야 하며 조건 절에 자주 사용 되면서 편중된 데이터 분포를 갖는 컬럼이 주 대상이다.
  • 인덱스 컬럼에 히스토그램을 수집해야 하며, 인덱스가 없는 조건절 컬럼이라도 테이블을 액세스하고 나서의 최종 선택도를 계산할때 필요한 컬럼의 히스토그램은 수집한다


히스토그램이 불필요
  1. 컬럼 데이터 분포가 균일
  2. Unique하고 항상 등치조건으로만 검색되는 컬럼
  3. 항상 바인드 변수로 검색되는 컬럼


  • dbms_stats.gather_table_stats에서 컬럼 히스토그램의 수집과 관련된 인자는 method_opt이다.
  • 8i, 9i 기본값은 'for all columns size 1' : 모든 컬럼에 대해 히스토그램을 수집하지 말것.
  • 10g 기본값 'for all columns size auto': 오라클이 모든 컬럼에 대해 skew 여부를 조사해서 버킷 개수를 결정하라는 뜻이다.
  • auto가 skewonly와 다른 점은 해당 컬럼의 조건절에 사용되는 비중까지 고려해서 결정한다.


  • 10g에서 dbms-stats의 기본 동작 방식이 바뀐 사실을 모른데 업그레이드를 할 경우 없던 히스토그램이 생길수 있으며
  • 주요 sql의 실행게획이 오히려 나쁜쪽으로 변경 된다던지, 수집 시간이 늘어 날수 있다.
  • 대용량 테이블일 경우는 관리자가 직접 히스토그램 수집 컬럼을 변경하는 것이 바람직 하다.
  • method_opt => 'for columns col1 size 20 col2 size 254 col3 size 100'


데이터 샘플링

  • 샘플링 비율을 높일수록 통계 정보의 정확도는 높아지지만 수집하는데 더 많은 시간이 소요 된다.


샘플링 비율
  • dbms 샘플링 비율을 조정 하기 위해 estimate_percent 인자를 사용한다.
  • 모든 테이블을 조상 대상으로 삼기 어려우므로 가장 큰 몇몇 테이블만 설정 해도 큰효과를 얻을수 있다.
  • 5%에서 시작값을 늘려가며 몇번의 통계 수집을 하다보면 적정 크기를 결정 할 수 있다.


블록 단위 샘플링
  • block_sample인자를 통해 블록 단위로 할지 로우 단위로 할지 결정
  • 기본값이 로우 단위이나 블록 단위 샘플링이 더 빠르고 효과적임 그러나 데이터 분포가 고르지 않을 경우 정확도가 떨어짐


안정적인 통계정보의 필요성
  • 샘플링 방식을 사용하면 매번 통계치가 다르게 구해질 수 있고 이는 실행 계획에 영향을 미쳐 SQL의 성능을 불안전 하게 한다.
  • 선택도 구하는 공식의 세가지 구성요소는 NULL제외한 로우수, Distinct Value 개수, 총 레코드 개수이다.
  • 그러므로 특히 NULL 값이 많거나 데이터 분포가 고르지 않을 경우가 불안정 하다고 할 수 있다.


해시 기반 알고리즘으로 NDV 계산 --11g
  • 분포가 고르지 않는 상항에서 샘플링 방식을 하용 하면 매번 다르게 구해질 수 있어 안정적인 실행 계획을 기대하기 어렵다.
  • 그래서 11g에서는 해시기반의 새로운 알로그림을 개발 하였고, 대용량 파티션 또는 테이블전체를 스캔하더라도 기존에 샘플링 방식을 사용 할 때보다는 오히려 빠른 속도를 낼 수 있게 되었다.
  • 소트를 수행 하지 않고, 전체 대상으로 NDV를 구하므로 정확도는 당연 100%에가깝다.


파티션 테이블 통계 수집

  • 파티션 레벨 통계: Static Partition Pruning이 작동될 때 사용된다.
  • 결합 파티션일 때는 서브파티션 레벨로 통계를 관리 할 수 있다


  • 테이블 레벨 통계 : Dynamic Partition Pruning이 작동될 때 사용된다.
  • 쿼리에 바인드 변수가 사용됐거나, 파티션 테이브링 NL조인에서 Inner쪽 테이블이면 엑세스해야 할 대상 파티션 목록을 쿼리 최적화 시점에 정할 수 없기 때문이다.
  • 또한 파티션 키에 대한 조건절이 없을 때도 테이블 레벨 통계가 사용된다.


  • dbms_stats은 global통계를 별도로 수행하는 반면 anlyze는 파티션 통계를 가지고 global 통계를 유추하므로 부정확하다
  • dbms_stats 패키지를 이용하여 파티션 테이블의 통계를 수집 할때는 granularity 옵션을 신중히 선택


  • global : 테이블 레벨 통계 수집
  • partition : 파티션 레벨 통계 수집 ( 테이블 레벨 통계 수집이 되어있지 않을경우, 파티션 레벨 통계로부터 추정된 값으로 테이블 레벨 통게를 설정한다.)
  • subpartition : 서브 파티션 레벨 통계 수집
  • global and partition : 테이블과 파티션 레벨 통계 수집 ( 테이블 레벨 NDV 정보를 정확하게 수집하기 위해 global 통계를 위한 쿼리를 한번 더 수행한다.)
  • all : 테이블, 파티션, 서브 파티션 레벨 통계 수집
  • auto : 파티션 유형에 따라 오라클이 결정


  • 대용량 파티션 테이블에 대한 통계 수집 효율성을 높일 방안과 전략이 필요하다.
  • 10g 이라 버전을 사용 하고 있다면 최근 파티션만 통계를 수집하고 나서 테이블 전체통계를 한 번 더 수행 하는 방식을 사용 하는 것이 효과적이다.

begin 
   dbms_stats.gather_table_stats ('ods', 'order'
   , partname => 'order_part_200912'
   , granularity => 'partition'
   , estimate_percent => 20
   );
end;

begin
dbms_stats.gather_table_stats ('ods'
			, 'order'
			, granularity => 'global'
			, estimate_percent => 20
);
end


NDV를 제외한 Incremental Grobal 통계 - 10.2.0.4
  • 10.2.0.4 에서 granularity인자에 선택 할 수 있는 값으로서 'approx_global and partition' 추가 되었다.
  • gobal and partition과 다른 점은 테이블 통계를 위한 쿼리를 따로 수행하지 않고 파티션 레벨 통계로부터 집계한다는데 있다.
  • 컬럼 NDV와 인덱스의 Distinct Key개수는 제외된다.
  • 아래는 2009년 11월 파티션만 통계를 수집하고(이때 해시 기반 NDV 계산 기능이 효과를 발휘함), ndv를 제외한 나머지 테이블 레벨 통계는 방급 수집한 새 파티션 통계와 다른 파티션의 기존 통계를 이용해 구한다.

begin
  dbms_stats.gather_table_stats ('ods'
				,'order'
				,partname => 'order_200911'
				,granularity => 'approx_global and partition');
end;

  1. NDV를 그대로 두지만 경우에 따라서 갱신이 이루어 진다.
  2. unique 인덱스를 가진 컬럼은 전체 레코드 개수가 ndv와 일치
  3. 파티션 키 컬럼은 파티션 레벨 NDV를 더한 값과 테이블 레벨 NDV가 일치
  • 위의 2~3의 경우를 제외 하고는 NDV를 그렇게 자주 갱신해 주지 않어라도 문제는 없다.
  • (좀 더 넓은 주기로 테이블 레벨 통계를 수집 하거나 테이블 전반에 많은 양의 추가/갱신이 일어날 때마다 한 번씩 수집해 주어야 한다.)
  • 새로운 값이 계속 입력 되는 컬럼은 NDV보다 주로 low_value/high_value때문에 문제가 발생한다.
  • 이 두 통계치는 기존 파티션 통계와 추가된 파티션 통계로 부터 정확한 테이블 레벨 통계를 구할 수 있다.


인덱스 통계 수집

  • 테이블 통계를 수집 하면서 cascade 옵션을 true로 설정하면 테이블에 속한 모든 인덱스 통계도 같이 수집된다.
  • 통계를 같이 수집 한다고 해서 빠린게 아니라 인덱스 마다 gather_index-stats 프로시저를 따로 수행 하는 것과 일량이 같다.
  • 테이블 통계 수집에서는 소트연산이 발생
  • 하지만 인덱스는 이미 정렬된 상태에서 소트 연산이 불필요하기 때문에 빠르다
  • 테이블을 통계만 샘플링 방식을 사용하고, 인덱스는 전수 검사하도록 각기 통계를 수집해 주는 것이 좋다.

  begin
   --테이블 통계는 estimate mode
   dbms_stats.gather_table_stats ('user', 'big_table', cascade=>false, estimate_percent=>10);
    
   --인덱스 통계는 compute mode
   dbms_stats.gather_index_stats ('user', 'big_table_pk', estimate_percent=>100);
   dbms_stats.gather_index_stats ('user', 'big_table_x1', estimate_percent=>100); 
  end;
/


캐싱된 커서 Invalidation

  • no_invalidate 옵션을 어떻게 지정하느냐에 따라 통계를 수집한 테이블과 관련된 SQL 커서의 무효화 시점이 달라진다.
  • false : 통계정보 변경 시 관련된 SQL 커서들이 즉시 무효화, 따라서 곧이어 첫 번째 수행하는 세션에 의해, 새로 갱신된 통계정보를 이용한 실행계획이 로드된다
  • true: 통계정보 변경 시관련된 SQL커서들을 무효화 하지 않는다. SQL 커서가 자동으로 Shared Pool에서 밀려 났다가 다시 로드될 때 비로서 새로 갱신된 통계 정볼르 사용한다.
  • dbms_stats.auto_invalidate:통계정보 변경 시 관련된 SQL 커서들을 한꺼번에 무효화하지 않고 정해진 시간 동안 조금씩 무효화한다. 무효화된 수많은 커서가 동시에 수행되면서 하드파싱에 의한 라이브러리 캐시 경합이 발생하는 현상을 방지하려고 10g에서 도입된 기능이다.
  • 중요한 것은 9i에서 false이던 기본 값이 10g에서 DBMS_STATS.AUTO_INVALIDATE로 바뀌었다.


자동 통계 수집

  • 10g 부터 기본적으로 매일 밤 10부터 다음날 아침 6시까지 모든 사용자 오브젝트에 대해 통계를 자동 수집하도록 job이 등록돼있다.
  • gather_stats_job에 의해 자동 수행되며 통계정보가 없거나 통계 테이블의 state_stats컬럼을 참조하여 통계정보 수집 후 DML이 많이 발생한 모든 오브젝트를 대상으로 한다.
  • gather_stats_job : 데이터베이스 생성시 자동 등록되며, Maintenance 윈도우 그룹에 의해 등록된 윈도우가 열릴 때마다 스케쥴러에 의해 수행된다


통계정보 갱신 대상 식별
  • 테이블 모니터링 기능
    • 9i는 nomonitorting이 기본값으로 필요한 테이블만 monitoring옵션을 지정한다 ( alter table emp monitoring; )
    • 10g는 모든 테이블 모니터링 한다,


  • DML발생량 모니터링 : statistic_level이 typical, all일 때 monitoring옵션이 지정된 테이블에서 발생하는 DML 발생량을 모니터링
  • 모니터링 대상 테이블이 10%이상 변경이 발생했을때 해당 테이블을 stale 상태로 변경 ( *_tab_statistic 뷰의 stale_stats = 'YES' )
  • stale 상태인 테이블들에 대해 통계정보 새로 수집 : gather_database_stats 또는 gather_schema_stats프로시저를 호출하면서 option인자에 'gather_stale' 또는 'gather_auto' 지정한다.
  • 11g에서는 stale상태로 바뀌는 임계치를 오브젝트별로 조정 가능하다.


  • 테이블 변경 사항은 Shared Pool에 모았다가 SMON이 주기적으로 데이터 딕셔너리에 반영하는 시점이후 *_tab_modification과 *_tab_statistic 뷰의 stale_stats컬럼이 변경된다.
  • dbms_stats.flush_database_monitoring_info 프로시저를 호출하면 현재 변경사항이 딕셔너리에 바로 반영된다.
  • gather_stats_job 등록상태 조회 : 457page
  • Maintenance 윈도우 그룹 등록상태 조회 : 458page


자동 통계 수집 기능 활용 가이드

  • 중대형급 이상 데이터베이스의 경우 10g에서 제공하는 자동 통계 수집 가능은 사용하지 않는것이 좋다.
  • Maintenance 윈도우 이내에 통계 수집이 완료되지 않은 경우에 시스템이 불안정한 상태에 빠질 수 있다.
  • 오브젝트별 전략을 세우고 짧은 시간 내에 정확하고 안정적인 통계정보를 수집할 수 있도록 별도의 스크립트를 준비한다.
  • 11g부터 Statistics Preference 기능을 이용하여 오브젝트별 통계정보 수집 선택사양을 설정한다.


Statistics Preference

  • gather_stats_jobs를 활성화한 상태에서 테이블 또는 스키마별로 통계 수집 방법을 따로 설정한다.
  • 시스템 여건과 테이블 특성에 맞는 통계수립 정책이 자동 통계 수집 기능에 반영되었다.
  • dbms_stats.set_table_prefs, dbms_stats.set_schema_prefs 프로시저를 사용하여 설정한다. ( 460 page )
  • perference 설정 내용 조회 : select * from dba_tab_stst_prefs;
"구루비 데이터베이스 스터디모임" 에서 2012년에 "오라클 성능 고도화 원리와 해법 II " 도서를 스터디하면서 정리한 내용 입니다.

- 강좌 URL : http://www.gurubee.net/lecture/3282

- 구루비 강좌는 개인의 학습용으로만 사용 할 수 있으며, 다른 웹 페이지에 게재할 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.~^^

- 구루비 강좌는 서비스 제공을 위한 목적이나, 학원 홍보, 수익을 얻기 위한 용도로 사용 할 수 없습니다.

댓글등록
SQL문을 포맷에 맞게(깔끔하게) 등록하려면 code() 버튼을 클릭하여 작성 하시면 됩니다.
로그인 사용자만 댓글을 작성 할 수 있습니다. 로그인, 회원가입