我在PostgreSQL 9.2中有一个表,它有一个text
列,我们称之为text_col
。(最多可能包含5-6个重复项)。该表有大约500万行。大约一半的这些行包含text_col
的null
值。当我执行以下查询时,我预计有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%左右。
3条答案
按热度按时间eagi6jfj1#
partial index是一个好主意,可以排除表中明显不需要的一半行。更简单:
字符串
在创建索引后运行
ANALYZE table
。(如果您不手动执行,autovacuum
会在一段时间后自动执行,但如果您在创建后立即进行测试,则测试将失败。)然后,为了说服查询规划器可以使用特定的部分索引,在查询中重复
WHERE
条件-即使它看起来多余:型
The manual:
但是,请记住, predicate 必须匹配应该从索引中受益的查询中使用的条件。只有当系统可以识别查询的
WHERE
条件在数学上暗示索引的 predicate 时,才可以在查询中使用部分索引。PostgreSQL没有复杂的定理证明器,可以识别数学上等价的表达式,以不同的形式书写。(不仅创建这样一个通用的定理证明器极其困难,而且它可能太慢而不能有任何真实的用途。)该系统可以识别简单的不等式含义,例如“x < 1”意味着“x < 2”;否则, predicate 条件必须完全匹配查询的WHERE
条件的一部分,否则索引将无法被识别为可用。因此,参数化查询子句不适用于部分索引。对于参数化查询:同样,添加部分索引的(冗余) predicate 作为额外的常量
WHERE
条件,它工作得很好。**更新:**Postgres 9.6或更高版本大大提高了index-only scans的机会。请参阅:
xe55xuns2#
在仔细查看
analyze
帮助构建的pg_stats
视图时,我在documentation上发现了这段摘录。关联
列值的物理行排序和逻辑排序之间的统计相关性。范围从-1到+1。当值接近-1或+1时,由于减少了对磁盘的随机访问,估计对列的索引扫描比接近零时更便宜。(如果列数据类型没有<运算符,则此列为空。)
在我的本地机器上,相关数是
0.97
,而在生产环境中,相关数是0.05
。因此,规划器估计,顺序遍历所有这些行比每次查找索引并在磁盘块上进行随机访问更容易。这是我用来查看相关数的查询。字符串
此表还对其行执行了一些更新。行的
avg_width
估计为20字节。如果更新的文本列值很大,则可能会超过平均值,并且也会导致更新速度较慢。我的猜测是,每次更新都会使物理和逻辑顺序变得缓慢。为了解决这个问题,我执行了以下查询。型
这个想法是,我可以给每个磁盘块给予20%的缓冲区和
vacuum full
表,以回收丢失的空间并维护物理和逻辑顺序。查询
型
部分索引扫描- 1.5ms
型
排除NULL条件将使用位图堆扫描拾取其他索引。
全索引- 0.08ms
型
[编辑]
虽然最初看起来
correlation
在选择索引扫描方面起着重要作用,但@Mike观察到,他的数据库中接近0的correlation
值仍然会导致索引扫描。更改填充因子和完全清空有帮助,但我不确定为什么。xu3bshqb3#
只有当WHERE条件匹配时才使用部分索引。因此,只有当您在
SELECT
中使用相同条件时,才可以使用WHERE text_col IS NOT NULL
索引。排序规则不匹配也可能导致损害。请尝试以下操作:
1.创建一个最简单的btree索引
CREATE INDEX foo ON table (text_col)
ANALYZE table
个1.查询