Анатомия Connection Pooling

Введение

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

Жизненный цикл соединений к БД

Каждая операция чтения или записи из БД требует соединения. Так давайте же представим как выглядит цикл соединения с БД:

Этапы:

  1.  Слой данных приложения запрашивает у DataSource соединения к БД
  2. DataSource использует драйвер к БД для открытия соединения
  3. Соединение БД создается и TCP сокет открывается
  4. Приложение пишет/читает в БД
  5.  Соединение больше не требуется и поэтому закрывается
  6. Сокет закрывается

Можно сделать вывод, что открытие/закрытие соединений довольно дорогая операция. PostgreSQL использует отдельный процесс для каждого клиентского соединения, так что высокая скорость открытия/закрытия соединений может обернуться загрузкой СУБД.

Самые очевидные причины для переиспользования соединений к БД:

  • уменьшение расходов I/O ресурсов приложения и СУБД для создания/закрытия TCP соединений
  • уменьшение JVM «мусора»

Пулинг vs Без пулинга

Давайте вместе сравним вариант без пулинга с пулингом HikariCP, который возможно является самым быстрым фреймворком пулинга соединений.

В этом тесте откроется и закроется 1000 соединений.


private static final Logger LOGGER = LoggerFactory.getLogger(DataSourceConnectionTest.class);
 
private static final int MAX_ITERATIONS = 1000;
 
private Slf4jReporter logReporter;
 
private Timer timer;
 
protected abstract DataSource getDataSource();
 
@Before
public void init() {
 MetricRegistry metricRegistry = new MetricRegistry();
 this.logReporter = Slf4jReporter
 .forRegistry(metricRegistry)
 .outputTo(LOGGER)
 .build();
 timer = metricRegistry.timer("connection");
}
 
@Test
public void testOpenCloseConnections() throws SQLException {
 for (int i = 0; i < MAX_ITERATIONS; i++) {
 Timer.Context context = timer.time();
 getDataSource().getConnection().close();
 context.stop();
 }
 logReporter.report();
}

График показывает время потраченное на открытие и закрытие соединенией (ниже значение, тем лучше).

Использование пулинга увеличивает скорость операций открытия/закрытия соединений в 600 раз, чем без него. Наши энтерпрайз системы состоят из десятков приложений и только лишь одна система пакетной обработки может использовать более 2 миллионов соединений к БД в час, поэтому оптимизация необходима.

Type No Pooling Time (milliseconds) Connection Pooling Time (milliseconds)
min 74.551414 0.002633
max 146.69324 125.528047
mean 78.216549 0.128900
stddev 5.9438335 3.969438
median 76.150440 0.003218

Почему пулинг так быстр?

Для того чтобы понять почему решение с пулингом работает так хорошо, нужно проанализировать цикл управления пулом соединений:

Всякие раз как запрашивается соединение, datasource пула будет использовать доступные соединения пула для получения нового соединения. Пул создаст новые соединения только в случае, если не осталось ни одного доступного и размер пула не достиг максимального. Метод close() вернет соединение в пул, вместо реального закрытия соединения.

Быстрее и безопасней

Пул соединений выступает как ограниченный буфер для входящих запросов на соединение. Если наступит резкий рост трафика пул соединений уравняет его, вместо того чтобы отдать все возможные русурсы БД.

Этап ожидания и механизм таймаута реализованы безопасно, предотвращая чрезмерную нагрузку на БД. Если одному приложение требуется слишком много трафика, то пул сдержит его, таким образом предотвращая падения сервера БД (следовательно падения всей системы в целом).

С большой силой приходит большая ответственность

Все эти преимущества имеют цену, которая материализуется в экстра сложную настройку пула (особенно для больших энтерпрайзных систем). Так что это не «серебрянная пуля» и нужно уделить время и внимание к многочисленным настройкам пула:

  • минимальный размер
  • максимальный размер
  • максимальное время бездействия
  • таймаут получения соединения
  • таймаут повтора попытки соединения.

В следующей статье я сравню пулы соединений с помощью FlexyPool .

 

(Visited 798 times, 1 visits today)

Добавить комментарий

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.