본문 바로가기

카테고리 없음

ORACLE 11g Dictionary View and Stats for Partitioned table v1.0

ORACLE 11g Dictionary Views and Stats for Partitioned table v1.0

 

Date

Ver

Etc.

2013.01.03

1.0

 

 

 

 

 

 

 

 

 

1.    Dictionary Views and Stats for Partitioned table

이 문서에서는 다음과 같은 내용을 다루려 한다.

 

l  Composite Partitioned Table ( list-range ) 과 인덱스를 생성

l  시스템 레벨에서의 통계정보 수집 정책을 확인

l  테이블 레벨에서 통계정보 수집 정책을 확인

l  파티션 테이블 구조 및 통계정보 조회하기

l  파티션 테이블과 연관된 인덱스 및 통계정보 조회하기

l  배치 및 신규 파티션 추가시 조치사항

 

여기서는 Global, Local Index, prefix, non-prefix 인덱스에 대한 내용은 다루지 않겠다.

 

2.    Composite Partitioned Table ( list-range ) 과 인덱스를 생성

create table range_list_tab (

id number(8),

rand1 number(2),

rand2 number(20),

dttm varchar2(21))

partition by range(id)

subpartition by list (rand1)

(partition range_tab_100000 values less than (100000)

(SUBPARTITION range_tab_100000_1 values (1),

SUBPARTITION range_tab_100000_2 values (2),

SUBPARTITION range_tab_100000_3 values (3)),

partition range_tab_200000 values less than (200000)

(SUBPARTITION range_tab_200000_1 values (1),

SUBPARTITION range_tab_200000_2 values (2),

SUBPARTITION range_tab_200000_3 values (3)))

enable row movement;

 

list-range 파티션 테이블로 파티션 키는 id + rand1 이다. id Unique 값으로 range partition 의 키컬럼이다. rand1 은 추후 insert 에서 1 ~ 3 의 값을 랜덤으로 입력 예정이며 이는 list partition 의 키컬럼이 된다.

rand2 컬럼은 1 ~ 99999999999999999999 의 값을 랜덤하게 입력받을 예정이다.

 

declare

begin

 for i in 1..199999 loop

  insert into range_list_tab values

  (i, trunc(dbms_random.value(1,4)), trunc(dbms_random.value(1,99999999999999999999)), to_char(systimestamp, 'YYYY-MM-DD HH24:MI:SS'));

end loop;

end;

/

 

테스트 데이터를 for loop 를 이용해 입력한다.

 

create unique index range_list_tab_pk on range_list_tab(id, rand1) local;

 

alter table range_list_tab

add constraint range_list_tab_pk primary key (id,rand1);

 

create index range_list_tab_ix1 on range_list_tab(rand2, rand1) local;

 

create index range_list_tab_ix2 on range_list_tab(id, rand2) local;

 

create index range_list_tab_ix3 on range_list_tab(rand2) local;

 

create index range_list_tab_ix4 on range_list_tab(dttm);

 

id, rand1 파티션 키 컬럼에 대해 primary key index 를 생성하고, 추가로 인덱스를 생성했다.

 

3.    시스템 레벨에서의 통계정보 수집 정책을 확인

11g 에서는 자동화된 작업으로 oracle 이 통계정보를 수집한다.

이 내용에 대해서는 dba_autotask_operation 을 조회함으로써 확인 가능하다.

 

SQL> SELECT client_name, status FROM dba_autotask_operation;

 

CLIENT_NAME                         STATUS

----------------------------------- --------

auto optimizer stats collection     ENABLED

auto space advisor                  ENABLED

sql tuning advisor                  ENABLED

 

위의 auto optimizer stats collection 이 위에 언급한 통계정보 수집 작업이며, 이의 활성화 / 비활성화는 다음과 같이 수행 가능하다.

 

BEGIN

  DBMS_AUTO_TASK_ADMIN.ENABLE(

    client_name => 'auto optimizer stats collection',

    operation => NULL,

    window_name => NULL);

END;

/

 

BEGIN

  DBMS_AUTO_TASK_ADMIN.DISABLE(

    client_name => 'auto optimizer stats collection',

    operation => NULL,

    window_name => NULL);

END;

/

 

흔히 통계정보 수집을 수동으로 하는 경우 DBMS_STATS.GATHER_TABLE_STATS 를 사용하게 되는데 별도 셋팅하지 않는 경우 default 값을 가져가게 된다. default 값은 변수로 사전 셋팅이 가능한 것들로 시스템 레벨의 정책으로 볼 수도 있다.

 

