database.sarang.net
UserID
Passwd
Database
DBMS
MySQL
PostgreSQL
Firebird
ㆍOracle
Informix
Sybase
MS-SQL
DB2
Cache
CUBRID
LDAP
ALTIBASE
Tibero
DB 문서들
스터디
Community
공지사항
자유게시판
구인|구직
DSN 갤러리
도움주신분들
Admin
운영게시판
최근게시물
Oracle Q&A 41937 게시물 읽기
No. 41937
병렬처리(Parallel Processing) 관련 사항[병렬 Parallel 처리]
작성자
조장복(cjb1609)
작성일
2023-03-21 10:51
조회수
8,659

[병렬 Parallen 처리]

 

large_pool_size 이 0 이므로, Parallel 로 처리할수 없고, Parallel 로 처리하더라도 *.parallel_max_servers=16 으로 설정되어
                                                 SQL에서 Parallel을 사용 하도록 지정하였더라도, 실제는 Parallel로 처리되지 않는다
         parallel_min_servers 을 cpu_count 의 수만큼 지정하면 될것 같다
   -> ps -ef | grep p00 으로 parallel 오라클프로세스가 몇개 있는지 확인이 필요함
   -> PARALLEL QUERY PROCESSING이 사용되어질 수 있는 SQL 문장의 종류
 
      (1) SELECT 문장
      (2) UPDATE, INSERT, DELETE 문 내의 subquery
      (3) CREATE TABLE ... AS SELECT 문, CREATE TABLE ... AS SELECT or PARALLEL INSERT AS SELECT
          CREATE TABLE hr.admin_emp_dept
           PARALLEL 4                                  <- 여기에서 4를 지정하지 않으면 default 값이 적용 된다
          AS SELECT * FROM hr.employees
          WHERE department_id = 10;
      (4) CREATE INDEX 문
      (5) alter table owner.SEGMENT_NAME move tablespace TS_IMSTEMP_05 compress  nologging parallel 4;
          alter index owner.index_name rebuild  nologging parallel 4;
          위와 같은 문장 내에 최소한 하나의 full table scan operation이 포함되어야 query가 parallelize된다
   -> Limitation on the Degree of Parallelism : SQL> desc user_tab_partitions
      If you are performing parallel UPDATE, MERGE, or DELETE operations,
      the DOP is equal to or less than the number of partitions in the table.

   ->  확인 결과
      parallel_max_servers 에 의해서 parallel 오라클프로세스가 기동되고, create/alter table이나, parallel hint에 의해서 parallel 처리된다
      CPU와 disk controller(I/O bound 작업의 경우는 disk drive 갯수) 에 따라서 DEGREE의 결정
   -> OS에서 구동되어 있는 parallel query process를 확인할 수 있다. init<ORACLE_SID>.ora file 내(parallel_min_servers, parallel_max_servers)
      ps -ef | grep p00
      oracle 2512 1 0.0 16:03:11 ?? 0:12.09 ora_p001_ORA7
      oracle 29162 1 0.0 16:03:11 ?? 0:30.16 ora_p000_ORA7
   -> alter system set LARGE_POOL_SIZE='32M' SCOPE=spfile; alter system set LARGE_POOL_SIZE=32M SCOPE=spfile;  (Dynamic 파라미터)
      - Large pool (LARGE_POOL_SIZE) (기본값 : 0 bytes) -- large pool의 크기로
        large pool은 shared server의 session memory, message buffer에 대한 병렬 처리 등에 사용
        되는 시스템 영역이며, disk I/O buffer의 backup 및 restore 처리에 의해 사용되기도 한다
      - (참고: LARGE_POOL_SIZE, JAVA_POOL_SIZE 는 Oracle 9.0.1에서는 값을 동적으로 바꿀 수 없으며, 후속 버젼에서 적용될 예정이다)
     --> *.large_pool_size=100000000 (*.shared_pool_size=251658240 의 1/3 정도 할당)
   -> *.pga_aggregate_target=2671771648      (workarea_size_policy                 string      AUTO)
       - workarea_size_policy 의 default는 pga_aggregate_target 이 설정되어 있으면 AUTO, pga_aggregate_target 이 설정되어 있지 않으면 MANUAL이다
   -> PARALLEL_MAX_SERVERS  5 (default 값)  CPU x 10 (Default if PARALLEL_AUTOMATIC_TUNING = true)
     -> If you manually set the PARALLEL_MAX_SERVERS parameter, set it to 16 times the number of CPUs.
        -> PARALLEL_MAX_SERVERS = the number of CPUs * 16    (메뉴얼 : Data Warehousing Guide)
   -> Note: When PARALLEL_AUTOMATIC_TUNING is set to true,
            Oracle allocates parallel execution buffers from the large pool.
            When this parameter is false, Oracle allocates parallel execution buffers from the shared pool.
     -> parallel_automatic_tuning  FALSE 이므로, 여기에서는 LARGE_POOL_SIZE 을 지정할 필요 없다
   -> parallel_execution_message_size = 4096
      The recommended value for PARALLEL_EXECUTION_MESSAGE_SIZE is 4 KB.
      If PARALLEL_AUTOMATIC_TUNING is true, the default is 4 KB.
      If PARALLEL_AUTOMATIC_TUNING is false, the default is slightly greater than 2 KB.
   -> Default Degree of Parallelism(DOP) :
      Modify the default DOP by changing the value for the PARALLEL_THREADS_PER_CPU parameter.
   -> Parallel DML Tip 2: Direct-Path INSERT
      the APPEND hint is optional.
      You should use append mode to increase the speed of INSERT operations,
      but not when space utilization needs to be optimized. You can use NOAPPEND to override append mode.

   -> DML operations are considered for parallelization only if the session is in a PARALLEL DML enabled mode.
      (Use ALTER SESSION ENABLE PARALLEL DML to enter this mode.) SQL> grant alter session to scott; 권한이 부여되었습니다.
      SQL> conn scott/scott
      SQL> ALTER SESSION ENABLE PARALLEL DML;
      세션이 변경되었습니다.
      The mode does not affect parallelization of queries or of the query portions of a DML statement

   -> Parallel DML : Parallelizing INSERT ... SELECT
      INSERT /*+ PARALLEL(EMP) */ INTO employees
      SELECT /*+ PARALLEL(ACME_EMP) */ *
      FROM ACME_EMP;
   -> Parallel DML : Parallelizing UPDATE and DELETE
      UPDATE /*+ PARALLEL(EMP) */ employees
      SET SAL=SAL * 1.1
      WHERE JOB='CLERK' AND DEPTNO IN
      (SELECT DEPTNO FROM DEPT WHERE LOCATION='DALLAS');

      DELETE /*+ PARALLEL(PRODUCTS) */ FROM PRODUCTS
      WHERE PRODUCT_CATEGORY ='GROCERY';

   -> EXECUTE DBMS_STATS.GATHER_TABLE_STATS ('SH','JOIN_SALES_TIME_PRODUCT_MV',
                                             estimate_percent=>20,block_sample=>TRUE,cascade=>TRUE);
   -> For all newly created objects without statistics, on schema level:
      EXECUTE DBMS_STATS.GATHER_SCHEMA_STATS('SH', options => 'GATHER EMPTY',
                                             estimate_percent=>20, block_sample=>TRUE, cascade=>TRUE);
   -> 통계정보 갱신/For all newly created objects without statistics, on schema level:   
      EXECUTE DBMS_STATS.GATHER_SCHEMA_STATS('SH',
                                             estimate_percent=>20, block_sample=>TRUE, cascade=>TRUE);

   -> PARALLEL_MAX_SERVERS
      If you set this parameter too low, some queries may not have a parallel execution
      process available to them during query processing. If you set it too high, memory
      resource shortages may occur during peak periods, which can degrade performance.

   -> Parallel Execution Hints
      PARALLEL and NOPARALLEL, PARALLEL_INDEX, NOPARALLEL_INDEX 

  -> The APPEND hint : The APPEND hint lets you enable direct-path INSERT if your database is running in serial mode
     Conventional INSERT is the default in serial mode, and direct-path INSERT is the default in parallel mode.
     In direct-path INSERT, data is appended to the end of the table, rather than using existing space currently
     allocated to the table. As a result, direct-path INSERT can be considerably faster than conventional INSERT.

  -> Direct-Load Insert 방법 :  insert into emp select * from t_emp;  <- serial mode
     - undo entry를 생성하지 않으므로 기본 insert보다 속도가 빠르다
     - 명령문 직후에는 반드시 commit이나 rollback이 필요하다
     - SGA의 buffer cache를 거치지 않고 직접 Oracle data를 구성하고 입력하는 방법이다

  -> Direct-Load Insert의 사용방법 *************************************************
     - Serial Direct-Load Insert는 APPEND hint를 통해 사용할 수 있다.
     - Parallel Direct-Load Insert는 APPEND 없이 PARALLEL hint만으로도 사용할 수 있다.
       즉 Insert 시에 PARALLEL을 지정하면 무조건 Direct-Load로 작동하게 된다
     - Direct-Load Insert는 명령문 직후에는 반드시 commit이나 rollback이 필요하다
    1) APPEND hint의 사용
       SQL> insert /*+ APPEND */ into emp select * from t_emp;
       SQL> commit;
       (direct insert후에 바로 select를 하기 전에 먼저 commit;을 해야 data를 확인할 수 있다.)
    2) PARALLEL hint의 사용
       SQL> alter session enable parallel dml;
       SQL> insert /*+ PARALLEL(emp,10) */ into emp
       select /*+ PARALLEL(t_emp,10) */ * from t_emp;
       SQL> commit;
       위와 같이 Direct-Load Insert는 대량의 데이타를 한꺼번에 입력하는 경우에 사용하는 것이 좋으므로 일반 insert into .. values
       구문에서의 사용은 지양된다

  -> Parallel Direct-Load Insert into a nonpartitioned table
     각 parallel server process는 새로운 temporary segment를 할당하고 데이타를 입력한 후에 commit이 실행되면 parallel
     coordinator가 모든 temporary segment를 기존의 segment와 합친다
     . Direct-Load 의 APPEND 는 HWM 다음에 저장하므로, 즉 빈공간을 찾지 않으므로 속도가 빠르다
     SQL> insert /*+ APPEND */ into emp select * from t_emp;        ****>> temp 테이블이 아니면 parallel 보다 append 사용 권장
     SQL> commit;

  -> Parallel Direct-Load Insert into a partitioned table
     각 partition마다 하나의 parallel server process를 할당받아서 hwm다음에 data를 입력하고 commmit이 일어나면 hwm이 바뀌게된다
     SQL> alter session enable parallel dml;
     SQL> insert /*+ PARALLEL(emp,10) */ into emp
          select /*+ PARALLEL(t_emp,10) */ * from t_emp;
     SQL> commit;

  -> Oracle ignores parallel hints on a temporary table.(temporary 테이블은 parallel hints 을 무시한다)
     Parallel DML and parallel queries are not supported for temporary tables.
     ** Restrictions on Temporary Tables
       - Temporary tables cannot be partitioned, clustered, or index organized.
       - You cannot specify any foreign key constraints on temporary tables.
       - Temporary tables cannot contain columns of nested table or varray type.
       - Distributed transactions are not supported for temporary tables.

  -> PARALLEL_INDEX hint
     specifies the desired number of concurrent servers that can be used to parallelize index range scans for partitioned indexes.
 
     /*+ ORDERED USE_HASH(B A) PARALLEL_INDEX(A, INS_IX01, 4) PARALLEL(B,4) */

  -> PQ_DISTRIBUTE hint
     improves the performance of parallel join operations.

  -> Enabling Parallel Queries(for index upper_ix)
     ALTER INDEX upper_ix PARALLEL;

  -> Parallel query 는 수행할 Query, DML, DDL문에 따라 parallelism decision 이 각각 다르다
     - 일반적인 Query의 경우, degree of parallelism이 가장 높은 table/index를 reference로 삼고
     - Parallel DML문이 subquery를 포함할 경우, subquery의 degree는 DML operation의 degree와 같게
     - 그리고 Parallel DDL문이 subquery를 포함할 경우, subquery의 degree는 DDL operation의 degree와 동일하게 설정
     - PARALLEL절이 없을 경우 CPU 갯수를 default로 이용한다

  -> PARALLEL DML 실행 방법
     - 먼저 다음의 parameter 를 init<SID>.ora file에 setting해 준다.
       PARALLEL_MAX_SERVERS - Parallel server process의 최대 개수(default 5)
       PARALLEL_MIN_SERVERS - Oracle startup 시 뜨는 parallel process 개수(default 0)

     - Parallel DML을 사용을 위해서는 먼저 해당 세션에서 parallel DML을 enable 시키는 명령문을 실행해야 한다.
       alter session enable parallel dml; -> enabling parallel dml
       alter session disable parallel dml; -> disabling parallel dml

     - parallel dml 을 enable하거나 disable 하기 전에는 반드시 commit 이나 rollback 을 해 주어야 한다.
       SQL> insert into dept values (50,'abc','abc');
       1 row created.

       SQL> alter session enable parallel dml;
       ERROR:
       ORA-12841: Cannot alter the session parallel DML state within a transaction

       SQL> commit;
       SQL> alter session enable parallel dml;
       Session altered.
       SQL> update /*+ parallel(testdml,10) */ testdml set c = '2';

     - serial update를 한 후에 같은 object를 다시 parallel하게 udpate 할 수는 없다. Parallel dml은 transaction의 첫번째 DML이 되어야 한다.
       SQL> alter session enable parallel dml;
       Session altered.
       SQL> update testdml set c = '1';
       999998 rows updated.
       SQL> update /*+ parallel(testdml,10) */ testdml set c = '2';
       update /*+ parallel(testdml,10) */ testdml set c = '2'
       *
       ERROR at line 1:
       ORA-12839: cannot modify an object in parallel after modifying it

     - default 는 disable이므로 parallel dml을 사용하기 전에 반드시 enabling 해주어야 한다. Disable parallel dml mode의 세션에서는
       parallel dml을 실행하여도 에러 없이 serial하게 실행된다

   -> ROW_LOCKING specifies whether row locks are acquired during UPDATE operations.
      ROW_LOCKING = {ALWAYS | DEFAULT | INTENT}
      Real Application Clusters : You must set this parameter for every instance, and multiple instances must have the same value.

   -> 실행계획과 Parallel Query : Parallel SQL문의 Explained 될때 PLAN_TABLE의 OTHER_TAG 컬럼이 사용됨
      : OTHER_TAG 컬럼 값
       - SERIAL (or blank) : 해당 step이 serial 하게 실행
       - PARALLEL_TO_SERIAL : Parallel query의 최상위 레벨, 병렬 처리된 결과를 query coordinator 로 넘겨줌
       - SERIAL_TO_PARALLEL : Serial로 처리한 결과를 후행 병렬 프로세스 집합에 넘겨줌
       - PARALLEL_TO_PARALLEL : 병렬로 처리한 결과를 후행 병렬 프로세스 집합에 넘겨줌(예, parallel table scan의 결과를 parallel sort로 넘겨줌
       - SERIAL_FROM_REMOTE : Remote에서 serial 하게 실행
       - PARALLEL_FROM_SERIAL : Serial operation의 결과를 parallel process로 넘겨줌, Parallel processing이 serial한 Processing을 기다려야
                                하므로 이 tag는 Serial bottleneck을 나타냄
       - PARALLEL_COMBINED_WITH_PARENT(PCWP) : 해당 step은 병렬로 실행됨, 해당 step에 참여한 병렬 프로세서가 자신의 output을 가지고 next step의
                               처리에 참여 
           . 예를들어, Parallel Nested Loops join에서 Parallel query process가 driving table을 scan하고 조인될 테이블에 대한 index lookup을
             동일 프로세스가 병렬처리
           . Parent 프로세서와 Interprocess communication이 발생하지 않음
       - PARALLEL_COMBINED_WITH_CHILD(PCWC) : 해당 step은 병렬로 실행됨, 해당 step이 prior step과 동일한 병렬 프로세서에 의하여 수행

   -> 실행계획과 Parallel Query
      - Full Partition-Wise Join : sale, sale1 는 time_id 를 동일 범위로 나눈 8개의 파티션으로 구성됨 -> 8개의 파티션은 8개의 parallel 지정
        select  /*+ parallel(p1 8) parallel(p2 8) use_hash(p1 p2)  */  *
        from    sale p1, sale1 p2
        where   p2.time_id = p1.time_id
        and     p2.prod_id between 10000 and 11000;

        -----------------------------------------------------------------------------------------------
        Operation                       Object Name             In/Out           Pstart        Pstop
        -----------------------------------------------------------------------------------------------
        SELECT STATEMENT                Hint=CHOOSE
          HASH JOIN                                             P->S            QC(RANDOM)    <- QC : Query Coordinator
           TABLE ACCESS FULL               SALE1                P->P            PART(KEY)
           PARTITION RANGE ALL                                  PCWP            1              8
             TABLE ACCESS FULL             SALE                 PCWP            1              8

   -> 일반적 배치(Batch) 처리의(Processing) 문제점
      - 커서 OPEN 후 반복적으로 FETCH 및 LOGIC 처리하는 것이 대부분
      - 대형 테이블들에 대한 처리시 적절한 JOIN METHOD 선택에 어려움을 겪음 : Nested Loops ?  Hash Join ?
      - PARALLEL DEGREE를 무조건 높게하여 RESOURECE를 많이 차지하도록 하는 PROGRAM들.
      - 반복적으로 동일 테이블을 읽도록 하는 집합개념의 부족으로 인한 비효율
      - 대량 데이터는 항상 HASH JOIN으로만 JOIN하려 하는 강박관념
      - 무조건 db_file_multiblock_read_count, hash_area_size를 증가시키면 다 되는줄 아는 생각!
      - 유지보수에 문제점 발생할 정도의 통합은 추후 문제점을 야기할 소지가 다분함

   -> PARTITIONING 의 Index 유형별 적용 기준(partitioned index의 종류)
    1) GLOBAL INDEX
       . table과는 다르게 partition이 된다. 즉, table과 같은 column으로 partition되나 그 범위가 틀리거나, 혹은 다른 컬럼으로 partition 한다
       . 하나의 index partition에 있는 모든 index는 모두 하나의 table partition에 속하게 되지 않고, 두 개 이상의 partition에 나누어 있을 수 있다
       . prefixed global index만이 존재하며, non-prefixed global index는 생성이 불가능하다. 즉, global index는 항상 index의 맨 앞 컬럼 값만을
         이용하여 partition된다.

         CREATE TABLE emp
