Segment Partitioning

EXEM Knowledge Base

Jump to: navigation, 찾기

대용량 데이터를 효과적으로 관리하기 위한 기법으로 세그먼트 파티셔닝(Segment Partitioning)이 보편적으로 활용된다. 오라클은 범위 파티셔닝(Range Partitioning), 해시 파티셔닝(Hash Partitioning), 리스트 파티셔닝(List Partitioning), 복합 파티셔닝(Composite Partitioning) 등 현존하는 DBMS 중 가장 다양한 파티셔닝 기법들을 제공한다. 세그먼트 파티셔닝을 사용하는 이유는 관리의 편이성과 성능 두 가지로 요약할 수 있다.

목차

[편집] 관리의 편이성

파티셔닝은 대용량의 데이터를 효과적으로 분할해서 관리할 수 있는 방법을 제공한다. 시간을 키로 하는 범위 파티셔닝이 대표적인 예라고 할 수 있다. 데이터를 시간 단위로 주기적으로 정리하는 작업을 수행한다면 사실상 파티셔닝을 사용하는 것만이 유일한 해결책이라고 볼 수 있다. 오라클은 파티셔닝 작업에 필요한 대부분의 작업을 온라인으로 수행할 수 있는 기능을 제공한다. 온라인 옵션을 파티셔닝 작업을 이용하면 DDL에 의한 시스템 정지 없이 데이터 정리 작업을 수행할 수 있다.

[편집] 성능

파티셔닝이 성능에 긍정적인 영향을 미칠 것이라는 일반적인 기대와는 달리 OLTP 환경에서는 파티셔닝과 어플리케이션의 성능은 큰 상관 관계가 없다고 봐도 무방하다. OLTP 환경에서 수행되는 대부분의 작업들은 인덱스를 경유한 작은 크기의 Logical Reads를 필요로 하기 때문에 파티셔닝의 혜택이 크지 않다. 하지만 파티셔닝으로 인해 기대되는 블록 분산 효과로 인해 OLTP 시스템에서도 긍정적인 성능 효과를 가져오는 경우도 있다. 반면 DSS 환경에서는 파티셔닝이 성능에 미치는 영향이 대단히 크다. 우선, 파티션 가지치기(Partition Pruning)를 통해 테이블 전체에 대한 스캔을 수행하지 않고 원하는 특정 범위의 파티션에 대한 스캔만을 수행할 수 있다. 아래 실행 계획은 파티션 가지치기가 발생한 경우의 간단한 예제로, FTS를 수행하더라도 테이블 전체를 스캔 하지 않고 하나의 파티션만을 스캔 하는 것을 확인할 수 있다.

SELECT STATEMENT ALL_ROWS-Cost : 25
  PARTITION RANGE SINGLE  <---  Partition Pruning
   TABLE ACCESS FULL OWI.PART_TEST(1)

여러 개의 파티션으로 구성된 테이블에 대한 DML은 파티션 단위의 병렬 수행이 가능하다. 따라서 대량 DML의 성능을 극대화할 수 있다. 파티션 테이블에 대한 병렬 DML의 성능에서는 디스크 경합(Disk Contention)에 대한 고려가 필요하다는 사실에 유의하자. 병렬 DML개별 파티션들이 각기 독립적인 디스크를 사용한다면, 병렬 프로세스간에 디스크 경합이 최소화되므로 병렬 DML의 성능을 더욱 높일 수 있다. 만일 파티션들이 물리적으로 같은 디스크에 존재한다면 디스크 경합에 의해 병렬 DML의 작업이 원하는 만큼의 성능을 내지 못할 수 있다. 개별 파티션을 독립적인 디스크에 배치하는 것이 불가능하다면, 거꾸로 모든 디스크에 골고루 스트라이핑 함으로써 비슷한 효과를 기대할 수 있다. 각 파티션들을 구성하는 블록들이 많은 수의 디스크에 골고루 흩어져 있다면, 병렬 프로세스간에 디스크 경합이 최소화될 수 있다.