DBMS_STATS.GATHER_TABLE_STATS (

   ownname          VARCHAR2,

   tabname          VARCHAR2,

   partname         VARCHAR2 DEFAULT NULL,

   estimate_percent NUMBER   DEFAULT to_estimate_percent_type(get_param('ESTIMATE_PERCENT')),

   block_sample     BOOLEAN  DEFAULT FALSE,

   method_opt       VARCHAR2 DEFAULT get_param('METHOD_OPT'),

   degree           NUMBER   DEFAULT to_degree_type(get_param('DEGREE')),

   granularity      VARCHAR2 DEFAULT GET_PARAM('GRANULARITY'),

   cascade          BOOLEAN  DEFAULT to_cascade_type(get_param('CASCADE')),

   stattab          VARCHAR2 DEFAULT NULL,

   statid           VARCHAR2 DEFAULT NULL,

   statown          VARCHAR2 DEFAULT NULL,

   no_invalidate    BOOLEAN  DEFAULT  to_no_invalidate_type (get_param('NO_INVALIDATE')),

   force            BOOLEAN DEFAULT FALSE);

 

dbms_stats 의 다른 메소드는 살펴보지 않겠다.

 

위 내용중에 Bold 로 표시한 변수의 내용을 확인하면 다음과 같다. (ORACLE default 값이다.)

 

ESTIMATE_PERCENT => DBMS_STATS.AUTO_SAMPLE_SIZE // ORACLE 이 샘플링 % 를 자동 결정

METHOD_OPT => FOR ALL COLUMNS SIZE AUTO // ORACLE Column 통계수집과 히스토그램 생성을 컨트롤

DEGREE => NULL

GRANULARITY => AUTO // 해당 파라미터는 파티션 테이블에 대해 적용되는 옵션이다.

AUTO 는 통계정보 수집단위를 ORACLE 이 판단한다는 의미이다.

-      ALL : subpartition + partition + global statistics

-      AUTO

-      DEFAULT : OBSOLETE OPTION

-      GLOBAL : global statistics

-      GLOBAL AND PARTITION : partition + global statistics

-      PARTITION : partition statistics

-      SUBPARTITION : subpartition-level statistics

-      PUBLISH : /*+ 11.1 이전에는 통계정보 수집이 완료되면 dictionary 에 바로 적용이 되었다. 하지만 이후 버전부터는 수집된 통계정보를 바로 적용하는게 아니라 컨트롤이 가능하다 (pending) */

-      INCREMENTAL : Incremental Global Stats 를 사용한다. 다음의 환경이 구성되어 있어야 한다.

n  파티션 테이블에 대한 INCREMENTAL 값이 TRUE

n  파티션 테이블에 대한 PUBLISH 값이 TRUE

n  통계 수집 시 ESTIMATE_PERCENT = AUTO_SAMPLE_SIZE, GRANULARITY = AUTO 를 사용

CASCADE => DBMS_STATS.AUTO_CASCADE // Index 를 통계수집 과정의 하나로 진행할지 말지를 ORACLE 이 판단.

NO_INVALIDATE => DBMS_STATS.AUTO_INVALIDATE // 통계정보와 관련있는 커서를 언제 INVALIDATE 할 지 ORACLE 이 판단한다.

 

4.    테이블 레벨에서 통계정보 수집 정책을 확인

위에 언급한 DBMS_STATS.GATHER_TABLE_STATS 에서 DEFAULT 값으로 DBMS_STATS 의 변수를 사용하는 항목들이 있었고, 이러한 항목들은 스키마 + 테이블 레벨로 셋팅이 가능하다.

 

/*+ 통계정보 레벨에 따른 의존성에 대해서는 여기선 언급하지 않겠다. ex) DB, SCHEMA, TABLE */

 

테이블 레벨로 셋팅 된 정보는 다음의 쿼리로 확인할 수 있다.

/*+ 다른 확인 방법이 있는지 추가 조사할 것 */

 

select 'AUTOSTATS_TARGET=>'||dbms_stats.get_prefs('AUTOSTATS_TARGET','TEST','RANGE_LIST_TAB') contents from dual union all

select 'CASCADE=>'||dbms_stats.get_prefs('CASCADE','TEST','RANGE_LIST_TAB') contents from dual union all

select 'DEGREE=>'||dbms_stats.get_prefs('DEGREE','TEST','RANGE_LIST_TAB') contents from dual union all

