예전에 Hash Join Right (Semi/Anti/Outer) 의 용도 라는 글에서 서브쿼리가 Semi Join 으로 적용될때 조인의 순서를 바꿀수 있는 방법에 대하여 설명한바 있다. 이어서 오늘도 예전글과 유사한 Semi Join 의 튜닝에 대해서 이야기 하려 한다.

환경 : Oracle 11.1.0.6
먼저 테이블을 단순한 구조로 만들기 위해서 테이블을 2개 생성한다.
ORACLE 의 Sample 스키마인 SH 를 이용한다.

CREATE TABLE SALES_T AS SELECT * FROM sh.sales;

CREATE TABLE PRODUCTS_T AS SELECT * FROM sh.products ;

ALTER TABLE PRODUCTS_T ADD CONSTRAINT PK_PRODUCTS_T         --> PK 및 인덱스 생성
PRIMARY KEY (PROD_ID) USING INDEX;

CREATE BITMAP INDEX SALES_T_PROD_BIX ON SALES_T (PROD_ID);  --> BIT MAP 인덱스 생성

dbms_stats.gather_table_stats(user,'SALES_T',cascade=>true);
dbms_stats.gather_table_stats(user,'PRODUCTS_T',cascade=>true);

이제 SQL 을 실행할 준비가 다 되었다.
아래는 상품의 Category 가 Software/Other 인 제품의 상품코드별, 채널별 판매량을 나타낸 SQL 이다.
이러한 SQL 은 주로 DW 에서 나타난다.

SELECT /*+ GATHER_PLAN_STATISTICS QB_NAME(MAIN) */
            s.prod_id, s.channel_id, SUM (quantity_sold) AS qs, SUM (amount_sold) AS amt
   FROM sales_t s
 WHERE s.prod_id IN (SELECT /*+ QB_NAME(SUB) */
                                             p.prod_id
                                   FROM products_t p
                                 WHERE p.prod_category_desc = 'Software/Other')
GROUP BY s.prod_id, s.channel_id ;

--------------------------------------------------------------------------
| Id  | Operation           | Name       | Starts |   A-Time   | Buffers |
--------------------------------------------------------------------------
|   1 |  HASH GROUP BY      |            |      1 |00:00:08.12 |    4445 |
|*  2 |   HASH JOIN         |            |      1 |00:00:06.94 |    4445 |
|*  3 |    TABLE ACCESS FULL| PRODUCTS_T |      1 |00:00:00.01 |       4 |
|   4 |    TABLE ACCESS FULL| SALES_T    |      1 |00:00:01.84 |    4441 |
--------------------------------------------------------------------------

헉 8초 이상 걸렸다!
Subquery Unnesting 이 발생하여 작은쪽 테이블인 Products_t 가 Driving 테이블로 선정되었고 Salse_t 테이블과 Hash Join 을 하게된다. Scan 한 블럭수는 4445블럭 이며 8초 이상 수행되었다. 조인건수(Starts 항목 참조)는 1회에 불과하다. 이것은 최적으로 튜닝된 SQL 인가?  이것을 알아보기 위해 Hash Join 대신 Nested Loop 조인으로 바꿔보자.

  SELECT /*+ GATHER_PLAN_STATISTICS */
               prod_id, channel_id, SUM (qs) AS qs, SUM (amt) amt
    FROM (SELECT /*+ NO_MERGE USE_NL(PR S) */
                            s.prod_id, s.channel_id,
                            quantity_sold AS qs, amount_sold AS amt
                  FROM sales_t s, products_t pr
                 WHERE s.prod_id = pr.prod_id
                     AND pr.prod_category_desc = 'Photo')
GROUP BY prod_id, channel_id ;

원본 SQL 의 경우 왠만해서는 Nested Loop Join 으로 바뀌지 않으므로 강제로 서브쿼리를 From 절로 끌어 올려서 Nested Loop Join 으로 유도 하였다.

------------------------------------------------------------------------------------
| Id |Operation                       |Name            |Starts|   A-Time   |Buffers|
------------------------------------------------------------------------------------
|  1 | HASH GROUP BY                  |                |    1 |00:00:10.07 |   3099|
|  2 |  VIEW                          |                |    1 |00:00:08.92 |   3099|
|  3 |   NESTED LOOPS                 |                |    1 |00:00:07.70 |   3099|
|  4 |    NESTED LOOPS                |                |    1 |00:00:02.03 |     41|
|* 5 |     TABLE ACCESS FULL          |PRODUCTS_T      |    1 |00:00:00.01 |      4|
|  6 |     BITMAP CONVERSION TO ROWIDS|                |   26 |00:00:00.41 |     37|
|* 7 |      BITMAP INDEX SINGLE VALUE |SALES_T_PROD_BIX|   26 |00:00:00.01 |     37|
|  8 |    TABLE ACCESS BY INDEX ROWID |SALES_T         |  405K|00:00:02.45 |   3058|
------------------------------------------------------------------------------------
 

Hash Join 보다 더 느려졌다
Nested Loop Join 으로 유도 하자 인덱스는 제대로 사용하였지만 비효율이 극심하게 드러난다. 먼저 Nested Loop Join 이 40만번 이상 수행된다.(Starts항목 참조) 그결과로 View 를 만드는데 8초 이상 수행되었다. Buffer 가 30% 이상 줄어들었지만 조인건수 때문에 시간이 많이 걸리므로 이것은 견딜수 없는 결과이다.

그럼 Hash Join 에 만족해야 하나?
지금까지의 상식으로는 그렇다. 하지만 Driving Semi Join 기법을 사용한다면 생각이 달라질것이다.

Driving Semi Join 이란?
Driving Semi Join 이란 Semi Join 의 변형된 형태로서 Hash Join 의 장점(조인건수의 최소화)과 Nested Loop Join 의 장점(선행테이블이 상수화 되어 후행 테이블에서 인덱스를 효율적으로 사용) 을 합친 개념이다. 물론 Driving Semi Join 이라는 Title 자체의 의미대로 Semi Join 이 Driving 집합이 되는것은 당연하다. 다음의 SQL 을 보자  

SELECT /*+ GATHER_PLAN_STATISTICS QB_NAME(MAIN) SEMIJOIN_DRIVER(@SUB) */
             s.prod_id, s.channel_id, SUM (quantity_sold) AS qs, SUM (amount_sold) AS amt
   FROM sales_t s
WHERE s.prod_id IN (SELECT /*+ QB_NAME(SUB) */
                                          p.prod_id
                                FROM products_t p
                               WHERE p.prod_category_desc = 'Photo')