병렬 DML 수행 시 주의할 점은 첫째, Update, Delete 문장은 파티션 테이블에 대해서만 적용 가능하며, 둘째, 세션 레벨에서 병렬 DML을 활성화해주어야 한다는 것이다. 아래 예제는 세션 레벨에서 병렬 DML을 활성화하지 않고 병렬 DML을 수행한 경우로 실제로는 병렬로 DML이 수행되지 않는 것을 확인할 수 있다.

-- Case 1: 병렬 DML이 활성화되지 않은 DML 작업
SQL> ALTER SESSION DISABLE PARALLEL DML; -- Default가 DISABLE 상태
SQL> UPDATE /*+ PARALLEL(PART_TEST) */
PART_TEST
SET ID = ID;
Rows   Row Source Operation
----- ---------------------------------------------------
     0 UPDATE  PART_TEST (cr=18 pr=0 pw=0 time=179908 us)
    30  PX COORDINATOR  (cr=18 pr=0 pw=0 time=169813 us)
     0   PX SEND QC (RANDOM) :TQ10000 (cr=0 pr=0 pw=0 time=0 us)
     0    PX BLOCK ITERATOR PARTITION: 1 3 (cr=0 pr=0 pw=0 time=0 us)
     0     TABLE ACCESS FULL PART_TEST PARTITION: 1 3 (cr=0 pr=0 pw=0 time=0 us)

위의 실행 계획을 자세히 보면 테이블을 스캔 하는 작업만이 병렬로 실행되며, 실제 UPDATE 문은 병렬로 수행되지 않는 것을 확인할 수 있다. 반면 세션 레벨에서 병렬 DML을 활성화한 경우에는 UPDATE 작업 자체가 병렬로 수행된다.

