SQL Patch로 CLUSTER_BY_ROWID 힌트를 적용하여 성능을 개선한 사례를 살펴보겠습니다. 글을 작성한 이후 2019년에 이미 유사한 내용의 글을 작성한 것을 알았습니다.
테스트를 위해 아래와 같이 테이블을 생성하겠습니다.
-- 1 DROP TABLE t1 PURGE; CREATE TABLE t1 (c1, c2) AS SELECT NVL (NULLIF (MOD (ROWNUM, 100), 0), 100), LPAD ('X', 100, 'X') FROM XMLTABLE ('1 to 10000'); CREATE INDEX t1_x1 ON t1 (c1);
아래 예제는 나쁜 CF로 인해 1번 오퍼레이션에서 10,000개의 블록 I/O가 발생합니다.
-- 2 SELECT /*+ INDEX(A) */ * FROM t1 a WHERE c1 > 0; SQL_ID 5wabjtzff74bx, child number 0 ------------------------------------- Plan hash value: 2596206637 ------------------------------------------------------------------------------------- | Id | Operation | Name | A-Rows | A-Time | Buffers | ------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 10000 |00:00:00.03 | 10121 | | 1 | TABLE ACCESS BY INDEX ROWID BATCHED| T1 | 10000 |00:00:00.03 | 10121 | -- 10000 = 10121 - 121 |* 2 | INDEX RANGE SCAN | T1_X1 | 10000 |00:00:00.01 | 121 | -------------------------------------------------------------------------------------
SQL Patch를 적용하기 위해 앞선 예제에 CLUSTER_BY_ROWID 힌트를 추가한 후 실행 계획을 검증하고 Outline Data 항목에서 힌트를 확인합니다.
-- 3 SELECT /*+ INDEX(A) CLUSTER_BY_ROWID(A) */ * FROM t1 a WHERE c1 > 0; SQL_ID 7tv953vgfxj7c, child number 0 ------------------------------------- Plan hash value: 2596206637 --------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | A-Rows | A-Time | Buffers | Used-Mem | --------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 10000 |00:00:00.02 | 273 | | | 1 | TABLE ACCESS BY INDEX ROWID BATCHED| T1 | 1 | 10000 |00:00:00.02 | 273 | | | 2 | SORT CLUSTER BY ROWID | | 1 | 10000 |00:00:00.01 | 21 | 361K (0)| |* 3 | INDEX RANGE SCAN | T1_X1 | 1 | 10000 |00:00:00.01 | 21 | | --------------------------------------------------------------------------------------------------------- Outline Data ------------- CLUSTER_BY_ROWID(@"SEL$1" "A"@"SEL$1" SORT BATCH=NO)
아래 예제로 SQL Patch를 생성합니다.
-- 4 SET SERVEROUT ON BEGIN DBMS_SQLDIAG.DROP_SQL_PATCH (name => 'patch_5wabjtzff74bx', ignore => TRUE); DBMS_OUTPUT.PUT_LINE ( DBMS_SQLDIAG.CREATE_SQL_PATCH ( sql_id => '5wabjtzff74bx' , hint_text => 'CLUSTER_BY_ROWID(@"SEL$1" "A"@"SEL$1" SORT BATCH=NO)' , name => 'patch_5wabjtzff74bx')); END; / patch_5wabjtzff74bx PL/SQL 처리가 정상적으로 완료되었습니다.
첫 번째 예제를 다시 수행하면 SQL Patch에 의해 실행계획이 변경된 것을 확인할 수 있습니다. 사용된 SQL Patch는 Note 항목에 표시됩니다.
-- 5 SELECT /*+ INDEX(A) */ * FROM t1 a WHERE c1 > 0; SQL_ID 5wabjtzff74bx, child number 0 ------------------------------------- Plan hash value: 2596206637 --------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | A-Rows | A-Time | Buffers | Used-Mem | --------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 10000 |00:00:00.02 | 273 | | | 1 | TABLE ACCESS BY INDEX ROWID BATCHED| T1 | 1 | 10000 |00:00:00.02 | 273 | | | 2 | SORT CLUSTER BY ROWID | | 1 | 10000 |00:00:00.01 | 21 | 361K (0)| |* 3 | INDEX RANGE SCAN | T1_X1 | 1 | 10000 |00:00:00.01 | 21 | | --------------------------------------------------------------------------------------------------------- Outline Data ------------- CLUSTER_BY_ROWID(@"SEL$1" "A"@"SEL$1" SORT BATCH=NO) Note ----- - SQL patch "patch_5wabjtzff74bx" used for this statement
아래 쿼리로 SQL Patch 관련 정보를 조회할 수 있습니다.
-- 6-1 SELECT name, status, force_matching FROM dba_sql_patches; NAME STATUS FORCE_MATCHING ------------------- ------- -------------- patch_5wabjtzff74bx ENABLED NO 1개의 행이 선택되었습니다. -- 6-2 SELECT a.name , b.comp_data FROM sys.sqlobj$ a , sys.sqlobj$data b WHERE a.obj_type = 3 AND b.signature = a.signature AND b.category = a.category AND b.obj_type = a.obj_type; NAME COMP_DATA ------------------- ---------------------------------------------------------------------------------------------------------- patch_5wabjtzff74bx <outline_data><hint><![CDATA[CLUSTER_BY_ROWID(@"SEL$1" "A"@"SEL$1" SORT BATCH=NO)]]></hint></outline_data> 1개의 행이 선택되었습니다.