티스토리 뷰

기술이야기

Connection Cache

novathinker 2009.05.26 19:07

 

1) connection과 session의 의미

- Connection과 Session은 엄밀히 말해 다른 것임

- Connection은 물리적인 Network Path를 의미

- Session은 유저가 인증을 통과하여 접속할 때부터 접속을 끊을 때까지의 Oracle과의 상호작용을 의미

- Session은 물리적인 Connection을 통해 Oracle에 접속됨

- Session과 Connection이 1:1일 경우도 있지만 0:1, N:1의 경우도 가능

 

2) Connection Pool

- Connection Pool은 다수의 client Session들이 공유할 수 있는 물리적인 Connection을 모아놓은 것을 의미

- Connection Pool은 보통 Application단위로 구성되어 Application의 시작과 동시에 생성되고 Application이 종료할 때 제거됨

- Connection Pool은 미리 연결된 소수의 connection을 다수의 클라이언트들이 사용할 수 있게 됨

- WAS를 사용하는 경우 보통 WAS에서 Connection Pool을 관리

- Application이 connection이 필요하게 되면 WAS는 Connection Pool에서 사용 가능한 Connection을 선택하여 "in use"로 표시한 후 이를 Application에게 전달하고 Application에서 connection을 Close하면 WAS는 이 connection의 상태를 Clear한 후 Connection Pool에게 돌려줌

- 이 경우 application이 idle한 경우 connection의 resource를 낭비하는 일이 없게 되어 동시에 접속되는 Oracle의 Connection 수 및 Connection시 사용되는 Resource를 줄일 수 있게 됨

- JDBC 3.0부터는 JDBC에서 Connection Pool을 사용할 수 있게 되었음

 

3) Connection Cache

- Connection Cache는 Connection Pool과 물리적인 Connection을 Cache한다는 것에서는 개념적으로 유사함.

- JDBC driver에서 제공되는 Connection Pooling Framework을 사용하며 Oracle 10g부터 data source 레벨에서 implicit하게 사용이 됨

- Oracle 10g implicit Connection Caching

à 같은 Cache에서 다른 유저와 password로 connection의 사용이 가능

à cache property로 cache를 제어할 수 있음. 사용할 수 있는 property는 다음과 같음

Properties

Default

설명

InitialLimit

0

Cache를 생성하거나 초기화 할 때 생성할 connection 개수

MaxLimit

No Limit

Cache를 할 Connection의 최대 개수

MaxStatementLimit

0

Connection에 의해 Cache될 Statement의 최대 개수

MinLimit

0

Cache가 항상 보장하는 Connection의 개수

LowerThresholeLimit

20% of MaxLimit

Cache의 최소 임계값으로 connection의 개수가 이 수치에 다다르면 Connection을 release하는 callback method가 수행됨

InactivityTimeout

0(no timeout)

물리적인 Connection이 Idle로 Cache에 남아 있을 수 있는 시간(초)

TimeToLiveTimeout

0(no timeout)

세션이 열려 있는 상태로 있을 수 있는 최대 시간(초)으로 이 시간이 지나면 cache로 반납됨

AbandonedConnectionTimout

0(no timeout)

SQL수행 없이 세션이 열려 있는 상태로 있을 수 있는 최대 시간(초)

ConnectionWaitTimeout

0(no timeout)

Connection의 수가 MaxLimit에 도달해 있고 모든 물리적인 Connection이 사용중일 때 새로운 요청이 들어와서 대기하는 시간. 대기 시간 이후에도 사용할 수 있는 Connection이 없으면 Cache는 null을 반환

PropertyCheckInterval

900 seconds

Cache Manager가 Property를 체크하는 시간 간격

 

à 여러 가지 callback Method 를 제공하며 OracleConnectionCacheManager를 이용하여 Connection Cache를 제어

 

- 이 글에서는 Connection 자체가 주는 부하와 Connection Cache를 사용하였을 때를 비교할 것임

 

4) 테스트 환경

- 테스트 환경은 2 Tier로 구성됨

Client : CPU – Intel() T2400 CPU 1.83Ghz

OS – MS Windows XP Home Edition version 2002 Service pack 2

Memory – 2 GB

JVM – java version "1.6.0_01"

Java(TM) SE Runtime Environment (build 1.6.0_01-b06)

JDBC Driver – Oracle JDBC (ojdbc14.jar)

Oracle – Version 10.2.0.1

 