(empno NUMBER NOT NULL,
ename VARCHAR2(10),
deptno NUMBER)
PARTITION BY RANGE (deptno)
(PARTITION part1 VALUES LESS THAN(30),
PARTITION part2 VALUES LESS THAN (MAXVALUE));

CREATE UNIQUE INDEX emp_pk on emp(empno)
GLOBAL PARTITION BY RANGE (empno)
(PARTITION p1 VALUES LESS THAN ('1000'),
PARTITION p2 VALUES LESS THAN ('2000'),
PARTITION p3 VALUES LESS THAN (MAXVALUE));

      - Global Prefixed Index-(2)
         . OLTP 환경에서 여러 파티션에 걸쳐 액세스가 빈번한 경우
         . table 파티션키가 아닌 컬럼들로 Unique Index 가 필요한 경우
         . 업무 특성이나 액세스 형태 등을 고려하여 파티션 인덱스를 생성하고자 하나 Local 인덱스를 만들수 없는 경우
         . 관리상 파티션 Drop을 자주 한다면 Global Index 적용시 주의
    2) LOCAL INDEX
       . index를 생성한 table과 partitioned index가 equi-partition된 경우   
       . 즉, index와 table은 같은 컬럼에 의해 partition되며,
       . 하나의 index partition이 table partition 하나와 대응되며,
       . 대응되는 index partition과 table partition은 각각 같은 범위를 갖게 된다.
       . 결국 특정한 하나의 index에 포함된 모든 key들은 하나의 table partition 내의 data만을 가리키게 된다
      - Local Prefixed Index-(1)
         . index에서 맨 앞에 위치한 column에 의해 partition되는 것
           CREATE TABLE dept
