Я пытаюсь использовать BasicDataSource для объединения соединений с JDBCTemplate в весеннем приложении. Из всего, что я читал, это должно быть очень просто: просто настройте BasicDataSource в XML, внедрите источник данных в bean-компонент и в методе установки создайте новый JDBCTemplate.
Когда я сделал это, я заметил, что мое выступление было ужасным. Затем я переключился на Spring SingleConnectionDataSource, просто чтобы посмотреть, что произойдет, и моя производительность стала намного лучше. Я начал исследовать с помощью инструмента профилирования и заметил, что при использовании BasicDataSource для каждого запроса создается новое соединение.
Продолжая расследование, я вижу, где соединение закрывается после завершения запроса. В частности, в классе Spring DataSourceUtil:
public static void doReleaseConnection(Connection con, DataSource dataSource) throws SQLException {
if (con == null) {
return;
}
if (dataSource != null) {
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (conHolder != null && connectionEquals(conHolder, con)) {
// It's the transactional Connection: Don't close it.
conHolder.released();
return;
}
}
// Leave the Connection open only if the DataSource is our
// special SmartDataSoruce and it wants the Connection left open.
if (!(dataSource instanceof SmartDataSource) || ((SmartDataSource) dataSource).shouldClose(con)) {
logger.debug("Returning JDBC Connection to DataSource");
con.close();
}
}
Я заметил, что для «SmartDataSource» существует некоторая особая логика, которая оставляет соединение открытым. Это частично объясняет поведение, которое я наблюдал: поскольку SingleConnectionDataSource реализует SmartDataSource, соединение не закрывается. Однако я думал, что при использовании BasicDataSource метод close() для соединения просто вернет соединение с пулом. Однако, когда я смотрю на то, что происходит в моем профилировщике, метод close на самом деле вызывается для моего соединения с sybase, а не для какой-либо «обертки объединенного соединения», как я ожидал увидеть.
И последнее (это то, что я собираюсь исследовать сейчас): я использую TransactionTemplate для некоторых моих запросов (включая фиксации в базе данных), но простые запросы не находятся внутри transactionTemplate. Не знаю, связано это с проблемой или нет.
РЕДАКТИРОВАТЬ 1:
Хорошо, наконец-то появилось еще немного времени для расследования после того, как проект немного оторвался, и вот очень простой тест, который показывает проблему.
public class DBConnectionPoolTest {
@Autowired
@Qualifier("myDataSource")
private DataSource dataSource;
@Test
public void test() throws Exception{
JdbcTemplate template = new JdbcTemplate(dataSource);
StopWatch sw = new StopWatch();
sw.start();
for(int i=0; i<1000; i++){
template.queryForInt("select count(*) from mytable");
}
sw.stop();
System.out.println("TIME: " + sw.getTotalTimeSeconds() + " seconds");
}}
Вот мои две конфигурации источника данных:
<bean id="myDataSource" class="org.springframework.jdbc.datasource.SingleConnectionDataSource">
<property name="driverClassName" value="${db.driver}" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
</bean>
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${db.driver}" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
</bean>
Когда я запускаю тест с первой конфигурацией, это занимает около 2,1 секунды. Когда я запускаю его со второй конфигурацией, это занимает около 4,5 секунд. Я пробовал различные параметры в BasicDataSource, такие как установка maxActive=1 и testOnBorrow=false, но ничего не изменилось.