18

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

我有几个关于准备好的陈述的问题。我已经阅读了关于准备好的陈述的优秀文件

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

目前我们有

 c3p0.maxStatements = 0
 c3p0.maxStatementsPerConnection  =   0

据我了解,准备好的语句和语句池是两个不同的东西:

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

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

对?

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

但是 c3p0 文档在c3p0 中说已知的缺点

Statement pooling 的开销太高。对于未对 PreparedStatements 执行大量预处理的驱动程序,池化开销超过了任何节省。语句池因此默认关闭。如果您的驱动程序确实对 PreparedStatements 进行了预处理,特别是如果它通过 IPC 和 RDBMS 进行预处理,那么您可能会通过打开 Statement pooling 看到显着的性能提升。(通过将配置属性 maxStatements 或 maxStatementsPerConnection 设置为大于零的值来执行此操作。)。

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

亲切的问候詹宁

4

2 回答 2

26

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

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

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

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

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

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

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

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

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

于 2010-05-27T13:07:21.707 回答
2

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

于 2010-06-23T10:12:32.580 回答