2

我在使用 PostgreSQL 9 后端的应用程序中遇到了扩展问题。我有一张表,其大小约为 4000 万条记录,并且还在不断增长,并且针对它的条件查询已大大减慢。

为了帮助找出问题所在,我拍摄了数据库的开发快照,并将查询和执行时间转储到日志中。

现在是令人困惑的部分,以及问题的要点....

我在日志中的查询的运行时间与我在 DbVisualizer 中运行“完全相同”的查询以获取解释计划时得到的有很大不同(一个数量级+)。

我说的是“精确”,但真正的区别在于,应用程序使用了一个准备好的语句,我在运行时将值绑定到该语句,而我在 DbVisualizer 中运行的查询已经有了这些值。这些值本身与我从日志中提取的完全相同。

使用准备好的语句能有那么大的不同吗?

4

2 回答 2

3

答案是肯定的。准备好的陈述是双向的。

一方面,不必为每次执行重新计划查询,从而节省了一些开销。根据查询的复杂性,这可能会有所不同或几乎不会引起注意。

另一方面,由于数据分布不均匀,一刀切的查询计划可能是一个糟糕的选择。使用特定值调用另一个查询计划可能(非常)更适合。

使用适当的参数值运行查询可能会导致不同的查询计划。更多的计划开销,可能是一个(很多)更好的查询计划。

还要考虑像@peufeu 提供的未命名的准备好的语句。那些每次都考虑参数重新计划查询 - 您仍然可以安全地处理参数。

类似的考虑适用于 PL/pgSQL 函数内部的查询,其中查询可以在内部被视为准备好的语句 - 除非使用EXECUTE. 我引用了关于执行动态命令的手册:

重要的区别在于EXECUTE会在每次执行时重新计划命令,生成特定于当前参数值的计划;而 PL/pgSQL 可能会创建一个通用计划并将其缓存以供重用。在最佳计划强烈依赖于参数值的情况下,使用它EXECUTE来积极确保不选择通用计划会很有帮助。

除此之外,还适用性能优化的一般准则

于 2012-02-02T17:54:52.193 回答
3

Erwin 指出了这一点,但让我补充一点,扩展查询协议允许您使用更多风格的预准备语句。除了避免重新解析和重新规划之外,prepared statements 的一大优点是单独发送参数值,这避免了转义和解析开销,更不用说如果你不使用处理的 API 的 SQL 注入和错误的机会参数以一种你不能忘记逃避它们的方式。

http://www.postgresql.org/docs/9.1/static/protocol-flow.html

处理 Parse 消息时会发生命名准备语句对象的查询计划。如果查询将使用不同的参数重复执行,则发送包含参数化查询的单个 Parse 消息,然后发送多个 Bind 和 Execute 消息可能会有所帮助。这将避免在每次执行时重新计划查询。

如果 Parse 消息未定义任何参数,则在 Parse 处理期间同样计划未命名的准备好的语句。但如果有参数,则每次提供 Bind 参数时都会进行查询计划。这允许规划器使用每个 Bind 消息提供的参数的实际值,而不是使用通用估计值。

因此,如果您的数据库接口支持它,您可以使用未命名的预准备语句。这是查询和通常的准备语句之间的中间立场。

如果你在 PDO 中使用 PHP,请注意 PDO 的prepared statement 实现对于 postgres 来说是相当无用的,因为它使用命名的prepared statements,但每次调用 prepare() 时都会重新准备,不会发生计划缓存。所以你得到了两者中最糟糕的:许多往返和没有参数的计划。我已经看到它比 pg_query() 和 pg_query_params() 在 postgres 优化器确实需要知道参数以生成最佳计划的特定查询上慢 1000 倍。pg_query 使用原始查询,pg_query_params 使用未命名的预处理语句。通常一个比另一个快,这取决于参数数据的大小。

于 2012-02-02T21:59:26.093 回答