Hello, database connection pools

lnoci043При работе с JDBC (или какой-нибудь обёрткой над JDBC) процесс работы с базы строится одинаковым образом:

  1. Открываем соединение с базой
  2. Делаем запросы.

Это прекрасно работает и не вызывает никаких проблем ровно до той поры, пока все запросы выполняются в одном потоке. Но в современном мире приложения многопоточны. В самом деле, если у вас web приложение, то скорее всего в нём будет несколько нитей, которые обслуживают запросы. Если речь идёт о ETL приложении, то наверняка данные, над которыми оно работает, будут разделены на блоки, которые будут перерабатывать несколько параллельных потоков. Даже настольные приложения сейчас имеют несколько потоков. А соединение то у нас с базой одно. И это проблема.

Во-первых, JDBC это интерфейс и его реализация может быть потокобезопасной, а может и не быть. И если сейчас используется какая-либо база данных, которая позволяет разделять один единственный объект Connection между потоками, то в будущем может потребоваться перейти на другую базу, реализация JDBC драйвера которой не будет потокобезопасной. Во-вторых, запросы из разных потоков буду выполняться вразнобой, что может привести к интересным последствиям в БД. В-третьих, и главных, транзакция на одно соединение может быть только одна и если нам нужно несколько параллельных транзакций, то реализовать это не получится.

Какие есть варианты решения этой проблемы? Можно обвеситься мутексами и запрещать параллельный доступ к базе из разных нитей. Это действительно решит проблему, но возникает вопрос, зачем тогда многопоточность и где от неё выгода? Можно создавать соединение в каждой нити отдельно и по завершению нити закрывать его. Это тоже решит проблему, но плохо скажется на производительности приложения — открытие соединения с базой довольно дорогая процедура. И, наконец, лучшее решение — открыть некоторое количество соединений заранее (сформировать пул соединений), а потоки будут брать готовые соединения из пула по мере надобности и возвращать их обратно после использования.

Разумеется, лучше всего взять готовое решение, чем изобретать велосипед. Для примера я использую HikariCP и базу данных .

Подготовка

Создадим пустой maven проект и добавим в него артефакты драйвера PostgreSQL JDBC и :

Кроме того, надо подготовить базу в PostgreSQL:

Создание пула

Пул соединений создавать не сложнее, чем одно соединение напрямую через JDBC. Мы указываем jdbc URL в объекте конфигурации, в нём же задаём дополнительные параметры, если требуется, и создаём из него объект DataSource Из DataSource можно запросить объект Connection и работать с ним как обычно:

Использование пула

Но использовать пул в один поток неинтересно. Давайте попробуем сделать к базе несколько параллельных запросов:

Я запускаю 16 потоков, которые ждут запуска друг друга и потом одновременно начинают выполнять один и тот же запрос. А в этом время, в PostgreSQL можно наблюдать, как 16 потоков открывают 16 соединений и делают 16 запросов:

Код примера доступен на github. Для запуска примера требуется установить PostgreSQL сервер и разрешить к нему доступ. Если сервер будет установлен не на локальной машине, требуется изменить его адрес в jdbc url.