[펌] 이클립스


@@ http://www.sjava.net/28

eclipse 에서 확장자 인식 추가..

Tools Posted at 2008/04/22 09:45
보통 파일에 대한 인식은 확장자를 기반으로 하고 있다..
하지만, 웹을 통한 서비스는 설정을 통해서 확장자가 다른 파일로 웹 어플리케이션으로 인식할 수 있다..

위의 상황에 따라서 만약 .xxx라는 확장자로 .jsp와 동일한 서비스를 하려고 이클립스에서
Windows -> Preferences -> General -> Content Types -> Text 밑에 JSP 를 클릭해서 원하는 확장자를 Add 시켜 주면 됩니다.

by 오서비네 | 2009/04/20 01:30 | java | 트랙백

[펌] 자동화 툴로 채울 수 없는 DB 성능관리 2%

unusable 상태로 index 를 모두 만든후 개별로 rebuild 한다.

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

From : http://ceusee.springnote.com/pages/510466


파티셔닝 테이블

 

자동화 툴로 채울 수 없는 DB 성능관리 2% 2

테이블 파티셔닝의 재발견

남준현 |데이터베이스 모델링, 튜닝 전문 컨설턴트

 

기업들의 데이터베이스가 대용량화 되면서 이를 효과적으로 관리할 수 있는 방안을 찾는 것이 관리자들의 주요 업무가 됐다. 이를 위한 매우 효과적인 방안 가운데 하나가 파티셔닝이다. 일반적으로 단순한 명령어 위주로만 알려져 있지만 실제 현장에서 접하는 파티셔닝의 효용은 그 이상이다. 익숙한 개념이지만 그동안 제대로 알지 못했던 파티셔닝의 의미와 대표적인 활용 사례를 살펴보자.

 

필자는 많은 현장 사이트에서 대용량의 가치있는 데이터들이 놀라운 능력을 보유하고 있는 데이터베이스 안에서 사용자의 무지로 인해 방치돼 있거나 잘못 사용되고 있어 역효과를 일으키는 모습을 많이 보아 왔다. 예를 들어 총 테이블 건수 1억 건이 넘는 상황에서 우리가 어떤 형태로든 건드려야 할 부분이 약 10% 정도라고 할 때 그 테이블 전체를 읽지 않고 1000만 건만 읽을 수 있게 해야 하는 것이 당연하지만 실제로는 그렇지 못한 경우를 많이 봐 왔다. 어떻게 처리해야겠다는 생각도 없이 무조건 명령어(command)부터 날리는 것이다. 그렇다면 필요한 테이블 만을 다루려면 어떻게 해야 할까. 이를 위해 필요한 개념이 바로 테이블 파티셔닝(Table Parti tioning)이다.

파티셔닝은 지난 강좌에서 살펴본 사항들과 함께 어떤 자동화된 툴로도 해결할 수 없는 부분으로 실제로 어떤 상황에서 파티셔닝이 필요하다고 정형화된 법칙은 없다. 중소 용량의 데이터베이스에서도 상황에 따라 꼭 사용해야 하는 경우가 있고, 초대용량의 경우 파티셔닝을 쓰지 않으면 시스템 자체가 관리되지 않을 수도 있다(필자 역시 컨설팅을 하면서 파티셔닝을 이용해 많은 시스템을 효율적으로 운영할 수 있다는 것을 직간접적으로 체험한 바 있다).

그러나 대부분의 파티셔닝 관련 자료들은 형식적으로 파티셔닝의 종류를 나열하고 스크립트 정도를 언급하는 수준이다. 이런 식의 접근은 한계가 명확하다. 오히려 파티셔닝을 올바르게 이용하기 위해서는 먼저 데이터베이스 액세스 방식의 정확한 차이와 장단점 그리고 파티션을 이용한 풀 스캔(full scan)에 대해 정확하게 이해할 필요가 있다. 파티셔닝은 일종의 기능일 뿐이어서 스캔에 대한 정확한 이해없이는 이를 사용할 이유도, 어떻게 사용해야 할지도 전혀 알 수가 없다. 각 스캔 방식의 장단점을 알고 어떤 상황에서 어떤 스캔 방법이 유리한 지를 명확하게 이해해야 그에 대한 보완책으로서 파티셔닝의 가치가 보이기 시작한다.

파티셔닝 세계 입문

대용량 테이블이나 인덱스를 파티셔닝한다는 것은 하나의 Object를 여러 개의 세그먼트로 나눈다는 의미이다. 즉 하나의 테이블이나 인덱스가 동일한 논리적 속성을 가진 여러 개의 단위(partition)로 나누어져 각각이 PCTFREE, PCTUSED, INITRANS, MAXTRANS, TABLESPACE, STORAGE PARAMETER 등 별도의 물리적 속성을 갖는 것이다. 특히 관리해야 할 데이터가 늘어나면 성능과 스토리지 관점에서 문제가 생길 수 있는데, 이를 해결할 수 있는 효율적인 방법 가운데 하나가 곧 파티셔닝이다. 파티셔닝은 보통 다음과 같은 장점을 갖고 있다.

◆ 데이터 액세스시(특히 풀 스캔시) 액세스의 범위를 줄여 성능을 향상시킨다.
◆ 물리적으로 여러 영역으로 파티셔닝해 전체 데이터의 훼손 가능성이 줄어들고 데이터 가용성이 향상된다.
◆ 각 파티션별로 백업, 복구 작업을 할 수 있다.
◆ 테이블의 파티션 단위로 디스크 I/O를 분산해 부하를 줄일 수 있다.

오라클 DBMS에서 제공하는 파티셔닝 방식에는 레인지(range) 파티셔닝, 해시(hash) 파티셔닝, 리스트(list) 파티셔닝, 컴포지트(composite) 파티셔닝(레인지-해시, 레인지-리스트) 등이 있다.

 

특정 컬럼 값을 기준으로 분할하는 레인지 파티셔닝

레인지 파티셔닝은 어떤 특정 컬럼의 정렬 값을 기준으로 분할하는 것이다. 주로 순차적인(historical) 데이터를 관리하는 테이블에 많이 사용된다. 예를 들면 ‘가입계약’이라는 테이블이 있고 여기에 몇 년 동안의 데이터가 쌓여 있다면, 보통 5년치 데이터만 관리하고 이 가운데 자주 액세스하는 하는 것은 최근 1~2년 정도가 일반적이다. 따라서 이를 년별, 월별로 파티셔닝하고 애플리케이션의 SQL을 조정해 전체 데이터가 아닌 최근 정보를 가지고 있는 파티션만 액세스하도록 하면 전체 데이터베이스의 성능을 향상시킬 수 있다. 일부 기업의 경우 가입계약_1999, 가입계약_2000처럼 월별 또는 년별로 테이블을 따로 만들어 사용하기도 하지만 실제로 쓰는데 불편한 점이 많고 액세스하는 SQL이 복잡해지는 단점이 있다. 다음은 레인지 파티션을 만드는 DDL(Data Definition Language) 스크립트다.

          CREATE TABLE CONTRACT

(I_YYYYMMDD VARCHAR2(8), I_CUSTOMER VARCHAR2(9), …… )
TABLESPACE TBS1
STORAGE (INITIAL 2M NEXT 2M PCTINCREASE 0)

PARTITION BY RANGE (I_YYYYMMDD)

(PARTITION PAR_200307 VALUES LESS THAN (‘20030801’),

PARTITION PAR_200308 VALUES LESS THAN (‘20030901’), …… )

PARTITION BY RANGE (COLUMN_LIST)는 특정 컬럼을 기준으로 파티셔닝할 것인지를 결정하는 것이고, VALUES LESS THAN (VALUE_LIST)는 해당 파티션이 어느 범위에 포함될 것인지 상한을 정하는 것이다. PARTITION BY RANGE에 나타나는 COLUMN_LIST를 파티셔닝 컬럼이라고 하며 이 값이 파티셔닝 키를 형성한다. 파티셔닝 컬럼은 결합 인덱스처럼 최대 16개까지 지정할 수 있다. VALUESS LESS THAN에 나타나는 VALUE_LIST는 파티셔닝 컬럼들의 상한 값으로, 여기 지정된 값보다 작은 값만을 저장하겠다는 의미이다. 이런 스크립트에서 지정한 물리적 속성들은 각 파티션들이 생성될 때 개별적으로 물리적 속성을 지정하지 않으면 각 파티션들은 이러한 속성 값을 적용받게 된다.

오직 성능 향상, 해시 파티셔닝

