postgresql:为什么空值比空字符串提高了性能?

标签 postgresql performance

我们有一个包含 6M 条记录的表。它包含分层实体,即每个实体都有一个到父级的链接 - Parent_id 字段。

95% 的行是顶级实体,即它们有一个空的 parrent_id 字段(空字符串)。

单个实体的递归查询(以获取其所有子实体)存在性能问题。查询规划器错误地估计子项计数,并且更喜欢通过 parrent_id 字段进行“顺序扫描”而不是“索引扫描”。 我认为规划器是这样工作的,因为我们的 parrent_id 值分布不均匀。 pg_stats 显示: 最常见的值:'' n_distinct:73(事实上,几乎所有子实体(表的 5%)都有不同的 parrent_id)

分析器似乎没有获取足够的行。然而,当我们将空字符串值更改为 NULL 时,一切都会变得更好。规划器使用索引。

我不是专家,我想知道这种 NULL 技巧是否是常见做法,或者这只是一个意外,分析器进程可能会随着时间的推移破坏统计数据,导致性能下降?

最佳答案

我猜你遇到的情况是这样的:

CREATE TABLE rare (x integer);

INSERT INTO rare
SELECT CASE WHEN random() < 0.05
            THEN i
            ELSE 0
       END
FROM generate_series(1, 100000) AS i;

ANALYZE rare;

SELECT null_frac, n_distinct, most_common_vals, most_common_freqs
FROM pg_stats
WHERE tablename = 'rare'
  AND attname = 'x';

 null_frac | n_distinct | most_common_vals | most_common_freqs 
-----------+------------+------------------+-------------------
         0 |       1471 | {0}              | {0.9526333}
(1 row)

SELECT count(DISTINCT x) FROM rare;

 count 
-------
  4904
(1 row)

对于 NULL 值,它看起来像这样:

TRUNCATE rare;

INSERT INTO rare
SELECT CASE WHEN random() < 0.05
            THEN i
            ELSE NULL
       END
FROM generate_series(1, 100000) AS i;

ANALYZE rare;

SELECT null_frac, n_distinct, most_common_vals, most_common_freqs
FROM pg_stats
WHERE tablename = 'rare'
  AND attname = 'x';

 null_frac  |  n_distinct  | most_common_vals | most_common_freqs 
------------+--------------+------------------+-------------------
 0.95093334 | -0.049066663 |                  | 
(1 row)

不同之处在于,在第二种情况下,PostgreSQL 意识到非空值几乎是唯一的(它们实际上是唯一的),因此它将不同值的数量表示为负比率。负数,将其与正常情况区分开来,以及比率,因为如果数据发生变化,比率仍然更准确。

PostgreSQL 仅采用表的样本来计算统计信息,因此不同值的绝对计数(本例中为 1471)当然远远超出了标准,但分数(当使用分数大小计算时!)是相当准确。

在第一种情况下,PostgreSQL 会估计

SELECT DISTINCT x FROM rare;

有 1471 个结果行,而在第二种情况下,它将估计 100000 * 0.049066663 ≈ 4907 行。

现在这解释了您所看到的内容,但是您可以采取哪些措施来改进估计?

答案是采取更大的样本:

ALTER TABLE rare ALTER x SET STATISTICS 1000;

ANALYZE rare;

然后 PostgreSQL 将采用更大的样本并得出更准确的估计。

关于postgresql:为什么空值比空字符串提高了性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65734658/

相关文章:

python - 如何附加到 numpy 数组而不将结果重新分配给新变量?

regex - 我应该创建一个复杂的RegEx还是创建多个且不太复杂的RegEx?

sql - 在 PostgreSQL 中合并两个 View

database - Laravel - 查询生成器选择具有唯一列值的多行(具有来自另一列的最大值)

java - 以最小内存消耗写入文件的设计模式

mysql - MySQL 中的高效子查询

c - 检查数组中的元素是否已更改的最有效方法

arrays - 在 Postgres 数组中排序有多可靠?

java - 无法使用 spring 和 hibernate 连接到 postgresQL

sql - PostgreSQL:通过数组的元素从另一个表中选择数据然后按顺序显示