상세 컨텐츠

본문 제목

JSP 공부 <6> - 트랜잭션 처리 , 풀

JSP

by oimb 2018. 9. 16. 14:11

본문


1. 트랜잭션 처리




트랜잭션은 데이터처리를 모두 일관되게 하기 위해서 반드시 수행되어야하는 작업이다.


트랜잭션을 구현하는 방법으로는

  • JDBC의 오토 커밋 모드를 false로 지정   → 단일 데이터베이스에 접근하는 경우

  • JTA(Java Tansaction API)를 이용           → 두개 이상의 데이터베이스를 처리하는 경우

코드를 살펴 보자

try{    

conn = DriverManager.getConnection(jdbcDriver,dbUser,dbPass);

//트랜잭션 시작

conn.setAutoCommit(false);

... //쿼리 실행1

... //쿼리 실행2


//트랜잭션 커밋

conn.commit(); 

}catch(SQLException e){

//오류 발생시 트랜잭션 롤백

if( conn!=null)

conn.rollback()

}

}finally{

if(conn!=null)try{conn.close();}catch(SQLException e){}

}





2. 커넥션 풀




이전 방식은 작업이 필요할 때 작업이 필요할 때마다 커넥션을 생성해서 사용했다. 이 방식은 지속적으로 시간이 소모되기 때문에 동시 접속자가 많은 웹사이트에서는 전체 성능이 낮아진다. 이를 해결하기 위해서 나온 방식이 커넥션 풀이다.


커넥션 풀을 사용하기 위해서는 DBCP 관련 파일들이 있어야한다.

 

이 파일들은 https://search.maven.org/ 에서 다운 받을 수 있다. 

이후 서블릿 클래스를 작성해야 한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package jdbc;
 
import java.sql.DriverManager;
 
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
 
import org.apache.commons.dbcp2.ConnectionFactory;
import org.apache.commons.dbcp2.DriverManagerConnectionFactory;
import org.apache.commons.dbcp2.PoolableConnection;
import org.apache.commons.dbcp2.PoolableConnectionFactory;
import org.apache.commons.dbcp2.PoolingDriver;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
 
 
 
@SuppressWarnings("serial")
public class DBCPInit extends HttpServlet{
 
    @Override
    public void init() throws ServletException {
        loadJDBCDriver();
        initConnectionPool();
    }
    private void loadJDBCDriver() {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        }catch(ClassNotFoundException ex) {
            throw new RuntimeException("fail to load JDBC Driver",ex);
        }
    }
    private void initConnectionPool() {
        try {
            String jdbcUrl = "jdbc:mysql://localhost:3306/DBname?"
                     + "useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
            String userName="userid";
            String userPassword = "userpwd";
            ConnectionFactory connFactory = 
                    new DriverManagerConnectionFactory(jdbcUrl,userName,userPassword);
            PoolableConnectionFactory poolableConnectionFactory = 
                    new PoolableConnectionFactory(connFactory, null);
            poolableConnectionFactory.setValidationQuery("select 1");
            
            GenericObjectPoolConfig poolConfig=
                    new GenericObjectPoolConfig();
            poolConfig.setTimeBetweenEvictionRunsMillis(1000L*60L*5L);
            poolConfig.setTestWhileIdle(true);
            poolConfig.setMinIdle(4);
            poolConfig.setMaxTotal(50);
            
            GenericObjectPool<PoolableConnection> connectionPool =
                        new GenericObjectPool<PoolableConnection>(poolableConnectionFactory,poolConfig);
            poolableConnectionFactory.setPool(connectionPool);
            
            Class.forName("org.apache.commons.dbcp2.PoolingDriver");
            PoolingDriver driver=(PoolingDriver)DriverManager.getDriver("jdbc:apache:commons:dbcp:");
            driver.registerPool("chap14", connectionPool);
        }catch (Exception e) {
            System.out.println("exception e");
            throw new RuntimeException(e);        
        }
    }
    
}
cs


각 과정은

  1. 실제 커넥셜을 생성할 ConnectionFactionFactory를 생성한다.

  2. 커넥셜 풀로 사용할 PoolableConnection을 생성하는 PoolableConnectionFactory를 생성한다.

  3. 커넥션 풀의 설정 정보를 생성한다.

  4. 커넥션 풀을 사용할 JDBC드라이버를 등록한다.

이 과정을 거친다.

소스코드를 작성했으므로 서블릿을 지정해주어야한다. web.xml 에 이 부분을 추가해준다.

