Лучший способ управлять подключением к базе данных для сервлета Java - PullRequest
27 голосов
/ 28 октября 2008

Каков наилучший способ управления подключением к базе данных в сервлете Java?

В настоящее время я просто открываю соединение в функции init(), а затем закрываю его в destroy().

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

Это правильный способ справиться с этим? Если нет, то какие есть лучшие варианты?

edit: чтобы дать немного больше разъяснений: я пытался просто открывать / закрывать новое соединение для каждого запроса, но с тестированием я видел проблемы с производительностью из-за создания слишком большого количества соединений.

Есть ли смысл разделять соединение по нескольким запросам? Запросы для этого приложения почти все «только для чтения» и приходят довольно быстро (хотя запрашиваемые данные довольно малы).

Ответы [ 9 ]

22 голосов
/ 25 мая 2009

Как все говорят, вам нужно использовать пул соединений. Зачем? Как дела? И т.д.

Что не так с вашим решением

Я знаю это, так как я тоже думал, что когда-то это была хорошая идея. Проблема двоякая:

  1. Все потоки (запросы сервлета обслуживаются по одному потоку на каждый) будут использовать одно и то же соединение. Поэтому запросы будут обрабатываться по одному. Это очень медленно, даже если вы просто сидите в одном браузере и опираетесь на клавишу F5. Попробуйте: это звучит высокоуровнево и абстрактно, но эмпирически и проверяемо.
  2. Если соединение разорвано по какой-либо причине, метод init больше не будет вызываться (поскольку сервлет не будет выведен из эксплуатации). Не пытайтесь решить эту проблему, поместив try-catch в doGet или doPost, потому что тогда вы окажетесь в аду (что-то вроде написания сервера приложений без запроса).
  3. Вопреки тому, что можно подумать, у вас не будет проблем с транзакциями, поскольку начало транзакции связано с потоком, а не только с соединением. Я могу ошибаться, но так как это в любом случае плохое решение, не переживайте.

Почему пул соединений

Пулы соединений дают вам массу преимуществ, но больше всего они решают проблемы

  1. Создание реального подключения к базе данных является дорогостоящим. Пул подключений всегда имеет несколько дополнительных подключений и дает вам одно из них.
  2. Если не удается установить соединение, пул соединений знает, как открыть новое
  3. Очень важно: каждый поток получает свое собственное соединение. Это означает, что многопоточность обрабатывается там, где она должна быть: на уровне БД. БД суперэффективны и могут с легкостью обрабатывать параллельные запросы.
  4. Другие вещи (например, централизованное расположение строк соединения JDBC и т. Д.), Но есть миллионы статей, книг и т. Д.

Когда устанавливать соединение

Где-то в стеке вызовов, инициированном в вашем сервисном делегате (doPost, doGet, doDisco и т. Д.), Вы должны установить соединение, а затем сделать правильные вещи и вернуть его в блоке finally. Я должен отметить, что главный архитектор C # однажды сказал, что вы должны использовать finally блоков в 100 раз больше, чем catch блоков. Истинные слова никогда не произносились ...

Какой пул соединений

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

В некоторых комментариях к ответам выше предлагается использовать определенный API-интерфейс пула соединений. Ваша WAR должна быть переносимой и «просто развернутой». Я думаю, что это в основном неправильно. Если вы используете пул соединений, предоставленный вашим контейнером, ваше приложение будет развернуто в контейнерах, которые охватывают несколько компьютеров, и все эти интересные вещи, которые предоставляет спецификация Java EE. Да, дескрипторы развертывания для конкретного контейнера должны быть написаны, но это EE-способ, мон.

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

14 голосов
/ 28 октября 2008

Я на самом деле не согласен с использованием Commons DBCP. Вы действительно должны обратиться к контейнеру, чтобы управлять пулом соединений для вас.

Поскольку вы используете сервлеты Java, это подразумевает работу в контейнере сервлетов и всех основных контейнерах сервлетов, с которыми я знаком, обеспечивают управление пулом соединений (спецификация Java EE может даже потребовать этого). Если ваш контейнер использует DBCP (как это делает Tomcat), то отлично, просто используйте то, что обеспечивает ваш контейнер.

4 голосов
/ 28 октября 2008

Я бы использовал Commons DBCP . Это проект Apache, который управляет пулом соединений для вас.

Вы бы просто получили ваше соединение в doGet или doPost, чтобы выполнить запрос, а затем закрыть соединение в блоке finally. (con.close () просто возвращает его в пул, но не закрывает его).

DBCP может управлять таймаутами соединения и восстанавливаться после них. То, как вы в настоящее время делаете вещи, если ваша база данных отключается на какой-то период времени, вам придется перезапустить приложение.

2 голосов
/ 28 октября 2008

Лучший способ, и я сейчас просматриваю Google для лучшего справочного листа, это использовать пулы.

При инициализации вы создаете пул, содержащий X количество объектов SQL-соединения с вашей базой данных. Храните эти объекты в каком-то списке List, например, ArrayList. Каждый из этих объектов имеет частное логическое значение для isLeased, долгое время его последнего использования и соединение. Всякий раз, когда вам нужно соединение, вы запрашиваете соединение из пула. Пул либо даст вам первое доступное соединение, проверив переменную isLeased, либо создаст новое и добавит его в пул. Обязательно установите метку времени. Как только вы закончите с подключением, просто верните его в пул, который установит isLeased в false.

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

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

2 голосов
/ 28 октября 2008

Вы объединяете свои соединения? Если нет, то вам, вероятно, следует уменьшить накладные расходы на открытие и закрытие соединений.

Раз уж это не так, просто держите соединение открытым до тех пор, пока это необходимо, как предложил Джон.

1 голос
/ 28 октября 2008

Объедините это.

Кроме того, если вы работаете с необработанным JDBC, вы можете заняться чем-то, что поможет вам управлять Connection, PreparedStatement и т. Д. Если у вас нет очень строгих требований к «легковесности», использование поддержки Spring JDBC, например, собирается упростить ваш код много, и вы не обязаны использовать любую другую часть Spring.

См. Несколько примеров здесь:

http://static.springframework.org/spring/docs/2.5.x/reference/jdbc.html

1 голос
/ 28 октября 2008

Пул соединений, связанный с источником данных, должен сделать свое дело. Вы можете получить соединение из источника данных в методе запроса сервлета (doget / dopost и т. Д.).

dbcp, c3p0 и многие другие пулы соединений могут сделать то, что вы ищете. Пока вы объединяете соединения, возможно, вы захотите объединить выписки и PreparedStatements; Кроме того, если вы используете среду READ HEAVY, как вы указали, вы можете кэшировать некоторые результаты, используя что-то вроде ehcache.

BR
~ A

1 голос
/ 28 октября 2008

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

0 голосов
/ 28 октября 2008

Обычно вы обнаружите, что открывать соединения по запросу проще в управлении. Это означает в методе doPost () или doGet () вашего сервлета.

Открытие его в init () делает его доступным для всех запросов и что происходит, когда у вас есть параллельные запросы?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...