select 'ESTIMATE_PERCENT=>'||dbms_stats.get_prefs('ESTIMATE_PERCENT','TEST','RANGE_LIST_TAB') contents from dual union all

select 'METHOD_OPT=>'||dbms_stats.get_prefs('METHOD_OPT','TEST','RANGE_LIST_TAB') contents from dual union all

select 'NO_INVALIDATE=>'||dbms_stats.get_prefs('NO_INVALIDATE','TEST','RANGE_LIST_TAB') contents from dual union all

select 'GRANULARITY=>'||dbms_stats.get_prefs('GRANULARITY','TEST','RANGE_LIST_TAB') contents from dual union all

select 'PUBLISH=>'||dbms_stats.get_prefs('PUBLISH','TEST','RANGE_LIST_TAB') contents from dual union all

select 'INCREMENTAL=>'||dbms_stats.get_prefs('INCREMENTAL','TEST','RANGE_LIST_TAB') contents from dual union all

select 'STALE PERCENT=>'||dbms_stats.get_prefs('STALE_PERCENT','TEST','RANGE_LIST_TAB') contents from dual;

 

위 쿼리의 결과로 다음과 같은 내용을 확인할 수 있었다.

 

CONTENTS

--------------------------------------------------------------------------------

AUTOSTATS_TARGET=>AUTO

CASCADE=>DBMS_STATS.AUTO_CASCADE

DEGREE=>NULL

ESTIMATE_PERCENT=>DBMS_STATS.AUTO_SAMPLE_SIZE

METHOD_OPT=>FOR ALL COLUMNS SIZE AUTO

NO_INVALIDATE=>DBMS_STATS.AUTO_INVALIDATE

GRANULARITY=>AUTO

PUBLISH=>TRUE

INCREMENTAL=>FALSE

STALE PERCENT=>10

 

5.    파티션 테이블 구조 및 통계정보 조회하기

파티션 테이블 구조를 확인하기 위해 다음과 같은 질문을 할 수 있다.

 

l  파티션 키 컬럼은 어떻게 되는가?

l  테이블에 구성된 인덱스는 어떤게 있는가?

 

위 질문과 연관해 다음의 view 를 사용할 수 있다.

dba_part_key_columns

dba_subpart_key_columns

dba_indexes

dba_ind_partition

 

SQL> select name, object_type, column_name

  2  from dba_part_key_columns

  3  where name = 'RANGE_LIST_TAB';

 

NAME                 OBJECT_TYPE     COLUMN_NAME

-------------------- --------------- --------------------

RANGE_LIST_TAB       TABLE           ID

 

위와같이 partition key 에 대해 조회가능하다.

 

SQL> select name, object_type, column_name

  2  from dba_subpart_key_columns

  3  where name = 'RANGE_LIST_TAB';

 

NAME                 OBJECT_TYPE     COLUMN_NAME

-------------------- --------------- --------------------

RANGE_LIST_TAB       TABLE           RAND1

 

위와같이 subpartition key 에 대해 조회가능하다.

 

composite 의 경우 subpartition key 에 대한 정보가 있지만 기본 partitioned table 은 그렇지 않다.

 

6.    파티션 테이블과 연관된 인덱스 및 통계정보 조회하기

테이블에 구성된 인덱스에 대해서는 다음과 같이 조회가능하다.

 

SQL> select index_name, partitioned from dba_indexes

  2  where table_name = 'RANGE_LIST_TAB';

 

INDEX_NAME                     PAR

------------------------------ ---

RANGE_LIST_TAB_PK              YES

RANGE_LIST_TAB_IX1             YES

RANGE_LIST_TAB_IX2             YES

RANGE_LIST_TAB_IX3             YES

RANGE_LIST_TAB_IX4             NO

 

위의 index 를 구성하는 컬럼에 대해서는 dba_ind_columns 에서 조회할 수 있다.

자세한 내용은 생략하겠다.

 

통계정보는 다음과 같은 dictionary view 를 사용하여 조회할 수 있다.

 

l  DBA_IND_STATISTICS

l  DBA_TAB_STATISTICS

 

7.    배치 및 신규 파티션 추가시 조치사항

파티션 세그먼트가 신규로 추가되거나, 기존의 세그먼트에 대량의 배치가 일어난 경우 통계정보 재수집이 필요하다.

 

다음의 쿼리로 결과값의 변화추이를 확인해 보겠다.

 

