파티셔닝을 통한 GC 경합 해소

2024. 3. 14.·Oracle/Performance

예전에 작성한 Right Growing Index 경합 해소 글에서 인스턴스 ID의 가상 칼럼을 서브 파티션 키로 사용해 GC 경합을 해소하는 방법을 소개했습니다. 이 방법은 아래와 같이 이미 서브 파티션을 생성한 경우에는 사용할 수 없습니다. 이번 글에서 다중 칼럼 리스트 파티션을 사용해 GC 경합을 해소하는 방법을 살펴보겠습니다.

-- 1
CREATE TABLE t1 (
    dt DATE
  , id NUMBER
  , vl NUMBER
)
PARTITION BY RANGE (dt)
SUBPARTITION BY HASH (id) SUBPARTITION TEMPLATE (
    SUBPARTITION sp_1
  , SUBPARTITION sp_2
) (
    PARTITION p_20240101 VALUES LESS THAN (DATE '2050-01-02')
  , PARTITION p_20240102 VALUES LESS THAN (DATE '2050-01-03')
  , PARTITION p_maxvalue VALUES LESS THAN (MAXVALUE)
);

 

테스트 환경은 19.21입니다.

-- 2
SELECT version_full FROM product_component_version;

VERSION_FULL
------------
19.21.0.0.0

1 row selected.

 

참고로 이 방법을 사용하기 위해서는 30887435 버그가 Fix되어야 합니다. 30887435 버그에 대한 내용은 하단 링크를 참고하세요.

-- 3-1
SELECT bugno, value, description, is_default
  FROM v$system_fix_control 
 WHERE bugno = 30887435;


   BUGNO VALUE DESCRIPTION                                IS_DEFAULT
-------- ----- ------------------------------------------ ----------
30887435     0 control scanning to unnecessary partitions          1

1 row selected.

-- 3-2
ALTER SESSION SET "_fix_control" = '30887435:1';

 

테스트를 위해 아래와 같이 테이블을 생성하겠습니다. 일 파티션을 위해 dt 칼럼을 일자로 TRUNC한 dd 가상 칼럼을 추가하고, CG 경합을 해소하기 위해 inst_id 가상 칼럼을 추가합니다. RANGE 파티션을 dd 칼럼과 inst_id 칼럼의 LIST 파티션으로 변경합니다.

-- 4
ALTER SESSION SET "_partition_large_extents" = FALSE;

DROP TABLE t1 PURGE;

CREATE TABLE t1 (
    dt      DATE
  , id      NUMBER
  , vl      NUMBER
  , dd      DATE   INVISIBLE AS (TRUNC (dt, 'DD'))
  , inst_id NUMBER INVISIBLE AS (SYS_CONTEXT ('USERENV', 'INSTANCE'))
)
PARTITION BY LIST (dd, inst_id)
SUBPARTITION BY HASH (id) SUBPARTITION TEMPLATE (
    SUBPARTITION sp_1
  , SUBPARTITION sp_2
) (
    PARTITION p_20240101_1 VALUES ((DATE '2050-01-01', 1))
  , PARTITION p_20240101_2 VALUES ((DATE '2050-01-01', 2))
  , PARTITION p_20240102_1 VALUES ((DATE '2050-01-02', 1))
  , PARTITION p_20240102_2 VALUES ((DATE '2050-01-02', 2))
  , PARTITION p_default    VALUES (DEFAULT)
);

 

아래와 같이 1번, 2번 인스턴스에서 INSERT 문을 수행합니다.

-- 5-1: instance 1
INSERT
  INTO t1 (dt, id)
SELECT DATE '2050-01-01' + NUMTODSINTERVAL (ROWNUM - 1, 'HOUR') AS dt
     , ROWNUM AS id
  FROM XMLTABLE ('1 to 48');

COMMIT;

-- 5-2: instance 2
INSERT
  INTO t1 (dt, id)
SELECT DATE '2050-01-01' + NUMTODSINTERVAL (ROWNUM - 1, 'HOUR') AS dt
     , ROWNUM AS id
  FROM XMLTABLE ('1 to 48');

COMMIT;

 

아래 쿼리의 결과에서 파티션 별로 24개의 로우가 삽입된 것을 확인할 수 있습니다.

