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
看起来非常有用,我很高兴能把它记在心里。我很想把目标提高一点,因为我怀疑这对我们系统中的许多情况都有帮助。
首先,注释:您的查询可以写得更简单,如下所示
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并没有在这方面带来任何改进。
归档时间: |
|
查看次数: |
1823 次 |
最近记录: |