PostgreSQL不使用部分索引
我在PostgreSQL 9.2中有一个表,它有一个PostgreSQL不使用部分索引,sql,database,performance,postgresql,indexing,Sql,Database,Performance,Postgresql,Indexing,我在PostgreSQL 9.2中有一个表,它有一个text列。让我们称之为text\u col。此列中的值相当独特(最多可能包含5-6个重复项)。该表有约500万行。这些行中大约有一半包含text\u col的null值。当我执行下面的查询时,我希望有1-5行。在大多数情况下(>80%),我只希望有一行 查询 btree索引存在于text\u col上。这个索引从来没有被查询计划器使用过,我也不知道为什么。这是查询的输出 计划者 我添加了另一个部分索引,试图过滤掉那些不是空的值,但没有帮助(有
text
列。让我们称之为text\u col
。此列中的值相当独特(最多可能包含5-6个重复项)。该表有约500万行。这些行中大约有一半包含text\u col
的null
值。当我执行下面的查询时,我希望有1-5行。在大多数情况下(>80%),我只希望有一行
查询
btree
索引存在于text\u col
上。这个索引从来没有被查询计划器使用过,我也不知道为什么。这是查询的输出
计划者
我添加了另一个部分索引,试图过滤掉那些不是空的值,但没有帮助(有或没有text\u pattern\u ops
。我不需要text\u pattern\u ops
,因为我的查询中没有表达LIKE
条件,但它们也匹配相等)
使用设置enable_seqscan=off禁用序列扫描代码>使计划员仍然在索引扫描上选择seqscan
。总之
此查询返回的行数很小
考虑到非空行是相当唯一的,对文本进行索引扫描应该会更快
清空和分析表无助于优化器选择索引
我的问题
为什么数据库选择序列扫描而不是索引扫描
当一个表有一个文本列,该列的相等条件应该被检查时,有什么我可以遵循的最佳实践吗
如何减少此查询所需的时间
[编辑-更多信息]
索引扫描是在我的本地数据库中进行的,该数据库包含了大约10%的生产可用数据
仅当WHERE条件匹配时才使用部分索引。因此,只有在SELECT
中使用相同条件时,才能使用文本列不为NULL的索引。排序规则不匹配也可能造成危害
请尝试以下操作:
制作尽可能简单的树索引在表上创建索引foo(text\u col)
分析表
质疑
一个是一个好主意排除表中明显不需要的一半行。更简单:
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
选择col1、col2、。。科恩
从桌子上
其中text\u col='my\u value'
并且text_col不为空;——重复条件
瞧
:
但是,请记住谓词必须与条件匹配
用于本应受益于索引的查询中。成为
精确地说,只有在系统能够
认识到查询的WHERE
条件在数学上意味着
索引的谓词。PostgreSQL没有复杂的
能够识别数学等价物的定理证明器
以不同形式书写的表达式。(不仅如此,
一般定理证明极难创建,它会
系统可以识别
简单的不等式含义,例如“x<1”意味着“x<2”;
否则,谓词条件必须与
查询的,其中
条件或索引将不会被识别为可用。
匹配发生在查询计划时,而不是运行时。作为一个
结果,参数化查询子句不能与部分索引一起使用
对于参数化查询:再次添加部分索引的(冗余)谓词作为附加的常量WHERE
condition,它可以正常工作
Postgres 9.6中的一个重要更新极大地提高了查询的机会(这可以使查询更便宜,查询计划人员也更容易选择此类查询计划)。相关的:
我想出来了。在仔细查看
pg_stats
视图(该视图由analyze
帮助构建)后,我在
相关性
物理行顺序和逻辑行顺序之间的统计相关性
列值的顺序。这个范围从-1到+1。当
值接近-1或+1时,将估计列上的索引扫描
要比接近零时便宜,因为减少了随机性
访问磁盘。(如果列数据类型为空,则此列为空。)
没有<运算符。)
在我的本地框中,相关号为0.97
,而在生产中,相关号为0.05
。因此,计划者估计,按顺序遍历所有这些行比每次查找索引并对磁盘块进行随机访问更容易。这是我用来查看相关号的查询
select * from pg_stats where tablename = 'table_name' and attname = 'text_col';
此表还对其行执行了一些更新。行的平均宽度
估计为20字节。如果文本列的更新值较大,则可能会超过平均值,并导致更新速度较慢。我的猜测是,随着每次更新,物理和逻辑顺序正在缓慢地分离。为了解决这个问题,我执行了以下查询
ALTER TABLE table_name SET (FILLFACTOR = 80);
VACUUM FULL table_name;
REINDEX TABLE table_name;
ANALYZE table_name;
我的想法是,我可以为每个磁盘块提供20%的缓冲,并真空填充
表,以回收丢失的空间并保持物理和逻辑顺序。在我这样做之后,查询将获取索引
查询
部分索引扫描-1.5ms
排除NULL条件将使用位图堆扫描拾取另一个索引
完整索引-0.08ms
[编辑]
虽然它最初看起来像是co
SELECT col1,col2, .. colN
FROM table
WHERE text_col = 'my_value'
AND text_col IS NOT NULL; -- repeat condition
select * from pg_stats where tablename = 'table_name' and attname = 'text_col';
ALTER TABLE table_name SET (FILLFACTOR = 80);
VACUUM FULL table_name;
REINDEX TABLE table_name;
ANALYZE table_name;
explain analyze SELECT col1,col2... colN
FROM table_name
WHERE text_col is not null
AND
text_col = 'my_value';
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))
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)