我在使用PostgreSQL 9后端的应用程序时遇到了扩展问题。我有一个表,其大小约为4000万条记录并且正在增长,并且针对它的条件查询已经大幅放缓。
为了帮助弄清楚出了什么问题,我已经获取了数据库的开发快照,并将带有执行时间的查询转储到日志中。
现在是令人困惑的部分,以及问题的要点......
我在日志中查询的运行时间与我在DbVisualizer中运行'完全'相同的查询以获得解释计划时得到的结果大不相同(一个数量级+)。
我说'确实',但实际上不同之处在于,应用程序正在使用预处理语句,我在运行时绑定值,而我在DbVisualizer中运行的查询已经具有这些值。值本身就像我从日志中提取它们一样。
准备好的陈述的使用是否会产生重大影响?
答案 0 :(得分:3)
答案是是。准备好的声明可以双管齐下。
一方面,不必为每次执行重新计划查询,从而节省了一些开销。这可能会产生影响或难以察觉,具体取决于查询的复杂程度。
另一方面,由于数据分布不均匀,一个通用的查询计划可能是一个糟糕的选择。使用特定值调用另一个查询计划可能(更多)更适合。
运行具有参数值的查询可能会导致不同的查询计划。更多的计划开销,可能是一个(更好)更好的查询计划。
另请考虑 未命名的预备语句 ,例如@peufeu provided。那些每次都考虑参数重新规划查询 - 你仍然有安全的参数处理。
类似的注意事项适用于PL / pgSQL函数内部的查询,其中查询可以在内部被视为预处理语句 - 除非使用EXECUTE
动态执行。我引用了Executing Dynamic Commands上的手册:
重要的区别是
EXECUTE
将重新规划命令 每次执行,生成一个特定于当前的计划 参数值;而PL / pgSQL可能会创建一个通用计划 并缓存它以便重复使用。在最佳计划取决于的情况下 强烈关注参数值,使用EXECUTE
会很有帮助 积极确保未选择通用计划。
答案 1 :(得分:3)
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使用未命名的预准备语句。通常一个比另一个快,这取决于参数数据的大小。