应用程序中的查询运行时间差异很大

时间:2012-02-02 17:17:26

标签: postgresql prepared-statement postgresql-performance

我在使用PostgreSQL 9后端的应用程序时遇到了扩展问题。我有一个表,其大小约为4000万条记录并且正在增长,并且针对它的条件查询已经大幅放缓。

为了帮助弄清楚出了什么问题,我已经获取了数据库的开发快照,并将带有执行时间的查询转储到日志中。

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

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

我说'确实',但实际上不同之处在于,应用程序正在使用预处理语句,我在运行时绑定值,而我在DbVisualizer中运行的查询已经具有这些值。值本身就像我从日志中提取它们一样。

准备好的陈述的使用是否会产生重大影响?

2 个答案:

答案 0 :(得分:3)

答案是。准备好的声明可以双管齐下。

一方面,不必为每次执行重新计划查询,从而节省了一些开销。这可能会产生影响或难以察觉,具体取决于查询的复杂程度。

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

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

另请考虑 未命名的预备语句 ,例如@peufeu provided。那些每次都考虑参数重新规划查询 - 你仍然有安全的参数处理。

类似的注意事项适用于PL / pgSQL函数内部的查询,其中查询可以在内部被视为预处理语句 - 除非使用EXECUTE动态执行。我引用了Executing Dynamic Commands上的手册:

  

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

除此之外,general guidelines for performance optimization适用。

答案 1 :(得分:3)

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

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

  

当命名的准备语句对象的查询计划发生时   处理解析消息。如果将重复执行查询   使用不同的参数,发送单个可能是有益的   解析包含参数化查询的消息,后跟多个   绑定和执行消息。这将避免重新计算查询   每次执行。

     

未命名的预备声明同样是在Parse期间计划的   如果Parse消息没有定义参数,则进行处理。但如果有的话   是参数,每次绑定参数时都会发生查询计划   提供。这允许计划者使用实际值   每个Bind消息提供的参数,而不是使用泛型   估计。

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

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