해시 파티셔닝은 특정 컬럼 값에 해시 함수를 적용해 분할하는 방식으로, 데이터의 관리보다는 성능 향상에 초점을 맞춘 개념이다. 레인지 파티셔닝은 각 범위에 따라 데이터 양이 달라 분포도가 일정치 않은 단점이 있는데, 해시 파티셔닝은 이런 단점을 보완해 일정한 분포를 가진 파티션으로 나누고, 균등한 분포도를 가질 수 있도록 조율해 병렬 프로세싱으로 성능을 높인다. 실제로 분포도를 정의하기 어려운 테이블을 파티셔닝을 할 때 많이 이용하고 2의 배수 개수로 파티셔닝하는 것이 일반적이다. 해시 파티셔닝으로 구분된 파티션들은 동일한 논리, 물리적 속성을 가진다(단 테이블스페이스(tablespace)는 유일하게 파티션별로 지정할 수 있다). 또한 레인지 파티션과 달리 각 파티션에 지정된 값들을 DBMS가 결정하므로 각 파티션에 어떤 값들이 들어 있는지 알 수 없다. 그러나 대용량의 분포도가 일정치 않은 테이블을 마이그레이션할 때는 프로그램 병렬 방식과 함께 유용하게 사용할 수 있다. 다음은 해시 파티션을 만드는 DDL 스크립트이다.

          CREATE TABLE CONTRACT

( SERIAL NUMBER, CODE VARCHAR2(4), ……)
TABLESPACE TBS1
STORAGE (INITIAL 2M NEXT 2M PCTINCREASE 0)
PARTITION BY HASH(SERIAL)

(PARTITION PAR_HASH_1 TABLESPACE TBS2,

PARTITION PAR_HASH_2 TABLESPACE TBS3, ……)

함께 쓰일 때 더욱 강력한 리스트 파티셔닝

리스트 파티셔닝은 특정 컬럼의 특정 값을 기준으로 파티셔닝을 하는 방식이다. 주로 이질적인(distinct) 값이 많지 않고 분포도가 비슷하며 다양한 SQL의 액세스 패스에서 해당 컬럼의 조건이 많이 들어오는 경우 유용하게 사용된다. 예를 들어 ‘서비스 계약’이라는 테이블이 있고 서비스를 최초 가입한 대리점을 ‘가입 대리점’, 변경사항을 처리한 대리점을 ‘처리 대리점’이라고 한다면 모든 서비스의 가입, 해지, 전환 등의 처리 데이터에는 이 두 대리점이 존재한다. 테이블 구조를 보면 다음과 같다.

          CREATE TABLE SERVICE_CONTRACT

(I_YYYYMMDD VARCHAR2(8), I_CUSTOMER VARCHAR2(6),

I_DLR_IND VARCHAR2(2), I_DEALER VARCHAR2(6), ……)

즉 I_DLR_IND(대리점 구분)라는 컬럼이 존재하고 ‘A’일 때는 ‘가입 대리점’, ‘S’일 때는 ‘처리 대리점’이라고 할 때 대부분의 조회 패턴에는 가입 대리점 또는 처리 대리점에 해당하는 값이 들어오기 마련이다. 이럴 때 I_DLR_IND로 리스트 파티셔닝을 한다면 어떨까. 즉 집합의 서브 타입을 분류할 때 리스트 파티션은 매우 유용하다. 지금 예로 든 것은 단편적인 것에 불과하지만 리스트 파티셔닝의 위력은 강력하다. 특히 컴포지트 파티션에서 레인지 파티션과 함께 사용하면 전체 데이터베이스의 성능을 크게 향상시킬 수 있다. 다음은 리스트 파티션을 만드는 DDL 스크립트이다.

          CREATE TABLE SERVICE_CONTRACT

(I_YYYYMMDD VARCHAR2(8), I_CUSTOMER VARCHAR2(6),

I_DLR_IND VARCHAR2(2), I_DEALER VARCHAR2(6), …….)
TABLESPACE TBS1
STORAGE (INITIAL 2M NEXT 2M PCTINCREASE 0)
PARTITION BY LIST (I_DLR_IND)

(PARTITION PAR_A VALUES (‘A’), PARTITION PAR_S VALUES (‘S’))

PARTITION BY LIST에 나타나는 COLUMN_LIST는 파티셔닝 컬럼으로 파티션 키에 해당하고(단 단일 컬럼만 지정할 수 있다), VALUESS LESS THAN에 나타나는 VALUE_LIST는 파티셔닝 컬럼들의 값이다. 여기에 나타낸 값에 해당하는 행들을 저장하겠다는 의미가 된다.

레인지의 장점을 그대로, 레인지-해시 컴포지트 파티셔닝

레인지-해시 컴포지트 파티셔닝은 레인지 방식을 사용해 데이터를 파티셔닝하고 각각의 파티션 내에서 해시 방식으로 서브 파티셔닝하는 방식이다. 서브 파티션이 독립된 세그먼트가 되는 것이 특징으로, 다음과 같은 장점이 있다.

◆ 관리와 성능 등 레인지 파티션의 장점을 그대로 수용한다.
◆ 해시 파티션의 이점인 데이터 균등 배치와 병렬화
◆ 서브 파티션에 특정 테이블스페이스를 지정할 수 있다.
◆ 서브 파티션별로 풀 스캔을 할 수 있어 스캔 범위를 줄여 성능을 향상시킨다.

레인지 파티션에서 해당 테이블이 단지 논리적인 구조이고 실제 데이터는 파티셔닝된 세그먼트에 저장됐던 것처럼 컴포지트 파티션에서도 해당 테이블과 파티셔닝된 테이블은 단지 파티셔닝을 위한 논리적인 구조일 뿐이다. 데이터는 가장 하위에 위치한 서브 파티션 영역에 저장된다. 다음은 레인지-해시 컴포지트 파티션을 생성하는 DDL 스크립트이다. PARTITION BY RANGE (I_YYYYMMDD)에 의해 레인지로 파티션을 한 후 SUBPARTITION BY HASH에 의해 서브 파티셔닝을 수행했음을 알 수 있다.

          CREATE TABE TB_RANGE_HASH

(I_YYYYMMDD VARCHAR2(8), I_SERIAL NUMBER, SALE_PRICE NUMBER, ……)
TABLESPACE TBS1
STORAGE (INITIAL 2M NEXT 2M PCTINCREASE 0)
PARTITION BY RANGE (I_YYYYMMDD)
SUBPARTITION BY HASH (I_SERIAL)

(PARTITION SALES_1997 VALUES LESS THAN (‘19980101’)

(SUBPARTITION SALES_1997_Q1 TABLESPACE TBS2,

SUBPARTITION SALES_1997_Q2 TABLESPACE TBS3), ……)

레인지-리스트 컴포지트 파티셔닝

레인지-리스트 컴포지트 파티셔닝은 레인지 방식을 사용해 데이터를 파티셔닝하고 각 파티션 안에서 리스트 방식을 이용해 서브 파티셔닝하는 방식이다(이때 서브 파티션은 독립된 세그먼트가 된다). 레인지-리스트 컴포지트 파티션은 레인지-해시 컴포지트 파티션과 비슷하지만 서브 파티션이 리스트 파티션이라는 점이 다르다. 실제 업무에서는 레인지-해시보다 유용한 면이 많다. 다음은 레인지-리스트 컴포지트 파티션을 생성하는 DDL 스크립트이다.

          CREATE TABLE TB_RANGE_LIST (

I_YYYYMMDD VARCHAR2(8), I_AGR_IND VARCHAR2(2), I_DELAER VARCHAR2(6), …….)
TABLESPACE TBS1
STORAGE (INITIAL 2M NEXT 2M PCTINCREASE 0 MAXEXTENTS UNLIMITED)
PARTITION BY RANGE (I_YYYYMMDD)
SUBPARTITION BY LIST (I_AGR_IND)

(PARTITION PAR_1997 VALUES LESS THAN (‘19980101’)

(SUBPARTITION PAR_1997_A VALUES (‘A’), SUBPARTITION PAR_1997_A VALUES (‘S’)),

……)

파티션된 인덱스의 참뜻

‘파티션된 인덱스(partitioned index)’라고 하면 대부분의 개발자들은 로컬 인덱스를 떠올린다. 또한 파티션된 테이블에서만 쓰이는 것으로 생각한다. 그러나 이것은 명백한 오산이다. 파티션된 인덱스는 파티션된 테이블과 별개의 것으로, 단지 많은 상호 연관을 갖고 있을 뿐이다. 파티션된 인덱스는 문자 그대로 인덱스를 파티셔닝한 것으로, 해당 테이블이 파티션된 테이블이든 파티션되지 않은(non-parti tioned) 테이블이든 상관없이 만들 수 있다. 예를 들면 ‘EMP’ 테이블의 크기가 상당히 크고 파티션되지 않은 일반 테이블일 경우 다음과 같은 과정을 통해 파티션된 인덱스를 만들 수 있다. 이를 ‘Global Prefixed Partitioned Index’라고 부르는데, 파티션 인덱스와 마찬가지로 대용량 데이터 환경에서 성능을 높이고 관리를 편리하게 하기 위해서다.

<그림 1> 파티션된 인덱스와 파티션되지 않은 인덱스의 차이

          CREATE INDEX EMP_IDX1 ON EMP (DEPTNO)
GLOBAL
PARTITION BY RANGE (DEPTNO)

(PARTITION PAR_10 VALUES LESS THAN (‘20’) TABLESPACE TBS1,

PARTITION PAR_20 VALUES LESS THAN (‘30’) TABLESPACE TBS2,

PARTITION PAR_30 VALUES LESS THAN (‘40’) TABLESPACE TBS3,

PARTITION PAR_40 VALUES LESS THAN (‘50’) TABLESPACE TBS4,

PARTITION PAR_MAX VALUES LESS THAN (MAXVALUE) TABLESPACE TBS5)

