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

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

  • 结果是否在function上正确?

  • 性能影响是什么?

  • 线程A是否必须等待线程B完全使用其查询?

  • 或者线程A能否在线程B发送其查询后立即发送其查询,之后数据库将并行执行两个查询?


我看到Apache DBCP使用同步协议来确保从池中删除的连接从池中删除,并且在它们关闭之前不可用。 这似乎比它需要的更不方便。 我正在考虑构建自己的“池”,只需创建一个静态的打开连接列表,并以循环方式分发它们。 我不介意偶尔的性能下降,并且每次使用后不必关闭连接的便利性似乎非常吸引人。 这样做有什么不好吗?

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

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

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

如果两个线程同时修改相同的状态,则可能会发生任何事情,具体取决于时间。 也许第二个覆盖第一个查询,然后两个都运行相同的查询。 也许库会检测到您的错误并抛出exception。 我不知道也不会打扰测试…(或者可能有人已经知道或者应该很明显会发生什么)所以这不是“答案”,而只是一些建议。 只需使用连接池,或使用synchronized块来确保不会发生问题。

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

由于他们使用相同的连接,因此java runnables也存在阻塞彼此的问题。

所以这只是不做的事情,没有什么可以获得的。

websphere中有一个选项可以检测此multithreading访问。 我实施了自己的,因为我们在开发中使用jetty。

我使用AWS RDS Postgres数据库和Java 11运行以下测试集:

  1. 创建一个包含11M行的表,每行包含一个TEXT列,并填充一个随机的100-char字符串

  2. 选择一个随机的5个字符的字符串,并在上表中搜索该字符串的部分匹配项

  3. 时间上述查询返回结果所需的时间。 在我的情况下,它需要约23秒。 因为返回的结果非常少,我们可以得出结论,这23秒的大部分用于等待DB运行全表扫描,而不是发送请求/响应数据包

  4. 使用不同的连接并行运行多个查询(使用不同的关键字)。 就我而言,我发现它们都在~23秒内完成。 即,查询被有效地并行化

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

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