GROUP BY s.prod_id, s.channel_id ;


----------------------------------------------------------------------------------
| Id | Operation                     |Name            |Starts|   A-Time  |Buffers|
----------------------------------------------------------------------------------
|   1|  HASH GROUP BY                |                |     1|00:00:03.40|   2231|
|   2|   TABLE ACCESS BY INDEX ROWID |SALES_T         |     1|00:00:02.04|   2231|
|   3|    BITMAP CONVERSION TO ROWIDS|                |     1|00:00:00.42|     63|
|   4|     BITMAP MERGE              |                |     1|00:00:00.02|     63|
|   5|      BITMAP KEY ITERATION  |                |     1|00:00:00.02|     63|
|*  6|       TABLE ACCESS FULL       |PRODUCTS_T      |     1|00:00:00.01|      4|
|*  7|       BITMAP INDEX RANGE SCAN |SALES_T_PROD_BIX|    26|00:00:00.02|     59|
----------------------------------------------------------------------------------


환상적이다

Hash Join (8초 이상), Nested Loop Join (10초 이상) 인데 반하여 Driving Semi Join 은 3.4초 만에 끝이 났다. 또한 조인횟수도 26 번에 불과하며 I/O Block 수도 2231 로 Hash Join 의 절반에 해당한다.

환상적인 이유는?
그 이유는 Join 대신에 BITMAP KEY ITERATION 이 수행된 까닭이다.
BITMAP KEY ITERATION 의 원리는
INLIST ITERATOR Operation 과 같다고 볼수 있다. 
INLIST ITERATOR 의 개념은 InList / Concatnation / Range Scan Control 하기 을 참조하라.

Driving Semi Join은 B-Tree 인덱스에서도 사용이 가능해
이기능은 Bit Map 인덱스 뿐만 아니라 B-Tree 인덱스 에서도 사용가능하다.
하지만 _b_tree_bitmap_plans 파라미터가 True 로 되어 있어야 한다.
아래의 SQL 을 보자.


DROP INDEX SALES_T_PROD_BIX;  --> Bit Map 인덱스 Drop

CREATE INDEX SALES_T_PROD_IX ON HR.SALES_T (PROD_ID); --> B-Tree 인덱스 생성

ALTER SESSION SET  "_b_tree_bitmap_plans" = TRUE;

SELECT /*+ GATHER_PLAN_STATISTICS QB_NAME(MAIN) SEMIJOIN_DRIVER(@SUB) */
     s.prod_id, s.channel_id, SUM (quantity_sold) AS qs,
     SUM (amount_sold) AS amt
FROM sales_t s
WHERE s.prod_id IN (SELECT /*+ QB_NAME(SUB) */
                          p.prod_id
                     FROM products_t p
                    WHERE p.prod_category_desc = 'Photo')
GROUP BY s.prod_id, s.channel_id;


-------------------------------------------------------------------------------------
| Id |Operation                          |Name           |Starts|   A-Time  |Buffers|
-------------------------------------------------------------------------------------
|   1| HASH GROUP BY                     |               |     1|00:00:04.94|   3045|
|   2|  TABLE ACCESS BY INDEX ROWID      |SALES_T        |     1|00:00:03.76|   3045|
|   3|   BITMAP CONVERSION TO ROWIDS     |               |     1|00:00:02.14|    877|
|   4|    BITMAP MERGE                   |               |     1|00:00:01.73|    877|
|   5|     BITMAP KEY ITERATION          |               |     1|00:00:01.73|    877|
|*  6|      TABLE ACCESS FULL            |PRODUCTS_T     |     1|00:00:00.01|      4|
|   7|      BITMAP CONVERSION FROM ROWIDS|               |    26|00:00:01.73|    873|
|*  8|       INDEX RANGE SCAN            |SALES_T_PROD_IX|    26|00:00:00.86|    873|
-------------------------------------------------------------------------------------

Bit Map 인덱스에 비하여 약간의 비효율이 있다. 그것은 BITMAP CONVERSION FROM ROWIDS Operation 이 추가되었기 때문이다. 하지만 기존의 Hash Join 이나 Nested Loop Join 보다는 수행시간, I/O Block 수, 조인건수등이 우월함을 알수 있다.

당신은 이미 Driving Semi Join을 사용했다
여러분의 SQL 이 Star Transformation 을 수행하는 경우는 싫든 좋든 Driving Semi Join 기능을 이용하고 있는 것이다. 아래는 Star Transformation 을 수행하는 SQL 의 Plan 과 Outline Data 를 출력한 것이다.


  SELECT /*+ gather_plan_statistics */
         p.prod_id,
         c.CHANNEL_ID,
         SUM (quantity_sold) AS qs,
         SUM (amount_sold) AS amt
    FROM sh.sales s, sh.CHANNELS c, sh.products p
   WHERE s.CHANNEL_ID = c.CHANNEL_ID
     AND c.CHANNEL_DESC = 'Internet'
     AND s.prod_id = p.prod_id
     AND p.prod_category_desc = 'Photo'
GROUP BY p.prod_id, c.CHANNEL_ID;

------------------------------------------------------------------+
| Id  | Operation                              | Name             |
------------------------------------------------------------------+
| 0   | SELECT STATEMENT                       |                  |
| 1   |  HASH GROUP BY                         |                  |
| 2   |   HASH JOIN                            |                  |
| 3   |    TABLE ACCESS FULL                   | CHANNELS         |
| 4   |    HASH JOIN                           |                  |
| 5   |     TABLE ACCESS FULL                  | PRODUCTS         |
| 6   |     PARTITION RANGE ALL                |                  |
| 7   |      TABLE ACCESS BY LOCAL INDEX ROWID | SALES            |
| 8   |       BITMAP CONVERSION TO ROWIDS      |                  |
| 9   |        BITMAP AND                      |                  |
| 10  |         BITMAP MERGE                   |                  |
| 11  |          BITMAP KEY ITERATION          |                  |
| 12  |           BUFFER SORT                  |                  |
| 13  |            TABLE ACCESS FULL           | CHANNELS         |
| 14  |           BITMAP INDEX RANGE SCAN      | SALES_CHANNEL_BIX|

| 15  |         BITMAP MERGE                   |                  |
| 16  |          BITMAP KEY ITERATION          |                  |
| 17  |           BUFFER SORT                  |                  |
| 18  |            TABLE ACCESS FULL           | PRODUCTS         |
| 19  |           BITMAP INDEX RANGE SCAN      | SALES_PROD_BIX   |

