准备好的语句以及连接池
Posted
技术标签:
【中文标题】准备好的语句以及连接池【英文标题】:Prepared Statements along with Connection Pooling 【发布时间】:2011-08-30 23:21:46 【问题描述】:我对 Prepared Statement 和连接池的一般使用有疑问。
Prepared Statements 通常只绑定到一个连接。在我们的应用程序中,PreparedStatement 在启动时创建并在稍后执行。
如果在执行某些特定的预准备语句时,与预准备语句相关联的连接正忙于执行其他语句,而不是该所需语句将如何执行。此语句将等待连接获得空闲还是此语句将执行优先?
更新
我已经通过使用 Apache derby 数据库的 SLEEP() 函数对此进行了测试,该数据库调用了 TimeHandlingTest 类中的 java 函数 sleep。
CREATE FUNCTION SLEEP() 返回整数语言 JAVA 参数样式 JAVA NO SQL 外部名称 'com.derby.test.TimeHandlingTest.sleep';
并从一个连接创建两个准备好的语句,并从一个准备好的语句和简单的 sql select 调用 Sleep() 函数。简单的 sql 选择几乎与第一个准备好的语句休眠的时间相同(10 秒)。这意味着一个连接对象一次不能用于执行多个准备好的语句。如果我错了,请纠正我。
【问题讨论】:
您的代码是否在应用服务器中运行?当您使用 DataSource 而不是 DriverManager 来获取连接时,答案会有所不同。 @Vineet:我们的应用程序是一个简单的 java 应用程序,它没有在应用程序服务器上运行。我们正在从驱动程序管理器获取连接。 【参考方案1】:如果您打算使用 PreparedStatement
,则不能将 Connection
返回到池中。
换句话说:您只能使用从您当前拥有的Connection
构造的PreparedStatement
。
【讨论】:
这似乎不是一个合理的解决方案,因为我的应用程序需要尽可能多的连接作为准备好的语句。 这不是解决方案,而是要求!如果你不遵循这一点,那么你迟早会遇到麻烦。【参考方案2】:PreparedStatement 的价值在于数据库本身能够为语句创建一个执行计划,该执行计划可以重复用于任意参数,因此是 本质上是通用的(当然这需要您在语句中使用参数,例如
PreparedStatement pstmt = con.prepareStatement("UPDATE EMPLOYEES
SET SALARY = ? WHERE ID = ?");
pstmt.setBigDecimal(1, 153833.00);
pstmt.setInt(2, 110592);
另一方面,如果您使用字符串连接将参数值粘贴到 SQL 代码中,则数据库将无法构建通用执行计划。因此,如果您使用 PreparedStatement 或 Statement 没有区别,例如
PreparedStatement pstmt = con.prepareStatement("UPDATE EMPLOYEES
SET SALARY = 1200 WHERE ID = 3");
无法利用 PreparedStatements 的优势。
您的问题暗示,您想重用 PreparedStatement 对象,这不是必需的。当然,如果您可以使用 PreparedStatement 对象来更新多个值等,则可以更有效地使用资源。尽管如此,PreparedStatement 的生命周期(或至少有用的生命周期)与连接相关,因此如果您调用 conn.close(),PreparedStatement 将变得无用。尽管如此,在池化情况下,大多数优秀的驱动程序都会再次重用相同的 PreparedStatement 对象。 简而言之,不要独立于连接缓存 PreparedStatement。
【讨论】:
感谢您的回答,但我的主要问题是,如果与我的准备好的语句相关联的连接忙于运行任何其他准备好的语句,我的准备语句将如何运行。我们可以运行多个准备好的语句吗?在某个时间点连接?\【参考方案3】:假设这是一个多线程应用程序,Connection
对象通常在任何时刻都与单个线程相关联。 Connection
线程获取的对象在关闭之前不会返回到池中。这既适用于应用程序的逻辑连接包装器(通常由应用程序服务器管理的 DataSource 返回),也适用于物理连接。此外,物理连接可以在多个逻辑连接之间共享,只要它们是同一事务的一部分。
这意味着,如果将逻辑连接句柄返回给您的应用程序,则底层物理连接不必相同并且正在竞争(除非它是同一事务的一部分)。如果您的应用程序希望能够轻松处理并发用户,则将在每个启动事务的线程中创建一个Connection
对象,并且不会跨线程争用该对象。在底层,池中的不同物理连接将跨多个线程执行与准备好的语句关联的 SQL 查询,同样没有任何争用。
【讨论】:
感谢您的回答。根据我的理解,PreparedStatements 是根据物理连接而不是逻辑连接缓存的。如果该物理连接已经忙于执行语句,那么我们可以运行另一个准备好的语句相同的物理连接,还是我们需要使用池中可用的空闲连接重新创建这个准备好的语句。【参考方案4】:这听起来像是使用连接池的一种不同寻常的方式。即使连接在池中,它们一次也只能由一个线程使用。我倾向于创建准备好的语句,并在非常接近创建点的地方使用它。此外,一些 JDBC 驱动程序现在支持语句缓存,从而减少了以这种方式使用它的开销。
【讨论】:
【参考方案5】:解决此问题的一种方法是维护一个缓存,将连接映射到准备好的语句。当您从池中获取连接时,请检查它是否映射到要执行的准备好的语句。如果不是,则将准备好的语句传递给 JDBC 驱动程序以便编译它。然后将其映射到连接。这种方法的缺点是不止一个连接可能会获得同一个准备好的语句的副本。不过好像this is what some J2EE servers do.
【讨论】:
以上是关于准备好的语句以及连接池的主要内容,如果未能解决你的问题,请参考以下文章
DBCP(Apache Commons 数据库连接池)是不是仍然相关?