(deptno NUMBER NOT NULL,
loc VARCHAR2(10))
PARTITION BY RANGE (deptno)
(PARTITION part1 VALUES LESS THAN(30),
PARTITION part2 values less than (MAXVALUE));

CREATE INDEX dept_idx ON dept(deptno) LOCAL;
 
      - Local Prefixed Index-(2)
         . OLTP 환경에서 특정 파티션 Access가 많고 조인이 빈번한 경우
         . 파티션키 컬럼을 선두로하는 인덱스가 필요한 경우
         . partition key 에 대응하는 leading index key(s)
         . 성능, 가용성, 관리의 용이성 등에서 다른 타입의 인덱스 보다 우수
          create index ix_test_a on test_a(col1, col2)
           local
            (partition in_test_a_1 tablespace ts0,
             partition in_test_a_2 tablespace ts1,
             partition in_test_a_3 tablespace ts2,
             partition in_test_a_4 tablespace ts3);

      - Local Prefixed Index-(3) 의 성능
         . 파티션 키 : YYYYMM
         . Prefixed Index : YYYYMM + CUST_ID
         . select * from tab1 where YYYYMM >= '200606' and YYYYMM <= '200612' and CUST_ID = '1234567';
         . Prefixed Index 의 컬럼 구성이 파티션 키 + 컬럼 순서이므로, 파티션 키의 조건이[=] -> 성능 유리, 파티션 키의 조건이[>] -> 성능 불리

      - Local Non-Prefixed Index-(1)
         . index에서 맨 앞의 컬럼을 제외한 다른 컬럼에 의해 partition된 경우
         . index columns들 중 맨 앞에 있는 column으로 partition되지 않은 경우
         . 
         . CREATE INDEX dept_locidx ON dept(loc) LOCAL;  <- 여기에서 Table partition key와 index partition key는 deptno 이다
                                                            deptno partition key를 인덱스의 첫번째 컬럼에 없으므로 Non-Prefixed Index 이다
         . 특히 historical한 data를 보관하는 table의 경우 유용
         . 즉, 날짜에 따라 table과 index의 partition은 이루어지고, 인덱스는 별도의 사원 번호나 제품번호와 같이 key가 되는 것에 생성
      - Local Non-Prefixed Index-(2)
         . Dw와 같은 대용량 Ad Hoc Query 중심 환경에서 다른 테이블과의 조인이 적고 좁은 범위의 Partition-wise access 가 많은 경우
         . 파티션키 이외의 컬럼으로 조건 검색시 파티션별 처리가 가능하여 Historical Data 검색에 유리
         . leading column 이 되는 partition key 를 포함하지 않는 index key
         . 하나의 index key 값에 해당하는 data가 여러 table partition에 존재할 수 있다는 것을 의미
         . 관리상 파티션 Drop을 자주 하면서도 인덱스의 가용성과 관리의 용이성을 유지하고자 하는 경우

      - Local Non-Prefixed Index-(3) 의 성능
         . 파티션 키 : YYYYMM
         . Non-Prefixed Index : CUST_ID + YYYYMM 
         . select * from tab1 where YYYYMM >= '200606' and YYYYMM <= '200612' and CUST_ID = '1234567';
         . Non-Prefixed Index 의 컬럼 구성이 컬럼 + 파티션 키 순서이므로, 컬럼의 조건이[=] -> 성능 유리, 컬럼의 조건이[>] -> 성능 불리
           -> Non-Prefix Index는 파티션 키의 조건 유형이 성능에 큰 영향을 미치지 않는다

    -> RANGE partitioned table을 생성한 후 GLOBAL index를 만든다
       SQL> create table orders (
2 order_no number,
3 part_no varchar2(40),
4 ord_date date
5 )
6 partition by range (ord_date)
7 (partition Q1 values less than (TO_DATE('01-APR-1999','DD-MON-YYYY')),
8 partition Q2 values less than (TO_DATE('01-JUL-1999','DD-MON-YYYY')),
9 partition Q3 values less than (TO_DATE('01-OCT-1999','DD-MON-YYYY')),
10 partition Q4 values less than (TO_DATE('03-JAN-2000','DD-MON-YYYY'))
11 );

SQL> create index orders_global_idx
2 on orders(ord_date)
3 global partition by range (ord_date)
4 (partition GLOBAL1 values less than (TO_DATE('01-APR-1999','DD-MON-YYYY')
),
5 partition GLOBAL2 values less than (TO_DATE('01-SEP-1999','DD-MON-YYYY')
),
6 partition GLOBAL3 values less than (TO_DATE('01-DEC-2000','DD-MON-YYYY')
),
7 partition GLOBAL4 values less than (MAXVALUE)
8 );


    -> HASH partitioned table을 생성 한 후 GLOBAL index를 만든다
SQL> CREATE TABLE emp_hpart(
2 empno NUMBER(4) NOT NULL,
3 ename VARCHAR2(10),
4 sal NUMBER(7,2))
5 PARTITION BY HASH(sal)
6 (PARTITION H1, PARTITION H2, PARTITION H3, PARTITION H4);


SQL> CREATE INDEX emp_global_HASH_idx ON emp_hpart(ename)
2 GLOBAL PARTITION BY RANGE (ename)
3 (PARTITION p1 VALUES LESS THAN ('N') ,
4 PARTITION p2 VALUES LESS THAN (MAXVALUE));

    -> COMPOSITE partitioned table을 생성한 후 GLOBAL index를 만든다
SQL> CREATE TABLE emp_composite(
2 empno NUMBER(4) NOT NULL,
3 ename VARCHAR2(10),
4 sal NUMBER(6))
5 PARTITION BY RANGE(empno)
6 SUBPARTITION BY HASH(sal) SUBPARTITIONS 4
7 (PARTITION p1 VALUES LESS THAN (50),
8 PARTITION p2 VALUES LESS THAN (100),
9 PARTITION p3 VALUES LESS THAN (150),
10 PARTITION p4 VALUES LESS THAN (MAXVALUE));


SQL> CREATE INDEX emp_global_composite_idx ON emp_composite(ename)
2 GLOBAL PARTITION BY RANGE (ename)
3 (PARTITION p1 VALUES LESS THAN ('N') ,
4 PARTITION p2 VALUES LESS THAN (MAXVALUE));

    -> LIST partitioned table을 생성 한 후 GLOBAL index를 만든다
SQL> CREATE TABLE locations (
2 location_id NUMBER, street_address VARCHAR2(80), postal_code
CHAR(12),
3 city VARCHAR2(80), state_province CHAR(2), country_id
VARCHAR2(20))
4 PARTITION BY LIST (state_province)
5 (PARTITION region_east
6 VALUES ('MA','NY','CT','NH','ME','MD','VA','PA','NJ'),
7 PARTITION region_west
8 VALUES ('CA','AZ','NM','OR','WA','UT','NV','CO'),
9 PARTITION region_south
10 VALUES ('TX','KY','TN','LA','MS','AR','AL','GA'),
11 PARTITION region_central
12 VALUES ('OH','ND','SD','MO','IL','MI',NULL,'IA'));


SQL> create index loc_global_idx
2 on locations (state_province)
3 global partition by range (state_province)
4 (partition p1 values less than ('NV'),
5 partition p2 values less than (maxvalue));


    -> 파티션분기(Partition Pruning/Elimination)
       - 판매실적 테이블의 '실적년월' 컬럼을 Range 파티션 키로 지정하면, 인덱스 없이도 해당 파티션 만을 액세스 할수 있다
       - SQL의 조건절에 파티션 키가(part_key_columns) 존재하면, 옵티마이져는 우선적으로 해당조건 문을 이용해 파티션분기(Partition Pruning)를 수행
         . select sum(판매금액) from 판매실적 where 실적년월 = '200704'
         . select sum(판매금액) from 판매실적 where 실적년월 >= '200701' and 실적년월 <= '200704'