------------------------------------------------------------------+

  Outline Data:
  /*+
    BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('11.1.0.6')
      ... 중간생략    
      STAR_TRANSFORMATION(@"SEL$1"
"S"@"SEL$1" SUBQUERIES(("C"@"SEL$1") ("P"@"SEL$1")))
      BITMAP_TREE(@"SEL$1"
"S"@"SEL$1" AND(("SALES"."CHANNEL_ID") ("SALES"."PROD_ID")))
     
SEMIJOIN_DRIVER(@"SEL$1898F719")
      SEMIJOIN_DRIVER(@"SEL$D750A531")
    END_OUTLINE_DATA
  */

Driving Semi Join의 장점은 또 있다
Driving Semi Join Nested Loop Join 시의 조인횟수에 의한 부하나 Hash 조인시의 Full Table Scan + Hashing 작업의 부하를 획기적으로 감소켜준다. 더 좋은것은 Star Transformation 사용시에 바인드 변수를 사용할수 없었지만 Driving Semi Join은 이러한 제약이 없다. 하지만 이 기능은 오라클사의 정식힌트가 아니므로 필자의 경우 데이터 분석 용도로만 사용하고 있다. 이기능이 11.2 버젼에는 정식으로 메뉴얼에 나오길 기대하며 이글을 마칠까 한다.


신고
Posted by extremedb

댓글을 달아 주세요

  1. Favicon of http://ukja.tistory.com BlogIcon 욱짜 2009.09.01 11:39 신고  댓글주소  수정/삭제  댓글쓰기

    재미있는 내용입니다.

    Hint가 소개된 시점과 비교해보면 보면 아마도 Star Transformation을 제어하기 위한 용도로 소개된게 아닌가 하는 생각이 드는군요.

    • Favicon of http://scidb.tistory.com BlogIcon extremedb 2009.09.01 11:56 신고  댓글주소  수정/삭제

      8i 에서 나온것을 보면 Star Transformation 의 용도로 나온것이 맞는것 같습니다.
      그런의미에서 본다면 위의 SQL 은 Single-Dimension-Star Transformation 로 불러도 될거 같습니다.^^

  2. feelie 2009.09.02 12:25 신고  댓글주소  수정/삭제  댓글쓰기

    좋은 내용 잘 봤습니다. 내용과는 차이가 있지만
    필터와 세미조인간에 차이에 대한 개념이 명확치가 않네요.
    세미조인으로 실행계획이 풀리면 여러가지 방법으로(NL,HASH) 조인이 가능하다. 메인쿼리가 드라이빙이된다
    필터는 메인 쿼리가 무조건 드라이빙이 된다(nested loop 방식과 유사하다)
    개념적으로만 봐서는 조건이 만족하면 더이상 조인을 진행하지 않기때문에 nested loop 세미조인과 필터는 성능상으로
    별차이가 없다. 단 hash join은 성능상으 차이가 발생할수 있다.
    이정도가 필터와 세미조인의 차이로 생각하면 됩니까?

  3. Favicon of http://scidb.tistory.com BlogIcon extremedb 2009.09.02 13:21 신고  댓글주소  수정/삭제  댓글쓰기

    feelie 님 께서 설명한것은 대부분 맞다고 보시면 됩니다. 하지만 2% 부족 한것이 있습니다.
    Filter 의 Caching 효과 에 의해서 Neste Loop Semi Join 과의 차이가 있을수 있습니다.
    즉 서브쿼리에서 return 되는 distinct 한 값의 갯수가 많다면 semi join 을 선택하면되고 적다면 filter 서브쿼리를 선택 하시면 됩니다.
    이때 주의할것은 서브쿼리에서 distinct 하게 return 되는 값은 서브쿼리와 메인쿼리의 조인컬럼을 기준으로 count 하셔야 합니다.
    ex) select * from a
    where exists (select 1 from b where b.col1 = a.col1 ) 이런 SQL 이 있을경우
    DISTINCT 한 값의 갯수는 SELECT COUNT(DISTINCT COL1) FROM B 가 될것입니다.
    DISTINCT 한값이 적을때 filter 가 좋은 이유는 filter 서브쿼리나 스칼라서브쿼리의 caching 기법 때문에 생기는 차이입니다.
    단 메인쿼리가 초대용량인 경우는 hash join right semi 를 선택하시면 됩니다.

    질문하신 내용은 http://scidb.tistory.com/entry/SubQuery-Using-Method-1 로 가셔서 질문하시는 것이 더 나을뻔 햇습니다.

부제목 : GBP (Group By Placement ) 의 목적및 용도

Parallel Query 수행시 Group by 를 먼저 수행하라(Group By Push Down) 라는 글에서 먼저 Group By 를 수행하여 성능을 향상시키는 경우를 설명하였다. 오늘도 Group By Push Dwon 과 아주 흡사한 Query Transformation 에 대하여 설명하려 한다.

Group By Push Down 은 Parallel Query 에서 한정적으로 나타나는 기능이지만 Group By Placement 는 이러한 제약이 없다. 또한 Group By Placement 는 Query Transformation 의 종류 이지만 Group By Push Down은 SQL 자체의 변환과정이 없다는 점에서 엄연히 다르다.

GBP 가 뭐하는 거지?
GBP 란 기본적으로 조인의 부하를 줄이기 위한 수단이다. 조인을 수행하기전에 Group By 를 먼저 수행하고 건수를 줄이고 난후에 조인을 수행함으로서 조인건수가 획기적으로 감소되게 하는데 그목적이 있다. 이 기능은 주로 OLTP 보다는 DW 의 대용량 Mart 등에서 사용할 경우 성능향상을 극대화 할수 있다. 아래의 SQL 을 보자.

환경 Oracle 11g (11.1.0.7)

  SELECT   /*+ qb_name(main) place_group_by(@main (S@main)) */

         cust_city_id, SUM (quantity_sold)

    FROM customers c, sales s

   WHERE c.cust_id = s.cust_id AND s.cust_id BETWEEN 5000 AND 5500

   GROUP BY c.cust_city_id ;


위의 SQL의 목적은 고객 테이블(customers)과 판매 테이블(sales)을 조인하여 고객의 도시별 판매수량을 구하는 것이다.

상식적으로는 ..
일반적인 상식으로는 customers 테이블과 sales 테이블을 조인한 후에 Group BY 가 한번 수행된다고 알고 있다. 하지만 Oracle 11g 로 넘어오면서 '상식의 파괴'가 일어난다.      

--------------------------------------------+-----------------------------------+

| Id  | Operation                | Name     | Rows  | Bytes | Cost  | Time      |

--------------------------------------------+-----------------------------------+

