DBCP 문제 해결기
1. 문제상황 :
- 오픈하고 지금까지 커넥션 문제가 없었으나, 요청이 현재 3배 이상 증가하여 커넥션 문제가 발생하고 있었음.
- 발생문제 : Cannot get a connection, pool error Timeout waiting for idle object 관련 예외가 계속해서 발생함.
- DBA로 부터 너무많은 커넥션 요청이 들어온다는 보고를 받음
2. 분석 :
- 커넥션을 대기하는 시간동안 커넥션을 획득하지 못해서 발생한 오류
- 즉, maxWaitMillis를 넘어서도 커넥션을 획득하지 못하는 문제가 발생.
3. 커넥션 설정 확인 :
BEFORE :
maxWait=600
slowQueryTime=300
initialSize=5
maxActive=30
maxIdle=20
minIdle=5
useStatementCache=true
statementCacheSize=250
testOnBorrow=false
testOnReturn=false
testWhileIdle=false
timeBetweenEvictionRunsMillis=600000
minEvictableIdleTimeMillis=3600000
queryTimeout=0
useSqlLogForamt=false
connectionProperties=oracle.net.CONNECT_TIMEOUT=5000;oracle.jdbc.ReadTimeout=60000
# beta
beta.queryTimeout=7
beta.useSqlLogForamt=false
--> 상기 설정은 하나의 커넥션이 1시간동안 idle로 되어 잇으면 커넥션이 notValid한 것으로 판단하는 것이다.
--> 초기 maxActive는 30으로 설정되었고, maxIdle값은 20으로 설정되었다.
#즉 상기 설정을 통해서 커넥션이 20개 이상이 되는경우 커넥션을 맺고, 사용후에 바로 close한다. 그리고 요청이 필요한경우 다시 connection을 맺기 위해서 DB접근을 계속하는 상황이 된다.
#또한 실제 Idle한 커넥션은 1시간이 지나면 evict 되어 재 접속이 필요한 상황이 된다.
AFTER : 수정이후
maxWait=600
slowQueryTime=300
initialSize=5
maxActive=30
maxIdle=30
minIdle=5
useStatementCache=true
statementCacheSize=250
testOnBorrow=false
testOnReturn=false
testWhileIdle=true
validationQuery=SELECT 1 FROM DUAL
timeBetweenEvictionRunsMillis=150000
minEvictableIdleTimeMillis=-1
numTestsPerEvictionRun=5
queryTimeout=0
useSqlLogForamt=false
connectionProperties=oracle.net.CONNECT_TIMEOUT=5000;oracle.jdbc.ReadTimeout=60000
# beta
beta.queryTimeout=7
beta.useSqlLogForamt=false
- maxActive와 maxIdle값을 같게 설정하였다. 즉, 커넥션제한을 미리 설정해두고, 사용후 반납하는 일이 없도록 설정하였다.
- 상기 설정은 idle상태의 커넥션을 대상으로 validationQuery를 보내게 설정하였다.
- 또한 커넥션 검사시간을 150000으로 설정하였다. 매 150000마다 커넥션 검사를 시작한다.
- minEvictableIdleTimeMillis=-1로 설정하여 풀에 있는 커넥션은 무조건 살아 있다고 가정한다.
- numTestsPerEviceionRun=5로 설정하여 한번 커넥션 valid 할때바다 5개를 검사하도록 지정하였다.
4. 결과
- 커넥션을 기다리다가 타임아웃 되는 현상이 사라졌다.
- 매 순간마다 커넥션을 계속 맺는 현상이 사라졌다.
--------------------------------------------------------------------------------------------------------
Apache Common DBCP
--------------------------------------------------------------------------------------------------------Common DBCP란 :
DB 트랜잭션을 처리하기 위해서 가장 비용이 많이 드는 것은 데이터베이스와 커넥션을 신규로 생성하는 과정으로 DBCP를 이용하면 생성된 커넥션을 pool에 넣고 재사용하여 성능을 극대화 하는 방법을 제공한다.
DBCP2의 특징 :
common-dbcp2 는 common-pool2 패키지 내에 포함되어 있으며, Java 7 이상에서 동작한다. (JDBC 4.1)
DBCP2는 Commons Pool2를 기반으로 한다.
기존 DBCP보다 성능이 더 좋아졌다.
JMX을 지원하여 DBCP 1.x버젼에 비해서 더 많은 기능을 제공한다.
DBCP Configuration :
1. 기본 설정 :
1.1 username : 커넥션을 생성하기 위한 DB사용자 이름
1.2 password : 커넥션을 생성하기 위한 DB사용자 비밀번호
1.3 url : 커넥션을 생성하기 위한 DB URL
1.4 driverClassName : 사용될 JDBC의 풀 자바 클래스 이름
1.5 connectionProperties : 새로운 커넥션 생성을 위해서 JDBC 드라이버에 전달할 속성들을 의미한다.
[propertyName=property;]* 의 형식을 가진다.
2. 트랜잭션 관련 설정 :
2.1 defaultAutoCommit
- 기본값 : driver default
- 설명 : 풀에 생성된 커넥션의 auto-commit상태를 설정한다. 설정을 하지 않으면 setAutoCommit메소드가 호출되지 않는다.
2.2 defaultReadOnly
- 기본값 : driver default
- 설명 : 풀에 의해서 생성된 커넥션의 read-only상태를 설정한다. 설정을 하지 않으면 setReadOnly메소드가 호출되지 않는다. (Infomix와 같은 몇몇 드라이버는 이 설정을 지원하지 않는다.)
2.3 defaultTransactionIsolation
- 기본값 : driver default
- 설명 : 기본 트랜잭션 Isolation을 지정한다.
NONE
READ_COMMITTED
READ_UNCOMMITTED
REPEATABLE_READ
SERIALIZABLE
2.4 defaultCatalog
- 기본값 : 없음
- 설명 : 풀에 생성된 커넥션의 기본 카탈로그를 설정한다.
2.5 cacheState
- 기본값 : true
- 설명 : true로 설정하면 풀된 커넥션은 첫번째 읽기와 쓰기를 수행할때 설정된 readOnly와 autoCommit 값을 캐시해두고 이하 쓰기시에 이를 적용한다. 만약 커넥션이 직접 접근 되어야 하거나 readOnly나 autoCommit 설정이 캐시에서 변경이 되더라도 현재 커넥션에는 반영되지 않는다. 캐싱을 하지 않고자 하는 케이스에서는 이 값을 false로 두면 된다.
2.6 defaultQueryTimeout
- 기본값 : null
- 설명 : 이값이 null이 아닌경우 여기에 설정된 정수형의 값은 풀에 의해서 관리되는 커넥션으로 부터 생성된 스테이트 먼트에 사용될 쿼리 타임아웃을 지정하게 된다. null의 의미는 driver의 기본값을 사용하겠다는 의미이다.
2.7. enableAutocommitOnReturn
- 기본값 : null
- 설명 : true로 설정하면 풀이 커넥션으로 반환될때 Connection.setAutoCommit(true)인지 체크되고, 해당값으로 설정된다. 만약 auto commit가 false인경우라면 true로 변경 설정된다.
2.8 rollbackOnReturn
- 기본값 : true
- 설명 : true의 의미는 풀로 커넥션이 반환될때 롤백이 수행된다. 단 auto commit이 false이고, 커넥션이 read only모드가 아니어야한다.
3. 커넥션 개수 설정
3.1 initialSize
- 기본값 : 0
- 설명 : 풀이 시작될때 초기에 설정될 커넥션의 개수를 지정한다. 1.2버젼부터 지원
3.2 maxTotal
- 기본값 : 8
- 설명 : active connection의 최대 개수를 지정한다. 이것은 동시에 풀에 할당될 수 있는 최대 커넥션의 개수이다. 음수로 설정하면 제한이 없다는 의미가 된다.
3.3 maxIdle
- 기본값 : 8
- 설명 : 풀에서 idle로 남아 잇을 수 있는 최대 개수이다. 이 값보다 넘는 커넥션은 릴리즈 된다. 음수는 제한이 없다는 의미이다.
3.4 minIdle
- 기본값 : 0
- 설명 : 풀에 idle로 남아있는 최소 개수이다. 즉 풀에 남아 있을 수 있는 최소 커넥션 개수이다.
3.5 maxWaitMillis
- 기본값 : idenfinitely
- 설명 : 최대 밀리세컨을 지정하는 것으로 커넥션을 얻기 위해 기다리는 시간이다. 커넥션이 모두 다 쓰고 있는 상태에서 스레드가 하나의 커넥션을 얻기 위해 대기하는 시간이다. -1로 설정하면 무한정 기다린다.
NOTE :
- 만약 매우 무거운 시스템에서 maxIdle값을 너무 작게 설정하면, 커넥션을 닫고나서 바로 즉시 커넥션을 연결하는 일이 발생할 수 있다. 이 결과는 커넥션을 순간만 이용하고 커넥션을 여는 시간보다 닫는시간이 짧게 되는 현상이 나타난다. 이것은 idle 커넥션의 개수가 maxIdle까지 증가하는 현상을 원인이 된다.
- 무거운 시스템에서 가장 좋은 maxIdle 값은 시스템마다 다를 것이다. 그러나 기본값은 좋은 시작 포인트가 된다.
4. 커넥션 유지를 위한 설정
4.1 validationQuery
- 기본값 : 없음
- 설명 : validationQuery는 풀에 커넥션을 반환하기 전이나, 풀을 획득하기 전에 커넥션이 valid한지를 검사한다. 이를 지정하고자 하는경우 select쿼리는 반드시 1로만 반환하도록 설정해야한다. 만약 이를 지정하지 않으면 커넥션은 isValid()메소드를 통해서 valid한지 검사하게 된다.
4.2 validationQueryTimeout
- 기본값 : no timeout
- 설명 : 커넥션 validation 쿼리가 실패하기 전까지 시간을 의미한다. 만약 양수로 설정하면 setQueryTimeout메소드를 통해서 설정이 되며, Statement를 통해서 validation쿼리가 수행된다.
4.3 testOnCreate
- 기본값 : false
- 설명 : 생성된 이후에 객체가 validate되었는지 여부를 나타낸다. 만약 객체가 validate에서 실패한경우 객체 생성은 실패가 된다.
4.4 testOnBorrow
- 기본값 : true
- 설명 : 풀에서 커넥션을 얻어오기전에 객체가 valid한지 검사한다. 만약 객체가 validate에서 실패하면, 풀로 부터 객체를 drop하며, 다른 객체를 얻기위해 시도한다.
4.5 testOnReturn
- 기본값 : false
- 설명 : 풀로 객체를 반환하기 전에 validated된 값인지 검사한다.
4.6 testWhileIdle
- 기본값 : false
- 설명 : idle object evictor에 의해서 검증되어지며, 만약 객체가 검증에서 false가 되면 pool로 부터 드롭한다.
4.7 timeBetweenEvictionRunsMills
- 기본값 : -1
- 설명 : idle object evictor스레드의 실행 주기간격을 설정한다. 양수 값이 아닌경우에는 idle object evictor를 실행하지 않는다.
4.8 numTestsPerEvictionRun
- 기본값 : 3
- 설명 : idle object evictor 스레드가 매번 실행할때 검사할 객체의 개수를 지정한다.
4.9 minEvictableIdleTimeMillis
- 기본값 : 1000 * 60 * 30
- 설명 :풀에 객체가 idle상태로 있을 수 있는 최소한의 시간값을 지정한다.
4.10 softMiniEvictableIdleTimeMillis
- 기본값 : -1
- 설명 : idle collection evictor에 의해서 제거되기 전에 풀에 idle 상태로 남아있을 수 있는 시간을 의마힌다. 이것은 추가적인 상황을 함께 고려되며 적어도 "minIdle"커넥션을 남기도록 한다. miniEvictableIdleTimeMillis가 양수로 설정이 되면 minEvictableIdleTimeMillis는 우선 수행된다. 이것은 idle connections들이 evictor에 의해서 방문되어질때 idle time은 우선 miniEvictableIdleTimeMillis에 대해서 비교되어진다. (풀에 있는 idle 커넥션의 수를 고려하지 않는다.) 그리고 softMinEvictableIdleTimeMillis에 대해서 수행이 되며 minIdle constraint를 포함한다.
4.11 maxConnLifetimeMillis
- 기본값 : -1
- 설명 : 커넥션의 최대 라이프타임을 지정한다. 이 값이 지나게 되면 다음번 activation, passivation,혹은 validation테스트에서 실패하게 된다. zero의 값 혹은 더 적은 값은 라이프 타임이 없는 것을 의미한다.
4.12 logExpiredConnections
- 기본값 : true
- 설명 : 로그 메시지로 maxConnLifetimeMillis를 초과한 경우에 커넥션이 닫혔음을 로그로 남긴다. 이 속성을 false로 설정하여 익스파이어된 커넥션로깅을 막도록 설정할 수 있다.
4.13 connectionInitSqls
- 기본값 : null
- 설명 : SQL statements의 컬렉션으로 물리적 커넥션의 최기화에 사용되어진다. 이것은 커넥션이 처음 생성될때 작용한다. 이 스테이트먼트는 오직 한번만 수행된다. 설정된 커넥션 팩토리가 커넥션을 생성한다.
4.14 lifo
- 기본값 : true
- 설명 : True로 설정하면 borrowObject를 하였을때 가장 최근에 사용된 (즉 마지막에 입력된) 커넥션을 반환한다. (만약 idle 커넥션이 가능한 경우에 가능하다.) False는 풀은 FIFO 큐로 동작한다. 커넥션들은 풀에 들어온 순서대로 풀에서 커넥션을 반환한다.
5. SQL문장에 대한 풀 설정
5.1 poolPreparedStatements :
- 기본값 : false
- 설명 : 현재 풀을 위한 statement 풀링을 설정한다.
5.2 maxOpenPreparedStatements
- 기본값 : undefined
- 설명 : 최대 개수의 오픈 스테이트먼트를 의미한다. 이것은 statement pool에 동시에 할당될 수 있는 값으로, 음수를 지정하면 제한이 없음을 의미한다.
- 상기 설정은 다음 메소드를 이용하는 경우 풀에 할당된다.
-- public PreparedStatement prepareStatement(String sql)
-- public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
NOTE :
- 커넥션이 다른 statements를 위해서 몇몇의 리소스를 남겨 두었느지 확인하라. Pooling PreparedStatements는 데이터베이스에서 해당 커서를 오픈항 상태로 둘수 있다. 이런경우 Pooling이 커서를 모두 소비해 버릴 수 있다. 특히 maxOpenPreparedStatements가 기본(제한없음)으로 설정된경우 그리고 어플리케이션이 큰 수의 서로다른 PreparedStatements를 하나의 커넥션에서 열고 있는 상태라면 더욱 주의해야한다. 이러한 문제를 피하기 위해서는 maxOpenPreparedStatements는 하나의 커넥션당 열수 있는 최대 커서의 수보다 작은 값을 가지도록 설정해야한다.
6. 나머지 설정 :
- 아래 참고자료를 참조하자.
참고자료 : https://commons.apache.org/proper/commons-dbcp/configuration.html
4.3 testOnCreate
- 기본값 : false
- 설명 : 생성된 이후에 객체가 validate되었는지 여부를 나타낸다. 만약 객체가 validate에서 실패한경우 객체 생성은 실패가 된다.
4.4 testOnBorrow
- 기본값 : true
- 설명 : 풀에서 커넥션을 얻어오기전에 객체가 valid한지 검사한다. 만약 객체가 validate에서 실패하면, 풀로 부터 객체를 drop하며, 다른 객체를 얻기위해 시도한다.
4.5 testOnReturn
- 기본값 : false
- 설명 : 풀로 객체를 반환하기 전에 validated된 값인지 검사한다.
4.6 testWhileIdle
- 기본값 : false
- 설명 : idle object evictor에 의해서 검증되어지며, 만약 객체가 검증에서 false가 되면 pool로 부터 드롭한다.
4.7 timeBetweenEvictionRunsMills
- 기본값 : -1
- 설명 : idle object evictor스레드의 실행 주기간격을 설정한다. 양수 값이 아닌경우에는 idle object evictor를 실행하지 않는다.
4.8 numTestsPerEvictionRun
- 기본값 : 3
- 설명 : idle object evictor 스레드가 매번 실행할때 검사할 객체의 개수를 지정한다.
4.9 minEvictableIdleTimeMillis
- 기본값 : 1000 * 60 * 30
- 설명 :풀에 객체가 idle상태로 있을 수 있는 최소한의 시간값을 지정한다.
4.10 softMiniEvictableIdleTimeMillis
- 기본값 : -1
- 설명 : idle collection evictor에 의해서 제거되기 전에 풀에 idle 상태로 남아있을 수 있는 시간을 의마힌다. 이것은 추가적인 상황을 함께 고려되며 적어도 "minIdle"커넥션을 남기도록 한다. miniEvictableIdleTimeMillis가 양수로 설정이 되면 minEvictableIdleTimeMillis는 우선 수행된다. 이것은 idle connections들이 evictor에 의해서 방문되어질때 idle time은 우선 miniEvictableIdleTimeMillis에 대해서 비교되어진다. (풀에 있는 idle 커넥션의 수를 고려하지 않는다.) 그리고 softMinEvictableIdleTimeMillis에 대해서 수행이 되며 minIdle constraint를 포함한다.
4.11 maxConnLifetimeMillis
- 기본값 : -1
- 설명 : 커넥션의 최대 라이프타임을 지정한다. 이 값이 지나게 되면 다음번 activation, passivation,혹은 validation테스트에서 실패하게 된다. zero의 값 혹은 더 적은 값은 라이프 타임이 없는 것을 의미한다.
4.12 logExpiredConnections
- 기본값 : true
- 설명 : 로그 메시지로 maxConnLifetimeMillis를 초과한 경우에 커넥션이 닫혔음을 로그로 남긴다. 이 속성을 false로 설정하여 익스파이어된 커넥션로깅을 막도록 설정할 수 있다.
4.13 connectionInitSqls
- 기본값 : null
- 설명 : SQL statements의 컬렉션으로 물리적 커넥션의 최기화에 사용되어진다. 이것은 커넥션이 처음 생성될때 작용한다. 이 스테이트먼트는 오직 한번만 수행된다. 설정된 커넥션 팩토리가 커넥션을 생성한다.
4.14 lifo
- 기본값 : true
- 설명 : True로 설정하면 borrowObject를 하였을때 가장 최근에 사용된 (즉 마지막에 입력된) 커넥션을 반환한다. (만약 idle 커넥션이 가능한 경우에 가능하다.) False는 풀은 FIFO 큐로 동작한다. 커넥션들은 풀에 들어온 순서대로 풀에서 커넥션을 반환한다.
5. SQL문장에 대한 풀 설정
5.1 poolPreparedStatements :
- 기본값 : false
- 설명 : 현재 풀을 위한 statement 풀링을 설정한다.
5.2 maxOpenPreparedStatements
- 기본값 : undefined
- 설명 : 최대 개수의 오픈 스테이트먼트를 의미한다. 이것은 statement pool에 동시에 할당될 수 있는 값으로, 음수를 지정하면 제한이 없음을 의미한다.
- 상기 설정은 다음 메소드를 이용하는 경우 풀에 할당된다.
-- public PreparedStatement prepareStatement(String sql)
-- public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
NOTE :
- 커넥션이 다른 statements를 위해서 몇몇의 리소스를 남겨 두었느지 확인하라. Pooling PreparedStatements는 데이터베이스에서 해당 커서를 오픈항 상태로 둘수 있다. 이런경우 Pooling이 커서를 모두 소비해 버릴 수 있다. 특히 maxOpenPreparedStatements가 기본(제한없음)으로 설정된경우 그리고 어플리케이션이 큰 수의 서로다른 PreparedStatements를 하나의 커넥션에서 열고 있는 상태라면 더욱 주의해야한다. 이러한 문제를 피하기 위해서는 maxOpenPreparedStatements는 하나의 커넥션당 열수 있는 최대 커서의 수보다 작은 값을 가지도록 설정해야한다.
6. 나머지 설정 :
- 아래 참고자료를 참조하자.
참고자료 : https://commons.apache.org/proper/commons-dbcp/configuration.html