PostgreSQL不使用部分索引

lxkprmvk  于 5个月前  发布在  PostgreSQL
关注(0)|答案(3)|浏览(67)

我在PostgreSQL 9.2中有一个表,它有一个text列,我们称之为text_col。(最多可能包含5-6个重复项)。该表有大约500万行。大约一半的这些行包含text_colnull值。当我执行以下查询时,我预计有1-5行。在大多数情况下(> 80%)我只期望一行。

查询

explain analyze SELECT col1,col2.. colN
FROM table 
WHERE text_col = 'my_value';

字符串
text_col上存在btree索引。查询规划器从不使用此索引,我不知道为什么。这是查询的输出。

计划器

Seq Scan on two (cost=0.000..459573.080 rows=93 width=339) (actual time=1392.864..3196.283 rows=2 loops=1)
Filter: (victor = 'foxtrot'::text)
Rows Removed by Filter: 4077384


我添加了另一个部分索引,试图过滤掉那些不为空的值,但这没有帮助(有或没有text_pattern_ops。我不需要text_pattern_ops,因为在我的查询中没有表达LIKE条件,但它们也匹配相等)。

CREATE INDEX name_idx
  ON table
  USING btree
  (text_col COLLATE pg_catalog."default" text_pattern_ops)
  WHERE text_col IS NOT NULL;


禁用使用set enable_seqscan = off;的序列扫描会使计划程序仍然选择seqscan而不是index_scan。总之.
1.此查询返回的行数很小。
1.考虑到非空行是相当独特的,索引扫描文本应该更快。
1.查询和分析表并不能帮助优化器选择索引。

我的问题

1.为什么数据库选择顺序扫描而不是索引扫描?
1.当一个表有一个文本列,应该检查它的相等条件时,有什么最佳实践可以遵循吗?
1.如何缩短此查询所需的时间?

[编辑-更多信息]

1.索引扫描是在我的本地数据库中进行的,该数据库包含生产中可用数据的10%左右。

eagi6jfj

eagi6jfj1#

partial index是一个好主意,可以排除表中明显不需要的一半行。更简单:

CREATE INDEX name_idx ON table (text_col)
WHERE text_col IS NOT NULL;

字符串
在创建索引后运行ANALYZE table。(如果您不手动执行,autovacuum会在一段时间后自动执行,但如果您在创建后立即进行测试,则测试将失败。)
然后,为了说服查询规划器可以使用特定的部分索引,在查询中重复WHERE条件-即使它看起来多余:

SELECT col1,col2, .. colN
FROM   table 
WHERE  text_col = 'my_value'
AND   text_col IS NOT NULL;  -- repeat condition


The manual
但是,请记住, predicate 必须匹配应该从索引中受益的查询中使用的条件。只有当系统可以识别查询的WHERE条件在数学上暗示索引的 predicate 时,才可以在查询中使用部分索引。PostgreSQL没有复杂的定理证明器,可以识别数学上等价的表达式,以不同的形式书写。(不仅创建这样一个通用的定理证明器极其困难,而且它可能太慢而不能有任何真实的用途。)该系统可以识别简单的不等式含义,例如“x < 1”意味着“x < 2”;否则, predicate 条件必须完全匹配查询的WHERE条件的一部分,否则索引将无法被识别为可用。因此,参数化查询子句不适用于部分索引。
对于参数化查询:同样,添加部分索引的(冗余) predicate 作为额外的常量WHERE条件,它工作得很好。

**更新:**Postgres 9.6或更高版本大大提高了index-only scans的机会。请参阅:

xe55xuns

xe55xuns2#

在仔细查看analyze帮助构建的pg_stats视图时,我在documentation上发现了这段摘录。

关联

列值的物理行排序和逻辑排序之间的统计相关性。范围从-1到+1。当值接近-1或+1时,由于减少了对磁盘的随机访问,估计对列的索引扫描比接近零时更便宜。(如果列数据类型没有<运算符,则此列为空。)
在我的本地机器上,相关数是0.97,而在生产环境中,相关数是0.05。因此,规划器估计,顺序遍历所有这些行比每次查找索引并在磁盘块上进行随机访问更容易。这是我用来查看相关数的查询。

select * from pg_stats where tablename = 'table_name' and attname = 'text_col';

字符串
此表还对其行执行了一些更新。行的avg_width估计为20字节。如果更新的文本列值很大,则可能会超过平均值,并且也会导致更新速度较慢。我的猜测是,每次更新都会使物理和逻辑顺序变得缓慢。为了解决这个问题,我执行了以下查询。

ALTER TABLE table_name SET (FILLFACTOR = 80);
VACUUM FULL table_name;
REINDEX TABLE table_name;
ANALYZE table_name;


这个想法是,我可以给每个磁盘块给予20%的缓冲区和vacuum full表,以回收丢失的空间并维护物理和逻辑顺序。

查询

explain analyze SELECT col1,col2... colN
FROM table_name 
WHERE text_col is not null 
AND 
text_col = 'my_value';

部分索引扫描- 1.5ms

Index Scan using tango on two (cost=0.000..165.290 rows=40 width=339) (actual time=0.083..0.086 rows=1 loops=1)
Index Cond: ((victor five NOT NULL) AND (victor = 'delta'::text))


排除NULL条件将使用位图堆扫描拾取其他索引。

全索引- 0.08ms

Bitmap Heap Scan on two  (cost=5.380..392.150 rows=98 width=339) (actual time=0.038..0.039 rows=1 loops=1)
    Recheck Cond: (victor = 'delta'::text)
  ->  Bitmap Index Scan on tango  (cost=0.000..5.360 rows=98 width=0) (actual time=0.029..0.029 rows=1 loops=1)
          Index Cond: (victor = 'delta'::text)

[编辑]

虽然最初看起来correlation在选择索引扫描方面起着重要作用,但@Mike观察到,他的数据库中接近0的correlation值仍然会导致索引扫描。更改填充因子和完全清空有帮助,但我不确定为什么。

xu3bshqb

xu3bshqb3#

只有当WHERE条件匹配时才使用部分索引。因此,只有当您在SELECT中使用相同条件时,才可以使用WHERE text_col IS NOT NULL索引。排序规则不匹配也可能导致损害。
请尝试以下操作:
1.创建一个最简单的btree索引CREATE INDEX foo ON table (text_col)

  1. ANALYZE table
    1.查询

相关问题