JDBC: Могу ли я разделить соединение в многопоточном приложении и наслаждаться приятными транзакциями? - PullRequest
5 голосов
/ 14 ноября 2008

Кажется, что классический способ обработки транзакций с JDBC - установить для автоматической фиксации значение false. Это создает новую транзакцию, и каждый вызов commit отмечает начало следующих транзакций. В многопоточном приложении я понимаю, что обычной практикой является открытие нового соединения для каждого потока.

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

Ответы [ 3 ]

4 голосов
/ 14 ноября 2008

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

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

  • Тема 1 делает запрос
  • Тема 2 делает запрос
  • Поток 1 читает байты из сокета, невольно читая ответ от запроса потока 2

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

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

обновление: Чтобы ответить на ваш вопрос в комментарии, большинство брендов баз данных не поддерживают несколько одновременных транзакций для одного соединения (InterBase / Firebird - единственное исключение, о котором я знаю).

Было бы неплохо иметь отдельный объект транзакции и иметь возможность запускать и фиксировать несколько транзакций для одного соединения. Но производители просто не поддерживают это.

Аналогичным образом, стандартные независимые от поставщика API, такие как JDBC и ODBC, предполагают, что состояние транзакции является просто свойством объекта соединения.

1 голос
/ 02 октября 2011

Один и тот же объект соединения может использоваться для создания нескольких объектов операторов, и эти объекты операторов могут одновременно использоваться различными потоками. Большинство современных БД с интерфейсом JDBC могут это сделать. Таким образом, JDBC может использовать параллельные курсоры следующим образом. PostgreSQL здесь не исключение, см. Например:

http://doc.postgresintl.com/jdbc/ch10.html

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

1. Thread 1 opens statement   
3. Thread 2 opens statement
4. Thread 1 does something         Thread 2 does something
5. ...                             ...
6. Thread 1 closes statement       ...
                                7. Thread 2 closes statement

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

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

1 голос
/ 14 ноября 2008

Это необычная практика - открывать новое соединение для каждого потока. Обычно вы используете пул соединений, например, c3po library.

Если вы находитесь на сервере приложений или, например, используете Hibernate, посмотрите документацию и вы найдете, как настроить пул соединений.

...