파티션된 인덱스가 유용한 이유는, 앞서 파티션의 개념에서 설명한 것처럼 하나의 인덱스를 여러 개의 독립적인 물리 속성을 가진 세그먼트로 나누어 생성, 관리할 수 있기 때문이다. 오라클 DBMS에서 제공하는 인덱스는 글로벌/로컬 인덱스와 Prefixed/Non-Prefixed 인덱스로 분류된다.

파티션된 인덱스와 일반 인덱스 사이의 차이점은 파티션 테이블과 일반 테이블의 그것과 동일하다. 인덱스는 인덱스 컬럼과 Rowid 순으로 값이 정렬되는데, 이런 특성은 파티션 인덱스에서도 동일하다. 많은 개발자들이 파티션된 인덱스는 전체 테이블 값이 정렬되지 않는다고 생각하지만 이것은 사실과 다르다. 글로벌 파티션된 인덱스의 경우 테이블에 대해 값 정렬이 보장돼 있으며, 인덱스도 파티션별로 독립적으로 관리할 수 있다. 두 가지 방식의 차이는 <그림 1>과 같다.

파티션되지 않은 인덱스는 하나의 루트(root) 노드에서 리프(leaf) 노드까지 전체적인 밸런스를 유지하는 구조이고, 파티션 인덱스는 파티션 별로 독립적인 루트 노드와 리프 노드를 갖고 있음을 알 수 있다. 따라서 파티션되지 않으면 대용량 테이블에서는 글로벌 인덱스의 깊이(depth)가 매우 깊어질 수 있는 단점이 있다. 반면 파티션된 인덱스는 각 파티션별 깊이가 일반 인덱스의 깊이보다 얕고 인덱스도 파티션별로 할 수 있어 병렬 프로세싱을 이용한 인덱스 관리에 매우 효과적이다.

그렇다면 글로벌 인덱스와 로컬 인덱스는 어떤 차이가 있는 것일까? 많은 개발자들이 파티션됐는지 여부로 판단하지만 이것은 잘못된 생각이다. 앞서 설명한 것처럼 글로벌 인덱스도 파티셔닝할 수 있으며, 이를 파티션별로 관리할 수 있다. 글로벌 인덱스와 로컬 인덱스의 가장 큰 차이는 ‘정렬’이다. 글로벌 인덱스는 테이블 전체에 대해 인덱스된 컬럼과 Rowid 순으로 정렬되고, 로컬 인덱스는 해당 파티션 내에서만 인덱스된 컬럼과 Rowid 순으로 정렬된다.

또한 로컬 인덱스는 ‘Local’이라는 말에서 알 수 있듯이 지역적인 인덱스로, 해당 테이블(base table)의 파티션 키로 파티셔닝된 인덱스다. 일반적으로 로컬 인덱스의 구성 컬럼에 반드시 파티션 키가 포함돼야 가능한 것으로 알려져 있지만 로컬 인덱스에는 파티션 키가 포함되어 있지 않아도 사용할 수 있다.

다음 예제를 보자. PACKAGE_DLR_IDX1 인덱스의 구성 컬럼에 테이블 파티션 키인 I_DLR_IND가 포함되지 않아도 검색조건에 I_ DLR_IND = ‘C’라는 검색 조건이 있기 때문에 해당 파티션의 로컬 인덱스를 이용하는 것을 알 수 있다.

          select
*from PACKAGE_DLR
where i_package = ‘AAA’ and i_dlr_ind = ‘C’
Operation
Object Name PStart PStop
SELECT STATEMENT Hint=CHOOSE

TABLE ACCESS BY LOCAL INDEX ROWIDPACKAGE_DLR 3 3

INDEX RANGE SCAN PACKAGE_DLR_IDX1 3 3

글로벌 인덱스는 전역적인 인덱스로, 기본적으로는 파티션되지 않은 인덱스이다. 대부분의 개발자들은 글로벌 인덱스를 파티셔닝해 사용할 생각을 하지 못하는데, 대용량 테이블에서 인덱스 관리의 효율성을 높이고 인덱스 검색 성능을 높이기 위해서는 이를 파티셔닝하는 것이 좋다.

글로벌 인덱스는 기본 테이블의 파티션 키와 무관하게 파티셔닝하는 것으로 설사 기본 테이블의 파티션 키로 글로벌 인덱스를 파티셔닝했다고 해도 로컬 인덱스처럼 동일파티셔닝(equipartitioning) 된 개념이 아니므로 테이블 DDL시 전체 인덱스를 다시 생성해야 한다.

그렇다면 글로벌 파티션 인덱스의 인덱스 컬럼 값은 어떻게 전체 테이블에 대해 정렬을 보장하는 것일까. 예를 들어 5000만 건의 파티션되지 않은 EMP 테이블을 부서번호에 따라 파티셔닝했다고 가정하면 다음과 같다.

          CREATE INDEX EMP_IDX1 ON EMP (DEPTNO)