SQL> select NAME, OBJECT_TYPE, COLUMN_NAME, COLUMN_POSITION  from user_part_key_columns;
NAME                           OBJEC COLUMN_NAME          COLUMN_POSITION
------------------------------ ----- -------------------- ---------------
COSTS                          TABLE TIME_ID                            1      -> 테이블의 파티션 키(part_key_columns) : TIME_ID
COSTS_PROD_BIX                 INDEX TIME_ID                            1      -> 인덱스의 파티션 키(part_key_columns) : TIME_ID
COSTS_TIME_BIX                 INDEX TIME_ID                            1
SALES                          TABLE TIME_ID                            1
SALES_CHANNEL_BIX              INDEX TIME_ID                            1
SALES_CUST_BIX                 INDEX TIME_ID                            1
SALES_PROD_BIX                 INDEX TIME_ID                            1
SALES_PROMO_BIX                INDEX TIME_ID                            1
SALES_TIME_BIX                 INDEX TIME_ID                            1

    -> 파티션분기의 적용 방안 ***
      1) 옵티마이저가 Partition Pruning을 수행하기 위해서는
         - from 절에 파티션 명칭을 구체적으로 지정한 경우  
             -> SELECT * FROM sales PARTITION (sales_q2_2000) s WHERE s.amount_sold > 10000;
             -> DELETE FROM sales PARTITION (sales_q1_1998) WHERE amount_sold > 10000;
         - where 절에 파티션 키에 대한 조건 값(상수, 변수)이 기술되는 경우
         - Join의 Inner 테이블에 대한 연결고리가 파티션 키인 경우
           . Nested Loop Join은 특별한 제약 사항이 불필요  
           . Hash / Merge Join은 Outer 테이블에서 파티션 키에 대한 조건 필요
           . 서브쿼리와 연결된 메인 쿼리의 경우에는 서브쿼리가 제공자로 수행되면 무방하나 그렇지 않으면 서브쿼리에 파티션 키에 대한 조건 필요
         - 파티션 키와 Sub-Query가 연결된 경우에는 반드시 제공자로 수행해야 함
      2) Partition Pruning의 주의사항
         - 반드시 Parsing 또는 Binding 단계에서 논리적으로 구체적인 Value의 Passing이 가능해야 함
         - Nested Loops Join을 제외한 데이터 연결 시에는 상대 연결고리 컬럼에 대한 상수 또는 변수 조건이 존재하지 않는 경우에는 Full Table Scan 발생함
         - Sub-Query 적용은 논리적으로 제공 값의 구체적인 Scope가 정의 되어야 함
      3) Partition Pruning  수행 조건을 요약하자면
         - 실행계획이 Nested Loops Join을 제외한 데이터 연결의 경우, 반드시 Parsing 단계에서 파티션 키에 대한 구체적 값의 인식이 필요
           . T1은 파티션 키가 YYYY인 파티션 테이블이고 T2는 일반 테이블인 경우
             select * from T1, T2 where T1.YYYY = T2.YYYY and T2.YYYY >= '2001' and T2.YYYY <= '2004'; 
              -> T2 일반 테이블은 전체 데이터를 읽고, T1은 전체 데이터를 읽지않고 2001 ~ 2004 데이터만 읽는다 
              -> 연결고리가 파티션 키인 경우( Nested Loop Join )

    -> Partition 인덱스 전략
       - OLTP 시스템의 Critical Access Path는 Global Index가 속도 면에서 가장 우월, 관리적 측면까지 고려한다면 Local Prefixed Index가 적합
       - Access Pattern이 다양한 Ad-Hoc Query 환경에서는 Local Non-Prefixed Index가 적합
       - 다수 파티션을 Access하는 경우에는 일반적으로 Local Non-Prefixed Index가 적합하다. 물론 Access 최적화를 고려하여 Query를 적용한다면
         Global Index나 Local Prefixed Index가 성능 면에서 우월
       - Unique Local Index를 만들려면 인덱스 컬럼에 파티션 키가 포함되어야 함
       - 파티션 키를 포함하지 않는 Unique Index는 Global Index 이용
       - Global Index는 관리적 측면에서 많은 Cost를 필요로 하므로 신중을 요함
       - skip_unusable_indexes = TRUE 파라메터를 이용하여 Unusable 상태의 Index가 발생할때 SQL의 오류가 발생하는 것을 방지


 
    -> CREATE TABLE customers_part (
        customer_id NUMBER(6),
        cust_first_name VARCHAR2(20),
        cust_last_name VARCHAR2(20),
        nls_territory VARCHAR2(30),
        credit_limit NUMBER(9,2))
        PARTITION BY RANGE (credit_limit)                  <- Composite RANGE-LIST PARTITION 
        SUBPARTITION BY LIST (nls_territory)               <-  
          SUBPARTITION TEMPLATE
            (SUBPARTITION east VALUES
              (’CHINA’, ’JAPAN’, ’INDIA’, ’THAILAND’),
             SUBPARTITION west VALUES
              (’AMERICA’, ’GERMANY’, ’ITALY’, ’SWITZERLAND’),
             SUBPARTITION other VALUES (DEFAULT))
         (PARTITION p1 VALUES LESS THAN (1000),
          PARTITION p2 VALUES LESS THAN (2500),
          PARTITION p3 VALUES LESS THAN (MAXVALUE));

    -> CREATE TABLE composite_sales
        ( prod_id NUMBER(6)
        , cust_id NUMBER
        , time_id DATE
        , channel_id CHAR(1)
        , promo_id NUMBER(6)
        , quantity_sold NUMBER(3)
        , amount_sold NUMBER(10,2)
        )
       PARTITION BY RANGE (time_id)                  <- Composite Range-Hash PARTITION
       SUBPARTITION BY HASH (channel_id)             <-
        (PARTITION SALES_Q1_1998 VALUES LESS THAN (TO_DATE(’01-APR-1998’,’DD-MON-YYYY’)),
         PARTITION SALES_Q2_1998 VALUES LESS THAN (TO_DATE(’01-JUL-1998’,’DD-MON-YYYY’)),
         PARTITION SALES_Q2_2000 VALUES LESS THAN (TO_DATE(’01-JUL-2000’,’DD-MON-YYYY’))
          SUBPARTITIONS 8,
         PARTITION SALES_Q3_2000 VALUES LESS THAN (TO_DATE(’01-OCT-2000’,’DD-MON-YYYY’))
         (SUBPARTITION ch_c,
          SUBPARTITION ch_t),
         PARTITION SALES_Q4_2000 VALUES LESS THAN (MAXVALUE)
          SUBPARTITIONS 4);

    -> CREATE BITMAP INDEX product_bm_ix
        ON product_information_part(list_price)
        TABLESPACE tbs_1
        LOCAL(PARTITION ix_p1 TABLESPACE tbs_2,             <- LOCAL INDEX : index 정의시 컬럼을 사용한다
              PARTITION ix_p2,
              PARTITION ix_p3 TABLESPACE tbs_3,
              PARTITION ix_p4,
              PARTITION ix_p5 TABLESPACE tbs_4 );

    -> CREATE INDEX cost_ix ON sales (amount_sold)
         GLOBAL PARTITION BY RANGE (amount_sold)            <- GLOBAL INDEX : index 정의 컬럼과 별도로 정의하여 사용한다
          (PARTITION p1 VALUES LESS THAN (1000),
           PARTITION p2 VALUES LESS THAN (2500),
           PARTITION p3 VALUES LESS THAN (MAXVALUE));

    -> 테이블이 어느(RANGE / LIST / HASH / COMPOSITE) PARTITION에 속하는지 확인하기 위해서는 TOAD 에서 해당 테이블의 Script 을 보면 알수 있다
       user_part_tables, user_tab_partitions,
       -> user_part_tables 에서 테이블별 PARTITIONING_TYPE(RANGE / LIST / HASH / COMPOSITE) 과 PARTITION 갯수, SUBPARTITION_COUNT 갯수 확인

          SQL> select table_name,PARTITIONING_TYPE,SUBPARTITIONING_TYPE,PARTITION_COUNT,DEF_SUBPARTITION_COUNT
               ,PARTITIONING_KEY_COUNT,SUBPARTITIONING_KEY_COUNT,DEF_TABLESPACE_NAME from user_part_tables;

         TABLE_NAME      PARTITI SUBPART PARTITION_COUNT DEF_SUBPARTITION_COUNT PARTITIONING_KEY_COUNT SUBPARTITIONING_KEY_COUNT TABLESPACE
         --------------- ------- ------- --------------- ---------------------- ---------------------- ------------------------- ----------
         COSTS           RANGE   NONE                 12                      0                      1                         0 EXAMPLE
         SALES           RANGE   NONE                 16                      0                      1                         0 EXAMPLE

       -> user_tab_partitions 에서 테이블별  PARTITION_NAME, PARTITION_POSITION, SUBPARTITION_COUNT, 파티션의HIGH_VALUE, HIGH_VALUE_LENGTH 확인

          SQL> select table_name, COMPOSITE COMP, PARTITION_NAME PART_NAME,PARTITION_POSITION PART_P,SUBPARTIT
ION_COUNT SUBP_C,  HIGH_VALUE,HIGH_VALUE_LENGTH from user_tab_partitions;