-- 6
SELECT   TBL$OR$IDX$PART$NUM (TUNA.T1, 0, 1, 0, ROWID) AS pp
       , dd
       , inst_id
       , COUNT (*) AS cnt
    FROM t1
GROUP BY TBL$OR$IDX$PART$NUM (TUNA.T1, 0, 1, 0, ROWID)
       , dd
       , inst_id;

PP DD                  INST_ID CNT
-- ------------------- ------- ---
 1 2050-01-01 00:00:00       1  24
 2 2050-01-01 00:00:00       1  24
 3 2050-01-02 00:00:00       1  24
 4 2050-01-02 00:00:00       1  24

4 rows selected.

 

아래 쿼리의 실행 계획에서 파티션 Pruning이 동작하는 것을 확인할 수 있습니다. 옵티마이저에 의해 파티션 키인 dd 칼럼이 아닌 dt 칼럼을 사용해도 파티션 Pruning이 동작하지만 dt 칼럼이 DATE 타입이 아닌 TIMESTAMP 타입인 경우에는 파티션 Pruning이 동작하지 않습니다. 아울러 앞서 언급한 30887435 버그가 Fix되지 않아도 파티션 Pruning이 동작하지 않습니다.

-- 7-1
SELECT * FROM t1;

----------------------------------------------------------------------------------------
| Id  | Operation                   | Name | Starts | Pstart| Pstop | A-Rows | Buffers |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |      |      1 |       |       |     96 |     177 |
|   1 |  PARTITION LIST ALL         |      |      1 |     1 |     5 |     96 |     177 |
|   2 |   PARTITION HASH ALL        |      |      5 |     1 |     2 |     96 |     177 |
|   3 |    TABLE ACCESS STORAGE FULL| T1   |     10 |     1 |    10 |     96 |     177 |
----------------------------------------------------------------------------------------

-- 7-2
SELECT * FROM t1 WHERE dt = DATE '2050-01-01';

----------------------------------------------------------------------------------------
| Id  | Operation                   | Name | Starts | Pstart| Pstop | A-Rows | Buffers |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |      |      1 |       |       |      2 |      89 |
|   1 |  PARTITION LIST ITERATOR    |      |      1 |   KEY |   KEY |      2 |      89 |
|   2 |   PARTITION HASH ALL        |      |      3 |     1 |     2 |      2 |      89 |
|*  3 |    TABLE ACCESS STORAGE FULL| T1   |      6 |       |       |      2 |      89 |
----------------------------------------------------------------------------------------

-- 7-3
SELECT * FROM t1 WHERE id = 1;

----------------------------------------------------------------------------------------
| Id  | Operation                   | Name | Starts | Pstart| Pstop | A-Rows | Buffers |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |      |      1 |       |       |      2 |      89 |
|   1 |  PARTITION LIST ALL         |      |      1 |     1 |     5 |      2 |      89 |
|   2 |   PARTITION HASH SINGLE     |      |      5 |     2 |     2 |      2 |      89 |
|*  3 |    TABLE ACCESS STORAGE FULL| T1   |      5 |       |       |      2 |      89 |
----------------------------------------------------------------------------------------

-- 7-4
SELECT * FROM t1 WHERE dt = DATE '2050-01-01' AND id = 1;

----------------------------------------------------------------------------------------
| Id  | Operation                   | Name | Starts | Pstart| Pstop | A-Rows | Buffers |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |      |      1 |       |       |      2 |      45 |
|   1 |  PARTITION LIST ITERATOR    |      |      1 |   KEY |   KEY |      2 |      45 |
|   2 |   PARTITION HASH SINGLE     |      |      3 |     2 |     2 |      2 |      45 |
|*  3 |    TABLE ACCESS STORAGE FULL| T1   |      3 |       |       |      2 |      45 |
----------------------------------------------------------------------------------------

 

인덱스 파티션 Pruning을 테스트하기 위해 아래와 같이 로컬 인덱스를 생성하겠습니다.

-- 8
CREATE INDEX t1_x1 ON t1 (dt, id) LOCAL;

 

아래 쿼리의 실행 계획에서 인덱스 파티션 Pruning이 동작하는 것을 확인할 수 있습니다.

-- 9-1
SELECT * FROM t1 WHERE dt = DATE '2050-01-01';

