在 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 JOINCROSS JOIN 或朴素的JOIN) 与在FROM 中列出输入关系语义相同,所以它 不限制连接顺序。

...

强制规划器遵循明确规定的连接顺序 JOINs,将join_collapse_limit运行时参数设置为1。(其他 下面讨论可能的值。)

...

以这种方式约束规划器的搜索是一种有用的技术 既可以减少计划时间,也可以将计划者引导至 好的查询计划

我的大胆强调。相反,您可以滥用相同的方法将查询计划器引导到 bad 查询计划以进行测试。阅读整个手册页。它应该是有用的。

此外,您可以通过disabling alternative methods 逐一强制嵌套循环(最好只在您的会话中使用)。喜欢:

SET enable_hashjoin = off;

等 关于检查和设置参数:

Query a parameter (postgresql.conf setting) like "max_connections"

强制实际估计误差

一种明显的方法是禁用autovacuum 并从表中添加/删除行。然后查询计划器正在处理过时的统计信息。请注意,其他一些命令也会更新统计信息。

统计信息存储在目录表pg_classpg_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中关于基数估计如何计算预估行数的一些探讨

SQL Server中关于基数估计如何计算预估行数的一些探讨

SQL Server 兼容性级别和基数估计

SQL Server 兼容性级别和基数估计

SQL Server 兼容性级别和基数估计

PostgreSQL——代价估计