메뉴 건너뛰기

bysql.net

8. 애플리케이션 커서 캐싱

2010.06.29 14:11

토시리 조회 수:6608

세션커서캐싱을 이용 하면 SGA이 공유 커서를 빠르게 찾아서 커서를 오픈 할 수 있다.
그렇다하더라도, 공유 커서 힙을 Pin 하고 실행에 필요한 메모리 공간을 PGA에 할당하는 등의 작업은 반복하게 한다.

애플리케이션 커서 캐싱을 이용하여 이 과정마저 생략하는 것이 가능하다.



  1. 패턴① : 바인드 변수를 사용하지 않을 때.
  2. 패턴② : 바인드 변수를 사용, 커서캐싱을 하지 않을 때.
  3. 패턴③ : 커서를 닫지 않고 재사용할 때.
  4. 패턴④ : 묵시적 캐싱 기능을 사용할 때.

각각의 패턴을 Java를 예로 살펴 보자.


패턴① 바인드 변수를 사용하지 않을 때.


    /**
     * 패턴1.
     */
    public static void noBinding(Connection conn) throws Exception {
        PreparedStatement stmt = null;
        ResultSet rs = null;
        String sql = null;
        for (int i = 1 ; i <= 5000; i++ ) {
            sql = "SELECT /* no_biding */ " + i + "," + i + ",'test',a.* FROM scott.emp a WHERE a.ename LIKE 'W%'";
            stmt = conn.prepareStatement(sql);
            rs = stmt.executeQuery();
            rs.close();
            stmt.close();
        }
    }


위 소스를 보면 SQL문이 계속 변하는 것을 알 수 있다.


select /* no_binding */ 1,1,'test',a.* FROM scott.emp a WHERE a.ename LIKE 'W%'

select /* no_binding */ 2,2,'test',a.* FROM scott.emp a WHERE a.ename LIKE 'W%'

select /* no_binding */ 3,3,'test',a.* FROM scott.emp a WHERE a.ename LIKE 'W%'

.

.

.

select /* no_binding */ 5000,5000,'test',a.* FROM scott.emp a WHERE a.ename LIKE 'W%'


즉, 하드파싱이 대입값만큼 발생 (1~5000)



패턴② 바인드 변수를 사용、커서캐싱을 하지않을때.


    /**
     * 패턴2.
     */
    public static void noCaching(Connection conn) throws Exception {
        PreparedStatement stmt = null;
        ResultSet rs = null;
        String sql = null;
        for (int i = 1 ; i <= 5000; i++ ) {
            sql = "SELECT /* no_biding */ ?,?,?,a.* FROM scott.emp a WHERE a.ename LIKE 'W%'";
            stmt = conn.prepareStatement(sql);

            stmt.setInt(1, i);

            stmt.setInt(2, i);

            stmt.setString(3, "test");

            rs = stmt.executeQuery();

            rs.close();
            stmt.close();  // 커서를 닫음.
        }
    }


위 소스를 보면 SQL문은 한개로 해결. 바인드값만이 변함.


select /* no_binding */ ?,?,?,a.* FROM scott.emp a WHERE a.ename LIKE 'W%'


즉, 하드파싱이 한번만 일어난다.

그러나, 루프내에서 커서를 닫아, 항상 커서를 생성하게 된다.

  • Parse Count : 5000
  • Execute Count : 5000
  • Fetch Count : 5000


패턴③ 커서를 닫지 않고 재사용할 경우.


    /**
     * 패턴3.
     */
    public static void cursorHolding(Connection conn) throws Exception {
        PreparedStatement stmt = null;
        String sql = "SELECT /* no_biding */ ?,?,?,a.* FROM scott.emp a WHERE a.ename LIKE 'W%'";

        stmt = conn.prepareStatement(sql);

        ResultSet rs = null;
        for (int i = 1 ; i <= 5000; i++ ) {
            stmt.setInt(1, i);

            stmt.setInt(2, i);

            stmt.setString(3, "test");

            rs = stmt.executeQuery();

            rs.close();
        }
        stmt.close(); // 루프가 끝나면 커서를 닫는다.

    }


위 소스를 보면 SQL문은 한개로 해결. 바인드값만이 변함.


select /* no_binding */ ?,?,?,a.* FROM scott.emp a WHERE a.ename LIKE 'W%'


즉, 하드파싱이 한번만 일어난다.

또한, 커서를 루프가 종료후에 닫고 있다. 이것을 애플리케이션 커서 라 한다.

  • Parse Count : 1
  • Execute Count : 5000
  • Fetch Count : 5000


패턴④ 묵시적 캐싱을 사용할 경우.


    /**
     * 패턴4.
     */
    public static void cursorCaching(Connection conn) throws Exception {

        // 캐시 사이즈를 1로 지정

        ((OracleConnection) conn).setStatementCacheSize(1);


        // 묵시적 캐싱 기능을 활성화

        ((OracleConnection) conn).setImplicitCachingEnabled(true);


        PreparedStatement stmt = null;

        ResultSet rs = null;
        for (int i = 1 ; i <= 5000; i++ ) {
            String sql = "SELECT /* no_biding */ ?,?,?,a.* FROM scott.emp a WHERE a.ename LIKE 'W%'";

            stmt = conn.prepareStatement(sql);

            stmt.setInt(1, i);

            stmt.setInt(2, i);

            stmt.setString(3, "test");

            rs = stmt.executeQuery();

            rs.close();

            stmt.close(); // 루프가 끝나면 커서를 닫는다. 그러나, 내부적으로는 닫지않고 캐시에 보관
        }
     }


위 소스를 보면 패턴②와 같은 형태를 하면서도 사실상 패턴③과 같은 성능을 보인다.

즉, JDBC의 API에서 제공하는 기능을 이용하여 매번 커서를 닫는듯한 코딩을 해도 내부적으로는 세션커서캐싱한다.
  • Parse Count : 1
  • Execute Count : 5000
  • Fetch Count : 5000