Skip to content
튜닝 연습인덱스 스캔 효율과 테이블 액세스
ACC-045

비트맵 인덱스가 없는 주문 테이블에서 매장 분기 주문 추이가 BITMAP AND로 1,100블록 넘게 읽는다

중급SELECT버거120 / 700

상황 ​

버거원 운영 주문 테이블 ORD_RANDOM에는 날짜 인덱스와 매장 FK 인덱스가 하나씩 있다. 점주 앱의 「오늘 주문」(S1)은 하루 약 1만 번, 본사 슈퍼바이저의 「매장 분기 추이」(S2)는 하루 약 300번 돈다. 둘 다 매장 하나와 날짜를 조건으로 준다. 점검 리포트에 두 SQL의 실행계획에 BITMAP 연산이 나온다는 지적이 올라왔다. DBA는 "비트맵 인덱스는 만든 적이 없다"고 한다. S1은 31블록이지만 S2는 1,132블록을 읽는다. 본사 DBA 규정상 이 테이블의 인덱스 수는 늘릴 수 없다(ACC-040).

테이블과 인덱스 ​

  • ORD_RANDOM(운영본): 1,000,000행 · 22,414블록 · 무작위 순서로 적재 · ORD_DT VARCHAR2(8) 하루 약 1,370건 · SHOP_NO 200곳에 고르게(매장당 약 5,000건) · 2013년 10 ~ 12월 약 125,000건
  • 매장 SH0042: 2013-12-24에 7건 · 2013년 10 ~ 12월에 641건(92일 모두 주문이 있다)
  • 인덱스 ORD_RANDOM_PK (ORD_NO) · ORD_RANDOM_X01 (ORD_DT, ORD_HMS): BLEVEL 2 · 리프 3,774 · CLUSTERING_FACTOR 999,958(교재 04화 실측)
  • 인덱스 ORD_RANDOM_X04 (SHOP_NO): 매장 FK(FK_ORD_SHOP)의 자식 쪽 인덱스 · BLEVEL 2 · 리프 약 2,700블록 · CLUSTERING_FACTOR 약 999,900 · 통계 최신
  • 비트맵 인덱스는 하나도 없다 · POS가 하루 약 1,370건 INSERT한다 · ORD_DT가 문자라서 옵티마이저는 기간의 행 수를 열 배쯤 낮게 본다

원본 SQL ​

sql
-- S1 점주 앱 「오늘 주문」 · 하루 약 1만 번
SELECT ORD_NO, ORD_HMS
  FROM ORD_RANDOM
 WHERE SHOP_NO = :v_shop
   AND ORD_DT  = :v_dt
 ORDER BY ORD_HMS;

-- S2 슈퍼바이저 「매장 분기 추이」 · 하루 약 300번
SELECT ORD_DT, COUNT(*) AS ord_cnt
  FROM ORD_RANDOM
 WHERE SHOP_NO = :v_shop
   AND ORD_DT BETWEEN :v_from AND :v_to
 GROUP BY ORD_DT
 ORDER BY ORD_DT;

실행계획 학습용 예시 · X01의 리프와 CF는 교재 04화 실측 · B-tree를 비트맵으로 바꿔 합치는 계획은 운영(EE)의 모양 ​

xplan
[S1 · SH0042 · 2013-12-24]
-----------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
-----------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |                |      1 |        |      7 |00:00:00.01 |      31 |          |
|   1 |  SORT ORDER BY                       |                |      1 |      7 |      7 |00:00:00.01 |      31 | 2048  (0)|
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| ORD_RANDOM     |      1 |      7 |      7 |00:00:00.01 |      31 |          |
|   3 |    BITMAP CONVERSION TO ROWIDS       |                |      1 |        |      7 |00:00:00.01 |      24 |          |
|   4 |     BITMAP AND                       |                |      1 |        |      1 |00:00:00.01 |      24 |          |
|   5 |      BITMAP CONVERSION FROM ROWIDS   |                |      1 |        |      1 |00:00:00.01 |       8 |          |
|   6 |       SORT ORDER BY                  |                |      1 |        |   1368 |00:00:00.01 |       8 |   43K (0)|
|*  7 |        INDEX RANGE SCAN              | ORD_RANDOM_X01 |      1 |   1370 |   1368 |00:00:00.01 |       8 |          |
|   8 |      BITMAP CONVERSION FROM ROWIDS   |                |      1 |        |      1 |00:00:00.01 |      16 |          |
|*  9 |       INDEX RANGE SCAN               | ORD_RANDOM_X04 |      1 |   5000 |   5000 |00:00:00.01 |      16 |          |
-----------------------------------------------------------------------------------------------------------------------------
   7 - access("ORD_DT"=:V_DT)
   9 - access("SHOP_NO"=:V_SHOP)

[S2 · SH0042 · 2013-10-01 ~ 2013-12-31]
--------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
--------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                         |                |      1 |        |     92 |00:00:00.14 |    1132 |          |
|   1 |  SORT GROUP BY                           |                |      1 |     63 |     92 |00:00:00.14 |    1132 | 6144  (0)|
|*  2 |   FILTER                                 |                |      1 |        |    641 |00:00:00.14 |    1132 |          |
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED   | ORD_RANDOM     |      1 |     63 |    641 |00:00:00.13 |    1132 |          |
|   4 |     BITMAP CONVERSION TO ROWIDS          |                |      1 |        |    641 |00:00:00.11 |     491 |          |
|   5 |      BITMAP AND                          |                |      1 |        |      1 |00:00:00.11 |     491 |          |
|   6 |       BITMAP CONVERSION FROM ROWIDS      |                |      1 |        |      1 |00:00:00.01 |      16 |          |
|*  7 |        INDEX RANGE SCAN                  | ORD_RANDOM_X04 |      1 |   5000 |   5000 |00:00:00.01 |      16 |          |
|   8 |       BITMAP CONVERSION FROM ROWIDS      |                |      1 |        |      6 |00:00:00.10 |     475 |          |
|   9 |        SORT ORDER BY                     |                |      1 |        |   125K |00:00:00.08 |     475 | 3036K (0)|
|* 10 |         INDEX RANGE SCAN                 | ORD_RANDOM_X01 |      1 |  12500 |   125K |00:00:00.03 |     475 |          |
--------------------------------------------------------------------------------------------------------------------------------
   2 - filter(:V_FROM<=:V_TO)
   7 - access("SHOP_NO"=:V_SHOP)
  10 - access("ORD_DT">=:V_FROM AND "ORD_DT"<=:V_TO)

문제 ​

  1. 비트맵 인덱스가 없는데 두 실행계획에 BITMAP 연산이 나오는 이유를 줄 번호로 설명하시오. 두 인덱스에서 각각 무엇을 얼마나 읽는지도 설명하시오.
  2. S1은 31블록인데 S2는 1,132블록인 이유를 설명하고, 매장과 날짜를 함께 담은 결합 인덱스 하나로 두 SQL을 읽을 때와 비교하시오.
  3. 인덱스 수를 늘리지 않고 두 SQL을 고치는 DDL을 작성하시오. 두 인덱스를 비트맵으로 바꿔 합치는 계획이 받아들일 만한 경우와 그렇지 않은 경우도 설명하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자