Статьи

Пулы соединений WTF

Давайте поговорим о пулах соединений.

Я утверждаю, что:

Настройки по умолчанию большинства популярных пулов соединений плохие!

Для вас это означает:

Перейдите к настройкам пула подключений.

У вас могут возникнуть проблемы, если вы полагаетесь на настройки по умолчанию. У вас могут быть утечки памяти и не отвечающее приложение (даже если загрузка совсем не высока).

Ниже я покажу некоторые из наиболее важных настроек и мои рекомендации, как они должны быть настроены.

Что такое пул соединений?

Обычное веб-приложение, которое должно записывать или читать данные из базы данных, делает это так:

  1. Открыть соединение с БД // занимает N мс
  2. чтение / запись данных
  3. закрыть соединение

(кстати, в старых добрых CGI-приложениях это был единственный возможный подход)

Этот подход прекрасно подходит во многих случаях. И вам, вероятно, больше ничего не нужно. Но у высокопроизводительных систем есть некоторые недостатки :

  • Шаг 1 может занять некоторое время. Вероятно, десятки или сотни миллисекунд (это зависит, конечно).
  • Легко забыть Шаг 3 (закрыть соединение), который вызывает утечку соединения (вызывает утечку памяти и другие проблемы).

Новый герой

Вот почему появился другой подход : приложение может предварительно открыть группу соединений и постоянно держать их открытыми . Группа открытых соединений называется пулом соединений . Тогда любая операция выглядит так:

  1. Взять соединение с БД из пула // невероятно быстро в большинстве случаев
  2. чтение / запись данных
  3. вернуть соединение в пул

Кажется классным Но новая власть всегда означает новые проблемы.

… И новые проблемы

При использовании пула соединений нам необходимо решить (как минимум) следующие вопросы :

  • Сколько соединений мы должны держать открытыми?
  • Как долго они должны храниться?
  • Что если они кажутся сломанными?
  • Что, если приложению нужно больше соединений, чем у пула в настоящее время
  • Что если кто-то забудет вернуть соединение в пул?

Чтобы ответить на эти вопросы, пулы соединений имеют множество настроек. И их значения по умолчанию в основном плохие. Заинтригованный? Позвольте мне показать.

Базовые настройки

Я рассмотрю 2 самых популярных пула соединений в мире Java:

Основные параметры причины:

  • минимальный размер (минимальное количество соединений, которые должны быть открыты в любой момент)
  • начальный размер (сколько подключений открывает приложение при запуске)
  • максимальный размер (максимальное количество соединений в пуле)

Кстати, это единственные настройки, которые имеют разумные значения по умолчанию. Они здесь:

c3p0 HikariCP
минимальный размер 3 10
начальный размер 3 10
максимальный размер 15 10

Давайте продолжим с более проблемными настройками.

Критические настройки

Оформить заказ

Как долго приложение может ждать, пока оно не получит соединение из пула.

  • Настройка c3p0: checkoutTimeout
  • Настройка HikariCP: connectionTimeout

Значения по умолчанию:

c3p0 HikariCP я рекомендую
checkoutTimeout 30 с 1 мс

Оба значения по умолчанию просто катастрофы.

Как я уже говорил, в большинстве случаев подключение к пулу происходит невероятно быстро. За исключением случая, когда в пуле больше нет открытых соединений. Затем пулу необходимо установить новое соединение (как правило, это занимает менее секунды). Но если достигается maxSize, пул не может открыть новое соединение и просто ждет, пока кто-нибудь не вернет его соединение в пул. Но если в приложении есть утечка соединения (ошибка, которая препятствует возвращению соединения), пул никогда не получит соединение обратно!

Что тогда происходит?

В случае c3p0 все потоки замораживаются в следующем состоянии:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
"qtp1905485420-495 13e09-3211" #495 prio=5 os_prio=0 tid=0x00007f20e078d800 nid=0x10d7 in Object.wait() [0x00007f204bc79000]
 
   java.lang.Thread.State: WAITING (on object monitor)
 
at java.lang.Object.wait(Native Method)
 
at com.mchange.v2.resourcepool.BasicResourcePool.awaitAvailable()
 
- locked <0x00000000c3295ef8> (a com.mchange.v2.resourcepool.BasicResourcePool)
 
at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource()
 
    
 
    at org.hibernate.jpa.internal.QueryImpl.getResultList()
 
    at domain.funds.FundsRepository.get()
 
    

Может показаться, что «30 секунд» по умолчанию для HikariCP немного лучше. Нет, это не очень помогает в высокопроизводительных приложениях. В течение этих 30 секунд может появиться много новых запросов, и все они просто заморожены. Видимо приложение скоро получит ошибку OutOfMemory. Любое ожидание просто откладывает смерть приложения на несколько секунд.

Вот почему я рекомендую установить checkoutTimeout на минимально возможное значение: 1 мс. К сожалению, мы не можем установить его в 0, потому что 0 означает бесконечное ожидание & # 55357; & # 56898; Чем раньше мы потерпим неудачу, тем больше у нас шансов на работу, чтобы завершить свою работу. И мы можем четко сообщить пользователю, что приложение в данный момент перегружено, и он должен попробовать позже.

проверить соединение при оформлении заказа

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

Самый простой способ сделать это — «testConnectionOnCheckout» в c3p0 (аналогичного параметра в HikariCP я не нашел, кажется, он всегда включен).