Server : CPU - Intel(R) Pentium(R) D CPU 3.00GHz

OS - Asianux release 2.0 (Trinity SP2) Kernel 2.6.9-42.7AXsmp on an x86_64

Memory – 2 GB

Oracle - Version : 10.2.0.3

- Total System Global Area 734003200 bytes

- Fixed Size 2075656 bytes

- Variable Size 251659256 bytes

- Database Buffers 473956352 bytes

- Redo Buffers 6311936 bytes

 

5) 테스트 : Connection의 부하

- Connection을 100번 맺고 끊는 작업을 수행하여 시간과 메모리 사용량을 체크

- 다음과 같은 App를 가지고 테스트

package exem.oracle.conncache;

 

import oracle.jdbc.pool.OracleDataSource;

import java.sql.*;

 

public class ConnTest {

 

    public static void main(String[] args) throws Exception{

        // TODO Auto-generated method stub        

OracleDataSource ods = new OracleDataSource();

ods.setURL("jdbc:oracle:thin:@210.122.227.247:1522:WASDB");

ods.setUser("PERSISTENCE");

ods.setPassword("PERSISTENCE");

PreparedStatement pstmt = null;

ResultSet rs = null;

int connnum = 100;    

long befmem = Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory();

Connection conns[] = new Connection[connnum];

long chktime = System.currentTimeMillis();

for (int i = 0 ; i < connnum; i++){

    try {conns[i] = ods.getConnection();     

    pstmt = conns[i].prepareStatement("select p.spid, s.sid from v$process p, v$session s where p.addr = s.paddr and s.sid = userenv('sid' )");

    rs = pstmt.executeQuery();

    rs.next();

    System.out.println("Current Connection number : "+i+"\t Server Process ID : " + rs.getString(1)+"\t SessionID : "+ rs.getString(2) );

    conns[i].close();    

    }

    catch(Exception e) {    System.out.println("Error" + i ); throw e;     }

}

System.out.println("Elapsed Time(ms) : "+(System.currentTimeMillis()-chktime));

System.out.println("Used Memory(Bytes) : " + (Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory()-befmem) );

Connection conn = ods.getConnection();

pstmt = conn.prepareStatement("select p.spid, s.sid from v$process p, v$session s where p.addr(+) = s.paddr and s.username = 'PERSISTENCE' and s.sid <> userenv('sid')" );

rs = pstmt.executeQuery();

while ( rs.next() ) {

    System.out.println("Server Process ID : " + rs.getString(1)+"\t SessionID : "+ rs.getString(2)+ "\t OracleProcessID : " + rs.getString(3) );

}

}

 

}

- 테스트 결과는 다음과 같음

Current Connection number : 0     Server Process ID : 1202     SessionID : 122

Current Connection number : 1     Server Process ID : 1204     SessionID : 122

Current Connection number : 2     Server Process ID : 1206     SessionID : 122

Current Connection number : 3     Server Process ID : 1208     SessionID : 122

Current Connection number : 4     Server Process ID : 1210     SessionID : 122

...

Current Connection number : 95     Server Process ID : 1392     SessionID : 122

Current Connection number : 96     Server Process ID : 1394     SessionID : 122

Current Connection number : 97     Server Process ID : 1396     SessionID : 122

Current Connection number : 98     Server Process ID : 1398     SessionID : 122

Current Connection number : 99     Server Process ID : 1400     SessionID : 122

Elapsed Time(ms) : 6203

Used Memory(Bytes) : 2707440

- 소요시간은 6.2초 , 사용 메모리는 2.58MB정도임

- 각 Connection을 한번 맺을 때 마다 26KB의 메모리와 0.06초의 시간이 소요됨을 알 수 있음

- 또한 Connection을 맺을 때마다 각각의 Oracle Sever Process를 하나씩 생성하는 것을 알 수 있음

 

6) 테스트 : Connection Cache를 사용할 경우

- 앞의 시나리오에 Connection Cache를 사용하도록 하여 CacheTest라는 Class를 생성

package exem.oracle.conncache;

 

import oracle.jdbc.pool.OracleDataSource;

import oracle.jdbc.pool.OracleConnectionCacheManager;

import java.sql.*;

import java.util.Properties;

 

public class CacheTest {

 

