12

我正在使用 JDBC 与我的 Postgres 数据库通信。如果我的整个应用程序只运行一个连接,即只有一个调用;

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

但是这个Connection对象在 Java 中的多个线程之间共享,我是否正确地假设任何使用 SQL 事务(BEGINCOMMIT样式)的尝试只会非常混乱和破坏,因为 Java 线程有可能交错?对象是否Connection“知道”哪个 Java 线程正在使用它进行查询?

我应该Connection每个 Java 线程有一个对象并以这种方式使用 SQL 事务吗?或者我应该使用 Java 在 Java 中执行所有事务隔离synchronized吗?

4

3 回答 3

14

只是为了详细说明现有的答案:

PgJDBC 的Connection对象是线程安全的,但仅限于语句级别。在自动提交模式下被多个线程使用时,它不会崩溃或产生错误的结果,但它不会为您隔离不同线程的事务。根据文档,您需要为此使用连接池。

实际上有很多方法可以在多个线程之间使用连接:

  • 使用内部连接池,您可以从中获取连接、处理它们并将它们返回到池中。对于大多数应用程序来说,这是非常可取的选择。Java 存在许多 JDBC 连接池实现,因此不要自行开发。dbcpc3p0是两种流行的实现,但如果您使用的是 servlet 环境或应用服务器,您通常应该使用服务器的连接池,而不是自带连接池。

  • 使用 pgbouncer 或 pgpool-II 之类的外部连接池并自由打开/关闭与它的连接。这会稍微慢一些,并且主要是在应用程序不能或由于各种原因不应该在内部池连接时使用的选项。除非您需要限制与数据库的总连接数并在多个应用程序或应用程序实例之间共享它们,否则您可能不需要这样做。

  • 不使用池并自由打开/关闭连接。这是非常低效的。不要这样做。

  • 使用线程本地存储保持每个线程的连接。这会起作用,但效率极低,因为每个打开的连接都会在空闲时占用数据库服务器资源。除非你在事务池模式下使用像 PgBouncer 这样的外部连接池,否则不要这样做,在这种情况下是可以的。

  • 仅使用单个连接并将事务包装在块中,在实例synchronized上同步。Connection这将起作用并且将有效地使用数据库连接,但会限制线程的性能。除了玩具/便利应用程序之外,它通常不是一个好的设计。

  • 仅使用具有自己专用线程的单个连接。让其他连接通过 FIFO 队列、生产者/消费者样式将描述要完成的工作的数据结构传递给该线程。如果线程将大部分时间花在 CPU 密集型或其他非数据库工作上,并且只需要有限的数据库访问,则此方法有效。几乎使用它而不是使用连接池的唯一原因是,如果您由于某些外部原因而被限制使用单个连接,但如果您是这样,那么它可能是一个不错的选择。

但是,通常,您应该只使用连接池并完成它。

于 2013-03-26T23:58:23.333 回答
5

考虑到 Java 线程交错的可能性,我是否正确地假设任何使用 SQL 事务(BEGINCOMMIT样式)的尝试只会变得非常混乱和破坏?

这是完全正确的。

Connection 对象是否“知道”哪个 Java 线程正在使用它进行查询?

不,不是的。

我应该每个 Java 线程有一个 Connection 对象并以这种方式使用 SQL 事务吗?

是的,这是一种方法。“每个线程的连接”分配的缺点是可能会打开比您需要的更多的连接,从而导致资源的次优使用。您还可以仅在线程需要时打开连接,并在线程完成 RDBMS 访问后关闭它。如果你走这条路,请确保使用连接池来减少多次重新打开连接的开销。

于 2013-03-26T16:25:14.640 回答
2
Does the Connection object 'know' which Java thread is using it to make queries?

不,连接对象不知道哪个 java 线程正在使用它。

我应该每个 Java 线程有一个 Connection 对象并以这种方式使用 SQL 事务吗?或者我应该使用同步在 Java 中执行所有事务隔离?

我们应该使用数据库特定的 jdbc 连接池数据源来做事务,这样当一个连接对象没有用时,连接会回到池中而不被垃圾收集。这样,应用服务器可以优化它的连接初始化性能。

此外,您应该在更新操作期间使用同步方法调用,因为这将使生产中的操作更加安全。

于 2013-03-26T16:35:19.870 回答