【问题标题】:should i activate c3p0 statement pooling?我应该激活 c3p0 语句池吗?
【发布时间】:2011-02-24 15:09:42
【问题描述】:

我们正在运行 java6/hibernate/c3p0/postgresql 堆栈。 我们的 JDBC 驱动是 8.4-701.jdbc3

我有几个关于准备好的陈述的问题。我读过了 关于Prepared Statements的优秀文档

但我仍然有一个问题,如何使用 postgresql 配置 c3p0。

目前我们有

 c3p0.maxStatements = 0
 c3p0.maxStatementsPerConnection  =   0

在我的理解中,prepared statements 和 statement pooling 是两个不同的东西:

我们的休眠堆栈使用准备好的语句。 Postgresql 正在缓存 执行计划。下次使用相同的语句时,postgresql 会重用 执行计划。这节省了在 DB 中规划语句的时间。

另外c3p0可以缓存“java.sql.PreparedStatement”的java实例 这意味着它正在缓存 java 对象。所以当使用
c3p0.maxStatementsPerConnection = 100 它最多缓存 100 个不同的
对象。它节省了创建对象的时间,但这与 postgresql 数据库及其准备好的语句。

对吗?

当我们使用大约 100 种不同的语句时,我会设置 c3p0.maxStatementsPerConnection = 100

但是 c3p0 文档在 c3p0 known shortcomings 中说

Statement pooling 的开销是 太高。对于没有的司机 执行重要的预处理 PreparedStatements,池化 开销超过任何节省。 语句池因此被关闭 默认。如果你的司机这样做 预处理 PreparedStatements, 特别是如果它通过 IPC 使用 RDBMS,您可能会看到一个 显着的性能增益 打开语句池。 (做这个 通过设置配置属性 maxStatements 或 maxStatementsPerConnection 到一个值 大于零。)。

那么:用 c3p0 和 Postgresql 激活 maxStatementsPerConnection 是否合理? 激活它有真正的好处吗?

亲切的问候 詹宁

【问题讨论】:

    标签: postgresql c3p0


    【解决方案1】:

    我不记得 Hibernate 是否实际存储 PreparedStatement 实例本身,或者依赖连接提供程序来重用它们。 (对 BatcherImpl 的快速扫描表明,如果连续多次执行相同的 SQL,它会重用最后一个 PreparedStatement)

    我认为 c3p0 文档试图说明的一点是,对于许多 JDBC 驱动程序,PreparedStatement 没有用:一些驱动程序最终会简单地将参数拼接到客户端,然后将构建的 SQL 语句传递给无论如何数据库。对于这些驱动程序,PreparedStatements 根本没有任何优势,任何重用它们的努力都是白费。 (Postgresql JDBC FAQ 表示在服务器协议版本 3 之前的 Postgresql 就是这种情况,documentation 中有更详细的信息。

    对于确实有效地处理 PreparedStatement 的驱动程序,仍然可能需要实际重用 PreparedStatement 实例以获得任何好处。例如,如果驱动程序实现:

    • Connection.prepareStatement(sql) - 创建服务器端语句
    • PreparedStatement.execute(..) 等 - 执行该服务器端语句
    • PreparedStatement.close() - 解除分配服务器端语句

    鉴于此,如果应用程序总是打开一个准备好的语句,执行一次然后再次关闭它,仍然没有任何好处;事实上,情况可能会更糟,因为现在可能有更多的往返行程。所以应用程序需要挂在 PreparedStatement 实例上。当然,这会导致另一个问题:如果应用程序挂起太多,并且每个服务器端语句都消耗一些资源,那么这可能会导致服务器端问题。在有人直接使用 JDBC 的情况下,这可能由人工管理 - 已知某些语句是可重用的,因此已准备好;有些不是,而是使用瞬态 Statement 实例。 (这跳过了准备好的语句的另一个好处:处理参数转义)

    这就是为什么 c3p0 和其他连接池也有准备好的语句缓存的原因——它允许应用程序代码避免处理所有这些。语句通常保存在一些有限的 LRU 池中,因此常见的语句重用 PreparedStatement 实例。

    难题的最后一部分是 JDBC 驱动程序可能自己决定要聪明并这样做;并且服务器自己也可能决定变得聪明并检测到客户端提交的语句在结构上与之前的语句相似。

    鉴于 Hibernate 本身并不保留 PreparedStatement 实例的缓存,您需要让 c3p0 这样做才能从它们中受益。 (由于重用缓存计划,这应该减少常见语句的开销)。如果 c3p0 没有缓存准备好的语句,那么驱动程序只会看到应用程序准备一个语句,执行它,然后再次关闭它。看起来 JDBC 驱动程序有一个 "threshold" setting 来避免在应用程序总是这样做的情况下准备/执行服务器开销。所以,是的,你需要让 c3p0 做语句缓存。

    希望对您有所帮助,抱歉有点啰嗦。答案是是的

    【讨论】:

    • 很好的答案!通过您的详细解释,我能够进行一些测试。确实:激活 c3p0 语句池是合理的。在我的语句每次都被解析和计划之前,现在它们只绑定和执行。但请注意,如果您的数据发生变化并且旧计划没有达到最佳效果。我还不知道 postgresql 是否会不时重新计划preparedstatements。非常感谢您的帮助!!
    • 很好的答案!实际上,我总是对 PreparedStatement 的实现感到困惑,并怀疑它的好处,就像每个人都告诉我的那样。
    【解决方案2】:

    请记住,每个连接都必须缓存语句,这意味着您将不得不消耗大量内存,并且需要很长时间才能看到任何好处。因此,如果您将其设置为使用 100 条语句进行缓存,这实际上是 100* 连接数或 100/无连接,但您仍需要花费相当长的时间才能使缓存产生任何有意义的效果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-06-02
      • 2017-02-02
      • 1970-01-01
      • 2021-01-06
      • 2013-02-12
      • 2011-08-21
      • 2021-12-25
      相关资源
      最近更新 更多