---------------------------------------------------------------------------------------------------------
| Id  | Operation                                   | Name  | Starts | Pstart| Pstop | A-Rows | Buffers |
---------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                            |       |      1 |       |       |      2 |       7 |
|   1 |  PARTITION LIST ITERATOR                    |       |      1 |   KEY |   KEY |      2 |       7 |
|   2 |   PARTITION HASH ALL                        |       |      3 |     1 |     2 |      2 |       7 |
|   3 |    TABLE ACCESS BY LOCAL INDEX ROWID BATCHED| T1    |      6 |       |       |      2 |       7 |
|*  4 |     INDEX RANGE SCAN                        | T1_X1 |      4 |       |       |      2 |       5 |
---------------------------------------------------------------------------------------------------------

-- 9-2
SELECT * FROM t1 WHERE dt = DATE '2050-01-01' AND id = 1;

---------------------------------------------------------------------------------------------------------
| Id  | Operation                                   | Name  | Starts | Pstart| Pstop | A-Rows | Buffers |
---------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                            |       |      1 |       |       |      2 |       5 |
|   1 |  PARTITION LIST ITERATOR                    |       |      1 |   KEY |   KEY |      2 |       5 |
|   2 |   PARTITION HASH SINGLE                     |       |      3 |     2 |     2 |      2 |       5 |
|   3 |    TABLE ACCESS BY LOCAL INDEX ROWID BATCHED| T1    |      3 |       |       |      2 |       5 |
|*  4 |     INDEX RANGE SCAN                        | T1_X1 |      2 |       |       |      2 |       3 |
---------------------------------------------------------------------------------------------------------

 

아래 구문으로 파티션을 관리할 수 있습니다. 인스턴스를 확장하는 경우에는 주석처럼 파티션을 추가해야 합니다.

-- 10-1
ALTER TABLE t1 SPLIT PARTITION p_default INTO (
    PARTITION p_20240103_1 VALUES ((DATE '2050-01-03', 1))
  , PARTITION p_20240103_2 VALUES ((DATE '2050-01-03', 2))
--, PARTITION p_20240103_3 VALUES ((DATE '2050-01-03', 3))
--, PARTITION p_20240103_4 VALUES ((DATE '2050-01-03', 4))
  , PARTITION p_default);
  
-- 10-2
ALTER TABLE t1 MERGE PARTITIONS p_20240101_1, p_20240101_2
                INTO PARTITION  p_20240101 UPDATE INDEXES;

 

관련링크

  • Jonathan Lewis - Virtual date partitions
  • Mohamed Houri - Partition by virtual column
  • Bug 30887435 - Excess Partitions Are Scanned for Multi Column List Partition Table (Doc ID 30887435.8)
저작자표시 비영리 변경금지 (새창열림)
'Oracle/Performance' 카테고리의 다른 글
  • CURSOR_SHARING_FORCE 힌트
  • 바인드 변수 값 조회
  • SQL 파싱 순서
  • INDEX_STATS 힌트
정희락
정희락
2007년부터 Oracle Database 성능 최적화에 주력해 왔으며, 현재 한국오라클 Engineered Systems Solution Engineering 팀에서 Solution Engineer로 근무하고 있습니다. 이 블로그는 개인적인 연구 목적으로 운영되며, Oracle 사의 공식 입장을 대변하지 않습니다.
  • 정희락
    TunA
    정희락
  • 전체
    오늘
    어제
    • 분류 전체보기 (194)
      • Oracle (166)
        • SQL (32)
        • PLSQL (10)
        • Performance (72)
        • Administration (36)
        • Installation (3)
        • Utilities (1)
        • JSON (8)
        • Vector (4)
      • Exadata (15)
      • SQL*Plus (2)
      • Linux (5)
      • Resources (6)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 도서

    • 불친절한 SQL 프로그래밍
    • 불친절한 PL/SQL 프로그래밍
  • 태그

    12c
    19c
    21c
    23ai
    case study
  • 공지사항

  • 최근 글

  • 최근 댓글

  • 인기 글

  • 링크

    • Connor McDonald
    • Frits Hoogland
    • Jonathan Lewis
    • Julian Dontcheff
    • Julian Dyke
    • Kun Sun
    • Maria Colgan
    • Martin Bach
    • Mike Dietrich
    • Tanel Poder
  • hELLO· Designed By정상우.v4.10.0
정희락
파티셔닝을 통한 GC 경합 해소
상단으로

티스토리툴바