select table_name, partition_name, subpartition_name, num_rows, to_char(last_analyzed, 'hh24:mi:ss') time, stale_stats

from dba_tab_statistics

where table_name = 'RANGE_LIST_TAB';

 

<신규 파티션 세그먼트 추가시>

select index_name, partitioned from dba_indexes

where table_name = 'RANGE_LIST_TAB';

 

위와 같이 파티션 추가 이후 다음과 같이 결과가 나온다.

TABLE_NAME                     PARTITION_NAME                 SUBPARTITION_NAME                NUM_ROWS TIME     STA

------------------------------ ------------------------------ ------------------------------ ---------- -------- ---

RANGE_LIST_TAB                                                                                   199999 06:00:07 NO

RANGE_LIST_TAB                 RANGE_TAB_300000

RANGE_LIST_TAB                 RANGE_TAB_100000                                                   99999 06:00:20 NO

RANGE_LIST_TAB                 RANGE_TAB_200000                                                  100000 06:00:21 NO

RANGE_LIST_TAB                 RANGE_TAB_300000               RANGE_TAB_300000_1

RANGE_LIST_TAB                 RANGE_TAB_300000               RANGE_TAB_300000_2

RANGE_LIST_TAB                 RANGE_TAB_300000               RANGE_TAB_300000_3

RANGE_LIST_TAB                 RANGE_TAB_100000               RANGE_TAB_100000_1                  33428 06:00:23 NO

 

~~~~~ 중략 ~~~~~~

 

통계정보가 비어있는 테이블은 새로 추가된 RANGE_TAB_300000 파티션과 연관된 정보들이다.

subpartition 에 대한 정보 3개와 partition 에 대한 정보 1개가 비어있다.

 

이 정보는 다른 파티션 세그먼트로부터 복제하는 것도 가능하다.

 

흔히 range partitioned table 을 생각할 때 보통 이력으로 관리하는 경우가 많고 시간 순서상 이전 이력의 파티션 세그먼트와 데이터의 통계정보가 유사할 가능성이 크다. 이런 경우 위와같이 통계정보가 누락된 대로 두는게 아니라 다른 파티션 세그먼트로부터 복제할 수 있다.

 

이를 도와주는 패키지는 다음과 같다.

 

DBMS_STATS.COPY_TABLE_STATS (

   ownname          VARCHAR2,

   tabname          VARCHAR2,

   srcpartname      VARCHAR2,

   dstpartname      VARCHAR2,

   scale_factor     VARCHAR2 DEFAULT 1,

   force            BOOLEAN DEFAULT FALSE);

 

위 프로시저를 통해 소스 파티션 세그먼트로부터 타겟 파티션 세그먼트로 통계정보 복제가 가능하다.

RANGE_TAB_200000 로부터 RANGE_TAB_300000 으로 복제한다 했을 때 실제 사용은 다음과 같다.

 

SQL> exec DBMS_STATS.COPY_TABLE_STATS(ownname=>'TEST',tabname=>'RANGE_TAB',srcpartname=>'RAN                                                      GE_TAB_200000',dstpartname=>'RANGE_TAB_300000');

 

PL/SQL procedure successfully completed.

 

위 결과로 앞서 봤던 통계정보에 대한 결과에 공백이 사라졌다. (이를 테면 NUM_ROWS 부분)

하지만 신규로 추가된 테이블에 대한 rows 0으로 나온다. 통계정보를 복제했음에도 불구하고 NUM_ROWS 0 건으로 나오는 걸 보아 오라클은 해당 파티션 세그먼트에 데이터가 입력된 적이 없다는 걸 아는 것으로 추정된다.

 

TABLE_NAME                     PARTITION_NAME                 SUBPARTITION_NAME                NUM_ROWS TIME     STA

------------------------------ ------------------------------ ------------------------------ ---------- -------- ---

RANGE_LIST_TAB                                                                                   199999 06:00:07 NO

RANGE_LIST_TAB                 RANGE_TAB_300000                                                       0 22:03:02 NO

RANGE_LIST_TAB                 RANGE_TAB_100000                                                   99999 06:00:20 NO

RANGE_LIST_TAB                 RANGE_TAB_200000                                                  100000 06:00:21 NO

RANGE_LIST_TAB                 RANGE_TAB_300000               RANGE_TAB_300000_1                      0 22:03:02 NO

RANGE_LIST_TAB                 RANGE_TAB_300000               RANGE_TAB_300000_2                      0 22:03:02 NO