    public static void main(String[] args) throws Exception{

OracleDataSource ods = new OracleDataSource();

ods.setURL("jdbc:oracle:thin:@210.122.227.247:1522:WASDB");

ods.setUser("PERSISTENCE");

ods.setPassword("PERSISTENCE");

ods.setConnectionCachingEnabled(true);

PreparedStatement pstmt = null;

ResultSet rs = null;

Properties prop = new Properties();

prop.setProperty("InitialLimit", "2");

prop.setProperty("MaxLimit", "5");

ods.setConnectionCacheProperties(prop);

ods.setConnectionCacheName("EXEM");

int connnum = 100;

long befmem = Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory();

Connection conns[] = new Connection[connnum];

long chktime = System.currentTimeMillis();    

for (int i = 0 ; i < connnum; i++){

    try {conns[i] = ods.getConnection();     

    pstmt = conns[i].prepareStatement("select p.spid, s.sid from v$process p, v$session s where p.addr = s.paddr and s.sid = userenv('sid' )");

    rs = pstmt.executeQuery();

    rs.next();

    System.out.println("Current Connection number : "+i+"\t Server Process ID : " + rs.getString(1)+"\t SessionID : "+ rs.getString(2) );

    conns[i].close();    

    }

    catch(Exception e) {    System.out.println("Error" + i ); throw e;     }

}

 

System.out.println("Elapsed Time(ms) : "+(System.currentTimeMillis()-chktime));

System.out.println("Used Memory(Bytes) : " + (Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory()-befmem)+"\n" );

OracleConnectionCacheManager ocman = OracleConnectionCacheManager.getConnectionCacheManagerInstance();

System.out.println("Total Active Conns : "+ ocman.getNumberOfActiveConnections("EXEM") +"\t Total Available Conns : "+ocman.getNumberOfAvailableConnections("EXEM"));

 

}

 

}

- 테스트 결과는 다음과 같음

Current Connection number : 0     Server Process ID : 1414     SessionID : 122

Current Connection number : 1     Server Process ID : 1416     SessionID : 80

Current Connection number : 2     Server Process ID : 1414     SessionID : 122

Current Connection number : 3     Server Process ID : 1416     SessionID : 80

Current Connection number : 4     Server Process ID : 1414     SessionID : 122

...

Current Connection number : 95     Server Process ID : 1416     SessionID : 80

Current Connection number : 96     Server Process ID : 1414     SessionID : 122

Current Connection number : 97     Server Process ID : 1416     SessionID : 80

Current Connection number : 98     Server Process ID : 1414     SessionID : 122

Current Connection number : 99     Server Process ID : 1416     SessionID : 80

Elapsed Time(ms) : 656

Used Memory(Bytes) : 581880

 

Total Active Conns : 0     Total Available Conns : 2

- 소요시간은 0.6초 메모리 사용량은 0.6MB정도로 10배의 시간과 4배의 메모리 절감 효과를 가져옴

- 또한 Oracle에서 유지되는 Session의 개수는 2개밖에 되지 않음.

- Oracle Server Process도 2개를 생성하여 계속 재활용하고 있음을 알 수 있음

 

7) 테스트 : Connection Pool을 사용할 경우

- 앞의 시나리오에 JDBC의 Connection Pool를 사용하도록 하여 PoolTest라는 Class를 생성

package exem.oracle.conncache;

 

import javax.sql.PooledConnection;

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.util.Properties;

 

import oracle.jdbc.pool.OracleConnectionPoolDataSource;

 

public class PoolTest {

    public static void main(String[] args) throws Exception{

OracleConnectionPoolDataSource ods = new OracleConnectionPoolDataSource();

ods.setURL("jdbc:oracle:thin:@210.122.227.247:1522:WASDB");

ods.setUser("PERSISTENCE");

ods.setPassword("PERSISTENCE");

PreparedStatement pstmt = null;

ResultSet rs = null;

int connnum = 100;

Properties prop = new Properties();

prop.setProperty("InitialLimit", "2");

prop.setProperty("MaxLimit", "5");

ods.setConnectionProperties(prop);

long befmem = Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory();

PooledConnection pconn = ods.getPooledConnection();

Connection conns[] = new Connection[connnum];

long chktime = System.currentTimeMillis();

for (int i = 0 ; i < connnum; i++){

    try {conns[i] = pconn.getConnection();     

    pstmt = conns[i].prepareStatement("select p.spid, s.sid from v$process p, v$session s where p.addr = s.paddr and s.sid = userenv('sid' )");

    rs = pstmt.executeQuery();

    rs.next();

    System.out.println("Current Connection number : "+i+"\t Server Process ID : " + rs.getString(1)+"\t SessionID : "+ rs.getString(2) );

    //conns[i].close();    

    }

    catch(Exception e) {    System.out.println("Error" + i ); throw e;     }

}

System.out.println("Elapsed Time(ms) : "+(System.currentTimeMillis()-chktime));

System.out.println("Used Memory(Bytes) : " + (Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory()-befmem) );

Connection conn = pconn.getConnection();

pstmt = conn.prepareStatement("select p.spid, s.sid , s.username from v$process p, v$session s where p.addr(+) = s.paddr and s.sid <> userenv('sid')" );

rs = pstmt.executeQuery();

while ( rs.next() ) {

    System.out.println("Server Process ID : " + rs.getString(1)+"\t SessionID : "+ rs.getString(2)+ "\t Username : " + rs.getString(3) );

}

 

}    

 

}

