CLOB 타입을 사용한 쿼리는 블록 I/O 분석에 주의가 필요하다. 관련 내용을 살펴보자.
테스트를 위해 아래와 같이 테이블을 생성하자. t1 테이블은 BASICFILE, t2 테이블은 SECUREFILE로 생성했다. t2 테이블만 16블록이고, 나머지 테이블을 8블록이다.
-- 1-1 DROP TABLE t1 PURGE; DROP TABLE t2 PURGE; DROP TABLE t3 PURGE; DROP TABLE t4 PURGE; CREATE TABLE t1 LOB (c2) STORE AS BASICFILE (DISABLE STORAGE IN ROW) AS SELECT ROWNUM AS c1, TO_CLOB ('X') AS c2 FROM XMLTABLE ('1 to 1000'); CREATE TABLE t2 LOB (c2) STORE AS SECUREFILE (DISABLE STORAGE IN ROW) AS SELECT * FROM t1; CREATE TABLE t3 AS SELECT ROWNUM AS c1 FROM XMLTABLE ('1 to 200'); CREATE TABLE t4 AS SELECT ROWNUM AS c1 FROM XMLTABLE ('1 to 100'); -- 1-2 SELECT segment_name, blocks FROM user_segments WHERE segment_name IN ('T1', 'T2', 'T3', 'T4'); SEGMENT_NAME BLOCKS ------------ ------ T1 8 T2 16 T3 8 T4 8 4 행이 선택되었습니다.
t1, t2 테이블의 전체 열을 조회하면 행의 건수인 1000개의 블록 I/O가 추가로 발생한다. CLOB 값을 조회하기 위한 랜던 I/O다.
-- 2-1 SET ARRAYSIZE 1000 SELECT * FROM t1; ----------------------------------------------------- | Id | Operation | Name | A-Rows | Buffers | ----------------------------------------------------- | 0 | SELECT STATEMENT | | 1000 | 1008 | | 1 | TABLE ACCESS FULL| T1 | 1000 | 1008 | -- ! ----------------------------------------------------- -- 2-2 SELECT * FROM t2; ----------------------------------------------------- | Id | Operation | Name | A-Rows | Buffers | ----------------------------------------------------- | 0 | SELECT STATEMENT | | 1000 | 1012 | | 1 | TABLE ACCESS FULL| T1 | 1000 | 1012 | -- ! -----------------------------------------------------
c1 열만 조회하면 두 테이블 모두 추가적인 블록 I/O가 발생하지 않는다.
-- 3-1 SELECT c1 FROM t1; ----------------------------------------------------- | Id | Operation | Name | A-Rows | Buffers | ----------------------------------------------------- | 0 | SELECT STATEMENT | | 1000 | 14 | | 1 | TABLE ACCESS FULL| T1 | 1000 | 14 | ----------------------------------------------------- -- 3-2 SELECT c1 FROM t2; ----------------------------------------------------- | Id | Operation | Name | A-Rows | Buffers | ----------------------------------------------------- | 0 | SELECT STATEMENT | | 1000 | 20 | | 1 | TABLE ACCESS FULL| T1 | 1000 | 20 | -----------------------------------------------------
c2 열에 LENGTH 함수를 사용해도 추가적인 블록 I/O가 발생하지 않는 것을 확인할 수 있다. LOB 로케이터에 길이 값이 저장되어 있기 때문인 것으로 보인다.
-- 4-1 SELECT LENGTH (c2) AS c2 FROM t1; ----------------------------------------------------- | Id | Operation | Name | A-Rows | Buffers | ----------------------------------------------------- | 0 | SELECT STATEMENT | | 1000 | 14 | | 1 | TABLE ACCESS FULL| T1 | 1000 | 14 | ----------------------------------------------------- -- 4-2 SELECT LENGTH (c2) AS c2 FROM t2; ----------------------------------------------------- | Id | Operation | Name | A-Rows | Buffers | ----------------------------------------------------- | 0 | SELECT STATEMENT | | 1000 | 20 | | 1 | TABLE ACCESS FULL| T1 | 1000 | 20 | -----------------------------------------------------
아래 Top-N 쿼리는 추가적인 블록 I/O가 표시되지 않는다.
-- 5-1 SELECT * FROM (SELECT * FROM t1 ORDER BY c1) WHERE ROWNUM <= 100; ----------------------------------------------------------- | Id | Operation | Name | A-Rows | Buffers | ----------------------------------------------------------- | 0 | SELECT STATEMENT | | 100 | 9 | |* 1 | COUNT STOPKEY | | 100 | 9 | | 2 | VIEW | | 100 | 9 | |* 3 | SORT ORDER BY STOPKEY| | 100 | 9 | | 4 | TABLE ACCESS FULL | T1 | 1000 | 9 | ----------------------------------------------------------- -- 5-2 SELECT * FROM (SELECT * FROM t2 ORDER BY c1) WHERE ROWNUM <= 100; ----------------------------------------------------------- | Id | Operation | Name | A-Rows | Buffers | ----------------------------------------------------------- | 0 | SELECT STATEMENT | | 100 | 15 | |* 1 | COUNT STOPKEY | | 100 | 15 | | 2 | VIEW | | 100 | 15 | |* 3 | SORT ORDER BY STOPKEY| | 100 | 15 | | 4 | TABLE ACCESS FULL | T2 | 1000 | 15 | -----------------------------------------------------------
아래 쿼리처럼 Top-N 처리 후 CLOB 열을 WHERE 절에 사용하면 추가적인 블록 I/O가 표시되는 것을 확인할 수 있다. t1 테이블은 500개, t2 테이블은 200개의 추가적인 블록 I/O가 발생한 것으로 표시되는데 LOBREAD 항목이 포함된 수치로 일반적인 논리 I/O와 다른 동작으로 판단된다. 1 2
-- 6-1 SELECT * FROM (SELECT * FROM t1 WHERE ROWNUM > 0 ORDER BY c1) WHERE ROWNUM <= 100 AND c2 LIKE '%'; ------------------------------------------------------------------- | Id | Operation | Name | A-Rows | Buffers | Reads | ------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 100 | 509 | 100 | |* 1 | COUNT STOPKEY | | 100 | 509 | 100 | |* 2 | VIEW | | 100 | 509 | 100 | -- ! | 3 | SORT ORDER BY | | 100 | 9 | 0 | | 4 | COUNT | | 1000 | 9 | 0 | |* 5 | FILTER | | 1000 | 9 | 0 | | 6 | TABLE ACCESS FULL| T1 | 1000 | 9 | 0 | ------------------------------------------------------------------- -- 6-2 SELECT * FROM (SELECT * FROM t2 WHERE ROWNUM > 0 ORDER BY c1) WHERE ROWNUM <= 100 AND c2 LIKE '%'; ------------------------------------------------------------------- | Id | Operation | Name | A-Rows | Buffers | Reads | ------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 100 | 215 | 100 | |* 1 | COUNT STOPKEY | | 100 | 215 | 100 | |* 2 | VIEW | | 100 | 215 | 100 | -- ! | 3 | SORT ORDER BY | | 100 | 15 | 0 | | 4 | COUNT | | 1000 | 15 | 0 | |* 5 | FILTER | | 1000 | 15 | 0 | | 6 | TABLE ACCESS FULL| T2 | 1000 | 15 | 0 | -------------------------------------------------------------------
아래 쿼리는 테이블 3개를 조인했다. CLOB이 없는 t4 테이블에 추가적인 I/O가 표시된다. 값은 정확히 100개의 블록 I/O다. 조인이 완료된 후에 LOB 로케이터를 통해 CLOB 값을 조회하는 것이다.
-- 7-1 SELECT /*+ ORDERED NO_SWAP_JOIN_INPUTS(C) */ * FROM t1 a, t3 b, t4 c WHERE b.c1 = a.c1 AND c.c1 = b.c1; ------------------------------------------------------- | Id | Operation | Name | A-Rows | Buffers | ------------------------------------------------------- | 0 | SELECT STATEMENT | | 100 | 130 | |* 1 | HASH JOIN | | 100 | 130 | |* 2 | HASH JOIN | | 200 | 22 | | 3 | TABLE ACCESS FULL| T1 | 1000 | 13 | | 4 | TABLE ACCESS FULL| T3 | 200 | 9 | | 5 | TABLE ACCESS FULL | T4 | 100 | 108 | -- ! ------------------------------------------------------- -- 7-2 SELECT /*+ ORDERED NO_SWAP_JOIN_INPUTS(C) */ * FROM t2 a, t3 b, t4 c WHERE b.c1 = a.c1 AND c.c1 = b.c1; ------------------------------------------------------- | Id | Operation | Name | A-Rows | Buffers | ------------------------------------------------------- | 0 | SELECT STATEMENT | | 100 | 136 | |* 1 | HASH JOIN | | 100 | 136 | |* 2 | HASH JOIN | | 200 | 28 | | 3 | TABLE ACCESS FULL| T2 | 1000 | 19 | | 4 | TABLE ACCESS FULL| T3 | 200 | 9 | | 5 | TABLE ACCESS FULL | T4 | 100 | 108 | -- ! -------------------------------------------------------
아래 쿼리는 집계 함수와 CASE 표현식을 사용했다. SORT AGGREGATE 오퍼레이션에 추가적인 블록 I/O가 표시되는 것을 확인할 수 있다. 6번 쿼리와 동일한 값이 표시된다.
-- 8-1 SELECT /*+ ORDERED NO_SWAP_JOIN_INPUTS(C) */ COUNT (CASE WHEN a.c2 LIKE '%' THEN 1 END) AS c1 FROM t1 a, t3 b, t4 c WHERE b.c1 = a.c1 AND c.c1 = b.c1; ----------------------------------------------------------------- | Id | Operation | Name | A-Rows | Buffers | Reads | ----------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 531 | 100 | | 1 | SORT AGGREGATE | | 1 | 531 | 100 | -- ! |* 2 | HASH JOIN | | 100 | 31 | 0 | |* 3 | HASH JOIN | | 200 | 22 | 0 | | 4 | TABLE ACCESS FULL| T1 | 1000 | 13 | 0 | | 5 | TABLE ACCESS FULL| T3 | 200 | 9 | 0 | | 6 | TABLE ACCESS FULL | T4 | 100 | 9 | 0 | ----------------------------------------------------------------- -- 8-2 SELECT /*+ ORDERED NO_SWAP_JOIN_INPUTS(C) */ COUNT (CASE WHEN a.c2 LIKE '%' THEN 1 END) AS c1 FROM t2 a, t3 b, t4 c WHERE b.c1 = a.c1 AND c.c1 = b.c1; ----------------------------------------------------------------- | Id | Operation | Name | A-Rows | Buffers | Reads | ----------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 237 | 100 | | 1 | SORT AGGREGATE | | 1 | 237 | 100 | -- ! |* 2 | HASH JOIN | | 100 | 37 | 0 | |* 3 | HASH JOIN | | 200 | 28 | 0 | | 4 | TABLE ACCESS FULL| T2 | 1000 | 19 | 0 | | 5 | TABLE ACCESS FULL| T3 | 200 | 9 | 0 | | 6 | TABLE ACCESS FULL | T4 | 100 | 9 | 0 | -----------------------------------------------------------------
지금까지 살펴본데로 CLOB의 블록 I/O 표시는 부정확한 면이 있다. 해석에 주의하도록 하자.
ora12cr2_ora_6132_basicfile.trc
ora12cr2_ora_6132_basicfile.prf
ora12cr2_ora_3584_securefile.trc
ora12cr2_ora_3584_securefile.prf
'Oracle > Tuning' 카테고리의 다른 글
TIMESTAMP 인덱스 - Right-Growing 경합 #2 (0) | 2019.03.06 |
---|---|
TIMESTAMP 인덱스 - Right-Growing 경합 #1 (0) | 2019.02.28 |
사용자 정의 함수의 수행 횟수 (0) | 2019.01.30 |
SQL Patch (0) | 2019.01.09 |
SQL Profile (0) | 2019.01.09 |