<servlet>

  <servlet-name>DBCPInit</servlet-name>

  <servlet-class>jdbc.DBCPInit</servlet-class>

  <load-on-startup>1</load-on-startup>

  </servlet>


이전 방식은 JDBC 드라이버를 로드한 이후에 여기서 커넥션을 바로 가져왔다 하지만 지금은 풀을 사용하므로 먼저 풀드라이버를 로드해서 접근할 수 있게 만들고 이후에

JDBC 드라이버를 이용해 이 풀에 접근해야 한다.

Class.forName("org.apache.commons.dbcp2.PoolingDriver");

PoolingDriver driver=(PoolingDriver)DriverManager.getDriver("jdbc:apache:commons:dbcp:");

driver.registerPool("poolName", connectionPool);

그리고 여기서 풀의 이름을 poolName으로 했으므로 이 풀을  사용하는 JSP페이지에서는 이렇게 작성 하면된다.


Connection conn =null;

try{

String jdbcDriver = "jdbc:apache:commons:dbcp:poolName";

// 커넥션 풀에서 커넥션을 구하는 것이다.

DriverManager.getConnection(jdbcDriver);

...

}finally{

//커넥션을 풀에 반환

if(conn!=null)try { conn.close();}catch(SQLException 




다음으로 커넥션 풀 속성에 대해서 설명 하겠다.


GenericObjectPoolConfig poolConfig=

new GenericObjectPoolConfig();

poolConfig.setTimeBetweenEvictionRunsMillis(1000L*60L*5L);

poolConfig.setTestWhileIdle(true);

poolConfig.setMinIdle(4);

poolConfig.setMaxTotal(50);


이러한 코드가 있는데 바로 여기서 커넥션에 대한 속성을 설정 한다. 


GenericObjecPoolConfig 클래스를 사용하여 설정할 수 있다.

일단 API 주소를 남기니 참고하시길 (http://www.shbaseball.co.kr/index.hs)

이를 이용하면 커넥션 풀의 최대 크기, 커넥션의 검사 주기 등을 설정할 수 있는 매서드를 제공하고 있다.


이 외에도 유휴 커넥션을 제거하는 기능을 제공한다.





커넥션 풀에 있는 커넥션 중 오랜 시간 동안 사용되지 않는 커넥션은 DBMS와 연결이 끊길 가능성이 높다. 따라서 유휴 커넥션을 검사하여 제거해줘야한다.


1. 커넥션이 최소 유휴 시간보다 오래 풀에 있는 경우 (MinEvictableIdleTimeMillis 검사)

2. 커넥션이 유효한지 여부 확인 (TestWhileIdle)

이 두 속성은 검사 주기 (TimeBetweenEvictionRunsMillis) 값이 양수일때 적용 된다


이후에 유휴 커넥션이 유효한지 검사한다면 검사할 때 사용할 쿼리를 지정해주면 된다.

PoolableConnectionFactory poolableConnectionFactory = 

new PoolableConnectionFactory(connFactory, null);

poolableConnectionFactory.setValidationQuery("select 1");


※ setValidationQuery 가 검사할 때 사용할 쿼리를 지정하는 매서드이다. 여기서 'select 1' 크게 의미 없는 쿼리인데 다만 커넥션이 끊킨 유효하지 않은 커넥션을 잡아주기 위한 것이다. 쿼리를 보냈을 때 오류가는 커넥션이 유효하지 않은 커넥션이기 때문에 가장  DB 부하가 적은 쿼리를 보내느 것이다.



풀속성에 대해 정리를 하면


  • maxTotal : 이 값이 불필요하게 커질 경우 커넥션 개수가 비대하게 늘어나 DBMS가 수용할 수 있는 수준을 넘어서면 오히려 전체 성능에 좋지 않은 영향을 끼칠 수 있다.

  • minIdle : 사용되지 않는 커넥션의 최소 개수를 0으로 지정하면 풀에 저장된 커넥션 개수가 0이 될 수 있다.

  • timeBetweenEvictionRunsMillis : 이 값을 설정해서  주기적으로 유휴 커넥션을 풀어서 제거하는 것이 좋다.

  • testWhileIdle : 유휴 커넥션을 검사할 때 유효하지 않은 커넥션도 검사해서 연결이 끊긴 커넥션을 사전에 제거하는 것이 좋다.

  • maxWaitMillis : 커넥션 풀에 커넥션이 없으면 일정 시간 대기 후 익셉션을 발생시키는 것이 좋다. 그렇지 않으면 클라이언트는 무한정 대기하게 될 수도 있다



관련글 더보기