JDBC Transaction vs Connection Clarification

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

DriverManager.getConnection("jdbc:postgresql://host:5432/database", user, pass);

Но этот объект Connection является общим для нескольких потоков в Java, я прав, полагая, что любая попытка использования транзакций SQL (BEGIN и COMMIT) будет только очень запутанной и сломанной, учитывая возможность чередования потоков Java? Объект Connection "знает", какой поток Java использует его для создания запросов?

Должен ли я иметь один объект Connection для потока Java и использовать транзакции SQL таким образом? Или я должен выполнить всю свою транзакционную изоляцию в Java с помощью synchronized?

Ответ 1

Чтобы подробно рассказать о существующих ответах:

Объект PgJDBC Connection является потокобезопасным, но только на уровне инструкций. Это не приведет к сбою или возникновению неправильного результата при использовании несколькими потоками в режиме autocommit, но не будет изолировать транзакции разных потоков для вас. В соответствии с документацией для этого вам нужно использовать пул соединений.

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

  • Используйте внутренний пул соединений, в котором вы получаете соединения, выполняете с ними работу и возвращаете их в пул. Это наиболее предпочтительный вариант для большинства приложений. Для Java существует множество реализаций пула соединений JDBC, поэтому не сворачивайте их самостоятельно. dbcp и c3p0 - две популярные реализации, но если вы используете сервлет-среду или сервер приложений, вы обычно должны использовать пул соединений с сервером, а не приносить свои собственные.

  • Используйте внешний пул соединений, например pgbouncer или pgpool-II, и свободно открывайте/закрывайте его. Это немного медленнее, и в основном это опция, используемая там, где приложение не может или по разным причинам не должно объединять соединения внутри. Вам, вероятно, не нужно это делать, если вам не нужно ограничивать общее количество подключений к БД и делиться ими между несколькими приложениями или экземплярами приложения.

  • Не используйте пул и открывайте/закрывайте соединения свободно. Это ужасно неэффективно. Не делайте этого.

  • Поддерживайте соединение в потоке, используя локальное хранилище потоков. Это будет работать, но оно крайне неэффективно, потому что каждое открытое соединение связывает ресурсы сервера базы данных, когда он сидит без дела. Не делайте этого, если вы не используете внешний пул соединений, например PgBouncer, в режиме пула транзакций, и в этом случае это нормально.

  • Используйте только одно соединение и завершайте транзакции в блоках synchronized, синхронизируя в экземпляре Connection. Это будет работать и будет эффективно использовать соединение с базой данных, но ограничит производительность ваших потоков. Это вообще не хороший дизайн ни для чего, кроме игрушек/удобных приложений.

  • Используйте только одно соединение со своим выделенным потоком. Другие подключения передают структуры данных, описывающие работу над этим потоком через очередь FIFO, стиль производителя/потребителя. Это работает, если потоки тратят большую часть своего времени на работу с процессором или другую работу без базы данных и нуждаются только в ограниченном доступе к базе данных. По большей части единственная причина использовать его вместо использования пула соединений - это если вы ограничены использованием одного соединения по какой-то внешней причине, но если вы тогда, то это может быть достойным вариантом.

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

Ответ 2

Правильно ли я полагаю, что любая попытка использовать транзакции SQL (стиль BEGIN и COMMIT) будет только очень запутанной и сломанной, учитывая возможность чередования потоков Java?

Это абсолютно правильно.

Объект Connection "знает", какой поток Java использует его для создания запросов?

Нет, это не так.

Должен ли я иметь один объект Connection в потоке Java и использовать транзакции SQL таким образом?

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

Ответ 3

Does the Connection object 'know' which Java thread is using it to make queries?

Нет, объект подключения не знает, какой поток java использует его.

Должен ли я иметь один объект Connection в потоке Java и использовать транзакции SQL таким образом? Или я должен выполнить всю свою транзакционную изоляцию в Java с помощью синхронизации?

Мы должны использовать базу данных jdbc для пула данных, специфичную для базы данных, для выполнения транзакции, поэтому, когда объект соединения бесполезен, соединение возвращается в пул без сбора мусора. Таким образом, сервер приложений может оптимизировать производительность инициализации соединения.

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