| 0   | SELECT STATEMENT         |          |       |       |   973 |           |

| 1   |  HASH GROUP BY           |          |   620 |   17K |   973 |  00:00:12 |

| 2   |   HASH JOIN              |          |  7059 |  193K |   972 |  00:00:12 |

| 3   |    VIEW                  | VW_GBC_1 |  7059 |  124K |   566 |  00:00:07 |

| 4   |     HASH GROUP BY        |          |  7059 |   90K |   566 |  00:00:07 |

| 5   |      PARTITION RANGE ALL |          |   28K |  370K |   492 |  00:00:06 |

| 6   |       TABLE ACCESS FULL  | SALES    |   28K |  370K |   492 |  00:00:06 |

| 7   |    TABLE ACCESS FULL     | CUSTOMERS|   54K |  542K |   405 |  00:00:05 |

--------------------------------------------+-----------------------------------+


Group By 가 두번 발생하다
위의 Plan 을 보면 Group By 가 두번 발생하였으며 조인도 sales 테이블을 Group By 한 이후에 발생하였다.

왜 두번 수행되나?
이것은 대용량 테이블인 sales 테이블을 먼저 조인 기준컬럼인 cust_id 로 먼저 Group By 하고 난후에 조인함으로서 조인의 부하를 줄이기 위함이다. 다시 말하면 오라클 Transformer는 SQL 을 아래와 같이 바꾼 것 이다.

  SELECT c.cust_city_id cust_city_id, SUM (vw_gbc_1.item_2) sum_qt
    FROM (SELECT   s.cust_id item_1, SUM (s.quantity_sold) item_2
              FROM sales s
             WHERE s.cust_id <= 5500 AND s.cust_id >= 5000
          GROUP BY s.cust_id) vw_gbc_1,
         customers c
   WHERE c.cust_id = vw_gbc_1.item_1
   GROUP BY c.cust_city_id;


sales 테이블을 Group By 하여 인라인뷰를 먼저 만들고 customers 와 조인후 다시 c.cust_city_id 로 Group By 하고 있다. 인라인 뷰의 이름이 vw_gbc_1 인데 GBP 가 여러번 발생되면 vw_gbc_1, vw_gbc_2, vw_gbc_3 ... 처럼 숫자 부분이 증가 된다.
 
GBP 는 CBQT(Cost Based Query Transformation) 이다
Query Transformer 는 GBP 를 수행하기 위해 변환된(GBP 가 수행된) SQL 과 변환되지 않은 SQL을 각각 비용을 계산하여 가장 비용이 낮은 SQL 을 선택하게 된다. GBP 가 수행된 SQL 은 여러개 일수 있다.
아래는 작업을 수행하는 과정을 보여주는 10053 Trace 내용이다.

***********************************

Cost-Based Group By Placement

***********************************

