여부 속성으로 인한 성능 저하 사례를 살펴보자.
테스트를 위해 아래의 테이블을 생성하자. t1을 전문 전송 테이블로 가정하자.
-- 1
DROP TABLE t1 PURGE;
CREATE TABLE t1 AS
SELECT ROWNUM AS id
, LPAD ('X', 4000, 'X') AS doc
, CASE WHEN ROWNUM > 9990 THEN 'N' ELSE 'Y' END AS if_yn
FROM XMLTABLE ('1 to 10000');
ALTER TABLE t1 ADD CONSTRAINT t1_pk PRIMARY KEY (id);
EXEC DBMS_STATS.GATHER_TABLE_STATS ('SCOTT', 'T1', method_opt => 'FOR ALL COLUMNS SIZE SKEWONLY');
전문 전송은 보통 아래의 철차로 수행된다. if_yn이 N인 미전송 전문을 전송하고, 전송이 완료되면 if_yn 값을 Y로 갱신하는 방식이다.
-- 2
BEGIN
FOR f1 IN (SELECT * FROM t1 WHERE if_yn = 'N' ORDER BY id)
LOOP
-- 전송
UPDATE t1 SET if_yn = 'Y' WHERE c1 = f1.c1;
END LOOP;
END;
아래는 CURSOR FOR LOOP 문의 쿼리의 실행 계획이다. 인덱스가 없으므로 t1 테이블을 전체 스캔한다. 전문 데이터를 삭제하지 않는다면 테이블 크기가 선형적으로 증가하므로 블록 I/O도 선형적으로 증가한다.1
-- 3
SELECT * FROM t1 WHERE if_yn = 'N' ORDER BY id;
------------------------------------------------------
| Id | Operation | Name | A-Rows | Buffers |
------------------------------------------------------
| 0 | SELECT STATEMENT | | 10 | 10012 |
| 1 | SORT ORDER BY | | 10 | 10012 |
|* 2 | TABLE ACCESS FULL| T1 | 10 | 10012 |
------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("IF_YN"='N')
-- 4
SELECT num_distinct, histogram FROM user_tab_columns WHERE table_name = 'T1' AND column_name = 'IF_YN';
NUM_DISTINCT HISTOGRAM
------------ ---------
2 FREQUENCY
1개의 행이 선택되었습니다.
실제 데이터를 조회해보면 if_yn이 N인 행은 10건에 불과한 것을 확인할 수 있다. 전문 전송의 특성에 따라 미전송 건은 항상 적을 수 밖에 없다.
-- 5 SELECT if_yn, COUNT (*) AS cnt FROM t1 GROUP BY if_yn; IF_YN CNT ----- ---- Y 9990 N 10 2 행이 선택되었습니다.
-- 6 CREATE INDEX t1_x1 ON t1 (if_yn); 인덱스가 생성되었습니다.
쿼리를 다시 수행하면 쿼리가 인덱스 스캔으로 수행된다. 블록 I/O가 10,012에서 12로 감소했다. 전문 전송이 1분 간격으로 수행된다면 일별 14,400,000개의 블록 I/O를 감소시킬 수 있다.
-- 7
SELECT * FROM t1 WHERE if_yn = 'N' ORDER BY id;
-----------------------------------------------------------------
| Id | Operation | Name | A-Rows | Buffers |
-----------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10 | 12 |
| 1 | SORT ORDER BY | | 10 | 12 |
| 2 | TABLE ACCESS BY INDEX ROWID| T1 | 10 | 12 |
|* 3 | INDEX RANGE SCAN | T1_X1 | 10 | 2 |
-----------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("IF_YN"='N')
추가로 if_yn 칼럼에 히스토그램이 생성하면 인덱스 생성으로 인한 부작용을 방지할 수 있다. 아래 쿼리는 if_yn를 Y로 조회했다. 히스토그램으로 인해 인덱스를 스캔하지 않고 테이블을 전체 스캔한 것을 확인할 수 있다.
-- 8
SELECT * FROM t1 WHERE if_yn = 'Y';
-----------------------------------------------------
| Id | Operation | Name | A-Rows | Buffers |
-----------------------------------------------------
| 0 | SELECT STATEMENT | | 9990 | 10011 |
|* 1 | TABLE ACCESS FULL| T1 | 9990 | 10011 |
-----------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("IF_YN"='Y')
- 이런 류의 성능 저하를 시한폭탄에 비유하곤 한다. [본문으로]
'Oracle > Tuning' 카테고리의 다른 글
| 쿼리 변환을 통한 Top-N 쿼리 성능 개선 사례 (0) | 2020.09.16 |
|---|---|
| 여부 속성으로 인한 성능 저하 사례 #2 (0) | 2020.07.09 |
| 뷰 병합이 동작하지 않는 사례 (0) | 2020.04.07 |
| 조인에 따른 사용자 함수의 동작 (0) | 2020.03.21 |
| 월 기준 이력 조회 (0) | 2020.03.16 |