Значения по умолчанию:

c3p0 HikariCP я рекомендую
testConnectionOnCheckout ложный правда? правда

Определенно, он должен быть включен по умолчанию!

В противном случае вы получите множество таких исключений в журнале:

1
2
3
org.hibernate.TransactionException: Unable to rollback against JDBC Connection
at o.h.r.j.i.AbstractLogicalConnectionImplementor.rollback()
at o.h.r.t.b.j.i.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.rollback(JdbcResourceLocalTransactionCoordinatorImpl.java:294)

PS Если вы хотите добиться еще лучшей производительности, вы можете рассмотреть тестирование соединения в фоновом режиме, а не на кассе:

  • testConnectionOnCheckout = ложь
  • testConnectionOnCheckin = TRUE
  • idleConnectionTestPeriod = 10

предпочтительный тестовый запрос

Но как именно пул должен проверять соединения?

Проблема в том, что это зависит от базы данных.

По умолчанию оба пула проверяют соединения, выполняя

  • «Connection.isValid ()» (в случае JDBC4) или
  • «Connection.getMetaData (). GetTables ()» (в случае JDBC3)

Это может быть медленно, потому что «getTables ()» извлекает метаинформацию обо всех таблицах каждый раз. Рекомендуемое значение — это что-то вроде

  • «ВЫБРАТЬ 1» (в случае MySql) или
  • «ВЫБРАТЬ 1 ИЗ ДВОЙНОГО» (в случае Oracle) и т. Д.

Выполнив этот простой и быстрый запрос, пул может проверить, живо ли соединение.

максимальное время простоя

Как долго неиспользуемое соединение может оставаться в бассейне

  • Настройка c3p0: maxIdleTime
  • Настройка HikariCP: idleTimeout

Значения по умолчанию:

c3p0 HikariCP я рекомендую
maxIdleTimeout 10 минут 1.10 минуты

Это, вероятно, не имеет большого значения, но каждое открытое соединение

  • содержит некоторые ресурсы в базе данных
  • предотвращает подключение других систем к одной и той же базе данных (каждая база данных имеет ограничение на максимально возможное количество подключений)

Вот почему рекомендуется закрыть неиспользуемое (незанятое) соединение. Я рекомендую установить это значение на бесконечный период. Вероятно, разумно несколько минут.

минимальный размер пула

Сколько пулов соединений должно быть всегда (даже если они не используются).

  • Настройка c3p0: minPoolSize
  • Настройка HikariCP: минимальный

Значения по умолчанию:

c3p0 HikariCP я рекомендую
maxIdleTimeout 3 максимальный размер пула 0 … N

По той же причине, вероятно, будет хорошей идеей закрыть неиспользуемые соединения. Я бы установил это значение на 0 или 1 в большинстве случаев. Если какой-то пользователь неожиданно решит войти в приложение в полночь, он просто подождет еще несколько миллисекунд. Не ахти какое дело.

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

Как долго соединение может находиться в пуле (независимо от того, используется оно или не используется)

  • Настройка c3p0: maxConnectionAge
  • Настройка HikariCP: maxLifetime

Значения по умолчанию:

c3p0 HikariCP я рекомендую
maxIdleTimeout 30 минут скажем, 30 минут

На всякий случай, это, вероятно, хорошая идея время от времени закрывать соединения. Вероятно, это помогает избежать утечек памяти.

Цитата из документации HikariCP:

«Мы настоятельно рекомендуем установить это значение, и оно должно быть на несколько секунд короче, чем ограничение времени соединения для любой базы данных или инфраструктуры».

таймаут невозвращенного соединения

Одна из типичных проблем — утечка соединения. Какой-то глючный код взял соединение из пула и не вернул его. Как обнаружить эту проблему?

К счастью, у нас есть хорошие настройки для этого случая:

  • Настройка c3p0: unreturnedConnectionTimeout
  • Настройка HikariCP: leakDetectionThreshold

Значения по умолчанию:

c3p0 HikariCP я рекомендую
maxIdleTimeout отключен отключен 5 минут?

Если какой-либо код ошибки взял соединение и не возвращал его в течение 5 минут, пул принудительно вернет соединение и напишет предупреждения, подобные этому:

1
2
3
4
5
[C3P0PooledConnectionPoolManager Logging the stack trace by which the overdue resource was checked-out.
java.lang.Exception: DEBUG STACK TRACE: Overdue resource check-out stack trace.
at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource()
at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1885)
at domain.application.ApplicationReportSender.sendWeeklyReport(ApplicationReportSender.java:63)

Это поможет вам узнать, где виноват код.

Вывод

Я дал обзор некоторых настроек пула соединений. Их больше. Я дал несколько советов, которые кажутся разумными из моего опыта. Но ваше приложение может иметь различную нагрузку. Ваши пользователи могут иметь другое поведение. Мои советы могут показаться вам глупыми.

Без проблем. Не верь мне. Но, пожалуйста, также не верьте значениям по умолчанию.

Иди проверь настройки своего пула!

Опубликовано на Java Code Geeks с разрешения Андрея Солнцева, партнера нашей программы JCG . Смотрите оригинальную статью здесь: WTF Connection pool

Мнения, высказанные участниками Java Code Geeks, являются их собственными.