GBP: Checking validity of GBP for query block MAIN (#1)

GBP: Checking validity of group-by placement for query block MAIN (#1)

 

GBP: Using search type: exhaustive

GBP: Considering group-by placement on query block MAIN (#1)

GBP: Starting iteration 1, state space = (1,2) : (0,0)

GBP: Transformed query
...중간생략


10053은 어렵지 않다
10053 을 어렵게 생각하는 DBA 들이 있다. 절대 어렵지 않다. GBP를 수행하기 위한 Using search type이 exhaustive 로 되어 있다. Using search type 이라는 것 은 변환 가능한 경우의 수를 어디까지 고려 할것인지 의 정도(level) 을 설명한 것이고 그 level 은 exhaustive 로 되어 있다. exhaustive 라는 것은 모든 변환가능한 경우의 수를 고려 하겠다는 뜻이다. 

Iteration 이란 무엇인가?
Iteration 이란 CBQT 에서만 발생하며 기본적으로 변환이 수행된 경우와 수행되지 않은 경우의 Cost 를 비교하기 위한 경우의 수이다. 일반적으로 iteration 1 에서 변환이 수행되지 않은 경우를 나타내며 iteration 2 에서는 변환이 수행된 경우의 일련의 과정을 나타낸다. 마지막에는 iteration 1 과 iteration 2 의 Cost 를 비교하여 Cost 가 낮은 경우를 선택하게 된다.
 
Iteration 은 여러번 생길 수 있다
복잡한 SQL 의 경우 변환의 결과가 여러개 일수 있는데 이때는 Starting iteration 1, Starting iteration 2, Starting iteration 3 ... 등으로 증가한다. 하지만 원본 SQL 은 place_group_by 힌트를 사용하였으므로 GBP 를 수행한 경우(iteration 1)와 수행하지 않은 경우(iteration 2)의 Cost 를 비교하지 않고 iteration 1 에서 멈추게 된다.


GBP 를 Control 하자
GBP Control 하는 파라미터는 _optimizer_group_by_placement 이며 Default True 이다. 힌트는 GBP 를 강제하려면 place_group_by 헤제 하려면 no_place_group_by 힌트를 사용하면 된다.
 
결론
GBP 는 기본적으로 오라클이 자동으로 수행한다.
GBP 는 성능을 향상시키는 훌륭한 기능이지만 잘못 사용하면 오히려 독이 될수 있다. 조인을 먼저 수행하는 것이 오히려 결과 건수를 획기적으로 줄여주는 경우가 있는데 이런 경우는 GBP 를 사용하면 안된다. 이런 경우가 아니면서 조인하기 전에 먼저 Group By 하여 건수를 확실히 줄일수 있을때만 사용하여야 한다.

 

신고
Posted by extremedb

댓글을 달아 주세요

  1. feelie 2009.08.25 18:49 신고  댓글주소  수정/삭제  댓글쓰기

    일주일이 무척 길다는 생각이 들 정도로 언제 새로운 내용을 올리시나 기다렸습니다.
    좋은 내용 잘 봤습니다.
    지난번에 보내주신 메일도 잘 받았습니다. 늦었지만 감사합니다.
    다음 내용을 기대하면서 열공하렵니다..

    • Favicon of http://scidb.tistory.com BlogIcon extremedb 2009.08.26 00:47 신고  댓글주소  수정/삭제

      네 반갑습니다.
      도움이 된다면 다행입니다.
      한달이 대부분 4주이기 때문에 글이 4번 혹은 5번 밖에 나가지 못합니다. 이점 양해 바랍니다.

  2. 칸쵸뿐이야흥 2009.08.26 11:13 신고  댓글주소  수정/삭제  댓글쓰기

    좋은글 감사드립니다

Parallel Query 수행시 튜닝 방법에 대해 아래와 같이 2개의 글을 연재한 바 있다.
1.Parallel Query 의 조인시 Row Distribution  --> Join 시 Row 의 분배방법 튜닝
2.Parallel Query 의 조인시 또다른 튜닝방법(Parallel Join Filter) --> Join Filter 튜닝

Parallel Query 의 튜닝방법은 많지 않아...
Parallel Query 의 튜닝 방법은 많지 않은데 그 이유는 Parallel Query 기능 자체를 튜닝 하는것이 아니라 Hash 조인의 튜닝 혹은 Block I/O 의 튜닝등 Parallel 자체와는 상관없는 것 을 튜닝하는 경우가 많기 때문이다.
오늘은 3번째로 Parallel Query 자체를 튜닝 하는 또다른 방법에 대하여 논의 할것이다.

잘못된 미신을 믿지 마라.
사실 필자가 Group By Push Down 기능을 설명하는 이유는 잘못된 미신 때문이다.
"Parallel Query 를 수행하면 Group By 가 두번 수행된다. 따라서 Group By 가 있는 SQL 은 Parallel 을 사용하지 마라."
실제 DBA 들의 입에서 오고 가는 말들이다. 잘못된 미신이 퍼져 있다니 참으로 안타까운 현실이 아닐수 없다.
Parallel Query 를 수행하면 Group By 가 무조건 두번 수행되는 것은 아니며 두번 수행 된다면 오히려 성능향상을 기대할 수 있다.
이 글을 읽고 개념을 확실히 하기 바란다.

TQ 를 알고 가자.
먼저 Group By Push Down 기능을 설명하기 전에 TQ(Table queues) 개념을 알아야 한다.
1.TQ 는 Processes간의 데이터를 주고받는 기능을 한다.
2.하나의 TQ 는 여러개의 parallel Slave 를 가진다.
3.TQ 는 Parallel Query 수행시 생성된다.
상세한 개념은 위에서 언급한 글중 2번을 참조하기 바란다.

Group By Push Down 이 뭐야?
1.Group By Push Down 이란 TQ 에 데이터를 전달하기 전에 Group By 를 수행하여 데이터의 건수를 대폭 줄인후에 TQ 에 데이터를 전달함으로서 일량을 줄이고 성능을 향상시키는데 목적이 있다.
2.Group By Push Down 은 Parallel Query 에 Group By 가 포함되어 있는 경우 발생한다.

원리는 같다.
이기능은 마치 DW 용 SQL 작성시 Fact 테이블(대용량) 을 먼저 Group By 한후에 Dimension 테이블(소용량 코드 테이블)과 조인하여 조인 건수를 대폭 줄임으로서  성능 향상을 꾀하는것과 같은 개념이다. 이기능은 오라클이 자동으로 해주기도 하는데 이것을 "Group By Placement" 라고 하며 추후 따로 글을 올릴 생각이다.

어떻게 하는건데?
Group By Push Down을 수행하기위해 아래의 SQL 을 보자.
환경: Oracle 11g 11.1.0.7

SELECT   /*+ GATHER_PLAN_STATISTICS FULL(A) parallel(A 4) GBY_PUSHDOWN */
         prod_id, cust_id, COUNT (*) cnt
    FROM sh.sales A
GROUP BY prod_id, cust_id;

GBY_PUSHDOWN 란 힌트를 사용하였으며 아래의 Plan 에서 보는바와 같이 성공적으로 Group By Push Down 이 수행되었다.

---------------------------------------------------------------------------
| Id  | Operation                | Name     | E-Rows | Used-Mem | Used-Tmp|
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT         |          |        |          |         |
|   1 |  PX COORDINATOR          |          |        |          |         |
|   2 |   PX SEND QC (RANDOM)    | :TQ10001 |    359K|          |         |
|   3 |    HASH GROUP BY         |          |    359K| 2197K (0)|         |
|   4 |     PX RECEIVE           |          |    359K|          |         |
|   5 |      PX SEND HASH        | :TQ10000 |    359K|          |         |
|   6 |       HASH GROUP BY    |          |    359K| 3284K (1)|    1024 |
|   7 |        PX BLOCK ITERATOR |          |    918K|          |         |
|*  8 |         TABLE ACCESS FULL| SALES    |    918K|          |         |
---------------------------------------------------------------------------

원래 수행되어야 하는 Group By 는 Id 기눈으로 3번(파랑색 부분) 이지만 Id 6번에서 먼저 Group By 가 수행되었다. 그이유는 Id 5번의 :TQ10000 에게 데이터를 전달하기전에 데이터를 줄여서 성능을 향상시키기 위함이다. 실제로 옵티마이져는 Id 5번에서 Group By 된 359K Row에 대한 데이터만 처리 할것으로 예상하고 있다.

튜닝은 Trade Off 이다.
주의 사항이 있다. 실제로 TQ의 일량은 줄어들지만 불필요한 Hash Group By 혹은 Sort Group By 가 수행되어 성능이 더 나빠질수 도 있다는것이다. Id 6 에서 Hash Area Size 가 부족하여 Disk 작업(Used-Tmp 부분 참조)이 발생하였다. 따라서 Group By 를 하면 건수가 몇배~ 몇십배이상 줄어드는 경우에 적용하여야 하며  Disk 에서 Sort 및 Hash 작업이 발생하는 경우는 PGA 튜닝을 동반하여야 한다.

Group By Push Down 이 적용되지 않은 Plan 을 보여다오.
GBY_PUSHDOWN 힌트 대신에 NO_GBY_PUSHDOWN 힌트를 사용하면 Group By Push Down이 발생하지 않는다.

SELECT   /*+ GATHER_PLAN_STATISTICS FULL(A) parallel(A 4) NO_GBY_PUSHDOWN */
         prod_id, cust_id, COUNT (*) cnt
    FROM sh.sales A
GROUP BY prod_id, cust_id;


----------------------------------------------------------------
| Id  | Operation               | Name     | E-Rows | Used-Mem |
----------------------------------------------------------------
|   0 | SELECT STATEMENT        |          |        |          |
|   1 |  PX COORDINATOR         |          |        |          |
|   2 |   PX SEND QC (RANDOM)   | :TQ10001 |    359K|          |
|   3 |    HASH GROUP BY        |          |    359K| 4492K (0)|
|   4 |     PX RECEIVE          |          |    918K|          |
|   5 |      PX SEND HASH       | :TQ10000 |    918K|          |
|   6 |       PX BLOCK ITERATOR |          |    918K|          |
|*  7 |        TABLE ACCESS FULL| SALES    |    918K|          |
----------------------------------------------------------------


정상적으로 Group By 가 한번만 수행되었지만 옵티마이져는 TQ 의 일량이 Group By Push Down 이 적용된 SQL 에 비하여 918K 로 약 3배정도 중가한것으로 판단하였고 실제로도 그렇게 수행된다.

Group By Push Down은 11g 신기능이 아니다.
GBY_PUSHDOWN / NO_GBY_PUSHDOWN 등의 힌트는 11g 에서 새로 나온 것 이다. 하지만 이전버젼에서도 _groupby_nopushdown_cut_ratio 파라미터를 0 으로 세팅하면 Group By Push Down 을 강제로 수행할수 있다. 이파라미터의 Default 값은 3 이며 이경우는 Group By Push Down 의 수행여부를 옵티마이져가 판단한다. 아래는 옵티마이져의 Costing 과정을 10053 Trace 에서 발췌한 것이다.

*********************************
Number of join permutations tried: 1
*********************************
GROUP BY adjustment factor: 0.707107
GROUP BY cardinality:  359386.000000, TABLE cardinality:  918843.000000
Costing group-by pushdown:
    SORT ressource         Sort statistics
      Sort width:         598 Area size:      552960 Max Area size:   104857600
      Degree:               1
      Blocks to Sort: 563 Row size:     20 Total Rows:         229711
      Initial runs:   2 Merge passes:  1 IO Cost / pass:        306
      Total IO sort cost: 869      Total CPU sort cost: 230852464
      Total Temp space used: 4629000
  Distribution cost: resc_cpu: 91885309  resp_cpu: 22971327
  Costing final group-by:
  Subtracting no-pushdown group-by:
    SORT ressource         Sort statistics
... 이후 생략

적재적소에 사용하자.
실제로 옵티마이져는 Group By Push Down 의 수행여부를 Cost 로서 판단하고 있으므로 기본적으로는 오라클에 맏기면 된다. 하지만 아주 복잡한 SQL 이나 옵티마이져가 판단을 잘못할 경우에 Group By Push Down 을 수동으로 적절히 발생시킨다면 성능향상을 꾀할수 있다.

편집후기:
필자가 이글을 올린다고 하니 어느 지인은 컨설턴트의 밥그릇 타령을 한다.
다시말해 이런것들을 모든 개발자가 안다면 컨설턴트가 설자리가 없다는 것인데...
글쎄...
여러분은 어떻게 생각하는가?    

신고
Posted by extremedb

댓글을 달아 주세요

  1. Favicon of http://ukja.tistory.com BlogIcon 욱짜 2009.08.12 11:43 신고  댓글주소  수정/삭제  댓글쓰기

    이런 글을 올린다고 해서 밥그릇이 없어진다면 Jonathan Lewis나 Steve Adams는 이미 굶어 죽었을 겁니다. ^^;

    이런 기세로 계속 정리하시면 Parallel Execution에 대해 좋은 내용의 글모음(아마 이번 책으로도 나오겠죠?)이 되겠네요. 개인적으로 한번 다루어주셨으면 하는 것이 RAC 환경에서의 Parallel Query에 대한 내용입니다. RAC와 PQ가 합쳐지면 많은 사람들에게 더 미지의 영역이지요.

    • Favicon of http://scidb.tistory.com BlogIcon extremedb 2009.08.12 12:53 신고  댓글주소  수정/삭제

      저도 같은 생각입니다.
      이런글을 올릴수록 오히려 설자리가 늘지 않을까요?
      RAC + PQ 가 합쳐진 경우는 고려 해야할 것 들이 많네요. 미쳐 생각치 못했던 부분인데 좋은정보 감사드립니다.
      내년에 나올책은 지면이 400 페이지로 한정적이라 ....
      한번 고려해 보아야 할거 같습니다.

  2. feelie 2009.08.12 21:18 신고  댓글주소  수정/삭제  댓글쓰기

    얼마전부터 이곳에서 좋은 자료를 보고 있는데 이제야 댓글을 달아봅니다.
    좋은 내용 감사합니다.
    오라클을 조금안다고 생각했는데. 이곳의 내용을 보니 오라클을 전혀 모르고 있었다는 생각이 듭니다.
    좋은 책들도 추천해주셨는데 대부분이 원서라 조금 아쉽습니다.
    혹시 저정도의 수준에서 (대용량데이터베이스 정독수준) 고수로 가기위해서 해주실 얘기나 책이있으면(원서는 제가 좀) 부탁드리겠습니다.
    다음주 내용도 기대가 됩니다.

    • Favicon of http://scidb.tistory.com BlogIcon extremedb 2009.08.12 22:41 신고  댓글주소  수정/삭제

      원서를 싫어하시니 답변하기가 어렵네요.^^
      하지만 제가 아는데로 나열해 보겠습니다.
      2가지 관점 입니다. 모델링과 튜닝.
      개발관점에서 제일 좋은것은 대용량 시리즈 이기때문에 제외 하였습니다.
      제일 좋은것은 오라클 메뉴얼 입니다.--> 영문임
      메뉴얼 중에서도 Concept Guide, Admin Guide, Performace Tuning Guide 를 추천합니다.
      그것이 영문이라서 싫으시면 OCP 과정의 책을 차선으로 추천합니다. 총 4권으로 알고 있고 한글도 있는것으로 알고 있습니다.
      다음으로 OTN 과정중 RAC for Admin 교육도 한글 책으로 나온것을 보았습니다.
      그리고 OWI 관련 책은 단연 조동욱님의 책 2권을 추천합니다. 튜닝을 하려면 OWI 는 필수라고 할수 있습니다.
      그리고 트랜잭션을 깊이 있게 공부하시려면 김시연님의 책을 추천합니다.
      오라클을 전체 DBMS 관점에서 산을 보고 튜닝하려면 이번에 나온 조시형님의 오라클 성능 고도화 해법시리즈에서 3장 을 추천합니다. 물론 다른장의 내용들은 기본적으로 아셔야 합니다.
      그리고 중요한 모델링 책인데요.
      어쩔수 없이 원서를 추천할수 밖에 없습니다.
      Richard Barker 가 1990년에 저술한 Case*Method: Entity Relationship Modelling 이란 책 입니다.
      저는 이책보다 좋은 책을 아직 보지 못했습니다.
      원서라고해도 반드시 정복해야 할 책입니다.
      지금 이책을 살수 있을지는 저도 확신할수 없습니다.
      그리고 원서를 쉽게 읽을수 있는 방법이 있습니다.
      여러군데의 카페에서 유명한 원서들의 스터디를 진행하고 있으니 기회가 되면 반드시 참석하시기 바랍니다.
      감사합니다.

    • feelie 2009.08.13 11:17 신고  댓글주소  수정/삭제

      너무 성의 없이 날로 먹으려고 생각하실수도 있지만
      여러 카페라면 이것도 좀 추천을 해주실수 있는지요?

      답변 감사합니다.

    • Favicon of http://scidb.tistory.com BlogIcon extremedb 2009.08.13 12:43 신고  댓글주소  수정/삭제

      메일을 남겨주시면 메일로 답변드리겠습니다.

  3. feelie 2009.08.13 16:43 신고  댓글주소  수정/삭제  댓글쓰기

    feelie@empal.com 입니다.
    도움 주시니 열심히 열공하겠습니다.
    감사합니다..

  4. Favicon of http://1ststreet.tistory.com BlogIcon SITD 2011.07.01 16:23 신고  댓글주소  수정/삭제  댓글쓰기

    저같은 경우엔(개발자입니다.)

    extremedb님의 글을 볼 때마다 아...이래서 컨설턴트가 대단한 거구나..하고 감탄을 합니다.

    대대수의 사람들에게 오히려 전문성을 더 보여줄 수 있는 방법이 아닐까 합니다.

    보통, 어렵고 힘들다는건 알면 알수록 더 많이 느끼니까요 ㅎ
    (최근에 한동안 놓았던 영어공부를 다시 시작했는데요,
    예전엔 몰랐는데 해보니까 더 어렵고 막막하다는 걸 느끼고 있습니다...)

아래는 약 3년전에 고객사 DBA 와 필자의 대화내용 이다.
물론 내용은 오라클 파티션 가이드 이다.

파티션 가이드? 구글에 있잖아!

DBA : Oracle 10g 기준으로 파티션 가이드를 하나 만들어 주세요.

필자 : 그런것은 구글에서 "파티션 가이드" 혹은 "Partiton 가이드" 로 검색을 하면 주루룩 하고 나올겁니다.

DBA : 제가 검색을 다 해보았지만 그런 것은 없었습니다.

필자 : 그럴리가요?

DBA : 파티션관련 자료는 많이 있지만 문제는 그 문서들이 파티션의 종류와 특성만을 설명하고 있다는 것입니다.
         파티션의 종류와 특징, 파티션 키의 선정, 인덱스 문제(Local, Global, Prefix ..등등 ) 은 왠만한
         개발자들도 알고 있습니다. 따라서 이런 문서는 개발팀에게 배포할 성격의 것은 아닙니다.

필자 : 그렇다면 하나의 테이블이 있을때 그 테이블을 파티션을 적용할것인지 아닌지 판단하는 로직을 원하시는 건가요?

DBA : 그렇습니다. 파티션을 적용할것인지 말것인지의 로직과 적용한다면 어떤 파티션을 적용할것인지 에 대한
         명쾌한 로드맵 같은것이 있으면 좋겠습니다.

이상하네? 가이드가 없네?
그 DBA 의 말이 일리가 있었다. 3년전 구글에서 조회 해보니 파티션 종류별 특성과 설명 뿐이었다. 이것은 놀라운 결과이다. 무수한 프로젝트에서 파티션을 적용해왔을 터
그런데 가이드가 없다니...

우리가 원하는 것은  "테이블을 어떠한 기준으로 무슨 종류의 파티션을 적용하느냐" 이다.
문제는 "각테이블을 어떤 기준으로 파티션을 적용하며 파티션의 종류는 무엇으로 할것인가?" 이다.  그런데 최근에 다시 파티션 가이드에 대해 구글에서 조회해본 결과 이런 성격의 자료가  전혀 없다. 물론 영문검색을 하면 오라클 메뉴얼이 나오긴 한다. 오라클 메뉴얼에도 명확한 기준은 없다. 또한 고객들이 원하는 것은 한글로된 문서이다.

가이드를 작성하는 방법
일반적으로 가이드라고 하는것은 아래와 같은 조건을 충족 해야한다.

1. 구체적인 표현으로 작성해야 한다. 모호한 표현은 가이드가 될수 없다. 더욱 모호해질 뿐이기 때문이다.
2. 판단 기준이 명확해야 한다. 이것은 분기로직이 있으면 더욱 명확해진다.
3. "10 중 8, 9" 라는 말이 있다. 이말은 예외적인 경우가 아니라면 가이드 대로 하는것이 항상 옳아야 한다는 것이다.
4. 분량이 작아야 한다. 가이드란 도로의 로드맵(이정표)라고 할수 있다.  가이드가 100~200 페이지 정도 된다면 이정표라고 할수 없을 것이다. 그것은 메뉴얼에 가깝다.

위의 방법대로 Oracle 10g 파티션 가이드를 작성해보면 다음과 같이 한장으로 요약 할수 있다.

사용자 삽입 이미지

위의 로드맵을 한글로 표현하면 다음과 같다.

사용자 삽입 이미지

이상으로 2장짜리 Oracle 10g 파티션 가이드를 만들어 보았다. 한글로된 가이드를 필자가 작성하였고 그것을 보고 한 지인이 즉석에서 로드맵을 작성한 것 인데 필자가 그 로드맵의 일부분을 알기쉽게 수정한것이다.
아래는 이 가이드를 정리한 pdf 문서이다.
이 문서에는 추가적으로 파티션 적용시의 주의사항 까지 덤으로 있으니 반드시 다운 받아서 읽어 보기 바란다.
물론 문서를 배포시 출처를 밝히는 것은 기본이고 매너의 문제이다.

Oracle_10g_Partition_적용_가이드.pdf

Oracle 10g Partition 적용가이드





편집후기 :
가이드를 작성 하고 보니 한가지가 위에서 언급한 가이드를 작성하는 방법1 에 위배된다.  대용량 혹은 소용량 테이블이 그것인데 이 용어는 필자로 서도 어쩔수 없다. 테이블의 용량이 어디서 부터 대용량이고 어디서 부터 소용량인지 구분하는 기준은 존재하지 않기 때문이다.

한가지 화두가 있는데 물리 모델링시에 모델러가 파티셔닝을 해야할까? 아니면 이것을 모델링의 개념이 아닌 튜닝의 개념으로 보아서 튜너 혹은 DBA 가 해야할까? 여러분은 어떻게 생각 하는가?

저작자 표시 비영리 동일 조건 변경 허락
신고

'Oracle > Partition' 카테고리의 다른 글

오라클 Upgrade 후에 파티션을 만들 수 없어요  (0) 2011.03.15
Oracle 10g Partitioning 가이드  (12) 2009.08.05
Posted by extremedb

댓글을 달아 주세요

  1. 지나가던 나그네 2009.08.06 09:53 신고  댓글주소  수정/삭제  댓글쓰기

    오도사(오(라클|동규) 도사)님! 파티션 가이드가 많지 않았는데 이렇게 좋은 자료를 만들어주셔서 감사합니다.

    넓은 아량으로 배포까지 해주시니 학습에 많은 도움이 될꺼 같습니다.

    • Favicon of http://scidb.tistory.com BlogIcon extremedb 2009.08.06 10:41 신고  댓글주소  수정/삭제

      ㅎㅎ 제 이름이 그렇게 연결되나요?
      도사라니요 당치도 않습니다.
      그저 오라클을 더많이 알기 위해 노력하는 사람 이지요.

  2. 혈기린 2009.08.06 10:49 신고  댓글주소  수정/삭제  댓글쓰기

    전 물리모델링시에 모델러가 파티션 적용을 고려해야 할거 같은데요 ^^
    좋은 가이드라인 감사합니다.

    • Favicon of http://scidb.tistory.com BlogIcon extremedb 2009.08.06 12:54 신고  댓글주소  수정/삭제

      제 생각도 마찬가지 입니다.
      이유는 데이터의 성격을 제일 잘아는 사람들이 모델러 이기 때문입니다. 파티션을 적용하려면 데이터의 성격을 잘알아야 하지요.
      개발 프로젝트시에 모델러들이 다빠져나가고 나면 새로들어온 튜너나 DBA, 개발자 의 경우 데이터의 성격을 잘알지 못하기 때문에 파티션을 적용하기 어렵죠. 따라서 최소한 모델러들이 물리 ERD 에 반영을 해놓거나 아니면 따로 목록이라도 만들어서 DBA 나 튜너에게 인수인계를 한다면 데이터를 재분석하는일 등은 없어질 것 입니다.

  3. 타락천사 2009.09.30 16:36 신고  댓글주소  수정/삭제  댓글쓰기

    좋은글 감사합니다.
    출처는 밝히고, OracleClub 에 남기겠습니다.

  4. 돌칼 2010.01.06 17:28 신고  댓글주소  수정/삭제  댓글쓰기

    음 저도 물리 모델링시에 파티션적용을 고려하는게 맞을것 같아요.
    사실 둘 다 같이 하는게 맞지 않을까 합니다.
    저는 보관주기가 있는 대용량 테이블일 경우만 생각을 하고 적용해왔는데, range + hash파티션도 고려를 해봐야할것 같아요. 가이드 정말 명쾌하게 잘 만드신것 같습니다. 도움이 많이 되었습니다.

    • Favicon of http://scidb.tistory.com BlogIcon extremedb 2010.01.06 19:20 신고  댓글주소  수정/삭제

      반갑습니다.
      말씀 하신것 처럼 튜너와 모델러가 같이 작업을 한다면 최적이죠.
      그리고 가이드가 도움이 되었다니 다행입니다.
      새해 복많이 받으세요.

  5. 금땡이 2010.07.13 15:37 신고  댓글주소  수정/삭제  댓글쓰기

    오~ 좋은 자료 입니다.

    실제 물리 모델 단계에서 파티션 대상 Table 선정시 잘 활용하고 있는데요,

    아래의 2가지 질문 사항이 있습니다.

    1. '6.동일Block 동시 처리'에 대한 판단을 정량적으로 할 수 있는 방법이 있는지 궁금 합니다.

    논리 모델링을 위주로 하는 모델러이다 보니 위의 사항에 대한 가이드시 애매한 부분이 많더라구요.

    결국, '거래로그'와 같이 대량의 Transaction에 대한 DML 경합에 대한 분산으로 생각되는데...

    2. 물리 모델 단계(구현 단계가 아닌 설계 단계)에서 가급적 완성도 높은 파티셔닝 전략을 위해 '6.동일Block 동시 처리'에 대한 판단을 할 수

    있는지도 궁금 하구요.


    고견 부탁 드립니다.

    • Favicon of http://scidb.tistory.com BlogIcon extremedb 2010.07.14 11:12 신고  댓글주소  수정/삭제

      오랜만 입니다.
      답변을 드리겠습니다.

      1.동일블럭 동시처리는 대량 Transaction에 대한 DML 경합도 해당이 되지만 select 도 해당됩니다. 가령 GC bufffer busy 나 buffer busy wait은 dml 과 select문을 가리지 않고 발생합니다.

      문제는 정량적으로 판단하여 Hash 파티션을 할것인지 말것인지 판단하는 것은 매우 어렵다는 것입니다.
      "GC bufffer busy 나 buffer busy wait등이 초당 몇번 발생하면 hash 파티션을 해야하나?"는 기준은 없다는 것입니다.

      하지만 rough 한 기준은 만들수 있습니다. SQL의 ELAPSTIME 시간 기준으로 1/3(33%)의 시간 이상을 GC bufffer busy 나 buffer busy wait EVENT 때문에 대기하고 있다면 문제가 있다고 보아도 무방합니다. 즉 수행시간중 1/3 이 HOT block 문제로 대기한다면 해결해 주어야 합니다. 물론 실시간 동시처리가 많은 테이블에 한 합니다. 이 정보는 AWR 이나 ADDM 에서 확인가능 합니다.

      2. 물리모델 단계에서 예상은 할 수 있지만 결론을 내릴수는 없다고 봅니다. 말씀하신 '거래로그'와 같이 대량의 Transaction은 뻔한것 이므로 Hash 파티션을 무조건 하여도 될것 같지만 문제는 Hash 파티션의 갯수를 몇개로 할것이냐 입니다. 4개로 쪼갤 것인지 아니면 8개로 쪼갤것인지? 아니면 265개? .....정확한 것은 테스트를 하여서 Wait Event가 줄어드는 양을 보고 결정하는 수 밖에 없다고 보여집니다. 아쉽네요.

  6. Favicon of http://www.perfectreplicawatch.co.uk/replica-breitling-c-142.html BlogIcon replica breitling 2011.08.06 16:36 신고  댓글주소  수정/삭제  댓글쓰기

    넓은 아량으로 배포까지 해주시니 학습에 많은 도움이 될꺼 같습니다.

  7. f0081 2014.05.26 22:04 신고  댓글주소  수정/삭제  댓글쓰기

    얼마전에 검색하다가 우연히 이 사이트를 알게되었는데, 많은 공부가 되고 있습니다. 가려운곳을 정확히 짚어주시니 감사할 따름입니다.