为什么无法根据已经执行的准备好的语句从池中为连接提供服务?
Why is it impossible to serve connections from pool according to the prepared statement already executed with them?
我一直在网上研究设计连接池的最有效方法,并尝试详细分析可用的库(HikariCP、BoneCP 等)。
我们的应用程序是一个重负载的消费者网络应用程序,大部分时间用户都在处理类似的业务对象(因此执行的底层 SQL 查询通常是相同的,但仍然有很多) .
它设计用于不同的 DBMS(尤其是 Oracle 和 MS SQL 服务器)。
所以一个简化的用例是:
- 用户进入特定的 JSP 页面(例如企业)。
- 创建对应的Bean
- 每次实现一个动作(例如
getEmployees()
、computeTurnover()
),Bean 都会向池请求一个连接,并在完成后 returns 返回。
如果我们想利用底层 JDBC 驱动程序的 Prepared Statement 缓存(因为 PStatements 附加到连接 - jTDS doc.),根据我的理解,最佳的做法是它将是:
- 分析特定 Bean 想要执行哪种类型的 SQL 查询,然后再向其提供来自池的可用连接。
- 如果可能,找到一个已经执行过相同准备语句的连接。
- 相应地提供连接(并利用 cache/precompiled 语句的好处)。
- Return 连接到池并重新开始。
我是否遗漏了重要的一点(例如 JDBC 驱动程序能够重用缓存语句而不管连接如何)或者我的分析是否正确?
不同 sources 我发现状态是不可能的,但为什么?
要使您的方案生效,您需要能够获得已经准备好该语句的连接。
这有两点犯规:
- 在JDBC中先获取连接,
- 缓存的准备好的语句(如果驱动程序或连接池甚至支持它)不会以标准化方式公开(如果有的话),您也无法反省它们。
寻找正确连接的性能开销(以及随后对已经准备好的少数连接的争用)可能会抵消重用准备好的语句的任何好处。
另请注意,一些数据库系统也有一个用于准备好的语句的服务器端缓存(意味着它已经有可用的计划等),限制了来自客户端的新准备的开销。
如果您真的认为性能优势足够大,您应该考虑使用特定于此功能的数据源(因此几乎可以保证连接将在其缓存中包含该语句)。
一个解决方案可能是连接池实现延迟从池中检索连接,直到 Connection.prepareStatement() 被调用。那时,连接池将通过 SQL 语句文本查找可用连接,然后转发在 Connection.prepareStatement() 之前进行的所有调用。这样就可以与准备好的 PreparedStatement 建立联系,而不会出现其他人提出的问题。
换句话说,当您从池中请求连接时,它将 return 一个包装器,记录所有内容,直到第一个需要数据库访问的操作(例如请求 prepareStatement() 被请求。
您需要咨询连接池功能的供应商才能添加此功能。
我用 C3P0 记录了这个请求:
https://github.com/swaldman/c3p0/issues/55
希望对您有所帮助。
我一直在网上研究设计连接池的最有效方法,并尝试详细分析可用的库(HikariCP、BoneCP 等)。
我们的应用程序是一个重负载的消费者网络应用程序,大部分时间用户都在处理类似的业务对象(因此执行的底层 SQL 查询通常是相同的,但仍然有很多) . 它设计用于不同的 DBMS(尤其是 Oracle 和 MS SQL 服务器)。
所以一个简化的用例是:
- 用户进入特定的 JSP 页面(例如企业)。
- 创建对应的Bean
- 每次实现一个动作(例如
getEmployees()
、computeTurnover()
),Bean 都会向池请求一个连接,并在完成后 returns 返回。
如果我们想利用底层 JDBC 驱动程序的 Prepared Statement 缓存(因为 PStatements 附加到连接 - jTDS doc.),根据我的理解,最佳的做法是它将是:
- 分析特定 Bean 想要执行哪种类型的 SQL 查询,然后再向其提供来自池的可用连接。
- 如果可能,找到一个已经执行过相同准备语句的连接。
- 相应地提供连接(并利用 cache/precompiled 语句的好处)。
- Return 连接到池并重新开始。
我是否遗漏了重要的一点(例如 JDBC 驱动程序能够重用缓存语句而不管连接如何)或者我的分析是否正确?
不同 sources 我发现状态是不可能的,但为什么?
要使您的方案生效,您需要能够获得已经准备好该语句的连接。
这有两点犯规:
- 在JDBC中先获取连接,
- 缓存的准备好的语句(如果驱动程序或连接池甚至支持它)不会以标准化方式公开(如果有的话),您也无法反省它们。
寻找正确连接的性能开销(以及随后对已经准备好的少数连接的争用)可能会抵消重用准备好的语句的任何好处。
另请注意,一些数据库系统也有一个用于准备好的语句的服务器端缓存(意味着它已经有可用的计划等),限制了来自客户端的新准备的开销。
如果您真的认为性能优势足够大,您应该考虑使用特定于此功能的数据源(因此几乎可以保证连接将在其缓存中包含该语句)。
一个解决方案可能是连接池实现延迟从池中检索连接,直到 Connection.prepareStatement() 被调用。那时,连接池将通过 SQL 语句文本查找可用连接,然后转发在 Connection.prepareStatement() 之前进行的所有调用。这样就可以与准备好的 PreparedStatement 建立联系,而不会出现其他人提出的问题。
换句话说,当您从池中请求连接时,它将 return 一个包装器,记录所有内容,直到第一个需要数据库访问的操作(例如请求 prepareStatement() 被请求。
您需要咨询连接池功能的供应商才能添加此功能。
我用 C3P0 记录了这个请求:
https://github.com/swaldman/c3p0/issues/55
希望对您有所帮助。