TABLE_NAME COM PART_NAME           PART_P     SUBP_C HIGH_VALUE                HIGH_VALUE_LENGTH
---------- --- --------------- ---------- ---------- ------------------------- -----------------
SALES      NO  SALES_1995               1          0 TO_DATE(' 1996-01-01 00:0                83
                                                     0:00', 'SYYYY-MM-DD HH24:
                                                     MI:SS', 'NLS_CALENDAR=GRE
                                                     GORIA

SALES      NO  SALES_1996               2          0 TO_DATE(' 1997-01-01 00:0                83
                                                     0:00', 'SYYYY-MM-DD HH24:
                                                     MI:SS', 'NLS_CALENDAR=GRE
                                                     GORIA
SALES      NO  SALES_Q4_2000           16          0 TO_DATE(' 2001-01-01 00:0                83
                                                     0:00', 'SYYYY-MM-DD HH24:
                                                     MI:SS', 'NLS_CALENDAR=GRE
                                                     GORIA
COSTS      NO  COSTS_Q1_1998            1          0 TO_DATE(' 1998-04-01 00:0                83
                                                     0:00', 'SYYYY-MM-DD HH24:
                                                     MI:SS', 'NLS_CALENDAR=GRE
                                                     GORIA
COSTS      NO  COSTS_Q4_2000           12          0 TO_DATE(' 2001-01-01 00:0                83
                                                     0:00', 'SYYYY-MM-DD HH24:
                                                     MI:SS', 'NLS_CALENDAR=GRE
                                                     GORIA


       -> user_part_indexes 에서 인덱스별  PARTITIONING_TYPE, LOCALITY(GLOBAL, LOCAL), ALIGNMENT(PREFIXED, NON_PREFIXED), PARTITION 갯수 확인
       
          SQL> select table_name, index_name,PARTITIONING_TYPE,SUBPARTITIONING_TYPE,LOCALITY,ALIGNMENT,PARTITI
ON_COUNT,DEF_SUBPARTITION_COUNT,PARTITIONING_KEY_COUNT,SUBPARTITIONING_KEY_COUNT,DEF_TABLESPACE_NAME
 from user_part_indexes;

TABLE_NAME                     INDEX_NAME                     PARTITI SUBPART LOCALI ALIGNMENT    PARTITION_COUNT DEF_SUBPARTI
------------------------------ ------------------------------ ------- ------- ------ ------------ --
COSTS                          COSTS_PROD_BIX                 RANGE   NONE    LOCAL  NON_PREFIXED              12                      0
COSTS                          COSTS_TIME_BIX                 RANGE   NONE    LOCAL  PREFIXED                  12                      0
SALES                          SALES_CHANNEL_BIX              RANGE   NONE    LOCAL  NON_PREFIXED              16                      0
SALES                          SALES_CUST_BIX                 RANGE   NONE    LOCAL  NON_PREFIXED              16                      0
SALES                          SALES_PROD_BIX                 RANGE   NONE    LOCAL  NON_PREFIXED              16                      0
SALES                          SALES_PROMO_BIX                RANGE   NONE    LOCAL  NON_PREFIXED              16                      0
SALES                          SALES_TIME_BIX                 RANGE   NONE    LOCAL  PREFIXED                  16                      0

        -> user_ind_partitions 에서 인덱스별  PARTITION_NAME, PARTITION_POSITION, SUBPARTITION_COUNT, 파티션의HIGH_VALUE, HIGH_VALUE_LENGTH 확인

           SQL> select index_name,COMPOSITE COMP, PARTITION_NAME PART_NAME,PARTITION_POSITION PART_P,SUBPARTITI
ON_COUNT SUBP_C,  HIGH_VALUE,HIGH_VALUE_LENGTH from user_ind_partitions;

INDEX_NAME                     COM PART_NAME           PART_P     SUBP_C HIGH_VALUE                HIGH_VALUE_LENGTH
------------------------------ --- --------------- ---------- ---------- ------------------------- -
SALES_PROD_BIX                 NO  SALES_1995               1          0 TO_DATE(' 1996-01-01 00:0                83
                                                                         0:00', 'SYYYY-MM-DD HH24:
                                                                         MI:SS', 'NLS_CALENDAR=GRE
                                                                         GORIA

SALES_PROD_BIX                 NO  SALES_1996               2          0 TO_DATE(' 1997-01-01 00:0                83
                                                                         0:00', 'SYYYY-MM-DD HH24:
                                                                         MI:SS', 'NLS_CALENDAR=GRE
                                                                         GORIA

         

==========
    ->1. Partition Method - Range partition :  dba_ind(tab)_partitions, user_ind(tab)_partitions, user_part_tables(indexes),
                                               DBA(User)_TAB(ind)_SUBPARTITIONS, DBA(user)_SUBPART_KEY_COLUMNS, DBA(user)_PART_KEY_COLUMNS
                                               user_segments ( PARTITION_NAME,SEGMENT_TYPE (Table/Index) )

      SQL> select NAME, OBJECT_TYPE, COLUMN_NAME, COLUMN_POSITION  from user_part_key_columns;
NAME                           OBJEC COLUMN_NAME          COLUMN_POSITION
------------------------------ ----- -------------------- ---------------
COSTS                          TABLE TIME_ID                            1
COSTS_PROD_BIX                 INDEX TIME_ID                            1
COSTS_TIME_BIX                 INDEX TIME_ID                            1
SALES                          TABLE TIME_ID                            1
SALES_CHANNEL_BIX              INDEX TIME_ID                            1
SALES_CUST_BIX                 INDEX TIME_ID                            1
SALES_PROD_BIX                 INDEX TIME_ID                            1
SALES_PROMO_BIX                INDEX TIME_ID                            1
SALES_TIME_BIX                 INDEX TIME_ID                            1

       1) 특징  
          - 데이터가 존재하는 파티션에 대한 인지 가능   <- 데이터가 어느 파티션에 있는지 확인 가능
          - 구간 개념으로서 파티션은 파티션 키로 정렬
          - 구간을 파티션 별로 상이하게 적용 가능
          - 파티션 키의 선행 컬럼에 대한 조건이 필수 **
          - 복합 컬럼의 경우는 컬럼의 순서가 중요함 **
          - 파티션 단독으로 백업 및 복구 가능
          - 범위를 잘 못 판단하면 데이터 SKEW (한쪽 파티션으로 취우치는것) 발생 
          - Partition Pruning 가능
          - 파티션 키 : 실적년월, 등록년월 등
       2) 활용      
          - Range 조건으로 파티션 제거(Partition Pruning/Elimination) 효과를 얻고자 하거나 과거 데이터를 Purge 할때 사용
          - 시계열성 데이터에 대한 액세스 및 관리
       3) 장점
          - 하나 이상의 column으로 key partition 가능하다.

     ->2. Partition Method - List partition
        - 각각의 partition에 대해 별개의 partition key값을 가지고 나타낼수 있는데, 이것은 range의 값을 가지고 partition하는 range partition과도
          구별되며 hash fucntion에 따라 나누는 hash partition과는 구별된다.
       1) 특징
          - 데이터가 존재하는 파티션에 대한 인지 가능   <- 데이터가 어느 파티션에 있는지 확인 가능
          - 이산적 컬럼 값을 각 파티션으로 분할 (대부분 조건에서 = 로 처리해야지, beetween 은 좋지 않다)
          - 데이터 상호간의 순서 및 관련성에 상관없이 그룹핑 가능
          - 파티션 키는 항상 단일 컬럼만 구성 가능
          - 파티션의 구성 Value는 다수 지정 가능
       2) 장점
          - 연관되지 않은 값들을 group화할 수 있는 점
       3) 단점
          - 오직 하나의 column만 key partition으로 가능하다.
          - IOT는 List partition 할수 없다.
          - 다른 partiton이나 같은 partition이라 할지라도 같은 값으로 partition으로 partition할수 없다.(다른 파티션에서 동일한 VALUE 중복 지정 불가)
          - RANGE partition에서 쓰이는 MAXVALUE로 List방법에는 bound을 줄수 없다(MAX VALUE 와 같은 UPPER-BOUNG 정의 불가)
       4) 활용
          - Business 친화도에 따른 이산 데이터 그룹핑
          - 일정한 순서가 없는 데이터를 인위적으로 한 곳에 모으고자 하는 경우 활용

     ->3. Partition Method - Hash Partitioning
        - partition column에 대해 hash function을 적용하여 입력되는 데이타가 파티션을 찾아가는 방법을 사용한다
          SQL> CREATE TABLE emp_hpart(
               empno NUMBER(4) NOT NULL,
               ename VARCHAR2(10),
               sal NUMBER(7,2))
               STORAGE (INITIAL 50k NEXT 50k)
               PARTITION BY HASH(empno) PARTITIONS 4
               STORE IN (data01,data02,data03,data04);
          위 예제에서는 system에서 자동으로 부여된 이름으로 4개의 partition을 생성하고, partition 이름은 SYS_Px와 같은 형태로 생성된다
          위에서는 partition 이름을 생략하였는데 명시적으로 파티션 이름을 주면서 생성할수 있다. 이경우는 partition number가 생략되며
          추가적인 partition 부분에 대한 선언부가 필요하다
          SQL> CREATE TABLE emp_hpart
               PARTITION BY HASH(empno)
               (PARTITION P1 TABLESPACE data01,
                PARTITION P2 TABLESPACE data02,
                PARTITION P3 TABLESPACE data03,
                PARTITION P4 TABLESPACE data04)

       1) 특징  
          - 데이터가 존재하는 파티션에 대한 인지 불가   <- 데이터가 어느 파티션에 있는지 확인 불가능
          - 파티션 개수만 지정 가능
          - 파티션 키로 Hash Value를 적용하며 동일 Hash Value들은 동일 파티션에 저장
          - 전체 파티션에 대한 균등한 데이터 분산
          - 파티션 수는 2의 배수로 설정(2의 배수가 아닌 경우 파티션간 균형이 깨질 수 있다)
          - hash partition 은 pruning 개념이 없다

       2) 단점
          - 기본 속성의 제한 때문에 partition에 관련된 작업을 수행하기에 제약사항이 있다.
            -> partition split, drop, merge등은 range partition에서는 가능한 작업이지만, hash partition에서는 다음과 같은 에러가 발생한다.
            ORA-14255: table is not partitioned by Range or Composite Range method
          - 그러나 partition add는 가능하다. 만약 P14 파티션을 다시 추가하려고 한다면 오라클 커널은 SYS_P10에 있는 row들을 rehash하려고
            할 것이다. 이러한 방법으로 partition을 추가하는 것은 data가 고르게 파티션에 분배되지 못하는 결과를 가져온다.
          - 그러므로 partition의 개수는 항상 2의 배수로 관리되는 것이 좋다

       3) 활용
          - 병렬 처리의 I/O 분산
          - 다량 Insert 작업시 Enqueue lock Striping
          - Static(다량, 전체) Partitioning에 의한 병렬 처리
          - 주어진 Range에 어느 정도의 Data가 Mapping  될지 예측하기 어려운 환경이나 Range별 Data량의 차이가 많이 나는 업무에 적당

     ->4. Partition Method - Composite Range-List Partitioning(List는 sub-partition)
        - Range 형태로 먼저 데이타를 partition하고, 그 다음에 List-method를 이용하여 subpartitioning을 하는 방법이다
        - 어떤 level에 모든 다른 subpartition들을 반드시 명시하지 않아도 된다. 아래와 같이 SUBPARTITION TEMPLATE 절을 사용할 수 있다
          CREATE TABLE empdata
