Постановка задачи
Мы уже давно используем H2 во встроенном режиме. Над ним настроен пул соединений. Ниже приведена текущая конфигурация пула:
h2.datasource.min-idle=10
h2.datasource.initial-size=10
h2.datasource.max-active=200
h2.datasource.max-age=600000
h2.datasource.max-wait=3000
h2.datasource.min-evictable-idle-time-millis=60000
h2.datasource.remove-abandoned=true
h2.datasource.remove-abandoned-timeout=60
h2.datasource.log-abandoned=true
h2.datasource.abandonWhenPercentageFull=100
Конфигурация H2:
spring.h2.console.enabled=true
spring.h2.console.path=/h2
h2.datasource.url=jdbc:h2:file:~/h2/cartdb
h2.server.properties=webAllowOthers
spring.h2.console.settings.web-allow-others=true
h2.datasource.driver-class-name=org.h2.Driver
* пропуск имени пользователя и пароля.
Мы убедились, что указанная выше конфигурация вступает в силу, зарегистрировав свойства пула.
Проблема с этой настройкой заключается в том, что мы наблюдаем регулярное (хотя и прерывистое) истощение пула соединений, и как только пул достигает максимального предела, он начинает выдавать следующее исключение для некоторых запросов.
SqlExceptionHelper.logExceptions (SqlExceptionHelper.java:129) - [http-apr-8080-exec-38] Время ожидания: пул пуст. Невозможно получить соединение в течение 3 секунд, недоступно [размер: 200; занят: 200; холостой ход: 0; lastwait: 3000]
.
И после этого он не может выйти из этого состояния даже через много часов, пока мы не перезапустим веб-сервер (в данном случае tomcat).
Зависимость драйвера H2:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.196</version>
<scope>runtime</scope>
</dependency>
Шаблон запроса и пропускная способность
Мы используем h2 для загрузки некоторых данных для каждого запроса, затем выполняем несколько (около 50) запросов SELECT и, наконец, удаляем данные. Это приводит к постоянным вызовам 30k-40k в минуту (кроме нерабочих часов) в h2 (согласно новому мониторингу реликвий).
Каждая операция чтения получает новое соединение и освобождает его после выполнения.
EntityManager entityManager = null;
try {
entityManager = entityManagerFactory.createEntityManager();
Query query = entityManager.createNativeQuery(sqlQuery);
query.setParameter("cartId", cartId);
List<String> resultList = query.getResultList();
return resultList;
} finally {
if(null != entityManager) { entityManager.close(); }
}
Наблюдения
- После перезапуска приложения загрузка пула минимальна, пока в один момент использование пула не резко возрастет и не достигнет максимального предела. Это происходит в течение 1-2 дней.
- Как только пул достигает максимального предела соединения, количество заимствованных соединений увеличивается более быстрыми темпами по сравнению с числом возвращенных соединений, которое в остальном остается очень близко друг к другу.
- В то же время количество заброшенных соединений также начинает увеличиваться вместе с журналами отмены.
- Интересно, что время ответа на запрос остается тем же после исчерпания пула. Так что этот вид исключает медленный запрос.
- Эта проблема возникает даже в самые неподходящие часы, когда трафик минимален. Так что это не имеет отношения к трафику.
Пожалуйста, направьте нас в правильном направлении, чтобы решить эту проблему.
UPDATE
Недавно мы обнаружили следующие причины в нашей трассировке стека, когда произошел один такой инцидент:
Причина: org.h2.jdbc.JdbcSQLException: база данных может быть уже в
использование: ноль. Возможные решения: закрыть все остальные соединения; использовать
режим сервера [90020-196]
Причина: java.lang.IllegalStateException: файл заблокирован:
nio: /root/h2/cartdb.mv.db [1.4.196 / 7]
Вызывается: java.nio.channels.OverlappingFileLockException
Поэтому, углубившись в это, мы решили перейти в режим в памяти, поскольку нам не требуется сохранять данные после истечения времени жизни приложения. В результате блокировка файла не должна происходить, тем самым уменьшая или устраняя эту проблему.
Вернется и обновит в любом случае.