多个线程并发使用相同的 JDBC 连接

Concurrent use of same JDBC connection by multiple threads

我试图更好地理解如果多个线程尝试同时使用相同的 JDBC 连接执行不同的 sql 查询会发生什么。


我看到 Apache DBCP 使用同步协议来确保从池中获得的连接从池中删除,并使其不可用,直到它们关闭。这似乎比它需要的更不方便。我正在考虑通过创建打开连接的静态列表并以循环方式分发它们来构建我自己的 "pool"。

我不介意偶尔的性能下降,每次使用后不必关闭连接的便利性似乎非常吸引人。我这样做有什么缺点吗?

以错误的方式做事会产生未定义的结果...如果有人 运行 进行了一些测试,也许他们会准确地回答您所有的问题,但是随后出现了新的 JVM,或者有人尝试了在另一个 jdbc 驱动程序或数据库版本上,或者他们遇到了一组不同的竞争条件,或者尝试了另一个平台或 JVM 实现,并且发生了另一个不同的未定义结果。

如果两个线程同时修改同一个状态,根据时间的不同,任何事情都可能发生。也许第二个覆盖第一个的查询,然后 运行 覆盖相同的查询。也许库会检测到您的错误并抛出异常。我不知道,也不会费心测试...(或者也许有人已经知道,或者很明显会发生什么)所以这不是 "the answer",而只是一些建议。只需使用连接池,或使用同步块以确保不会发生问题。

由于 JDBC 规范不保证并发执行,这个问题只能通过测试您感兴趣的驱动程序或阅读它们的源代码来回答。

在 MySQL Connector/J 的情况下,execute 语句的所有方法都使用 synchronized 块锁定连接。也就是说,如果一个线程正在 运行 查询,则使用该连接的其他线程将被阻塞,直到它完成。

我们不得不禁用 Websphere 上的语句缓存,因为它在 PreparedStatement 级别抛出 ArrayOutOfBoundsException。 问题是有些人虽然与多个线程共享连接很聪明。 他说这是为了保存连接,但是多线程查询没有意义,因为数据库不会 运行 它们并行。

java 运行nable 也存在问题,因为它们使用相同的连接而互相阻塞。

所以那只是不做,没有什么可得的。

websphere 中有一个选项可以检测这种多线程访问。 因为我们在开发中使用码头,所以我自己实现了。

我 运行 下面的一组测试使用 AWS RDS Postgres 数据库,Java 11:

  1. 创建一个包含 1100 万行的 table,每行包含一个 TEXT 列,填充一个 运行dom 100 个字符的字符串

  2. 选取一个运行dom 5个字符串,搜索这个字符串的部分匹配,在上面table

  3. 上述查询需要多长时间才能获得 return 结果。就我而言,大约需要 23 秒。因为结果很少 returned,我们可以得出结论,这 23 秒的大部分时间都花在等待数据库 运行 完整-table-扫描,而不是发送request/response 个数据包

  4. 运行 多个并行查询(使用不同的关键字),使用 不同的连接 。就我而言,我看到它们都在大约 23 秒内完成。即,查询正在 高效并行化

  5. 运行 并行线程上的多个查询,使用相同的连接。我现在看到第一个结果在大约 23 秒后返回。第二个结果在大约 46 秒后返回。第三个在大约 1 分钟内。等等等等。所有结果在功能上都是正确的,因为它们匹配该线程查询的特定关键字

补充一下 Joni 之前提到的内容,他的结论也符合我在 Postgres 上看到的行为。如果同时在同一连接上发送多个查询,似乎 所有“正确性”都得到保留,但所有并行性优势都会丢失