( empno number,
ename varchar2(20),
deptno number,
continent varchar2(6),
hiredate date,
job varchar2(10),
salary number)
PARTITION BY RANGE (deptno)
SUBPARTITION BY LIST (continent)
SUBPARTITION TEMPLATE
(SUBPARTITION d1_con1 VALUES ('ASIA', 'AUST') tablespace PART1,
SUBPARTITION d1_con2 VALUES ('AMER') tablespace PART2,
SUBPARTITION d1_con3 VALUES ('AFRICA') tablespace PART3,
SUBPARTITION d1_con4 VALUES ('EUROPE') tablespace PART3
)
(PARTITION d1_emp VALUES LESS THAN (100),
PARTITION d2_emp VALUES LESS THAN (maxvalue)
);

           Storage 절은 TABLESPACE 절을 제외하고는 partition level로부터 상속받는다.
           만약, partition level에서 정의되어 있지 않으면 underlying table로 부터 storage 절을 상속받는다.
       1) 장점
          - Composite Range-Hash method와는 달리, 이 방법은 각 row가 어느 subpartition에 속하게 될지를 조절할 수 있다.
       2) 단점
          - 그러나, multiple subpartition keys 는 지원하지 않는다. 이는 List method가 이것을 지원하지 않기 때문이다.
          - 오직 하나의 column만 key partition으로 가능하다

      ->5. Partition Method - Composite Range-Hash Partitioning(Hash는 sub-partition)
         - range partition과 hash partition의 혼합된 형식임.
         - 분기마다 한번씩 상당히 많은 량의 데이타가입력되는 datawarehouse와 같은 환경에서 사용되어질 수 있다
         - composite partitioned index는 항상 local index이며 default로 table subpartition과 같은 tablespace에 저장된다.
           그러나 index level이나 index subpartition level에서 tablespace를 지정하는 것이 가능하다.
         - composite partitioned table에 대해서 range partitioned global index는 지원이 되지만 composite partitioned globall index는 지원되지 않음
       1) 장점
          - 우선 range에 의해서 우선 partitioning을 하고, 다시 그 partition을 hash function을 이용해서 sub-partitioning을 한다.
          - 이런 방법은 range partitioning과 hash partitioning의 장점을 동시에 가질수 있다
          SQL> PARTITION BY RANGE(sale_date)
               SUBPARTITION BY HASH(prod_id) SUBPARTITIONS 4
               STORE IN (data01,data02,data03,data04)
               (PARTITION cp1 VALUES LESS THAN('01-APR-1999'),
                PARTITION cp2 VALUES LESS THAN('01-JUL-1999'),
                PARTITION cp3 VALUES LESS THAN('01-OCT-1999'),
                PARTITION cp4 VALUES LESS THAN('01-JAN-2000'));
             위의 예에서는 SALE_DATE에 대해서 분기별로 4개의 partition으로 range partition되어 있는 SALES99_CPART 테이블을 보여주고 있다.
             각각의 range는 PROD_ID에 의해 hash 되어 다시 각각 4개의 partition으로 나뉘어서 결과적으로 총 16개의 partition이 생성된다.
          SQL> select SEGMENT_NAME,PARTITION_NAME,SEGMENT_TYPE from user_segments
          SEGMENT_NAME                                                                      PARTITION_NAME                 SEGMENT_TYPE
          --------------------------------------------------------------------------------- ------------------
          COSTS_TIME_BIX                                                                    COSTS_Q1_1999                  INDEX PARTITION
          COSTS_TIME_BIX                                                                    COSTS_Q2_1999                  INDEX PARTITION 
          SALES99_CPART                                                                     SYS_SUBP36                     TABLE SUBPARTITION DATA04  

      ->6. Composite Partition
        1) 특징
           - Range + Hash, Range + List 로 Composite Partition 생성 (10G 까지 지원 Composite Partition)
           - Sub-Partition 은 독립적인 Segment
           - Data Move, Index Rebuild 와 같은 데이터 작업은 Sub-Partition만 가능
           - Historical Data와 I/O Striping 모두를 적용할 수 있는 이상적인 방법
           - 복합 파티션의 서브 파티션 구성시 Range Partition의 파티션 키에 대한 조건 존재 여부에 상관없이 Partition Pruning 가능
        2) 활용
           - 성능 개선, I/O 분산, 관리 모두 적용 가능
           - 질의 유형이 다양한 비정형 질의 업무 적용
              
      ->7. Partition Method
          - partitioning 을 최대한 활용하기 위해서는 partitioned tables/indexes 를 생성할 때에 STORAGE clause 를 반드시 사용해야 합니다.

   ***-> 파티션 키의 구성 컬럼이 YY,MM,DD인 Range Partition 의 경우(Range + List Composite Partition 의 활용)
         개선전 : select * from sales where YY = '2007' and MM >= '01' and MM <= '02' and DD = '01'
                  -> I/O 비효율 발생, MM의 1월 데이터와 2월 데이터 전체를 Access 한다
                  -> Range Partition 이기 때문에 MM >= '01' and MM <= '02' 에 해당하는 데이터를 모두 Access 하면서 DD = '01' 을 찾는다
         개선후 : select * from sales where YY = '2007' and MM in('01','02') and DD = '01'
                  -> MM의 1월 데이터에서 DD가 01인 데이터와 2월 데이터에서 DD가 01인 데이터만 Access 한다

   ***-> 파티션 키의 구성 컬럼이 Range[YY,MM] + List[DD]인 Partition 의 경우(Range + List Composite Partition 의 활용)
         select * from sales where YY = '2007' and MM >= '01' and MM <= '02' and DD = '01'
          -> I/O 비효율 없음, MM의 1월 데이터에서 DD가 01인 데이터와 2월 데이터에서 DD가 01인 데이터만 Access 한다
          -> DD가 List Partition 이기 때문에 MM >= '01' and MM <= '02' 일 지라도, DD Access는 DD = '01' 만 Access 한다
         select * from sales where YY = '2007' and DD = '01'
          -> I/O 비효율 없음, MM의 1월 데이터에서 DD가 01인 데이터, 2월 데이터에서 DD가 01인 데이터, 3월 데이터에서 DD가 01인 데이터만 Access 한다
          -> DD가 List Partition 이기 때문에 YY = '2007' 일 지라도, 1월 ~ 12월 데이터 전체를 Access하는 것이 아니라, DD Access는 DD = '01' 만 Access 한다

      -> COMPRESS INDEX 사용 방법
         . 인덱스 키 컬럼의 중복을 방지해서 저장 공간의 낭비를 막아 줍니다.
         . <PREFIX, SUFFIX> 를 해석하기 위해서 인덱스 SCAN 자체의 성능은 약간 저하 될 수 있습니다

      -> 파티션 키 선정 예제(파티션 적용 여부)
        1) 가입 고객유형별로 List 파티션 고려
        2) 가입계약, 가입서비스의 Billing 업무 영역에서는 항상 해당 테이블들의 전월 분 데이터 전체가 작업 대상이므로 종료일 관점의
           구간 개념을 적용한 Range 파티션 고려
        3) 월 단위로 데이터 처리 및 관리가 수행되므로 청구 월을 파티션 키로 하는 Range 파티션 적용
        4) 청구상세내역의 미납여부 컬럼에 대한 List 파티션으로 적용이 필요
        5) 청구상세내역의 청구년월 + 미납여부로 구성된 Range
        6) 수납에서 다수의 발생일자가 존재하고 있으나 해당 집합에 대한 Access Path 중에서 다량 데이터에 대한 넓은 범위의 추출 기준일자가 파티션 키 후보
        7) 수납의 후속 업무는 회계 처리이며 이는 데이터 발생이 아닌 실질적인 처리 관점으로 수행하므로 수납처리일자가 파티션 키로서 타당함
        8) 과금내역에서 실질적인 적용 기준은 행위의 종료 관점인 종료일자가 아니라 행위가 종료된 데이터에 대한 처리 관점이 중요하므로 처리일자 기준의
           파티션 적용
        9) 과금내역에서 통화 패턴과 같은 분석을 위한 집계 작업을 위해서는 서비스 유형에 대한 파티션 키 참여 고려
       10) 반도체 공정의 Lot은 년월 + 일련번호 형식으로 생성
           -> 문제점 : Lot을 년월 기준의 구간 단위로 Range 파티션을 적용하면 Data Skew가 발생
           -> 해결방안 : Lot을 파티션 키로 적용하여 Hash 파티션 적용

      -> Partition Exchange를 이용한 Update
         - 문제점 : Update 작업을 수행할때 데이터 압축 해제 현상 발생, 디스크 용량 증대 및 데이터 처리 성능 저하 발생
         - 해결방안 : 데이터 변경 작업을 Update 대신 Partition Exchange 기법을 적용
         - Partition Exchange
           . Exchange는 Partitioned Table의 특정 Partition과 Non Partitioned Table 간의 저장공간을 상호 교환
           . 대용량 데이터 관리 용도로 적절
           . Data Dictionary 정보만 변경
         - Partition Exchange 적용시의 주의사항
           alter table 가입계약 exchange partition p_200706 with table DUMMY_FACT without validation;
           -> partition key는 partition pruning 만 적용되며 data validation check는 skip

       -> 향후 Partition 발전 방향(Oracle 11g partition)
         - 기본 파티션 : Range, List, Hash
         - Composite Partition(복합 파티션) : Range-Range, Range-List, Range-Hash, List-List, List-Range, List-Hash
         - Extension Partition(확장 파티션) : Interval Partition, REF Partition, Virtual Column Partition

       -> Partition 의 제약사항
        1) 일반적인 제약사항
           - Data Type 제약 : LONG, LONGRAW 및 Object Types 등을 가질수 없음
           - Cluster, Snapshot 등은 Partitioning 불가
           - Bitmap Index 생성 제약
             . Local Index 는 생성 가능, Global Index는 생성 불가
           - Multi Block Size 사용 불가(9i), 동일한 Block Size의 Tablespace 들에서만 생성 가능
           - Partition Key Update 제약
             . 다른 Partition 으로 이동을 유발하는 Partition Key Update는 에러 발생
               (Alter table ~ enable row movement 하면 가능, 그러나 Delete(기존 파티션)-Insert(신규 파티션) 수행으로 부하발생) ???
                 -> Enable Row Movement 가 활성화 되어 있으면        -> Enable Row Movement 가 활성화 되어 있지 않으면
             . Partition 내에서는 Partition Key Update 가능하나, 가능하면 Partition Key는 Update 하지 말것
              
       -> HASH JOIN 
        1) HASH JOIN 이란
          - index 가 여러 level 의 depth를 가질 때 Sort Merge Join (SMJ) 이나 Nested Loops (NL)보다 좋은 효과를 낸다.
          - sort 를 하지 않으므로 SMJ 보다 좋은 성능을 내며, 작은 table 과 큰 table 의 join 시에 유리하다.
          - 주의해야 할 것은 hash join 은 equi join 에서만 가능하다는 것이다.(Equal 조인에서만 가능)
            ** equi join : 조인 대상 테이블에서 공통 컬러 Where 절에 ' = '(equal) 비교를 통해 같은 값을 가지는 행을 연결하여 결과를 생성하는 조인
                           예) deptno가 동시에 들어가므로 where 절에 equi를 해줘야 한다
                               select e.ename, d.dname from emp e, dept d where e.deptno = d.deptno;
          - HJ 은 driving table 에 index 를 필요로 하지 않는다(Driving Table에 인덱스를 필요로 하지 않고 각 테이블을 한번만 읽음)
          - HJ는 각 table 에 대해 1 번만 pass 한다.
          - 적은테이블과 큰테이블의 조인시에 유리
          - 다른조인방법보다 CPU자원을 많이 소비하며 양쪽 테이블의 scan이 동시에 일어남
        2) SELECT S.a, B.a FROM S,B WHERE S.a = B.a;    S는 small table 이고, B는 big table 이다.
           (* analyze 를 수행하면 CBO 는 이미 S가 out table 이고 B 가 inner table 이며 , S 가 driving table 임을 인식한다. )
        3) hash_area_size : hash meomry 효율을 높이기 위해 sort_area_size 의 2배 설정
        4) parameter 설정
          - HASH_JOIN_ENABLED : true 로 지정시 사용가능
          - HASH_AREA_SIZE : sort_area_size 의 2배가 기본
          - HASH_MULTIBLOCK_IO_COUNT : db_file_multiblock_read_count가 기본
          - USE_HASH : hint   -> sql의 힌트 사용시
 
       ->  다음 파라미터는 ALTER SYSTEM을 이용해서 시스템 레벨에서 동적으로 변경할 수 있다.
           SQL> alter system set pga_aggregate_target=20000000;
           SQL> alter system set workarea_size_policy=AUTO;

       -> 9i 버전에서 OPTIMIZER_MODE 의 default 값 choose 이다

       -> HASH 힌트
          - hash scan을 선택하도록 지정한다. 이 hint는 HASHKEYS parameter를 가지고 만들어진 cluster내에 저장된 table에만 적용이 된다.
          - eab table과 gsh table이 있고 각각의 table에 있는 num column에는 ieab, igsh라는 index 가 잡혀져 있다고 가정한다.
            HASHKEY parameter를 2로 한 num column을 가진 hash cluster c21을 만든다. eab table과 gsh table이 cluster c21에 지정된다.
          - explain plan set statement_id='cbo1' for
            select /*+ HASH(eab) */ eab.num,eab.data from eab,gsh
            where eab.num=7 and eab.num=gsh.num;

       -> 힌트를 사용할 때 고려되어야 하는 사항
          - *+ ALL_ROWS */
            ALL_ROWS는 Full Table Scan을 선호하며 CBO(Cost Based Optimization)는 default로 ALL_ROWS를 선택 합니다.
          - /*+ CHOOSE */
            Hint Level의 CHOOSE는 RBO(Rule Based Optimization)인지 CBO(Cost Based Optimization) 인지를 선택 합니다.
            만약 주어진 table의 통계 정보가 없다면 Rule Based 접근 방식을 사용 합니다.
          - /*+ FIRST_ROWS */
            Full Table Scan보다는 index scan을 선호하며 Interactive Application인 경우 best response time을 제공 합니다.
            또한 sort merge join보다는 nested loop join을 선호 합니다.
          - /*+ RULE */
            Rule Based 접근 방식을 사용하도록 지정 합니다
          - <<Access Methods - 접근 방법>>
            . /*+ CLUSTER(table_name) */
              Cluster Scan을 선택하도록 지정한다. 따라서 clustered object들에만 적용 됩니다.
            . /*+ FULL(table_name) */
              Table을 Full Scan하길 원할 때 사용 합니다
            . /*+ HASH(table) */
              Hash scan을 선택하도록 지정한다. 이 hint는 HASHKEYS parameter를 가지고 만들어진 cluster내에 저장된 table에만 적용이 됩니다
            . /*+ INDEX(table_name index_name) */
              지정된 index를 강제적으로 쓰게끔 지정 합니다.
            . /*+ INDEX_ASC(table_name index_name) */
              지정된 index를 오름차순으로 쓰게끔 지정 합니다. Default로 Index Scan은 오름차순 입니다
            . /*+ INDEX_DESC(table_name index_name) */
              지정된 index를 내림차순으로 쓰게끔 지정 합니다. 위 문장은 제일 큰 것 하나만 조회되므로, max function의 기능을 대신할 수 있습니다
            . /*+ INDEX_FFS(table index) */
              Full table scan보다 빠른 Full index scan을 유도 합니다
            . /*+ ROWID(table) */
              Rowid로 Table Scan을 하도록 지정 합니다.
          - <<Join Orders>>
            . /*+ ORDERED */
              From절에 기술된 테이블 순서대로 join이 일어나도록 유도 합니다.
          - <<Join Operations>>
            . /*+ USE_HASH (table_name) */
              각 테이블간 HASH JOIN이 일어나도록 유도 합니다
            . /*+ USE_MERGE (table_name) */
              지정된 테이블들의 조인이 SORT-MERGE형식으로 일어나도록 유도 합니다.
          - <<Parallel Execution>>
            . /*+ NOPARALLEL(table_name) */
              NOPARALLEL hint를 사용하면, parallel query option을 사용하지 않도록 할 수 있다
            . /*+ PARALLEL(table_name, degree) */
              PARALLEL hint를 사용하면 query에 포함된 table의 degree를 설정할 수 있습니다
          - USE_NL HINT 사용방법
            . NESTED LOOP
              SQL> explain plan for select /*+ ordered use_nl(d) */
                   * from big_emp e,big_dept d where d.deptno = e.deptno;
            . HASH JOIN
              SQL> explain plan for select /*+ ordered use_nl(e) */
                   * from big_emp e,big_dept d where d.deptno = e.deptno;
            . HASH JOIN
              SQL> explain plan for select /*+ use_nl(d) */
                   * from big_emp e,big_dept d where d.deptno = e.deptno;
            . HASH JOIN
              SQL> explain plan for select /*+ use_nl(e) */
                   * from big_emp e,big_dept d where d.deptno = e.deptno;
            -> <해답>
              use_nl의 hint 와 ordered 가 어떻게 사용되는지는 다음과 같다.
              Tuning reference guide 에 의하면 use_nl과 use_merge 는 모두 ordered hint 와 함께 사용되어져야 하며, 오라클은 이 hint 가
              inner/probed(driving 되지 않는 테이블) table 인 경우 사용되어진다고 언급하고 있다
          - USE_NL  : USE_NL(a b c) 테이블 지정 순서가 조인 순서에 영향을 미치지 않는다 ?,(힌트의 앞에 설정한 테이블 먼저 엑세스함), 테스트 필요함
            Nested Loops방식을 사용하여 조인을 수행하도록 유도하는 힌트이다
            이 힌트는 대상 집합 간의 조인 방식을 지칭할 뿐이며, 조인 순서에는 영향을 미치지 않는다
            얘) SELECT /*+ USE_NL(a b c) */...
                FROM TAB1 a, TAB2 b, TAB3 c
                WHERE ...
          - USE_HASH : USE_HASH (a b) 테이블 지정 순서가 조인 순서에 영향을 미치지 않는다?,(힌트의 앞에 설정한 테이블 먼저 엑세스함), 테스트 결과(무관함)
            해쉬조인 방식으로 조인이 수행되도록 유도하는 힌트이다. 해쉬조인은 어느 한쪽 테이블이작아서 인-메모리 해쉬조인으로
            수행될 수 있다면 매우 양호한 속도를 갖는다.
            대부분 경우는 옵티마이져가 통계정보를 토대로 (빌드/build input)입력과 (검색/probe input)입력을 결정하므로 일부러 'ordered'힌트를 주어 함부로 순서를
            결정하는 것은 바람직하지 못하다.
            그러나 옵티마이져의 판단에 문제가 있을 때나 인라인뷰에서 가공한 결과 집합처럼 적절한 통계정보를 가질 수 없는 경우에는 활용가치가 있다.
            예) SELECT /*+ USE_HASH (a b) */...
                FROM sale a, pre_order b
                WHERE a.order_id = b.order_id
         AND a.sale_dt LIKE '2005%';
            . build input : 차곡차곡 data를 쌓는다, build input 이 끝나야 결과 값이 나온다
            . probe input : 같은 값을 찾는다

 

