dbms_stats
패키지를 사용해서 수집
DBMS_STATS.SEED_COL_USAGE ( sqlset_name IN VARCHAR2, owner_name IN VARCHAR2, time_limit IN POSITIVE DEFAULT NULL);
SQL> exec dbms_stats.seed_col_usage(null,'SCOTT',1800);
* 컬럼 사용 통계 정보 수집
* 특정 기간 동안 현재 부하를 모니터링해서 생성
* 컬럼 그룹 통계를 생성할 수 있는 특정 컬럼을 제안
* 백그라운드 작업 수행
-- 위의 seed_col_usage를 수행 후, 아래 SQL 문장 수행
SQL> select mgr, job, count(*)
2 from emp
3 where job='CLERK' and mgr=7902 and hiredate < sysdate
4 group by mgr, job;
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 196K| 3840K| 4693 (1)| 00:00:01 |
| 1 | SORT GROUP BY NOSORT | | 196K| 3840K| 4693 (1)| 00:00:01 |
|* 2 | TABLE ACCESS FULL | COPY_EMP | 196K| 3840K| 4693 (1)| 00:00:01 |
----------------------------------------------------------------------------------------
SQL> select dbms_stats.report_col_usage('GHLEE','EMP') from dual;
###############################################################################
COLUMN USAGE REPORT FOR GHLEE.EMP
................................. --> 컬럼 조건 상태 확인
1. HIREDATE : RANGE
2. JOB : EQ
3. MGR : EQ
DBMS_STATS.REPORT_COL_USAGE('GHLEE','EMP')
--------------------------------------------------------------------------------
4. (JOB, MGR) : FILTER --> 컬럼 그룹 통계 제안
###############################################################################
SQL> SELECT DBMS_STATS.CREATE_EXTENDED_STATS('GHLEE', 'EMP') FROM DUAL
DBMS_STATS.CREATE_EXTENDED_STATS('GHLEE','EMP')
--------------------------------------------------------------------------------
###############################################################################
EXTENSIONS FOR GHLEE.EMP
........................
1. (JOB, MGR) : SYS_STULPA1A#B6YL4KQ59DQO3OADQ created
###############################################################################
-- 확장 통계 생성 후 통계 정보 재 수집
SQL> EXEC DBMS_STATS.GATHER_TABLE_STATS('GHLEE', 'EMP', CASCADE=>TRUE);
SQL> select column_name, num_distinct, histogram
2 from user_tab_col_statistics
3 where table_name = 'EMP'
4 order by 1;
COLUMN_NAME NUM_DISTINCT HISTOGRAM
------------------------------ ------------ ---------------
COMM 3 NONE
DEPTNO 3 NONE
EMPNO 14 NONE
ENAME 14 NONE
HIREDATE 13 FREQUENCY
JOB 5 FREQUENCY
MGR 6 FREQUENCY
SAL 12 NONE
SYS_STULPA1A#B6YL4KQ59DQO3OADQ 8 FREQUENCY
SQL> insert into emp
2 select rownum no, ename, job, mgr, hiredate, sal, comm, deptno
3 from (select * from emp),
4 (select rownum from dual connect by level <= 200000);
SQL> exec dbms_stats.gather_table_stats('GHLEE', 'EMP', CASCADE=>TRUE);
SQL> select column_name, num_distinct, histogram
2 from user_tab_col_statistics
3 where table_name = 'EMP'
4 order by 1;
COLUMN_NAME NUM_DISTINCT HISTOGRAM
------------------------------ ------------ ---------------
COMM 3 NONE
DEPTNO 3 NONE
EMPNO 2800014 NONE
ENAME 14 NONE
HIREDATE 13 FREQUENCY
JOB 5 FREQUENCY
MGR 6 FREQUENCY
SAL 12 NONE
SYS_STULPA1A#B6YL4KQ59DQO3OADQ 8 FREQUENCY
SQL> select mgr, job, count(*)
2 from emp
3 where job='CLERK' and mgr=7902 and hiredate < sysdate
4 group by mgr, job;
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 200K| 3906K| 4708 (1)| 00:00:01 |
| 1 | SORT GROUP BY NOSORT| | 200K| 3906K| 4708 (1)| 00:00:01 |
|* 2 | TABLE ACCESS FULL | EMP | 200K| 3906K| 4708 (1)| 00:00:01 |
-----------------------------------------------------------------------------
-- MGR과 JOB 컬럼 값의 선택도가 높지 않기 때문에 FULL TABLE SCAN을 이용
SQL> update emp
2 set mgr=empno;
SQL> exec dbms_stats.gather_table_stats('GHLEE', 'EMP', CASCADE=>TRUE);
SQL> select column_name, num_distinct, histogram
2 from user_tab_col_statistics
3 where table_name = 'EMP'
4 order by 1;
COLUMN_NAME NUM_DISTINCT HISTOGRAM
------------------------------ ------------ ---------------
COMM 3 NONE
DEPTNO 3 NONE
EMPNO 2800014 NONE
ENAME 14 NONE
HIREDATE 13 FREQUENCY
JOB 5 FREQUENCY
MGR 2800014 NONE
SAL 12 NONE
SYS_STULPA1A#B6YL4KQ59DQO3OADQ 2776064 NONE
SQL> select mgr, job, count(*)
2 from emp
3 where job='CLERK' and mgr=7902 and hiredate < sysdate
4 group by mgr, job;
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 22 | 4 (0)| 00:00:01 |
| 1 | SORT GROUP BY NOSORT | | 1 | 22 | 4 (0)| 00:00:01 |
|* 2 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 22 | 4 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | EMP_IDX1 | 1 | | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
job_queue_processes
파라미터를 정의해서 컨트롤 하는 동시성 레벨을 결정한다.JOB_QUEUE_PROCESSES
파라미터 값은 3 이상 이여야함
SQL> show parameter resource
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
resource_limit boolean TRUE
resource_manager_cpu_allocation integer 64
resource_manager_plan string
SQL> alter system set resource_manager_plan = 'DEFAULT_PLAN';
SQL> show parameter job_queue
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
job_queue_processes integer 1000
SQL> exec dbms_stats.set_global_prefs('CONCURRENT', 'ALL');
SQL> SELECT DBMS_STATS.GET_PREFS('CONCURRENT') FROM DUAL;
DBMS_STATS.GET_PREFS('CONCURRENT')
--------------------------------------------------------------------------------
ALL
-- 해당 스키마 각각의 오브젝트(테이블, 파티션, 인덱스, 기타)별로 Job을 생성한다. 이런 통계 작업들은 병렬로 각각의 작업을 수행 할 수 있다.
SQL> exec dbms_stats.gather_schema_stats('GHLEE', CASCADE => TRUE);
SQL> SELECT TARGET, TARGET_TYPE, JOB_NAME, TO_CHAR(START_TIME, 'DD-MON-YYYY HH24:MI:SS') DAY
2 FROM DBA_OPTSTAT_OPERATION_TASKS
3 WHERE STATUS = 'COMPLETED'
4 AND OPID = (SELECT MAX(ID)
5 FROM DBA_OPTSTAT_OPERATIONS
6 WHERE OPERATION = 'gather_schema_stats')
TARGET TARGET_TYPE JOB_NAME DAY
------------------------------------------------------------ -------------------- ---------- ------------------------------
GHLEE.RTE_BACKUP_INX.RTE_BACKUP_20151125 INDEX PARTITION 13-JAN-2017 10:49:35
GHLEE.RTE_BACKUP_INX.RTE_BACKUP_20151126 INDEX PARTITION 13-JAN-2017 10:49:35
GHLEE.RTE_BACKUP_INX.RTE_BACKUP_20151127 INDEX PARTITION 13-JAN-2017 10:49:35
GHLEE.RTE_BACKUP_INX.RTE_BACKUP_20151128 INDEX PARTITION 13-JAN-2017 10:49:35
GHLEE.RTE_BACKUP_INX.RTE_BACKUP_20151129 INDEX PARTITION 13-JAN-2017 10:49:35
GHLEE.RTE_BACKUP_INX.RTE_BACKUP_20151130 INDEX PARTITION 13-JAN-2017 10:49:35
GHLEE.RTE_BACKUP_INX.RTE_BACKUP_20151201 INDEX PARTITION 13-JAN-2017 10:49:35
GHLEE.RTE_BACKUP_INX.RTE_BACKUP_20151202 INDEX PARTITION 13-JAN-2017 10:49:35
GHLEE.RTE_BACKUP_INX.RTE_BACKUP_20151203 INDEX PARTITION 13-JAN-2017 10:49:35
GHLEE.RTE_BACKUP_INX.RTE_BACKUP_20151204 INDEX PARTITION 13-JAN-2017 10:49:35
JOB_QUEUE_PROCESS
파라미터의 디폴트 값이 1000이라는 사실이다. 만약 많은 개수의 저용량 디스크를 보유하고 있거나 500개 이상의 CPU 개수를 가진 머신이 아니라면 1000이라는 수치는 너무 높을 수 있다.
-- INCREMENTAL TRUE로 설정
SQL> EXEC DBMS_STATS.SET_TABLE_PREFS('GHLEE', 'RTE_BAK_20170113', 'INCREMENTAL', 'TRUE');
-- 일반 테이블에 INCREMENTAL 설정
SQL> EXEC DBMS_STATS.SET_TABLE_PREFS('GHLEE', 'RTE_BAK_20170113', 'INCREMENTAL_LEVEL', 'TABLE');
-- 통계 정보 수집
SQL> exec dbms_stats.gather_table_stats('GHLEE', 'RTE_BAK_20170113');
-- PARTITION TABLE에 INCREMENTAL TRUE 설정
SQL> exec dbms_stats.set_table_prefs('GHLEE', 'RTE_BACKUP', 'INCREMENTAL', 'TRUE');
-- 파티션 통계 정보 수집
SQL> exec dbms_stats.gather_table_stats('GHLEE', 'RTE_BACKUP','RTE_BACKUP_20160224', granularity => 'partition');
-- 파티션 exchange 수행
SQL> alter table CP_LG_RTE_BACKUP exchange partition CP_LG_RTE_BACKUP_20160224 with table RTE_BAK_20170113;
-- 비 파티션 테이블의 시놉시스는 파티션 테이블로 이동되고 글로벌 통계도 업데이트가 되므로, Exhcnage 한 후에 GRADE 파티션 테이블에 대해 통계 정보를 수집할 필요성이 없어진다.
DBMS_STATS
를 실행해야 한다.USE_STALE_PERCENT
: (USE_LOCKED_STATS
와 같이 사용 될 수 있음) 오라클이 파티션이나 서브 파티션이 Stale 여부를 결정하는 것은 디폴트 STALE_PERCENT 설정을 사용한다.USE_LOCKED_STATS
: (USE_STALE_PERCENT
와 같이 사용될 수 있음) 만약 특정 파티션이나 서브 파티션이 LOCKED 되어 있는 상태라면 DML 작업으로 변경 여부에 상관없이 Stale 상태를 고려하지 않음Extended 파티션 Syntax
INSERT INTO mypart_tab PARTITION part_1 VALUES (1,'Some Text');
DBA_TAB_COL_STATISTICS
뷰를 통해 확인 가능하다.이 뷰에서 BULK LOAD 동안 컬럼 통계 정보가 수집된다면 이 뷰의NOTES 컬럼에 STATS_ON_LOAD
라는 값으로 표시된다.dbms_stats.gather_table_stats
를 GATHER_AUTO로 수집하는 경우에는 누락되거나 Stale 된 통계만을 수집한다. 이런 경우에는 인덱스에 대한 통계가 생성되므로 테이블이나 컬럼 통계 정보를 수집할 필요가 없다.GATHER AUTO
exec dbms_stats.gather_schema_stats(ownname => 'SCOTT', options => 'GATHER AUTO');
GATHER AUTO
SQL> CREATE TABLE MY_OBJECTS_STATS AS SELECT /*+ GATHER_OPTIMIZER_STATISTICS */ * FROM MY_OBJECTS; ==> Default behavior ;hint is of no help here.
SQL> CREATE TABLE MY_OBJECTS_NOSTATS_HINT AS SELECT /*+ NO_GATHER_OPTIMIZER_STATISTICS */ * FROM MY_OBJECTS; ==> Hint instructing Oracle not to collect Stats
SYS가 소유한 오브젝트에 대해서는 작동하지 않는다.
Nested table, IOT, External, on commit delete rows로 정의된 GTT에서도 작동하지 않는다.
테이블 Prference중에 Publish가 FALSE로 설정되었거나 Virtual Column을 가진 테이블에서도 작동하지 않는다.
[참고자료]
11g에서는 통계정보의 'Gathering'과 'Publish'를 분리할 수 있게 되어, 검증된 통계정보만 'Publish'되도록 하여 어플리케이션 성능을 보장받을 수 있음.
SELECT DBMS_STATS.GET_PREFS('PUBLISH') PUBLISH
FROM DUAL;
– TRUE : 통계정보가 수집되자마자 바로 Publish
– FALSE : 수집된 통계정보는 Publish 되지 않고 Pending
GLOBAL_TEMP_TABLE_STATS
를 SESSION으로 설정하여 사용 가능
SQL> select dbms_stats.report_gather_schema_stats('GHLEE', DETAIL_LEVEL=>'TYPICAL')
from dual;
dbms_stats.report_stats_operations
: 시작과 종료 시간 내에 발생한 모든 통계 작업에 대해서 보여주는 리포트를 생성dbms_stats.report_single_Stats_operations
: 특정 통계 수집 작업에 대한 리포트를 생성- 강좌 URL : http://www.gurubee.net/lecture/4268
- 구루비 강좌는 개인의 학습용으로만 사용 할 수 있으며, 다른 웹 페이지에 게재할 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.~^^
- 구루비 강좌는 서비스 제공을 위한 목적이나, 학원 홍보, 수익을 얻기 위한 용도로 사용 할 수 없습니다.