改进 Postgres 中的不同值估计

Mor*_*ryx 3 postgresql distinct cardinality-estimates

Postgres 中的完整计数可能会很慢,其原因众所周知且经过多次讨论。因此,在可能的情况下,我一直在使用估计技术。对于行, pg_stats 似乎很好,对于视图,提取由 工作返回的估计也EXPLAIN可以。

https://www.cybertec-postgresql.com/en/count-made-fast/

但不同的价值观又如何呢?在这里,我的运气要差很多。有时估计是 100% 正确的,有时会偏离 2 或 20 倍。截断的表似乎特别有严重过时的估计(?)。

我刚刚运行了这个测试并提供了一些结果:

analyze assembly_prods; -- Doing an ANLYZE to give pg_stats every help.

select 'count(*) distinct' as method,
        count(*) as count
from (select distinct assembly_id 
      from assembly_prods) d 
union all
select 'n_distinct from pg_stats' as method,
        n_distinct as count
from pg_stats 
where tablename  = 'assembly_prods' and
      attname    = 'assembly_id';
Run Code Online (Sandbox Code Playgroud)

结果:

method                      count
count(*) distinct           28088
n_distinct from pg_stats    13805
Run Code Online (Sandbox Code Playgroud)

虽然只相差了 2 倍,但我的数据似乎更糟糕。到了我不会使用估计的地步。我还有什么可以尝试的吗?这是PG 12改进的吗?

跟进

我以前从未尝试过SET STATISTICS,因为一天只有那么几个小时。受到劳伦斯回答的启发,我快速浏览了一下。这是文档中的有用评论:

https://www.postgresql.org/docs/current/planner-stats.html

pg_statistic存储在by中的信息量ANALYZE,特别是most_common_vals每列的 和 histogram_bounds 数组中的最大条目数,可以使用该命令逐列设置ALTER TABLE SET STATISTICS,也可以通过设置default_statistics_target配置变量进行全局设置。当前默认限制为 100 个条目。提高限制可能允许进行更准确的规划器估计,特别是对于数据分布不规则的列,但代价是消耗更多的空间和pg_statistic稍微更多的时间来计算估计。相反,对于具有简单数据分布的列,较低的限制可能就足够了。

我经常得到包含一些常见值和许多稀有值的表格。或者反过来,所以正确的阈值将取决于。对于那些没有使用过的人SET STATISTICS,它可以让您将采样率设置为目标条目数。默认值为 100,因此 1000 应该是更高的保真度。看起来是这样的:

ALTER TABLE assembly_prods 
    ALTER COLUMN assembly_id
    SET STATISTICS 1000;
Run Code Online (Sandbox Code Playgroud)

您可以SET STATISTICS在表或索引上使用。这是一篇关于索引的有趣文章:

https://akorotkov.github.io/blog/2017/05/31/alter-index-weird/

请注意,当前文档确实列出SET STATISTICS了索引。

因此,我尝试了 1、10、100、1000 和 10,000 的阈值,并从包含 467,767 行和 28,088 个不同值的表中获得了这些结果:

Target   Estimate  Difference  Missing
     1   13,657    14,431      51%
    10   13,867    14,221      51%
   100   13,759    14,329      51%
 1,000   24,746     3,342      12%
10,000   28,088         0       0%
Run Code Online (Sandbox Code Playgroud)

显然你不能从一个案例中得出任何一般性的结论,但SET STATISTICS看起来非常有用,我很高兴能把它记在心里。我很想把目标提高一点,因为我怀疑这对我们系统中的许多情况都有帮助。

Lau*_*lbe 6

首先,注释:您的查询可以写得更简单,如下所示

SELECT count(DISTINCT assembly_id) FROM assembly_prods;
Run Code Online (Sandbox Code Playgroud)

另外,您的统计查询是错误的,因为n_distict也可能是负数。您应该查询:

SELECT CASE WHEN s.n_distinct < 0
            THEN - s.n_distinct * t.reltuples
            ELSE s.n_distinct
       END AS n_distinct
FROM pg_class t
   JOIN pg_namespace n ON n.oid = t.relnamespace
   JOIN pg_stats s ON t.relname = s.tablename
                      AND n.nspname = s.schemaname
WHERE s.schemaname = 'public'
  AND s.tablename = 'assembly_prods'
  AND s.attname = 'assembly_id';
Run Code Online (Sandbox Code Playgroud)

对于这样的简单查询,统计信息应该包含一个很好的估计。

如果估计不准确,请尝试查看ANALYZE表格。TRUNCATE这也将修复新表的结果。TRUNCATE不会导致 PostgreSQL 自动分析表(这里可能还有改进的空间)。

如果这改善了结果,请通过配置更频繁地分析该表

ALTER TABLE assembly_prods SET (autovacuum_analyze_scale_factor = 0.05);
Run Code Online (Sandbox Code Playgroud)

也可以设置autovacuum_analyze_scale_factor为 0 并提高autovacuum_analyze_threshold到表的每日变化率。

如果ANALYZE单独不能改善估计,请增加样本大小:

ALTER TABLE assembly_prods ALTER assembly_id SET STATISTICS 1000;
Run Code Online (Sandbox Code Playgroud)

新的ANALYZE现在应该产生更好的估计。

对更复杂的查询进行良好的n_distinct估计变得越来越困难。有时,扩展统计数据会大大改善估计结果。

据我所知,PostgreSQL v12并没有在这方面带来任何改进。