GLOBAL
PARTITION BY RANGE (DEPTNO)
(PARTITION PAR_10 VALUES LESS THAN (‘20’) TABLESPACE TBS1,
PARTITION PAR_20 VALUES LESS THAN (‘30’) TABLESPACE TBS2,
PARTITION PAR_30 VALUES LESS THAN (‘40’) TABLESPACE TBS3,
PARTITION PAR_40 VALUES LESS THAN (‘50’) TABLESPACE TBS4,
PARTITION PAR_MAX VALUES LESS THAN (‘MAXVALE’) TABLESPACE TBS2,

<그림 2>는 Global Prefixed Partitioned 인덱스의 구조다. Pre fixed와 Non-Prefixed는 인덱스 파티셔닝 키가 인덱스의 선두 컬럼으로 오는가 그렇지 않은가의 차이가 있다. <그림 2>에서도 ‘Pre fixed’란 인덱스의 파티션 키(DEPTNO)가 인덱스 선두 컬럼(DEPTNO)이 되는 것을 알 수 있다. 글로벌 인덱스의 경우 모든 인덱스 컬럼 값이 정렬돼 있다. 각 인덱스 파티션의 루트 블럭(root block)에 들어가는 값들이 인덱스 파티션에 따라 정렬되기 때문에 자연적으로 리프 블럭(leaf block)에 들어가는 모든 값들도 정렬되는 것이다. 반면 Global Non-Prefixed 인덱스를 파티셔닝하면 레인지 파티셔닝 방식으로만 가능하다. 이것은 정렬 때문인데, 레인지 파티션은 정렬 기능을 이용해 파티셔닝 키 자체를 생성하는데 반해 다른 파티셔닝 방식은 정렬과 상관없이 수행하기 때문이다.

로컬 인덱스는 Prefixed 인덱스와 Non-Prefixed 인덱스를 모두 지원한다. 로컬 인덱스는 기본적으로 현재 테이블의 파티션 키가 곧 인덱스 파티션 키가 되기 때문에 인덱스 컬럼에 현재 테이블의 파티션 키가 포함되지 않아도 인덱스를 생성할 수 있다. 또한 인덱스 컬럼 값의 정렬이 전체 테이블에 대해 보장된 것도 아니기 때문에 인덱스 파티션 키가 인덱스의 선두 컬럼이 될 필요가 없다. 또한 Non-Parti tioned 인덱스이든 파티션 인덱스든 상관없이 인덱스를 이용하고자 할 때는 무조건 인덱스 파티션 키를 조회해야 하는 글로벌 인덱스와 달리 로컬 인덱스는 조회 검색조건에 파티션 키가 들어올 수도 있고 들어오지 않을 수도 있다.

<그림 2> Global Prefixed Partitioned 인덱스

대용량 DB 테이블과 인덱스 전략

파티션 인덱스 전략은 파티션 테이블과 밀접하게 연관돼 수립해야 하지만 여기서는 파티션 인덱스를 위주로 이야기를 풀어본다. 먼저 인덱스 크기에 대한 논의는 기본적으로 테이블보다는 훨씬 작게 생성, 관리하는 것이 원칙이다. 따라서 중소 용량의 데이터베이스 환경에서는 파티션 인덱스의 유용성을 따질 필요가 없다. 단 중소 용량의 데이터 환경에서도 테이블이 파티셔닝돼 있다면 파티션 인덱스를 고려해야 한다. 또한 파티션되지 않은 인덱스(일반 인덱스) 전략을 기본으로 해 테이블이 파티셔닝된 경우와 인덱스를 파티셔닝했을 때의 장점을 비교해 봐야 한다.

먼저 테이블 파티션 키가 항상 ‘=’로 들어오는 경우 또는 파티션 범위가 크지 않은 경우에는 로컬 인덱스가 최상이다. 인덱스 컬럼의 순서와 구성은 액세스 패스에 따라 생성하면 되지만 최대한 가볍게 생성하는 것이 좋다. 기본 테이블의 파티션 키는 반드시 포함될 필요가 없으나, 테이블이 레인지 파티션이고 한 파티션 범위 안에서 파티션 키의 분포도가 좋을 경우 이를 포함하는 것을 고려해 볼만하다. 이렇게 하면 각 파티션당 인덱스가 파티션되지 않았을 때보다 가벼워지고 데이터 마이그레이션을 할 때도 테이블 파티션과 인덱스 파티션이 동일하므로 exchange, add, drop, split 등 파티션별 관리도 용이하다.

또한 빠른 응답 시간을 요구하는 환경에서 대용량 파티션 테이블의 조회 조건에 파티션 키가 들어오지 않을 가능성이 있다면 파티션 글로벌 인덱스를 고려해 볼만하다. 이렇게 하면 파티션되지 않은 글로벌 인덱스와 달리 레인지 파티션 별로 인덱스가 가벼워지는 장점이 있고, 레인지 파티션 별로 인덱스 split와 rebuild 명령을 독립적으로 수행할 수 있다. 컬럼 분포도에 따른 파티셔닝이나 민감한(critical)한 상수 레인지에 대해서는 파티션을 독립적으로 생성해 인덱스 크기를 줄임으로써 인덱스 검색 시간을 줄일 수 있는 이점도 있다.

exchange는 파티션된 테이블의 특정 파티션과 파티션되지 않은 일반 테이블 간의 구조를 서로 바꾸는 것으로, 대용량의 파티션된 테이블을 관리하는데 상당한 효과가 있다. <그림 2>와 같이 데이터가 없는 새로운 데이터 테이블과 데이터가 들어 있는 파티션 2를 exch ange하면 파티션 2에 해당하는 디렉토리 정보가 새로운 데이터로 바뀌고 새 테이블에 데이터가 들어간다. 이것은 실제 데이터가 이동하는 것이 아니라 데이터를 저장하는 테이블 정보만을 업데이트하는 것이다. 한 가지 주의할 점은 exchange하고자 하는 파티션과 테이블의 구조가 같아야 하고 속성들의 특성도 같아야 한다는 사실이다. exchange의 기본 문법은 다음과 같다.

<그림 3> 대용량 DB에서 exchange 작업

<그림 4> 대용량 DB에서 split 작업

          Alter table Tb_Partition
Exchange partition par_200306
With table Tb_Exchange
(Without validation Including indexes)

한편 파티션된 대용량 테이블에 split 함수를 실행하면 많은 시간이 걸린다. 이럴 때 exchange 기능을 이용하면 빠르고 안전하게 작업할 수 있다. <그림 4>에서 보는 것처럼 split를 해야 하는 파티션을 exchange에 의해 빈 공간으로 만든 다음 split을 하고 다시 데이터를 채우기 위해 split하는 것이다. 이렇게 하면 대용량의 데이터라도 매우 빠른 시간 내에 split 작업을 마칠 수 있다. 한편 대부분의 DBA들과 개발자들은 동일한 테이블을 생성할 때 create table ~ as select 구문을 이용한다. 대용량의 데이터일 경우 parallel 옵션을 주고 생성하기도 한다. 만약 1억 건의 테이블을 그대로 생성한다고 할 때 어떤 방법이 효과적일까. 이렇게 파티션된 대용량 테이블을 생성할 때는 exchange, program parallel 방법을 사용하는 것이 바람직하다.

<그림 5>는 이 과정을 도식화한 것이다. 먼저 생성할 TB_PART_1 테이블의 빈 껍데기를 만든다. 대용량의 파티션된 테이블의 파티션 각각을 create table ~ as select 구문의 parallel 옵션을 이용해 각 테이블로 생성한다. 미리 생성해 놓은 TB_PART_1 테이블의 파티션과 만들어 놓은 테이블들을 exchange하는 것이다. 이 때 파티션별로 200105.sql, 200106.sql, 200107.sql…… 형식으로 만들어 놓고 이 프로그램들을 동시에 실행하면(program parallel) 극적인 효과를 볼 수 있다.

이번엔 데이터 마이그레이션에 대해 살펴 보자. 원격으로 데이터를 옮겨야 할 때 보통 database link를 이용한다. 네트워크를 통해 데이터를 옮기면 직렬(serial)로 데이터가 이동되므로 속도가 현저하게 떨어지기 때문이다. 따라서 소스 테이블을 파티셔닝하고 해당 파티션을 액세스하는 프로그램을 각각 띄워 병렬 프로세싱을 하면 매우 빠른 속도로 데이터를 옮길 수 있다. 소스 테이블을 파티셔닝할 수 있는 상황이라면 테이블의 분포를 보고 레인지나 리스트 방식으로 파티셔닝할 수 있고, 일정한 분포가 존재하지 않는 테이블이라면 해시 파티셔닝으로 분포도를 고르게 나눈 다음 해당 파티션을 읽는 뷰를 액세스해 데이터를 옮기는 것이 좋다.

예를 들어 다음은 중대형 정도 크기인 약 2700만 건의 회원 테이블을 옮기는 DDL 스크립트다. 앞서 언급한 대로 database link를 이용해 처리하면 네트워크의 속도가 떨어져 엄청난 시간이 소요된다. 그러나 이것을 일반 테이블을 여러 개로 파티션을 나누어서 파티션과 병렬 처리하면 성능이 크게 향상된다. 작업 순서는 다음과 같다.

          create table t_cust_hash
storage (initial 5M next 5M pctincrease 0)
partition by hash(mem_no)

(

partition par_hash_1 TABLESPACE TS_DATA,

partition par_hash_2 TABLESPACE TS_DATA,

partition par_hash_3 TABLESPACE TS_DATA,

partition par_hash_4 TABLESPACE TS_DATA,

partition par_hash_6 TABLESPACE TS_DATA,

partition par_hash_7 TABLESPACE TS_DATA,

partition par_hash_8 TABLESPACE TS_DATA,

partition par_hash_9 TABLESPACE TS_DATA,

partition par_hash_10 TABLESPACE TS_DATA,

)
nologging
as
select /*+ parallel(x 10) */ * from t_cust x
이제 다음과 같이 소스 테이블 뷰를 생성한 후
create or replace view t_cust_1
as select * from t_cust_hash partition (par_hash_1);
create or replace view t_cust_2
as select * from t_cust_hash partition (par_hash_2);
create or replace view t_cust_3
as select * from t_cust_hash partition (par_hash_3)
……
다음과 같이 프로그램 패러럴(program parallel) 작업을 동시에 실행한다.
T_cust_1.sql
create table t_cust_1
storage (initial 5M next 5M pctincrease 0)
nologging
tablespace njh
as
select /*+ parallel(x 4) */ * from t_cust_1@remote x;
T_cust_2.sql
create table t_cust_2
storage (initial 5M next 5M pctincrease 0)
nologging
tablespace njh
as
select /*+ parallel(x 4) */ * from t_cust_2@remote x

이것은 단적인 예에 지나지 않는다. 활용할 수 있는 사례는 얼마든지 있다. 한편 인덱스는 전체 데이터에 대해 해당 컬럼의 값으로 정렬하기 때문에 대용량 테이블의 경우 create, rebuild 명령을 실행할 때 많은 시간이 필요하다. 이 때 파티션된 인덱스를 만들면 인덱스의 생성과 관리를 더 효과적으로 할 수 있다. 다음은 파티션된 인덱스를 Unusable로 생성한 사례다(로컬/글로벌 파티션된 인덱스).

먼저 파티션 인덱스를 ‘unusable’ 옵션을 이용해 생성한다. 실제 데이터를 정렬해 만드는 것이 아니라 일종의 껍데기를 만드는 과정이다. 이제 앞서 살펴본 병렬 처리를 이용해 여러 파티션을 동시에 rebuild를 하면 대용량 데이터라도 빠른 시간에 인덱스를 생성할 수 있다.

<그림 5> 동일 테이블을 만들 때

          CREATE INDEX EMP_IDX1 ON EMP (DEPTNO)
GLOBAL
PARTITION BY RANGE (DEPTNO)

(PARTITION PAR_10 VALUES LESS THAN (‘20’) TABLESPACE TBS1,

PARTITION PAR_20 VALUES LESS THAN (‘30’) TABLESPACE TBS2,

PARTITION PAR_30 VALUES LESS THAN (‘40’) TABLESPACE TBS3,

PARTITION PAR_40 VALUES LESS THAN (‘50’) TABLESPACE TBS4,

PARTITION PAR_MAX VALUES LESS THAN (MAXVALUE) TABLESPACE TBS5)
UNUSABLE;
이제 파티션별로 index1.sql, index2.sql 등을 독립적으로 병렬 실행한다.
ALTER INDEX EMP_IDX1 REBUILD PARTITION PAR_10 PARALLEL 4;
---‘ index1.sql
ALTER INDEX EMP_IDX1 REBUILD PARTITION PAR_20 PARALLEL 4;
---‘ index2.sql
ALTER INDEX EMP_IDX1 REBUILD PARTITION PAR_30 PARALLEL 4;
---‘ index3.sql
ALTER INDEX EMP_IDX1 REBUILD PARTITION PAR_40 PARALLEL 4;
---‘ index4.sql
ALTER INDEX EMP_IDX1 REBUILD PARTITION PAR_MAX PARALLEL 4; ---‘ index5.sql

지금까지 테이블 파티셔닝에 대해 다뤄봤다. 자동화된 성능관리 툴로 커버할 수 없는 영역을 살펴보고 있으나 가장 중요한 것은 데이터베이스 액세스 개념에 대해 정확하게 이해하는 것이다. 많은 사람들이 파티셔닝을 알고 있지만 정확하게 사용하고 있지 못하는 현실이 안타까울 때가 많다. 그러나 이 점은 역설적으로 파티셔닝의 매력이기도 하다. 노력하는 데이터베이스 관리자 만이 도전해 볼 수 있는 영역이 바로 ‘파티셔닝’ 분야이기 때문이다.


by 오서비네 | 2009/03/27 16:13 | Oracle | 트랙백

[펌] PE(Partition Exchange)를 이용해 대량의 Delete를 대신하기

From : http://www.egloos.com/egloo/insert.php?eid=e0040769 (욱짜님의 블로그)

PE(Partition Exchange)를 이용해 대량의 Delete를 대신하기

Advanced Oracle 2008/10/31 17:43

대량의 데이터에 대한 Delete 작업이 필요한 경우가 있다. 가령 다음과 같이...

Table t1의 Column c1 값이 1인 경우에 대해 모든 Data를 삭제하고 싶다.

하지만 이 경우 대량의 Undo와 Redo Data로 인해 심각한 성능 문제가 대두된다. 그래서 이런 경우 다음과 같은 방식이 권장되기도 한다.

  • Table t1의 Copy 본인 Table t2를 만든다.
  • Table t2에 Column c1 값이 1인 경우를 제외하고 Data를 Copy한다.
  • Table t1을 rename(혹은 drop)하고 Table t2를 t1으로 rename한다.
  • Index를 기존과 똑같은 이름으로 생성한다.

위의 방법이 가끔 사용되기는 하지만 몇 가지 골치아픈 절차가 따른다. 가령 Index이름을 어떻게 맞추어 줄 것인가, Down Time을 어떻게 최소화시킬 것인가....

이 경우에 Partition Exchange를 사용하면 좀 더 간편하게 동일한 목적을 달성할 수 있다.

아래 예제를 보자.

다음과 같은 Table t1이 있다.

create table t1(c1 int, c2 int);

alter table t1 add constraint t1_pk
primary key (c1, c2);

create index t1_n2 on t1(c2);

insert into t1
select mod(level,5)+1, level
from dual
connect by level <= 10000
;

select c1, count(*)
from t1
group by c1
;

C1 COUNT(*)
---------- ----------
1 2000
2 2000
4 2000
5 2000
3 2000


우리의 목적은 Column c1의 값이 "1"인 경우의 Data를 모두 삭제하는 것이다.


이를 위해서 Table t1의 복사본인 Table t2를 만든다.

단 Partition Exchange를 사용하기 위해 하나의 Partition으로 구성한다. Column c1 값이 1이 배제되도록 Data를 생성한다. 즉 Column c1의 값이 1인 경우가 삭제된 것과 동일하다.

create table t2(c1, c2)
nologging
partition by range(c1)
(
partition p1 values less than(maxvalue)
)
as
select c1, c2 from t1
where c1 in (2, 3, 4, 5)
;



Partition Exchange를 위해 Index 구성도 통일시켜준다.

create unique index t2_n1 on t2(c1, c2) local;

alter table t2 add constraint t2_pk
primary key (c1, c2) using index;

create index t2_n2 on t2(c2) local;


다음과 같이 Table t1과 Partition t2.p1을 Exchange한다.

alter table t2
exchange partition p1
with table t1
including indexes
;


Table t1에서 Column c1의 값이 1인 경우는 완전히 삭제되었다.

select c1, count(*)
from t1
group by c1
;

C1 COUNT(*)
---------- ----------
2 2000
4 2000
5 2000
3 2000


Table t2는 Table t1으로 교체되었으므로 모든 Data를 가지고 있다.

select c1, count(*)
from t2
group by c1
;

C1 COUNT(*)
---------- ----------
1 2000
2 2000
4 2000
5 2000
3 2000




Index 또한 이름이 그대로 유지되며 VALID 상태를 유지한다.



select index_name, status
from user_indexes
where table_name = 'T1'
;

INDEX_NAME STATUS
-------------------- ------------------------
T1_PK VALID
T1_N2 VALID
 


Partition Exchange를 사용함으로써 훨씬 간단하게 원하는 바를 달성할 수 있다.


간단하지만 유용한 기법이라고 하겠다.

크리에이티브 커먼즈 라이선스
Creative Commons License

'Advanced Oracle' 카테고리의 다른 글

Oracle Library Cache를 이해하기 위한 숙제  (1)2008/11/09
[새로쓴 대용량 데이터베이스 솔루션 I]을 보고 깜짝 놀라다.  (9)2008/11/01
PE(Partition Exchange)를 이용해 대량의 Delete를 대신하기  (11)2008/10/31
FIRST_ROWS vs. FIRST_ROWS_N 그리고 Cost의 개념  (10)2008/10/23
11g is better - 10046 Trace가 좋아졌다!!!  (0)


=======
  1. extremedb 2008/11/02 17:50 Modify/Delete Reply

    아주 유용한 예제입니다.
    PE 를 이런용도로도 사용하는 군요.^^
    대용량 테이블일 경우 down time 을 더 낮추려면 Without Validation 을 적용하는것을
    추가적으로 고려해야 성능이 더욱 좋아질 것입니다.
    물론 PE 될 테이블의 데이터가 이미 VALID 된 상태라고 가정하고 말입니다.
    좋은글 감사합니다.

  2. Stargazer 2008/11/03 09:46 Modify/Delete Reply

    생각의 전환...역시...대단하십니다..

  3. 백면서생 2008/11/03 16:27 Modify/Delete Reply

    좋은 글 잘 봤습니다.~

  4. oracler 2008/11/08 08:58 Modify/Delete Reply

    오...이런,, 좋으네요 그냥 대단하시다는 말밖에 안나옵니다.

    그런데 전통적인 위의 방법에서 인덱스 이름 맟추기와 다운타임 최소화가
    왜 문제가 되는지 모르겠네요 ?
    결국 alter index rename 과 drop, rename 시간인데
    인덱스 이름 맟추는것도 문제가 없으며 모두 금방 끝나는거 아닌가여

  5. 욱짜 2008/11/08 11:35 Modify/Delete Reply

    인덱스 이름의 경우 전통적인 방법에 비해서 좀 더 편하다 정도로 이해하시면 될 거 같습니다. 수동으로 인덱스 이름을 맞추어주는 경우 오타 등으로 인해 생각지 못한 문제가 발생하는 경우가 종종 있습니다.

    Down Time의 경우에는 DML을 Blocking하는 DDL Operation의 회수가 여러번에서 1번으로 줄어든다 정도의 의미로 이해하시면 될 거 같습니다. 이로 인해 시간이 크게 절약된다는 의미는 아니구요.


by 오서비네 | 2009/03/27 15:46 | Oracle | 트랙백 | 덧글(2)

[펌] 파티션 관리

From : http://abcpost.net/598

파티션 관리 Rdbms 2009/03/16 09:18


파티션은 파티션 테이블 전체가 하나의 오브젝트로 등록되어 관리되며 개별 파티션도 역시 하나의 오브젝트로 관리됨으로 일반  테이블과 인덱스와는 약간 다른 처리가 발생 하게 된다.

파티션 테이블의 일반적인 백업과 복구에서는 파티션 특성을 제외하면 일반 테이블의 백업과 복구와 특별한 차이가 없다.

하지만 파티션 인덱스에서는 로컬인덱스와 글로벌 인덱스에 따라서 다른 처리가 발생하게 되며 관리상의 부하도 클 수 있으므로 주의해야 한다.

파티션의 관리는 크게 다음과 같이 나눌 수 있다.

  • 파티션 추가/삭제/병합
  • 파티션 변환(파티션 테이블과 일반테이블의 변환)
  • 기타 관리

파티션의 종류에 따라 가능한 작업이 정해져 있으며 이는 종류에 따른 파티션의 특성에 기인한다. 파티션의 종류별로 가능한 ALTER 명령은 오라클 매뉴얼을 참고하기 바란다.

(http://otn.oracle.com/docs/products/oracle9i/
doc_library/901_doc/server.901/a90117/partiti.htm#1311
)

파티션의 관리는 실제적인 방법과 명령의 사례가 필요하므로 테스트를 위해서 오라클 9i에서 추가된 데모 데이터베이스 Sales History를 예로 든다.

테스트 환경 설정

오라클 9i에 접속 가능한 경우에는 sh 유저의 데이터 베이스에서 확인이 가능한다. 없는 경우를 위해서 간단히 다음과 같이 작업한다.

테스트를 위해서 먼저 partition table을 다음과 같이 생성한다. 테스트를 위해서 파티션의 데이터 타입을 DD-MON-YYYY 에서 YYYYMMDD로 하였다.

CREATE TABLE sales
(prod_id NUMBER(6) CONSTRAINT sales_product_nn NOT NULL,
cust_id NUMBER CONSTRAINT sales_customer_nn NOT NULL,
time_id DATE CONSTRAINT sales_time_nn NOT NULL,
channel_id CHAR(1) CONSTRAINT sales_channel_nn NOT NULL,
promo_id NUMBER(6) ,
quantity_sold NUMBER(3) CONSTRAINT sales_quantity_nn NOT NULL,
amount NUMBER(10,2) CONSTRAINT sales_amount_nn NOT NULL,
cost NUMBER(10,2) CONSTRAINT sales_cost_nn NOT NULL)
PARTITION BY RANGE (time_id)
(PARTITION Q1_1998 VALUES LESS THAN (TO_DATE(''''19980401'''',''''YYYYMMDD'''')),
PARTITION Q2_1998 VALUES LESS THAN (TO_DATE(''''19980701'''',''''YYYYMMDD'''')),
PARTITION Q3_1998 VALUES LESS THAN (TO_DATE(''''19981001'''',''''YYYYMMDD'''')),
PARTITION Q4_1998 VALUES LESS THAN (TO_DATE(''''19990101'''',''''YYYYMMDD'''')),
PARTITION Q1_1999 VALUES LESS THAN (TO_DATE(''''19990401'''',''''YYYYMMDD'''')),
PARTITION Q2_1999 VALUES LESS THAN (TO_DATE(''''19990701'''',''''YYYYMMDD'''')),
PARTITION Q3_1999 VALUES LESS THAN (TO_DATE(''''19991001'''',''''YYYYMMDD'''')),
PARTITION Q4_1999 VALUES LESS THAN (TO_DATE(''''20000101'''',''''YYYYMMDD'''')),
PARTITION Q1_2000 VALUES LESS THAN (TO_DATE(''''20000401'''',''''YYYYMMDD'''')),
PARTITION Q2_2000 VALUES LESS THAN (TO_DATE(''''20000701'''',''''YYYYMMDD'''')),
PARTITION Q3_2000 VALUES LESS THAN (TO_DATE(''''20001001'''',''''YYYYMMDD'''')),
PARTITION Q4_9999 VALUES LESS THAN (MAXVALUE));

INSERT INTO SALES
SELECT PROD_ID, CUST_ID,
ADD_MONTHS(TIME_ID,(ROWNUM-1)*3)+1 TIME_ID,
CHANNEL_ID, PROMO_ID, QUANTITY_SOLD, AMOUNT_SOLD, ROWNUM
FROM SH.SALES
WHERE ROWNUM <= 20 ;

COMMIT ;


위 테이블은 time_id를 partition key로 한 range partition table이다. 다음엔 이sales table에 SQL*Loader를 이용하여 데이터를 loading한다. 해당 control 화일 및 데이터 화일은다음 디렉토리에 있다. 이 디렉토리로 이동해서 작업을 하면 된다.

$ORACLE_HOME/demo/schema/sales_history

Note :

Oracle 9i부터는 $ORACLE_HOME/demo/schema 서브 디렉토리에 HR(human resource),OE(order entry), PM(product media), SH(sales history), QS(QueuedShipping) 등의 샘플 스키마 및 데이터들이 있다.

가장 일반적인 RANGE PARTITION을 기준으로 설명하며 COMPOSITE PARTITION의 경우 서브파티션만HASH로 추가된 경우이므로 RANGE PARTITITON 과 같다고 보면 되며 HASH와 LIST 파티션의 경우에는 따로설명하였다.

1. 파티션의 추가/삭제/분할/병합

1.1. 추가

기존에 존재하는 파티션에 새로운 파티션을 추가하려는 경우 (예:리스트 파티션에서 2001년 1분기 데이터를 추가하려는 경우등) 예제처럼 MAXVALUE로 지정한 파티션처럼 기존 파티션안에 대상이 포함되어 있다면 아래의 분할(SPLIT)을 이용하여처리하고 MAXVALUE가 없는 경우처럼 기존 파티션의 범위 밖으로 새로이 추가되는 경우라면 다음과 같이 한다.

SQL>alter table sales add partition Q1_2001
values less than (''''20010101'''') tablespace pts_2001Q1 ;

MAXVALUE로 지정한 파티션이 있는데 위와 같이 추가(add)하면 다음과 같은 에러가 발생한다.

"ORA-14120:DATE열에 파티션 바운드가 불완전하게 지정되었습니다."

1.2. 삭제

해당 단위 파티션을 삭제하는 경우 1998년 1분기 및 이전 데이터를 더 이상 관리하지 않아 해당 파티션 Q1_1998을 없애고 싶은 경우는 다음과 같이 실행한다.

SQL>alter table sales drop partition Q1_1998 ;

해당 파티션 및 파티션 안의 모든 데이터가 같이 삭제 된다. drop된 후에 새로 1998년 1분기 및 이전 데이타가입력되면 Q2_1998 partition이 less then (''''19980701'''') 으로 되어 있으므로 Q2_1998partition에 저장된다.

1.3. 분할

하나의 파티션을 분할하여 여러 개의 파티션으로 재 생성시 MAXVALUE를 이용한 경우에 경계값으로 새로 추가되는 경우,MAXVALUE로 바운드를 지정하지 않은경우 새로운 최대값을 지정하는, 기존 하나의 파티션을 둘 이상의 파티션으로 나누는 경우등이 해당된다.

새로 추가될 데이터 2001년 1분기 데이터를 저장할 파티션을 추가하려는 경우 기존 MAXVALUE 파티션에 이 조건이 포함되어 있으므로 이 파티션을 분할하면 된다.

SQL>alter table sales split partition Q4_9999 at (TO_DATE(''''20010101'''',''''YYYYMMDD''''))
into (partition Q4_2000 tablespace pts_2000Q4, partition Q4_9999 tablespace pts_9999Q4)

분할되는 기존 파티션을 그대로 사용할 경우에는 기존 파티션 정보를 그대로 기술해 주면 된다.

SPLIT PARTITION은 매우 큰 partition table이나 view를 handling 하는데 유용하다.

1.4. 병합

파티션을 병합하는 방법은 MERGE명령을 이용하는 방법과 SQL로 임시테이블에 옮기는 방법과 Export/Import를 이용하는 방법이 있다.

MERGE를 이용하는 방법이 빠르고 단순하게 처리 가능한다.

  • MERGE를 이용하는 방법

SQL> ALTER TABLE SALES MERGE PARTITIONS Q2_1999, Q3_1999 INTO PARTITION Q3_1999 ;

1.5. 이름 변경

partition name 을 바꾸고 싶다면 다음과 같이 실행한다.

SQL>alter table sales rename partition Q4_9999 to Q4_Max ;

1.6. 테이블 스페이스 이동

partition Q4_9999을 저장하는 tablespace를 pts_9999Q4 에서 pts_maxQ4로 바꾸고 싶은 경우아래와 같은 command를 사용한다.

SQL>alter table sales move partition Q4_9999 tablespace pts_maxQ4 nologging ;

1.7. Truncate

partition의 data를 모두 삭제하려면 truncate하는 방법을 사용할 수 있는 데, truncate는 rollback 이 불가능하며 특정 partition 전체를 삭제하므로 주의하여 사용해야 한다.

SQL>alter table sales truncate partition Q4_9999 ;

1.8. 속성 변경

partition table은 특정 partition의 속성만 변경할 수도 있고, table의 속성을 변경 하여 전체 partition에 대해 동일한 변경을 할 수 있다.

SQL>alter table sales storage(next 10M);

sales 의 모든 partition의 next 값이 변경된다.

SQL>alter table part_tbl modify partition Q4_9999 storage(maxextents 100);

Q4_9999 partition의 maxextents 값만 변경한다.

1.9. 인덱스 관리

파티션은 파티션별로 RowID를 다르게 갖게 됨으로 파티션의 변경이 발생하게 되면 RowID의 변경이 발생하게 된다.

그래서 실제 파티션 테이블의 RowID와 변경된 파티션 로컬 인덱스와 글로벌 인덱스의  RowID와 가 일치하지 않게 되어인덱스 사용 중지 즉, IU상태(INDEX UNUSABLE)가 발생하게 되어 인덱스의 사용 불가 및 재 성성에 따른 운영 및관리상의 부하 및 재 생성에 따른 시스템의 부하가 발생하게 된다.

partition index가 IU 상태가 되었을때 그 partition index을 사용 하게 되면 다음과 같은 에러가 발생하게 된다.

SQL>SELECT /*+ INDEX(SALES SALES_GPNK1) */ *
FROM SALES WHERE PROD_ID >= 10000

ORA-01502: 인덱스 ''''SALES_GPNK1''''또는 인덱스 분할영역은 사용할 수없는 상태이다

이와 같은 에러가 발생시 해당 파티션을 해당 인덱스를 통해서 엑세스 할 수 없게 됨으로 해당 어플리케이션의 수행을 유지 할 수 없는 상황이 발생하게 된다.

물론 인덱스를 사용하지 않고 해당 파티션을 TABLE FULL SCAN 하면 에러가 발생 하지 않는다.

하지만 옵티마이져가 실행계획 수립시에 INDEX UNUSABLE 상태 여부를 체크하지 않으므로 해당 인덱스를 사용하는실행계획이 수립되고 실제 수행시 오류가 발생하게 됨으로 이미 운용중인 어플리케이션이나 SQL이 정상 수행되지 않는다.

이런 경우에는 skip_unusable_indexes = TRUE 파라메터를 지정하면 수행시에 해당 인덱스가 UNUSABLE이면 이를 사용하지 않고 TABLE FULL SCAN 등을 한다. 경우에 따라서 유용하게 사용 가능하므로 고려 해 볼 수 있다.

어떤 partition이 IU 상태가 되면 그 partition을 사용하기 전에 재생성(rebuild)해야 한다. 그러나IU partition을 제외한 다른 partition만을 읽거나 DML을 수행하는 작업은 IU partition을access하지 않는 한 오류가 발생하지 않는다. 단, IU partition을 rebuild하기 전에 split이나rename이 가능하며, IU상태인 global index를 drop하는 것도 가능하다. 관리상으로 IU상태인 인덱스를 다시수행 가능하게 하는 작업은 경우에 따라 업무적인 부하가 큰 경우가 발생 할 수 있다.

파티션은 원래 대용량 데이터에 대한 처리이므로 인덱스 재생성 작업은 부하가 큰 작업인 경우가 많다. 또한 로컬파티션인덱스의 경우에는 해당 로컬인덱스만 재 생성해주면 되지만 글로벌 파티션 인덱스의 경우 글로벌 인덱스를 한번에 재 생성할 수 없고개별 파티션 단위로만 재생성이 가능하므로 글로벌 인덱스의 파티션 수 만큼 나누어서 지정하여 재생성 해야 한다.

이와 같은 관리상의 문제점을 해결하기 위한 여러가지 방법이 있으며 가장 큰 원인은 파티션의 변경과 파티션 데이터의 RowID가 변경되는 경우이다.

RowID가 변경되는 경우는 자주 발생하지 않고 어쩔수 없는 경우가 대부분일 것이므로 그렇다고 하여도 파티션의 변경은 가능한 발생하지 않게 유도할 수 있다.

특정 파티션의 데이터를 파티션에서 제외시키는(백업으로 이동 등) 경우에는 실제 데이터를 로우 단위로 Delete 하는것이아니라면 파티션에 대한 명령에서는 실제 데이터의 RowID 변경이 일어난다면 어떤 방법을 써도 Global Index의Unusable을 피할 수는 없다.

Partition Index를 Unusable상태로 만들 수 있는 명령.(Oracle9i 기준).

작업 대상

작업유형

인덱스

Unusable 상태 변경

파티션 테이블

ADD

LOCAL

새로 생성되므로 상관없다.

GLOBAL

파티션만 추가되므로 상관없다.

DROP

LOCAL

같이 삭제되므로 상관없다.

GLOBAL

모든 GLOBAL INDEX가 Unusable

SPLIT

LOCAL

SPLIT된 파티션 인덱스 Unusable

GLOBAL

모든 GLOBAL INDEX가 Unusable

MERGE

LOCAL

머지되어 남는 파티션 인덱스 Unusable

GLOBAL

모든 GLOBAL INDEX가 Unusable

RENAME

LOCAL

실제 변경이 없으므로 상관없다.

GLOBAL

실제 변경이 없으므로 상관없다.

MOVE

LOCAL

MOVE된 파티션 인덱스 Unusable

GLOBAL

모든 GLOBAL INDEX가 Unusable

TRUNCATE

LOCAL

남은 로우가 없으므로 상관없다.

GLOBAL

모든 GLOBAL INDEX가 Unusable

EXCHANGE

LOCAL

EXCHANGE한 파티션 인덱스 Unusable

GLOBAL

모든 GLOBAL INDEX가 Unusable

(표를 보면 알겠지만 Unusable상태가 되는 것은 실제 파티션 테이블 데이터의 RowID를 변경시킨 경우(파티션 단위의 변경이 발생시에도 해당 파티션에 실제 로우 데이터가 있어서 RowID 변경이 발생된 경우)에 해당된다.)

  •  인덱스 재생성(Rebuild)

partition table관련 작업을 한 후에는 table에 걸려 있는 local(partitioned) index 나 global index를 반드시 rebuild해 주어야 한다.

특정 partition의 index를 rebuild 하려면

SQL>alter index idx01_sales rebuild partition idx_Q4_9999 ;

global index를 rebuild하려면 non-partition index 인덱스인 경우는

SQL>alter index sales_idx01 rebuild;

와 같이 처리하면 되지만 global partition index 인덱스는 인덱스 전체를 한번에 재생성 할 수 없다.

SQL>alter index SALES_GPNK1 rebuild

"ORA-14086:분할영역된 인덱스는 전체를 다시 만들 수 없습니다." 와 같은 에러를 발생하게 된다.

SQL>alter index SALES_GPNK1 rebuild partition SALES_GPI01 ;

위와 같이 인덱스 파티션 단위로 재 생성 해주어야만 한다.

global partition index의 경우 non-partition index 이든partition index 이든 파티션 테이블 관련 작업인 경우 재 생성이 필수 이므로 파티션 테이블별로 globalindex의 rebuild 작업용의 스크립트를 작성해 놓아야 빠른 시간안에 관련 인덱스를 재 생성 할 수 있다.

1.10. Index Unusable 상태 예방

특히 Range Partition의 경우 대개 날짜를 키로 하여 파티션되며 시간의 경과에 따라 관리하는 데이터의 날짜값도 증가하여 새로운 파티션으로 관리해야 하는 경우가 많다.

오라클에서 NULL 데이터는 정렬에서 마지막에 위치하게 되어 파티션 키의 값이 NULL인 경우 MAXVALUE 파티션에 존재하게 된다.

즉 키에 NULL인 데이터가 존재시에는 MAXVALUE에 실제로 데이터가 위치하게 되며 이 경우 최신의 데이터를 위해서파티션을 추가시 MAXVALUE를 SPLIT하게 되면 실제 데이터 로우가 있는 파티션에서 변경 작업이 일어났으므로 GlobalIndex 의 Unusable를 피할 수 없게 된다.

이를 방지 하기 위해서는 실제 운영데이터의 최종 파티션과 MAXVALUE 파티션 사이에 dummy 파티션을 하나 두어 NULL값이 위치한 MAXVALUE 파티션을 분리하는 것이 좋다.

예를 들어 날짜가 키인 경우 ''''20021001''''인 현재 파티션과 MAXVALUE 사이에''''21000101'''' 이나 ''''99990101'''' 과 같은 dummy 파티션을 유지 하여 실무에서 유용하게사용하는 방법이다.

1.11. 파티션 키 데이터의 변경

파티션을 운영 중에 키 컬럼의 데이터가 변경되어 파티션을 이동 시에 다음과 같은 에러를 발생시킨다.

"ORA-14402: 분할영역 키 열을 수정하는 것은 분할영역 변경이 생깁니다."

이 문제를 해결하기 위해 잘못하면  프로그램 상에서 Delete & Insert 방식으로 코딩을 하기도 하지만 Oracle8i부터 SQL의 Syntax에 이 문제에 대한 옵션이 존재한다.

ENABLE ROW MOVEMENT 또는 DISABLE ROW MOVEMENT

디폴트로 DISABLE ROW MOVEMENT 가 셋팅 되어 있다. 즉 파티션간 로우의 이동이 금지되어 있었기 때문에 위와 같은 에러를 만나게 된 것이다.

CREATE TABLE시 이 옵션을 주거나, ALTER TABLE 명령어로 설정 값을 바꿀 수 있다.

ALTER TABLE SALES ENABLE ROW MOVEMENT;

2. 파티션 테이블의 변환 (파티션과 일반테이블의 변환)

파티션 테이블과 일반 테이블을 직접 변환시키는 명령은 EXCHANGE 명령이 있으며 다른 데이터베이스에의 이전이라면 Export/Import를 이용하는 방법이 있다.

EXCHANGE 명령은 특정 partition을 non partitioned table로, non partitioned table을 특정 partition으로 변환시켜 주는 역할을 한다.

내부적으로는 data(index) segment를 교환(data dictionary의 변경)하는 것이지 실제로 각각의 row들을 옮기는 것은 아니다.

Export/Import를 이하는 방법은 다른 데이터 베이스에서의 이전 등이나 재생성 등에 유용하며 일반 테이블의 Import와 동일한 절차로 수행된다.

table-level import시 우선 table creation 문장을 수행하고 row insert문을 수행 하는 것과마찬가지로, partition-level import도 우선 partitioned table의 생성문장을 수행하고 rowinsert문을 수행하게 된다.

따라서 ignore=y option등을 적절히 사용하면, non-partitioned table과 partitioned table간의 변경, partitioned table의 구조 변경 등을 수행할 수 있게 된다.

2.1. EXCHANGE 이용 시

  • 파티션 => 일반 테이블의 변환

table을 변환하기 위해 dummy table을 생성하고, alter table EXCHANGE PARTITION 명령어를 통해 수행한다. 이 명령어는 매우 빨리 data dictionary 를 update 시킨다

i) 변경하려는 파티션과 동일한 구조를 같는 테이블을 생성한다.

SQL>create table sales_EXCH as select * from sales where 1 = 2 ;

ii) alter table EXCHANGE을 이용하여 변경한다.

SQL>ALTER TABLE sales EXCHANGE PARTITION Q4_9999 WITH TABLE
sales_EXCH WITHOUT VALIDATION ;

EXCHANGE 으로 변경시에 파티션 테이블의 해당 파티션의 구조는 그대로 유지된다.(기존 파티션은 데이터 로우는 없는 빈파티션이 된다) 즉 EXCHANGE를 사용하면 실제 데이터가 파티션에서는 더 이상 유지되지 못하므로 해당 경우에만 사용할 수있는 단점이 있다.

WITHOUT VALIDATION 을 사용하면 유일성 조건 등의 기존 테이블에 걸린 제약 조건을 체크하지 않으므로 변환 전에 미리 체크 하는 것이 좋다.

  • 일반 테이블 => 파티션 의 변환

alter table EXCHANGE을 이용하여 변경한다.

SQL>ALTER TABLE sales EXCHANGE PARTITION Q3_1999 WITH TABLE
sales_EXCH WITHOUT VALIDATION ;

변환될 파티션테이블의 인덱스와 완전히 매치되는 인덱스 구조를 가지고 있다면 INCLUDING INDEX 옵션을 추가하여 로컬인덱스 생성에 대한 시간을 줄일 수 있다.

EXCHANGE는 전체적으로 가장 빠르게 파티션 관련 데이터를 변경할 수 있는 방법이다.

2.2. CTAS 이용 시

파티션 테이블의 특정 테이블을 일반 테이블로 변경시에 EXCHANGE를 사용시에는 실 데이터의 변경이 발생하므로 또 다른 방법으로 CTAS(CREATE TABLE .. NOLOGGING AS SELECT ..)가 있다.

적당한 SORT_AREA_SIZE의 지정과 NOLOGGING옵션으로 원하는 성능을 얻을 수 있다.

3. 기타 관리

파티션의 관리에서 발생하게 되는 몇 가지 중요한 실례를 살펴보기로 한다.

3.1. 파티션 데이터 파일 삭제시의 처리

Partitioned table의 partition이 포함되어 있는 datafile이 OS level에서 삭제된 경우,해당 datafile을 offline drop하고 open한 후 해당 table을 access하고자 하는 경우, 또는 해당TableSpace를 Drop하고자 하면 에러가 발생한다. 다음과 같은 error가 유발된다.

ORA-00376: (File cannot be read at this time) ,
ORA-14404: (Partitioned table contains partitions in a different tablespace)

파일 삭제에 대한 처리는 일반 데이터 파일 삭제의 처리와 같다.

이때 파티션의 복구에 대해서는 다음과 같이 3가지로 조치 가능 한다.

1) 해당 partitioned table전체를 drop한 후 recreate한다.

  • drop table sales ;
  • drop tablespace pts_9999Q4 including contents ;
  • tablespace recreate.
  • table rebuild .

문제점은 전체 partitioned table을 복구해야 하므로 시간이 오래  소요된다.

2) 해당 partition만을 drop후 재 생성 한다.

  • alter table sales drop partition Q4_9999 ;
  • drop tablespace pts_9999Q4 including contents ;
  • tablespace recreate.
  • add partition or split partition.
  • 해당 partition만의 data를 reload한다.

partitioned table전체 data를 reload하는 것 보다 짧은 시간이 소요된다.

3) temporary table을 생성하여 exchange한다.

  • sales table과 동일한 구조의 dummy table을 다른 tablespace에 생성한다.
    • create table dummy_sales as select * from sales where 1=2;
  • 이 table을 문제의 partition과 exchange한다.
    • alter table sales exchange partition Q4_9999 with table dummy_sales without validation ;
  • drop tablespace pts_9999Q4 including contents ;
  • 새로운 partition에 필요한 data를 load한다.

3.2. INDEX UNUSABLE 상태 예방 (9iR1 기준)

Oracle 9i에서는 base table에 대한 DDL 수행 시 UPDATE GLOBAL INDEXES라는 option을 사용해서 global index에 대해서도 ONLINE으로 update할 수 있는 기능을 제공한다.

이 기능으로 Global index가 base table과 동시에 변경이 되어 Global index에 대한 rebuild를 관리하지 않아도 된다.

  • 가능한 DDL 명령

: SPLIT, MERGE, ADD, MOVE, COALESCE, DROP, TRUNCATE, EXCHANGE UPDATEGLOBAL INDEXES라는 option을 partition specification 바로 뒤, 그리고 PARALLELclause 바로 앞에 기술해 두면 ONLINE으로 base table에 대한 DDL을 수행 하면서 global index를그에 맞춰 변경시킬 수 있다.

SQL>ALTER TABLE sales DROP PARTITION sales1 UPDATE GLOBAL INDEXES
PARALLEL (DEGREE 4);

OnLine 변경은 전체적인 처리의 부하가 Rebuild보다 크며 트랜젝션에 대한 처리 시간이 증가 하므로 이 방법은 처리해야 할 row의 양이 적을 때 유용하다.

변경하려는 데이터에 대한 처리와 재생성의 부하를 비교하여 적정한 방법을 선택하는 것이 좋다. (즉 전체 글로벌 인덱스를 재생성하는 시간보다 ONLINE update 하는 시간이 적을 때 유리하며 다량의 데이터의 경우에는 오히려 전체 시간이 더 걸릴수 도 있으므로 테스트하여 운영 시스템마다 적정선의 처리 한계를 정하는 것이 좋다.)

3.3. 파티션 추가시 로컬인덱스의 속성 관리

ALTER TABLE ADD PARTITION을 할 경우 table partition의 경우 table partition,tablespace, physical storage attribute를 지정할 수 있으나, local indexpartition의 경우 관련 syntax를 지원하지 않는다.

오라클은 자동으로 index partition, tablespace, physical storage attribute를 지정한다. (ALTER TABLE SPLIT PARTITION도 마찬가지)

new index partition의 이름은 new table partition과 같은 이름이 할당되고,tablespace, physical storage attribute는 local index에 지정된 default값이사용되고, local index에 지정된 default값이 없으면 user의 default tablespace가 사용된다.그러므로 PARTITION을 추가하여 사용하는 경우 index partition 관리에 주의가 요구된다.

지금까지 파티션 테이블 및 인덱스의 관리에서 발생할 수 있는 문제점들을 살펴보았다.

실무에서 파티션의 INDEX UNUSABLE 상태를 일으킬 수 있는 상황들에 대해서 특히 주의 깊게 보고 운영 버전별로 테스트 해보아 각 시스템에 맞는 관리 지침을 수립 하여 숙지 하는 것이 중요하다.

이상으로 Partition table 및 인덱스의 관리에 대한 일반적인 사례 중심으로 알아보았으며 다음 연재에서는 Partition table 활용 및 제한 사항에 대하여 다루게 될 것이다.

'Rdbms' 카테고리의 다른 글

오라클 테이블 스페이스 이동(데이터 파일 이동)  (0)2009/03/16
오라클 함수(ORACLE FUNCTION)  (0)2009/03/16
8i 오라클 종료시 반응이 없을때 처리 방법  (0)2009/03/16
파티션 관리  (0)2009/03/16
테이블스페이스 현황  (0)2009/03/16
오라클 락(Lock) 정보  (0)2009/03/16
특정 세션이 접근하고 있는 OBJECT  (0)2009/03/16
Dictionary 조회 SQL  (0)2009/03/16

by 오서비네 | 2009/03/27 15:34 | Oracle | 트랙백 | 덧글(1)

[펌] sqlldr 성능 옵션

From : http://www.oracleclub.com/articlelist.action

73 Oracle SQL*Loader Case Study X 안병훈 2006-06-11 2116 8

72 Oracle SQL*Loader Case Study VIV 안병훈 2006-06-11 787 7

71 Oracle SQL*Loader Case Study VIII 안병훈 2006-06-11 730 8

70 Oracle SQL*Loader Case Study VII 안병훈 2006-06-10 832 8

69 Oracle SQL*Loader Case Study VI 안병훈 2006-06-10 742 7
68 Oracle SQL*Loader Case Study V 안병훈 2006-06-10 703 7

67 Oracle SQL*Loader Case Study IV 안병훈 2006-06-10 822 8

66 Oracle SQL*Loader Case Study III 안병훈 2006-06-10 1141 9

65 Oracle SQL*Loader Case Study II 안병훈 2006-06-10 972 7

64 Oracle SQL*Loader Case Study I 안병훈 2006-06-10 2220 9

63 SQL*Loader [참고용] [1] 안병훈 2006-06-09 1362 8

by 오서비네 | 2009/03/09 23:57 | Oracle | 트랙백 | 덧글(3)

<< 이전 페이지     다음 페이지 >>