파티션을 쓰면 대량 추가 변경 삭제 작업을 빠르게 처리할 수 있다.
파티셔닝은 테이블 또는 인덱스 데이터를 특정 컬럼 값에 따라 별도 세그먼트에 나눠서 저장하는 것이다.
파티션에는 Range, Hash, List 세 종류가 있다.
가장 기초적인 방식의 파티션.
주로 날짜 컬럼을 기준으로 파티셔닝한다.
create table 주문(주문번호 number, 주문일자 varchar2(8), 고객id varchar2(5)
, 배송일자 varchar2(8, 주문금액 number, … )
partition by range(주문일자) (
partition p2017_q1 values less than (‘20170401’)
, partition p2017_q2 values less than (‘20170701’)
, partition p2017_q3 values less than (‘20171001’)
, partition p2017_q4 values less than (‘20180101’)
, partition p2018_q1 values less than (‘20180401’)
, partition p9999_mx values less than (maxvalue) --> 주문일자 >= '20180401'
);
위와 같은 파티션 테이블에 값을 입력하면 각 레코드를 파티션 키 값에 따라 분할 저장하고, 읽을때도 검색조건을 만족하는 파티션만 골라 읽어줄 수 있어 이력성 데이터를 Full Scan 방식으로 조회할 때 성능을 크게 향상시킨다.
부관주기 정책에 따라 과거 데이터가 저장된 파티션만 백업하고 삭제하는 등 데이터 관리 작업을 효율적이고 빠르게 수행할 수 있는 장점도 있음.
파티션 Pruning이 답이다.
파티션 Pruning은 SQL 하드파싱이나 실행 시점에 조건절을 분석해 읽지 않아도 되는 파티션 세그먼트를 액세스 대상에서 제외하는 기능이다.
select * from 주문 where 주문일자 >= '20120401' and 주문일자 <= '20120630'
위 조건절에 해당하는 데이터는 1200만건중 25%에 해당하는 300만건이라고 치면,
인덱스 타고 랜덤 액세스하면 더 느리다. 그런데 테이블 전체 스캔은 사이즈가 너무커 부담이다.
이때 테이블 파티션을 사용하면 Full Scan 해도 전체가 아닌 일부라서 일부 파티션 세그먼트만 읽고 멈출 수 있다.
파티션과 병렬 처리가 만나면 그 효과는 배가 된다.
해시 파티션은 고객ID처럼 변별력이 좋고 데이터 분포가 고른 컬럼을 파티션 기준으로 선정해야 효과적이다. 아래는 고객ID 기준으로 고객 테이블을 해시 파티셔닝하는 방법을 예시한다.
create table 고객(
고객id varchar2(5)
, 고객명 varchar2(10)
, …
)
partition by hash(고객id) partitions 4;
아래는 지역분류 기준으로 인터넷매물 테이블을 리스트 파티셔닝하는 방법을 예시한다.
create table 인터넷매물(
물건코드 varchar2(5)
, 지역분류 varchar2(4)
, …
)
partition by list(지역분류) (
partition p_지역1 values (‘서울’)
, partition p_지역2 values (‘경기’. ‘인천’)
, partition p_지역3 values (‘부산’, ‘대구’, ‘대전’, ‘광주’)
, partition p_기타 values (default) -> 기타 지역
);
Range 파티션에선 값의 순서에 따라 저장할 파티션이 결정
하지만 리스트 파티션은 순서와 상관없이 불연속적인 값의 목록에 의해 결정된다.
해시 파티션은 오라클이 정한 해시 알고리즘에 따라 임의로 분할함.
반면 리스트 파티션은 사용자가 정의한 논리적인 그룹에 따라 분할함.
💡 업무적인 친화도에 따라 그룹핑 기준을 정하되, 될 수 있으면 각 파티션에 값이 고르게 분산되도록 해야 한다.
인덱스 파티션은 테이블 파티션과 맞물려 다양한 구성이 있다.
테이블 파티션 구성
- 비파티션 테이블
- 파티션 테이블
인덱스 파티션 구성
- 로컬 파티션 인덱스
- 글로벌 파티션 인덱스
- 비파티션 인덱스
LOCAL
옵션을 추가하면 된다.create index 주문_x01 on 주문 (주문일자, 주문금액) LOCAL;
create index 주문_x02 on 주문 (고객ID, 주문일자) LOCAL;
GLOBAL
옵션을 추가하고 파티션을 정의하면 된다.create index 주문_x03 on 주문 (주문금액, 주문일자) GLOBAL
partition by range(주문금액) (
partition P_01 values less than (100000)
partition P_MX values less than (MAXVALUE) --> 주문금액 >= 100000
);
글로벌 파티션 인덱스는 Prefixed만 지원한다. 로컬 파티션 인덱스는 Prefixed, Nonprefixed 다 가능하다.
그래서 비파티션을 포함해 4가지 유형으로 정리할 수 있다.
Unique 인덱스를 파티셔닝 하려면, 파티션 키가 모두 인덱스 구성 컬럼이여야 한다.
- 파티션 키가 인덱스 컬럼에 포함되어야 하는 조건은 DML 성능 보장을 위해 당연히 있어야 할 제약조건임.
- 파티션 키 조건 없이 PK 인덱스로 액세스 하는 수많은 쿼리 성능을 위해 필요한 제약조건이다.
- 이 제약으로 인해 PK 인덱스를 로컬 파티셔닝하지 못하면, 파티션 Drop, Truncate, Exchange, Split, Merge 같은 파티션 구조 변경 작업도 쉽지 않음. (Unusable 되기 때문)
- 서비스 중단 없이 파티션 구조를 빠르게 변경하려면 PK를 포함한 모든 인덱스는 로컬 파티션 인덱스여야 한다.
인덱스는 DML 성능에 큰 영향을 미침. 그래서 인덱스 없이 대량의 데이터 작업 후, 인덱스 생성이 더 빠르고 그런 방법을 많이 쓴다.
테이블이 파티셔닝이 되어있고, 인덱스도 로컬 파티션이라면, 수정된 값을 갖는 임시 세그먼트를 만들어 원본 파티션과 바꿔치기 하는 방법을 사용한다.
create table 거래_t
nologging
as
select * from 거래 where 1 = 2;
insert /*+ append */ into 거래_t
select 고객번호, 거래일자, 거래순번 ...
,(case when 상태코드 <> 'ZZZ' then 'ZZZ' else 상태코드 end) 상태코드
from 거래
where 거래일자 < '20150101';
alter table 거래
exchange partition p201412 with table 거래_t
including indexes without validation;
DELETE가 느린 이유는 여러 부수적인 작업을 수반해야해서 느리다.
특히 인덱스 레코드를 찾아 삭제하는 작업 부담이 크다.
삭제 조건절 컬럼 기준으로 파티셔닝 되어 있고, 로컬 파티션이라면 1줄로 대량 데이터를 삭제할 수 있다.
alter table 거래 drop partition p201412;
-- 오라클 11g 버전 : 대상 파티션 지정
alter table 거래 drop partition for('20141201');
거래일자 조건에 해당하는 데이터를 일괄 삭제하지 하지 않고 다른
비파티션 테이블에 손익분기점 넘는 대량 데이터 INSERT 하려면, 인덱스 Unusable하고 재생성하는 방식이 빠를 수 있음.
테이블이 파티셔닝 되어 있고, 인덱스도 로컬 파티션이면 파티션 단위로 인덱스 재생성 할 수 있다.