- 테스트 결과는 다음과 같음

Current Connection number : 0     Server Process ID : 1421     SessionID : 122

Current Connection number : 1     Server Process ID : 1421     SessionID : 122

Current Connection number : 2     Server Process ID : 1421     SessionID : 122

Current Connection number : 3     Server Process ID : 1421     SessionID : 122

Current Connection number : 4     Server Process ID : 1421     SessionID : 122

...

Current Connection number : 95     Server Process ID : 1421     SessionID : 122

Current Connection number : 96     Server Process ID : 1421     SessionID : 122

Current Connection number : 97     Server Process ID : 1421     SessionID : 122

Current Connection number : 98     Server Process ID : 1421     SessionID : 122

Current Connection number : 99     Server Process ID : 1421     SessionID : 122

Elapsed Time(ms) : 391

Used Memory(Bytes) : 421096

- 소요 시간은 0.4초, 메모리 사용은 0.4MB로 Connection Pool을 사용할 때 가장 좋은 성능이 나타남을 알 수 있음

 

8) 결론

- connection을 맺을 때마다 기존의 물리적인 Connection의 재사용 여부가 성능에 큰 영향을 미침을 알 수 있음

- connection Cache 와 connection Pool은 물리적인 Connection을 JDBC또는 DataSource에서 미리 소유하고 요청이 있을 때마다 이를 재사용하는 공통점이 있음

- 그러나 Connection Pool의 경우 SQL Request가 있을 경우 Connection을 제공하고 Response가 끝나면 이를 반환하는 구조이나 Cache의 경우는 세션이 유지되는 동안 별도의 Property가 설정되지 않으면 접속이 끝날 때까지 계속 Connection을 유지하는 차이가 있음

- 이를 확인하는 방법은 Connection을 Close하지 않고 두 Class를 수행해 보면 됨

- PoolTest는 위와 동일한 결과가 나타나지만 CacheTest는 다음과 같은 결과를 나타냄

Current Connection number : 0     Server Process ID : 1833     SessionID : 86

Current Connection number : 1     Server Process ID : 1835     SessionID : 80

Current Connection number : 2     Server Process ID : 1837     SessionID : 50

Current Connection number : 3     Server Process ID : 1839     SessionID : 102

Current Connection number : 4     Server Process ID : 1841     SessionID : 122

Error Connection5

Exception in thread "main" java.lang.NullPointerException

    at exem.oracle.conncache.CacheTest.main(CacheTest.java:29)

- MaxLimit인 5개까지 새로운 Connection을 맺어 나가지만 그 다음에 바로 Exception이 발생함.

- connection을 Close했을 때 Connection을 재활용하던 것과는 다름

- WAS와 같은 Middle Ware를 사용하는 경우는 기본적으로 connection pool을 사용하게끔 되어 있기 때문에 Connection Cache와 같은 기능의 필요성은 거의 느껴지지 않게 됨.

- 그러나 별도의 Connection Pool을 구현하지 않고 JDBC만으로도 Connection Pool이나 Cache를 사용할 수 있다는 사실만 기억하면 될 것으로 생각됨

 

'기술이야기' 카테고리의 다른 글

JDBC Connection때 Client 정보를 내맘대로…  (0) 2009.05.27
Connection Cache  (1) 2009.05.26
Oracle JDBC Driver  (0) 2009.05.26
Statement Cache  (0) 2009.05.26
댓글
댓글쓰기 폼