RANGE_LIST_TAB                 RANGE_TAB_300000               RANGE_TAB_300000_3                      0 22:03:03 NO

RANGE_LIST_TAB                 RANGE_TAB_100000               RANGE_TAB_100000_1                  33428 06:00:23 NO

RANGE_LIST_TAB                 RANGE_TAB_100000               RANGE_TAB_100000_2                  33209 06:00:23 NO

RANGE_LIST_TAB                 RANGE_TAB_100000               RANGE_TAB_100000_3                  33362 06:00:23 NO

RANGE_LIST_TAB                 RANGE_TAB_200000               RANGE_TAB_200000_1                  33218 06:00:24 NO

RANGE_LIST_TAB                 RANGE_TAB_200000               RANGE_TAB_200000_2                  33260 06:00:24 NO

RANGE_LIST_TAB                 RANGE_TAB_200000               RANGE_TAB_200000_3                  33522 06:00:24 NO

 

어느정도 rows 가 들어가야 값이 온전하게 복제되는가 확인을 위해 다음을 수행 후 복제를 다시 수행해 보았다. 프로시저의 내용은 RANGE_TAB_300000 파티션 세그먼트에 데이터를 넣어준다.

 

declare

begin

 for i in 200000..299999 loop

  insert into range_list_tab values

  (i, trunc(dbms_random.value(1,4)), trunc(dbms_random.value(1,99999999999999999999)), to_char(systimestamp, 'YYYY-MM-DD HH24:MI:SS'));

end loop;

end;

/

 

수행 이후 통계정보를 조회했을 때 아까와 마찬가지로 신규 생성 된 파티션에 대해 통계정보가 공백인 걸 확인할 수 있었다.

 

exec DBMS_STATS.COPY_TABLE_STATS(ownname=>'TEST',tabname=>'RANGE_LIST_TAB',srcpartname=>'RANGE_TAB_200000',dstpartname=>'RANGE_TAB_300000');

 

PARTITION => PARTITION 으로 통계정보를 복제 후 통계정보를 조회했을 때 아까와 다른점이 있다.

 

RANGE_LIST_TAB RANGE_TAB_300000 파티션에 대해 통계정보가 갱신되었다.

반면 subpartition 정보는 아직도 비어있다.

 

이번에는 subpartition 단위로 RANGE_TAB_200000_1 에서 RANGE_TAB_300000_1 로 복제를 해 보았다.

 

RANGE_LIST_TAB                 RANGE_TAB_300000               RANGE_TAB_300000_1                  33218 06:00:24 NO

RANGE_LIST_TAB                 RANGE_TAB_200000               RANGE_TAB_200000_1                  33218 06:00:24 NO

 

num_rows last_analyzed 정보가 복제되어 변경됨을 확인하였다.

정말 이것이 복제된 값이 확실한가 RANGE_TAB_300000 에 대해 통계정보 수집을 해 보았다.

 

DBMS_STATS.GATHER_TABLE_STATS ('TEST','RANGE_LIST_TAB','RANGE_TAB_300000',100,FALSE);

 

TABLE_NAME                     PARTITION_NAME                 SUBPARTITION_NAME                NUM_ROWS TIME     STA

------------------------------ ------------------------------ ------------------------------ ---------- -------- ---

RANGE_LIST_TAB                                                                                   299999 23:32:46 NO

RANGE_LIST_TAB                 RANGE_TAB_300000                                                  100000 23:32:45 NO

RANGE_LIST_TAB                 RANGE_TAB_300000               RANGE_TAB_300000_1                  33229 23:32:44 NO

RANGE_LIST_TAB                 RANGE_TAB_200000               RANGE_TAB_200000_1                  33218 06:00:24 NO

 

RANGE_TAB_200000_1 RANGE_TAB_300000_1 num_rows time 정보가 달라진걸 확인할 수 있었다.

 

한가지 특이점으로는 subpartition 에 대해 통계정보를 수집한 결과가 subpartition 을 포함하는 partition 의 통계정보, partition 을 포함하는 partitioned table 의 통계정보가 연쇄적으로 영향 받는 다는 것이다.

 

전체에서 부분이 변경되면 당연히도 전체에 영향이 가는 걸 생각하면 당연한 수순이기도 하다.

 

8.    References

A.     DBMS_STATS | http://docs.oracle.com/cd/E11882_01/appdev.112/e25788/d_stats.htm#BEIBJJHC