-- Case 2: 병렬 DML이 활성화된 DML 작업
SQL> ALTER SESSION ENABLE PARALLEL DML; 
SQL> UPDATE /*+ PARALLEL(PART_TEST) */
PART_TEST
SET ID = ID;
Rows       Row Source Operation
-------- ---------------------------------------------------
         4 PX COORDINATOR  (cr=36 pr=0 pw=0 time=677646 us)
         0  PX SEND QC (RANDOM) :TQ10000 (cr=0 pr=0 pw=0 time=0 us)
         0   UPDATE  PART_TEST (cr=0 pr=0 pw=0 time=0 us)
         0    PX BLOCK ITERATOR PARTITION: 1 3 (cr=0 pr=0 pw=0 time=0 us)
         0     TABLE ACCESS FULL PART_TEST PARTITION: 1 3 (cr=0 pr=0 pw=0 time=0

위의 실행 계획은 앞의 실행 계획과는 달리 UPDATE 문 자체가 병렬로 수행되며, 파티션 단위로 병렬화가 이루어지는 것을 확인할 수 있다.

두 개의 파티션 테이블, 혹은 하나의 파티션 테이블과 일반 테이블을 조인하는 경우 오라클은 가능한 파티션 지향 조인(Partition-wise Join)을 수행한다. 파티션 지향 조인이란 하나의 조인 작업을 파티션을 기준으로 구분된 여러 개의 작은 조인으로 분리해서 병렬적으로 조인을 수행하는 작업을 말한다. 대량의 데이터를 조인해서 결과를 추출하는 경우에는 파티션 지향 조인을 활용함으로써 성능을 극대화할 수 있다. 이 경우에도 병렬 DML과 마찬가지로 디스크 경합을 최소화하는 것이 성능 면에서 유리하다. 파티션 지향 조인에서 주의해야 할 점은 최대 DOP가 파티션 수에 의해 제한된다는 것이다. 파티션 지향 조인은 서로 다른 파티션 테이블들을 각 파티션 단위 별로 조인하는 작업이기 때문에 최대한 동시에 수행할 수 있는 병렬 프로세스의 수는 파티션 수를 넘을 수 없다. 결국 파티션 단위의 작업 분할이 유리한지, 최대한 많은 수의 CPU를 동원해서 작업하는 것이 유리한지를 테스트를 통해 판단하는 수 밖에 없다.

[편집] 해시 파티셔닝의 의미

오라클이 제공하는 파티셔닝 기법 중 해시 파티셔닝(Hash Partitioning)은 파티션 키의 해시 값에 따라 파티션을 할당하는 기능을 제공한다. 범위 파티셔닝이나 리스트 파티셔닝과 같이 값의 범위에 따라 데이터를 분산 저장하는 역할을 하는 다른 파티셔닝 기법들과는 달리 해시 파티션은 해시 값에 따라 무작위적으로 파티션을 할당하기 때문에 대용량의 데이터를 효율적으로 관리하는 목적에는 맞지 않는다. 반면 무작위적인 분산이 가져오는 블록 분산의 효과를 이용해 블록 레벨의 경합을 줄이는 목적으로 활용 가능하다. 아래 예제는 블록 경합에 의해 buffer busy waits 대기 이벤트가 광범위하게 발생하는 테이블을 해시 파티셔닝을 이용해 강제로 분산시킨 경우에 buffer busy waits 이벤트 대기가 얼마나 줄어드는지 보여주고 있다.

-- Case 1: 파티셔닝을 적용하지 않은 테이블에 대해 10 개의 세션이 동시에 UPDATE를 수행하는 경우 buffer busy waits 이벤트 대기가 광범위하게 발생하는 것을 확인할 수 있다.
CREATE TABLE t_buffer_busy_waits
 (
  id  NUMBER,
  name  VARCHAR2(10)
 )
*****************************************************************************
UPDATE T_BUFFER_BUSY_WAITS SET NAME = 'name'||:B1 
WHERE
 ID = 10*(:B2 -1) + :B1 

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  --------
Parse        1      0.00       0.00          0          0          0           0
Execute   8000      3.01      25.57          0     373741      23516        8000
Fetch        0       0.00       0.00          0          0          0           0
-------  ------  -------- ---------     ------- ------- ----------   -------
total     8001      3.01      25.58          0     373741      23516        8000

Misses in library cache during parse: 1
Misses in library cache during execute: 1
Optimizer mode: CHOOSE
Parsing user id: 63     (recursive depth: 2)

Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  -------------------------------------   Waited  ----------  ------------
  buffer busy waits                             843        1.00         14.29
  latch: In memory undo latch                 97        0.07          0.46
  latch: cache buffers chains                127        0.10          1.09
  latch free                                      4        0.04          0.04
  latch: cache buffers lru chain              2        0.00          0.00
  log file switch completion                   3        0.99          1.00
  log file switch (checkpoint incomplete)    3        1.00          2.09
  latch: undo global data                       6        0.01          0.01
  enq: HW - contention                           1        0.00          0.00
  latch: library cache                           1        0.00          0.00
*****************************************************************************

-- Case 2: 100개의 파티션으로 해시 파티셔닝을 적용한 후, 동일한 부하로 UPDATE를 수행한 경우 buffer busy waits 이벤트 대기가 크게 줄어드는 것을 확인할 수 있다. 이는 해시 파티셔닝에 의해 블록이 분산된 효과가 나타난 것이다.
CREATE TABLE t_buffer_busy_waits
 (
  id  NUMBER,
  name  VARCHAR2(10)
 )
 PARTITION BY HASH(id) PARTITIONS 100;
******************************************************************************
UPDATE T_BUFFER_BUSY_WAITS SET NAME = 'name'||:B1 
WHERE
 ID = 10*(:B2 -1) + :B1 

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  -------
Parse        1      0.00       0.12          0          0          0           0
Execute  13000     3.31      16.96          0     273825      38193       13000
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  --------
total    13001      3.31      17.08          0     273825      38193       13000

Misses in library cache during parse: 0
Optimizer mode: CHOOSE
Parsing user id: 63     (recursive depth: 2)

Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ------------------------------------   Waited  ----------  ------------
  latch: library cache                           1        0.00          0.00
  buffer busy waits                              62        0.14          1.18
  latch: cache buffers chains                  28        0.18          1.31
  enq: HW - contention                            1        0.03          0.03
  latch free                                       11        0.07          0.24
  latch: In memory undo latch                    8        0.13          0.35
  latch: object queue header operation         1        0.00          0.00
  latch: checkpoint queue latch                  1        0.02          0.02
  latch: enqueue hash chains                     3        0.02          0.04
  latch: cache buffers lru chain                13        0.03          0.09
  latch: undo global data                         3        0.03          0.04
******************************************************************************

위의 테스트 결과를 보면 해시 파티셔닝을 통해 블록을 분산시킨 경우 buffer busy waits 대기가 14.29 초에서 1.18 초로 크게 줄어든 것을 확인할 수 있다.

RAC 시스템에서는 파티셔닝에 의한 블록 분산 효과는 싱글 인스턴스 환경에서보다 더 큰 의미를 지닌다. RAC 시스템에서의 핫 블록은 로컬 버퍼 락 경합뿐만 아니라, 글로벌 버퍼 락(BL 락) 경합을 유발하며, 노드 간에 블록의 최신 버전을 끊임없이 전송해야 하기 때문에 인터커넥트에 많은 부하를 유발한다. 이 경우 각 프로세스들은 gc buffer busy 이벤트나 gc current request 이벤트 및 그에 해당하는 Fixed-up 이벤트들인 gc current block busy, gc current grant busy, gc current block 2/3-way 류의 이벤트들을 대기하게 된다.

[편집] 해시 파티셔닝 적용 방법

실제 업무에서 파티셔닝을 적용해 본 경험이 있는 독자라면 해시 파티셔닝의 실용성에 대해 많은 의구심을 가지고 있을 것이다. 대용량 데이터 관리를 위해서라면 범위 파티셔닝이나 리스트 파티셔닝으로 대부분 처리할 수 있기 때문에 해시 파티셔닝을 적용할 이유를 찾기가 어렵기 때문이다. 하지만 높은 동시성을 가지는 싱글 인스턴스 환경이나 RAC 환경에서는 해시 파티셔닝이 가져다 주는 블록 분산 효과를 통해 성능 향상을 기대할 수 있다. 문제는 블록이 임의로 분산되어 버리는 해시 파티셔닝 고유의 특징으로 인해 실제 업무에서 적용하기가 불가능한 경우가 많다는 것이다. 이런 고민은 복합 파티셔닝을 적용함으로써 해결할 수 있다. 복합 파티셔닝이란 파티션에 대해 또 다시 하위 파티셔닝을 적용하는 기법을 말한다. 아래 예제와 같이 범위 파티셔닝과 해시 파티셔닝을 이용해 복합 파티셔닝을 적용할 수 있다.

CREATE TABLE PART_TEST(ID NUMBER,NAME VARCHAR2(100))
PARTITION BY RANGE(ID)
SUBPARTITION BY HASH(NAME)
SUBPARTITION TEMPLATE
(
    SUBPARTITION SUBPART1,
    SUBPARTITION SUBPART2,
    SUBPARTITION SUBPART3,
    SUBPARTITION SUBPART4
)
(
    PARTITION PART1 VALUES LESS THAN(10),
    PARTITION PART2 VALUES LESS THAN(20),
    PARTITION PART3 VALUES LESS THAN(30)
);
SELECT TABLE_NAME, PARTITION_NAME, SUBPARTITION_NAME
FROM DBA_TAB_SUBPARTITIONS WHERE TABLE_NAME = 'PART_TEST';
TABLE_NAME		PARTITION_NAME	SUBPARTITION_NAME
----------		-------------	-------------------
PART_TEST		PART1		PART1_SUBPART1
PART_TEST		PART1		PART1_SUBPART2
PART_TEST		PART1		PART1_SUBPART3
PART_TEST		PART1		PART1_SUBPART4
PART_TEST		PART2		PART2_SUBPART1
PART_TEST		PART2		PART2_SUBPART2
PART_TEST		PART2		PART2_SUBPART3
PART_TEST		PART2		PART2_SUBPART4
PART_TEST		PART3		PART3_SUBPART1
PART_TEST		PART3		PART3_SUBPART2
PART_TEST		PART3		PART3_SUBPART3
PART_TEST		PART3		PART3_SUBPART4

다시 한번 정리하면, 대용량 테이블에서 발생하는 핫 블록 현상을 블록 분산을 통해 해결하고자 하는 경우에는, 우선 범위/리스트 파티셔닝을 통해 전체 레벨의 파티션을 구성하고 해시 파티셔닝을 통해 하위 파티션을 구성함으로써 데이터 관리의 용이성이라는 본래의 목적과 블록 분산이라는 성능 개선의 목적을 모두 달성할 수 있다.

[편집] 파티셔닝과 디스크 I/O 분산

특정 디스크에 I/O 작업이 몰리는 경우 핫 스팟(Hot Spot) 현상이 발생할 수 있다. 특정 디스크가 핫 스팟이 되는 경우에는 I/O 대기 시간이 크게 증가하고 이로 인해 I/O 작업 성능이 크게 저하된다. 최악의 경우 오라클이 단 하나의 블록을 읽어 들이는데 수 초 ~ 수십 초 정도의 시간이 소요될 정도의 성능 저하 현상을 경험하게 된다.

핫 스팟 현상을 해소하는 유일한 방법은 특정 디스크에 I/O 작업이 몰리는 현상을 제거하는 것이다. 이것은 어플리케이션 수정이나 튜닝으로는 달성할 수 없고, 디스크 레벨의 튜닝으로만 해결할 수 있다. 가장 좋은 방법은 오라클에서 사용하는 데이터 파일을 가능한 한 많은 수의 디스크에 스트라이핑하는 것이다. LVM(Logical Volume Manager)을 사용하는 경우에는 하나의 Logical Volume에 많은 수의 디스크를 할당하고, Logical Volume 레벨에서 스트라이핑을 수행하면 된다. 하나의 데이터 파일이 여러 개의 디스크에 스트라이핑 된 경우 특정 디스크에 I/O 작업이 몰리는 현상이 자연스럽게 해소된다. 만일 LVM을 사용하지 않거나 로 디바이스 사용으로 인해 LVM 레벨의 스트라이핑을 적용하기 힘든 상황이라고 하면 세그먼트 레벨의 파티셔닝을 통해 비슷한 효과를 낼 수 있다. 가령 하나의 세그먼트를 범위+해시 복합 파티셔닝으로 구성한 경우 각 파티션 별로 최대한 독립적인 디스크를 할당할 수 있다. 이렇게 하면 하나의 세그먼트가 많은 수의 디스크로 자연스럽게 분산된다. 자연스럽게 특정 디스크가 핫 스팟으로 변하는 현상을 방지할 수 있다.

디스크 레벨에서 발생하는 경합 현상은 디스크의 배치 방법을 전면적으로 수정해야만 해결이 가능하기 때문에 가능한 시스템 설계 및 구축 단계에서 다양한 검토와 테스트를 통해 최적의 시스템을 성능을 보장받도록 고려해야 한다. 일단 구축이 완료되고 운영에 들어간 시스템의 디스크 배치를 수정하는 것이 현실적으로 거의 불가능한 경우가 많기 때문이다.