h1.08. 애플리케이션 커서 캐싱
h2.Pro*C
Pro*C 에서는 SQLㅇ르 수행하는 부분을 아래처럼 두 개 옵션으로 감싸면 커서를 놓지 않고 반복 재사용한다.
For(;;-) \{
EXEC ORACLE OPTION (HOLD_CURSOR=YES) ;
EXEC ORACLE OPTION (RELEASE_CURSOR=NO) ;
EXEC SQL INSERT .... ; // SQL 수행
EXEC ORACLE OPTION ( RELASE_CURSOR=YES) ;
\}
아래는 애플리케이션에서 커서를 캐싱한 채 같은 SQL을 5,000번 반복 수행했을 때의 SQL 트레이스 결과다.
call count cpu elapsed disk query current rows
\----\--\- --\-\--\- --\---\--\- --\----\--\- --\---\--\- --\---\--\- --\-----\--\- --\------\-
Parse 1 0.00 0.00 0 0 0 0
Execute 5000 0.18 0.14 0 0 0 0
Fetch 5000 0.17 0.23 0 10000 0 5000
\----\--\- --\-\--\- --\---\--\- --\-----\--\- --\-----\--\- --\-----\--\- --\-----\--\- --\-\-
total 10001 0.35 0.37 0 10000 0 5000
Misses in library cache during parse: 1
h2.JAVA
Java 에서 이를 구현하려면 묵시적 캐싱(Implicit Caching)옵션을 사용하거나 Statement 를 닫지 않고 재사용하면 된다.
구체적인 사용법을 익혀 보자. SQL을 아래 4가지 패텬으로 작성하고, 각각에 대한 수행속도를 비교해 볼 것이다.
-* 패턴 1 : 바인드 변수를 사용하지 않을 때
-* 패턴 2 : 바인드 변수를 사용하지만, 커서를 캐싱하지 않을 때
-* 패턴 3 : 커서를 닫지 않고 재사용할 때
-* 패턴 4 : 묵시적 캐싱 기능을
Public class Parsecall
\{
Public static void nobining(connection conn, int count)
Throw Exception(
PreparedStatement stmt;
ResultSet rs ;
For ( int i = 1 ; I <= count ; I \+\+ ) \{
Stmt=conn.preparestatement(
"SELECT /\* no_biding */ " + i + "," + I + ",'test',a.* ' +
"FROM emp a WHERE a.ename LIKE 'W%');
*Rs = stmt.executequery();*
Rs.close();
Stmt.close();
\}
\}
\-----------------------------------------------------------\-
SELECT /\* no_biding */ 5000,5000, 'test',a.*
FROM
Emp a WHERE a.ename LIKE 'W%'
\\
call count cpu elapsed disk query current rows
\----\--\- --\-\--\- --\---\--\- --\----\--\- --\---\--\- --\---\--\- --\-----\--\- --\------\-
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 1 0.00 0.00 0 0 0 0
\----\--\- --\-\--\- --\---\--\- --\-----\--\- --\-----\--\- --\-----\--\- --\-----\--\- --\-\-
total 10001 0.35 0.37 0 2 0 1
Misses in library cache during parse: 1
Public static void nocaching(connection conn, int count)
Throw Exception(
PreparedStatement stmt;
ResultSet rs ;
For ( int i = 1 ; I <= count ; I \+\+ ) \{
Stmt=conn.preparestatement(
"SELECT /\* no_caching */ ?,?,?,,a.* " +
"FROM emp a WHERE a.ename LIKE 'W%');
Stmt.setInt(1,i);
Stmt.setInt(2,i);
Stmt.setInt(3,"test")
*Rs=stmt.executequery();*
\\
Rs.close();
Stmt.close();
\}
\}
\-----------------------------------------------------------\-
SELECT /\* no_caching */ :1,:2,;3, a.*
FROM
Emp a WHERE a.ename LIKE 'W%'
call count cpu elapsed disk query current rows
\----\--\- --\-\--\- --\---\--\- --\----\--\- --\---\--\- --\---\--\- --\-----\--\- --\------\-
Parse 5000 0.06 0.05 0 0 0 0
Execute 5000 0.14 0.13 0 0 0 0
Fetch 5000 0.20 0.24 0 10000 0 5000
\----\--\- --\-\--\- --\---\--\- --\-----\--\- --\-----\--\- --\-----\--\- --\-----\--\- --\-\-
total 15000 0.40 0.42 0 10000 0 5000
Misses in library cache during parse: 1
Public static void CursorHolding(connection conn, int count)
Throw Exception(
// PreparedStatment 를 루프문 바깥에 선언
PreparedStatement stmt = conn.preparedstatment (
"SELECT /\* cursor_holding */ ?,?,?,,a.* " +
"FROM emp a WHERE a.ename LIKE 'W%');
ResultSet rs ;
For ( int i = 1 ; I <= count ; I \+\+ ) \{
Stmt.setInt(1,i);
Stmt.setInt(2,i);
Stmt.setInt(3,"test")
*Rs=stmt.executequery();*
Rs.close();
\}
// 루프를 빠져 나?을 때 커서를 닫는다.
Stmt.close();
\}
\-----------------------------------------------------------\-
SELECT /\* cursor_holding */ :1,:2,;3, a.*
FROM
Emp a WHERE a.ename LIKE 'W%'
\\
call count cpu elapsed disk query current rows
\----\--\- --\-\--\- --\---\--\- --\----\--\- --\---\--\- --\---\--\- --\-----\--\- --\------\-
Parse 5000 0.06 0.00 0 0 0 0
Execute 5000 0.18 0.14 0 0 0 0
Fetch 5000 0.17 0.23 0 10000 0 5000
\----\--\- --\-\--\- --\---\--\- --\-----\--\- --\-----\--\- --\-----\--\- --\-----\--\- --\-\-
total 15000 0.35 0.37 0 10000 0 5000
Misses in library cache during parse: 1
Public static void CursorHolding(connection conn, int count)
Throw Exception(
// 캐시 사이즈를 1로 지정
*((OracleConnection)conn).setStatementCacheSize(1);*
// 묵시적 캐시 기능을 활성화
*((OracleConnection)conn).setImplicitCachingEnabled(true);*
\\
For ( int i = 1 ; I <= count ; I \+\+ ) \{
PreparedStatement stmt = conn.preparedstatment (
"SELECT /\* implicit_caching */ ?,?,?,,a.* " +
"FROM emp a WHERE a.ename LIKE 'W%');
\\
Stmt.setInt(1,i);
Stmt.setInt(2,i);
Stmt.setInt(3,"test")
*Rs=stmt.executequery();*
\\
Rs.close();
// 커서를 닫지만 내부적으로는 닫히지 않은채 캐시에 보관
Stmt.close();
\}
\}
\-----------------------------------------------------------\-
SELECT /\* implicit_cacing */ :1,:2,;3, a.*
FROM
Emp a WHERE a.ename LIKE 'W%'
call count cpu elapsed disk query current rows
\----\--\- --\-\--\- --\---\--\- --\----\--\- --\---\--\- --\---\--\- --\-----\--\- --\------\-
Parse 1 0.01 0.00 0 0 0 0
Execute 5000 0.23 0.14 0 0 0 0
Fetch 5000 0.23 0.22 0 10000 0 5000
\----\--\- --\-\--\- --\---\--\- --\-----\--\- --\-----\--\- --\-----\--\- --\-----\--\- --\-\-
total 10001 0.48 0.36 0 10000 0 5000
Misses in library cache during parse: 1
-( Dynamic SQL 사용시, 커서 캐싱(10g 이후 )
SQL> alter session set session_cached_cursors = 100 ;
Session altered.
SQL> create table t ( x number );
Table created.
SQL> alter system flush shared_pool ;
System altered.
SQL> declare
2 i number ;
3 begin
4 for i in 1..100
5 loop
6 execute immediate 'insert into t values(' \|\| mod(i, 10) \|\| ')';
7 end loop;
8 commit ;
9 end ;
10 /
PL/SQL procedure successfully completed.
SQL> select count(distinct sql_text) sql_cnt
2 , sum(parse_calls) parse_calls
3 , sum(executions) executions
4 from v$sql
5 where sql_text like 'insert into t values%';
SQL_CNT PARSE_CALLS EXECUTIONS
\-------\--\- --\------\--\- --\-------\-
10 100 100
SQL> select distinct sql_text from v$sql where sql_text like 'insert into t values%';
SQL_TEXT
\-------------------------------------------------------------------------------\-
insert into t values(3)
insert into t values(6)
insert into t values(5)
insert into t values(0)
insert into t values(1)
insert into t values(8)
insert into t values(9)
insert into t values(2)
insert into t values(4)
insert into t values(7)
10 rows selected.
SQL> alter system flush shared_pool ;
System altered.
SQL> declare
2 i number ;
3 begin
4 for i in 1..100
5 loop
6 execute immediate 'insert into t values(' \|\| ceil(i/10)\|\| ')';
7 end loop ;
8 commit ;
9 end ;
10 /
PL/SQL procedure successfully completed.
SQL> select count(distinct sql_text) sql_cnt
2 , sum(parse_calls) parse_calls
3 , sum(executions) executions
4 from v$sql
5 where sql_text like 'insert into t values%';
SQL_CNT PARSE_CALLS EXECUTIONS
\-------\--\- --\------\--\- --\-------\-
10 10 100
SQL> select distinct sql_text from v$sql where sql_text like 'insert into t values%';
SQL_TEXT
\-------------------------------------------------------------------------------\-
insert into t values(3)
insert into t values(6)
insert into t values(5)
insert into t values(10)
insert into t values(1)
insert into t values(8)
insert into t values(9)
insert into t values(2)
insert into t values(4)
insert into t values(7)
10 rows selected.
SQL> alter session set session_cached_cursors = 0 ;
Session altered.
SQL> alter system flush shared_pool ;
System altered.
SQL> declare
2 i number ;
3 begin
4 for i in 1..100
5 loop
6 execute immediate 'insert into t values(' \|\| ceil(i/10) \|\|')';
7 end loop ;
8 commit ;
9 end ;
10 /
PL/SQL procedure successfully completed.
SQL> select count(distinct sql_text) sql_cnt
2 , sum(parse_calls) parse_calls
3 , sum(executions) executions
4 from v$sql
5 where sql_text like 'insert into t values%';
SQL_CNT PARSE_CALLS EXECUTIONS
\-------\--\- --\------\--\- --\-------\-
10 100 100
SQL> select distinct sql_text from v$sql where sql_text like 'insert into t values%';
SQL_TEXT
\-------------------------------------------------------------------------------\-
insert into t values(3)
insert into t values(6)
insert into t values(5)
insert into t values(10)
insert into t values(1)
insert into t values(8)
insert into t values(9)
insert into t values(2)
insert into t values(4)
insert into t values(7)
10 rows selected.