******************************
*** 최종적으로 설정 파라미터는(먼저 확인사항, ps -ef | grep p00 해서 확인된 갯수)
   -> PARALLEL_MAX_SERVERS = the number of CPUs * 16  <- Static 파라미터로 Alter System Set 명령으로 변경되지 않는다
      - the number of CPUs * 16 로 해야되는 이유는 ? -> 아래와 같이 select 외에도 group by, order by 에 따라서 process 가 증가하기 때문
        . select  /*+ PARALLEL(S,2) */  * from sales S order by custoner_id;
          하게되면 parallel prcesss 는 총 4개가 수행된다 : SCAN parallel process 2개, SORT parallel process 2개(order by에 의해서)
        . select  /*+ PARALLEL(S,2) */ CUSTOMER_ID, SUM(SALE_VALUE) from sales S group by customer_id  order by 2;
          하게되면 parallel prcesss 는 총 6개가 수행된다 : SCAN parallel process 2개, Group by parallel process 2개, SORT parallel process 2개(order by에 의해서)
   -> PARALLEL_MIN_SERVERS = 약 CPU 갯수로 지정       <- Static 파라미터
   -> parallel_execution_message_size = 4096 또는 (8192)        <- Static 파라미터
       specifies the size of messages for parallel execution (formerly referred to as parallel query, PDML, Parallel Recovery, replication).
   -> PGA_AGGREGATE_TARGET > 0 일 경우만 PARALLEL_MAX_SERVERS = the number of CPUs * 16 으로하고, 0 이면 the number of CPUs * 약 10 정도 한다
   -> UPDATE, MERGE, or DELETE operations 의 경우는 parallel 갯수는 table에서 partitions 갯수까지만 지정
      SELECT 는 table에서 partitions 갯수에 제약 없이 지정
   -> table 이나 index 을 생성할때 parallel 을 지정하였다면, DML 이나 SELECT 문에서 parallel 을 지정할 필요가 없다
   -> [NO]LOGGING Clause 적용 여부 : NOLOGGING option set run faster because no redo is generated. ????????
      ALTER or CREATE statement for a table, partition, index, or tablespace
   -> DML을 parallel로 처리하기 위해서는
      SQL> grant alter session to scott; 권한이 부여되었습니다.
      SQL> ALTER SESSION ENABLE PARALLEL DML;   -> table 이나 index 을 생성할때 parallel 을 지정하였어도 이 명령은 실행해야한다
      -> Parallel DML : Parallelizing INSERT ... SELECT
      -> Parallel DML : Parallelizing UPDATE and DELETE
   -> Parallel로 처리되기 위해서는
      1) PARALLEL_MAX_SERVERS 파라미터 정의
      2) table 이나 index 정의에서 Parallel 로 정의되어 있다면(ALTER TABLE customers PARALLEL;)
         - select, update, insert, delete 문장에 PARALLEL 힌트나, PARALLEL 문을 기술할 필요가 없다
         - 문장에 PARALLEL 힌트나, PARALLEL 문을 기술하면 table 정의시 지정된 PARALLEL 보다 우선하여 적용된다
      3) DML(update, delete, insert) 문장은 PARALLEL로 처리하기 위해서 해당 세션에서 SQL> ALTER SESSION ENABLE PARALLEL DML; 을 수행후 해야한다
         - DML(update, delete, insert) 문장 PARALLEL 처리(작업) 순서 반드시 준수
           1) SQL> commit;
           2) SQL> alter session enable parallel dml;  **> serial update 후 commit; 하지 않고 enable parallel dml; 명령 발행시 error 발생하기때문.
           3) DML(update, delete, insert) 문장 PARALLEL 처리
           4) SQL> alter session disable parallel dml; **> disable parallel dml; 하지 않고 serial update 후 PARALLEL DML(update, delete, insert) 
                                                           문장 실행시 PARALLEL DML이 error 가 발생하기 때문임  
    
         - parallel dml 을 enable하거나 disable 하기 전에는 반드시 commit 이나 rollback 을 해 주어야 한다.
           SQL> commit;
           SQL> alter session enable parallel dml;
           Session altered.
           SQL> update /*+ parallel(testdml,10) */ testdml set c = '2';
         - serial update를 한 후에 같은 object를 다시 parallel하게 udpate 할 수는 없다. Parallel dml은 transaction의 첫번째 DML이 되어야 한다.
           SQL> alter session enable parallel dml;
           Session altered.
           SQL> update testdml set c = '1';
           999998 rows updated.
           SQL> update /*+ parallel(testdml,10) */ testdml set c = '2';
           update /*+ parallel(testdml,10) */ testdml set c = '2'
           *
           ERROR at line 1:
           ORA-12839: cannot modify an object in parallel after modifying it
           -> 이것은
                    SQL> update testdml set c = '1';
                    999998 rows updated.
                    SQL> commit;
                    SQL> alter session enable parallel dml;
                    Session altered.
                    SQL> update /*+ parallel(testdml,10) */ testdml set c = '2';   뒤에는 아마도 여러개의 연속된 parallel을 처리하면된다

      4) 최소한 하나의 table에 대한 full scan 또는 여러 partition에 걸친 index range scan이 이루어져야 한다. 
      5) Parallel DML and parallel queries, partitioned are not supported for temporary tables
      6) UPDATE 와 DELETE 문은 non-partitioned table에서는 parallel 하게 실행되지 않는다.
      7) Initial parameter Row_locking = intend 가 지정된 경우는 Parallel insert, update, delete가 실행되지 않는다.<- 파라미터 9i는 있고, 10G는 없다
        
      8) Trigger가 걸린 table에 대해서는 parallel dml을 실행할 수 없다.
         trigger가 걸린 table에 사용할 때에는 trigger를 disable해야 한다
      9) 같은 transaction 내에서 parallel DML 문장에 의해 update된 table에 대해 다시 dml 문을 실행할 수는 없다. Parallel DML 문장
         이후에는 반드시 commit을 해야 한다
     10) self-referential integrity, delete cascade 등에서는 parallel DML을 사용할 수 없다
     11) Parallel DML은 object , LOB column 을 가진 table에서는 사용할 수 없다
     12) Distributed transaction 에서는 parallel DML을 사용할 수 없다
     13) Clustered table에서는 parallel DML을 사용할 수 없다

   -> Direct-Load Insert의 사용방법 *************************************************
     - Serial Direct-Load Insert는 APPEND hint를 통해 사용할 수 있다.
     - Parallel Direct-Load Insert는 APPEND 없이 PARALLEL hint만으로도 사용할 수 있다.
       즉 Insert 시에 PARALLEL을 지정하면 무조건 Direct-Load로 작동하게 된다
     - Direct-Load Insert는 명령문 직후에는 반드시 commit이나 rollback이 필요하다
    1) APPEND hint의 사용
       SQL> insert /*+ APPEND */ into emp select * from t_emp;
       SQL> commit;
       (direct insert후에 바로 select를 하기 전에 먼저 commit;을 해야 data를 확인할 수 있다.)
    2) PARALLEL hint의 사용
       SQL> alter session enable parallel dml;
       SQL> insert /*+ PARALLEL(emp,10) */ into emp
       select /*+ PARALLEL(t_emp,10) */ * from t_emp;
       SQL> commit;

   -> parallel(Degree) 갯수 지정은 파티션 수만큼 지정해야 효율적이다(table 파티션 갯수 이상은 Parallel process 수가 실행되지 않는다/된다(Hash Join 등) ????)
      -> Full Partition-wise Join 외의 또는 Full Partition-wise Join 처리인 Hash join 등에서는 parallel(Degree) 갯수 지정은 파티션 수 이상을 지정한다
          -> ORACLE 사용자를 위한 Tuning 실무-4 참조
      -> select 에서는 주로 그렇지 않고, update, delete, merge 에서 주로 parallel(Degree) 갯수 지정은 파티션 수만큼 지정 한다
      -> 파티션 테이블의 인덱스 생성시 병렬처리(GLOBAL INDEX, LOCAL INDEX) : LOCAL INDEX은 parallel(Degree) 갯수보다 Parallel process 수가 적을수있다
      -> select 에서는 주로 파티션 수 이상을 지정한다
      - select  /*+ parallel(p1 8) parallel(p2 8) use_hash(p1 p2)  */  *

   -> HASH JOIN 사용시 parameter 설정
          - HASH_JOIN_ENABLED : true 로 지정시 사용가능
          - HASH_AREA_SIZE : sort_area_size 의 2배가 기본
          - HASH_MULTIBLOCK_IO_COUNT : db_file_multiblock_read_count가 기본   ***> 보통 설정하지 않는다
          - USE_HASH : hint   -> sql의 힌트 사용시
       주의해야 할 것은 hash join 은 equi join 에서만 가능하다는 것

   -> HP 서버에서 sar, vmstat 를 사용하여 cpu, memory 사용율 확인
     - sar 1 10  : 1초 간격으로 10회
     - vmstat 1 10 : 1초 간격으로 10회

  -> 파티션분기(Partition Pruning/Elimination)
       - 판매실적 테이블의 '실적년월' 컬럼을 Range 파티션 키로 지정하면, 인덱스 없이도 해당 파티션 만을 액세스 할수 있다
       - SQL의 조건절에 파티션 키가(part_key_columns) 존재하면, 옵티마이져는 우선적으로 해당조건 문을 이용해 파티션분기(Partition Pruning)를 수행
         . select sum(판매금액) from 판매실적 where 실적년월 = '200704'
         . select sum(판매금액) from 판매실적 where 실적년월 >= '200701' and 실적년월 <= '200704'

   -> 파티션분기의 적용 방안 ***
      1) 옵티마이저가 Partition Pruning을 수행하기 위해서는
         - from 절에 파티션 명칭을 구체적으로 지정한 경우  
             -> SELECT * FROM sales PARTITION (sales_q2_2000) s WHERE s.amount_sold > 10000;   <** PARTITION 2개 이상일때 기술방법 ??
             -> DELETE FROM sales PARTITION (sales_q1_1998) WHERE amount_sold > 10000;
         - where 절에 파티션 키에 대한 조건 값(상수, 변수)이 기술되는 경우
         - Join의 Inner 테이블에 대한 연결고리가 파티션 키인 경우
           . Nested Loop Join은 특별한 제약 사항이 불필요  
           . Hash / Merge Join은 Outer 테이블에서 파티션 키에 대한 조건 필요
           . 서브쿼리와 연결된 메인 쿼리의 경우에는 서브쿼리가 제공자로 수행되면 무방하나 그렇지 않으면 서브쿼리에 파티션 키에 대한 조건 필요
         - 파티션 키와 Sub-Query가 연결된 경우에는 반드시 제공자로 수행해야 함
      2) Partition Pruning의 주의사항
         - 반드시 Parsing 또는 Binding 단계에서 논리적으로 구체적인 Value의 Passing이 가능해야 함
         - Nested Loops Join을 제외한 데이터 연결 시에는 상대 연결고리 컬럼에 대한 상수 또는 변수 조건이 존재하지 않는 경우에는 Full Table Scan 발생함
         - Sub-Query 적용은 논리적으로 제공 값의 구체적인 Scope가 정의 되어야 함
      3) Partition Pruning  수행 조건을 요약하자면
         - 실행계획이 Nested Loops Join을 제외한 데이터 연결의 경우, 반드시 Parsing 단계에서 파티션 키에 대한 구체적 값의 인식이 필요
           . T1은 파티션 키가 YYYY인 파티션 테이블이고 T2는 일반 테이블인 경우
             select * from T1, T2 where T1.YYYY = T2.YYYY and T2.YYYY >= '2001' and T2.YYYY <= '2004'; 
              -> T2 일반 테이블은 전체 데이터를 읽고, T1은 전체 데이터를 읽지않고 2001 ~ 2004 데이터만 읽는다 
              -> 연결고리가 파티션 키인 경우( Nested Loop Join )

[Top]
No.
제목
작성자
작성일
조회
41940ORACLE DB SQL 쿼리 튜닝-1번째[DB 튜닝]
조장복
2023-03-21
718
41939ORACLE DB SQL 쿼리 튜닝[DB 튜닝]
조장복
2023-03-21
632
41938대량 데이터 SQL 튜닝 및 병렬처리 활용 방법[DB 튜닝 및 Parallel 처리]
조장복
2023-03-21
524
41937병렬처리(Parallel Processing) 관련 사항[병렬 Parallel 처리]
조장복
2023-03-21
8659
41936Oracle 온라인 백업중 HA Failover가 실패하는 이유 및 조치 방법[DB 복구]
조장복
2023-03-21
402
41935SQL 쿼리에서 인덱스 컬럼이 조건절에 없을때 인덱스 컬럼을 만들어서 검색속도 개선[DB 튜닝]
조장복
2023-03-21
536
41934데이터 저장 [1]
costudy
2023-03-12
521
Valid XHTML 1.0!
All about the DATABASE... Copyleft 1999-2024 DSN, All rights reserved.
작업시간: 0.021초, 이곳 서비스는
	PostgreSQL v16.2로 자료를 관리합니다