在 PostgreSQL 中显示基数估计错误的示例查询
Posted
技术标签:
【中文标题】在 PostgreSQL 中显示基数估计错误的示例查询【英文标题】:Sample Query to show Cardinality estimation error in PostgreSQL 【发布时间】:2014-09-20 01:19:09 【问题描述】:我正在开发一个使用 PostgreSQL9.3 的项目。我使用下面的查询来展示选择性估计错误如何导致使用 PostgreSQL8.3 在 TPC-H 工作负载上的查询执行时间增加数倍。
select
n_name,
sum(l_extendedprice * (1 - l_discount)) as revenue
from
customer,
orders,
lineitem,
supplier,
nation,
region
where
c_custkey = o_custkey
and l_orderkey = o_orderkey
and l_suppkey = s_suppkey
and c_nationkey = s_nationkey
and s_nationkey = n_nationkey
and n_regionkey = r_regionkey
and (r_name='ASIA' or r_name='AFRICA')
and o_orderdate >= date '1994-01-01'
and o_orderdate < date '1994-01-01' + interval '1 year'
and l_shipdate <= l_receiptdate
and l_commitdate <= l_shipdate + integer '90'
and l_extendedprice <= 20000
and c_name like '%r#00%'
and c_acctbal <=2400
group by
n_name
order by
revenue desc
问题在于 PostgreSQL8.3 选择了一个涉及大量 NestedLoop 连接的计划,因为对 lineitem 和客户的选择性估计错误很大。我认为这主要是由于 LIKE 模式匹配。但最佳方案应该是使用 Hash Joins。
最近我为我的项目升级到 PostgreSQL9.3 并观察到上述查询不再给出错误的计划。我花了一些时间试图在 TPC-H 1GB 数据上找到具有较大 基数估计错误 的查询,但到目前为止还没有成功。是否有任何 PostgreSQL 极客知道 TPC-H 基准测试中的一些现成查询或任何显示 PostgreSQL9.3 中基数估计错误的查询
【问题讨论】:
很遗憾,您的问题的答案是否定的,尽管我遇到过它们。您正在使用 pre ansi-92 标准进行连接...我记得在 8.x 的日子里,我偶尔会得到非常糟糕的执行计划,而重写为 ansi-92 语法改进了它......我认为 9.3 改进了它能够处理这种语法 问题可能适合 dba.SE。 @Twelfth:对于 Postgres 查询计划器,WHERE
子句和 INNER JOIN
子句在内部都是相同的,AFAIK。显式连接语法仍然更易于阅读和维护。
@ErwinBrandstetter - 我也是这么想的,但是我之前在 where 子句连接上提出了一些非常糟糕的执行计划……不过那是几年前的事了。
@Twelfth:我提供了一个答案来解决这个问题。
【参考方案1】:
这是为了回答comment by @Twelfth以及问题本身。
Three quotes from this chapter in the manual:
"Controlling the Planner with Explicit JOIN
Clauses"
显式内连接语法(
INNER JOIN
、CROSS JOIN
或朴素的JOIN
) 与在FROM
中列出输入关系语义相同,所以它 不限制连接顺序。
...
强制规划器遵循明确规定的连接顺序
JOIN
s,将join_collapse_limit
运行时参数设置为1。(其他 下面讨论可能的值。)
...
以这种方式约束规划器的搜索是一种有用的技术 既可以减少计划时间,也可以将计划者引导至 好的查询计划。
我的大胆强调。相反,您可以滥用相同的方法将查询计划器引导到 bad 查询计划以进行测试。阅读整个手册页。它应该是有用的。
此外,您可以通过disabling alternative methods 逐一强制嵌套循环(最好只在您的会话中使用)。喜欢:
SET enable_hashjoin = off;
等 关于检查和设置参数:
Query a parameter (postgresql.conf setting) like "max_connections"强制实际估计误差
一种明显的方法是禁用autovacuum 并从表中添加/删除行。然后查询计划器正在处理过时的统计信息。请注意,其他一些命令也会更新统计信息。
统计信息存储在目录表pg_class
和pg_statistics
。
SELECT * FROM pg_class WHERE oid = 'mytable'::regclass;
SELECT * FROM pg_statistic WHERE starelid = 'mytable'::regclass;
这让我有了另一个选择。您可以在这两个表中伪造条目。需要超级用户权限。 您并不认为我是新手,而是对公众的警告:如果您破坏了目录表中的某些内容,您的数据库(集群)可能会崩溃。您已收到警告。
【讨论】:
喜欢你的回答。评论者可以接受答案吗?呵呵 @Twelfth:猜猜你刚刚做到了。 :) @Erwin:我知道使用 GUC 配置参数我们可以禁用一些连接或扫描,并使优化器选择一个错误的计划。但这不是我想做的。我想要一个查询来显示基数估计错误及其对优化器的影响。你对此有什么想法吗? @ErwinBrandstetter:感谢您的补充意见。如您所料,我在使用 PostgreSQL 源代码方面有相当多的经验。您提到的统计数据覆盖机制很好。但不幸的是,这并不能解决我的问题。尽管有所有最新的统计数据,但我对优化器所产生的真实基数估计错误很感兴趣。 @TheGame:这是一个非常具体的问题。如果我知道这个问题的答案,我最好提交一份错误报告——除非它是一个无法修复的错误。什么都想不出来……以上是关于在 PostgreSQL 中显示基数估计错误的示例查询的主要内容,如果未能解决你的问题,请参考以下文章
SQL Server中关于基数估计如何计算预估行数的一些探讨