为什么这个查询在PostgreSQL中不能使用索引扫描?

3

我有一张拥有28列和7M记录但没有主键的表。

CREATE TABLE records (
  direction smallint,
  exporters_id integer,
  time_stamp integer
  ...
)

我在这个表上创建了索引,并在此之后对表进行了清理(自动清理已开启)

CREATE INDEX exporter_dir_time_only_index ON sacopre_records
USING btree (exporters_id, direction, time_stamp);

我想执行这个查询。

SELECT count(exporters_id) FROM records WHERE exporters_id = 50
该表中有6982224条记录,导出者ID为50。我期望此查询使用索引唯一扫描来获取结果,但它使用了顺序扫描。
以下是"EXPLAIN ANALYZE"输出:
Aggregate  (cost=204562.25..204562.26 rows=1 width=4) (actual time=1521.862..1521.862 rows=1 loops=1)
->  Seq Scan on sacopre_records (cost=0.00..187106.88 rows=6982149 width=4) (actual time=0.885..1216.211 rows=6982224 loops=1)
    Filter: (exporters_id = 50)
    Rows Removed by Filter: 2663
Total runtime: 1521.886 ms

但是当我将exporters_id更改为另一个id时,查询会使用索引扫描。

Aggregate  (cost=46.05..46.06 rows=1 width=4) (actual time=0.321..0.321 rows=1 loops=1)
->  Index Only Scan using exporter_dir_time_only_index on sacopre_records  (cost=0.43..42.85 rows=1281 width=4) (actual time=0.313..0.315 rows=4 loops=1)
    Index Cond: (exporters_id = 47)
    Heap Fetches: 0
Total runtime: 0.358 ms

问题出在哪里?

你尝试过 SELECT COUNT(exporters_id=50) FROM records 吗? - Tordek
@Tordek,我现在测试了一下,得到了相同的结果,它使用了顺序扫描。 - Foad Tahmasebi
也许新的索引没有被分析,因此没有呈现给规划器?尝试“vacuum analyze records”。 - Vao Tsun
@VaoTsun,我之前说过了,我执行了“vacuum analyze”,并且自动清理功能已经开启。 - Foad Tahmasebi
1个回答

5
这句话的意思是解释原因。仔细看一下。
Aggregate  (cost=204562.25..204562.26 rows=1 width=4) (actual time=1521.862..1521.862 rows=1 loops=1)
->  Seq Scan on sacopre_records (cost=0.00..187106.88 rows=6982149 width=4) (actual time=0.885..1216.211 rows=6982224 loops=1)
    Filter: (exporters_id = 50)
    Rows Removed by Filter: 2663
Total runtime: 1521.886 ms

您的过滤器仅从表中的6982149行中删除了2663行,因此顺序扫描应该比使用索引更快,因为磁盘头必须穿过6979486条记录。磁盘头开始按顺序读取整个表,并在路上删除那些不符合您条件的微小部分(0.000004%)。而在索引扫描情况下,它应该跳转到索引文件然后6979486次返回数据文件,这肯定会比现在所需的1.5秒慢!


3
“然后回到数据文件”……但他们正在对一个索引字段进行COUNT,引擎肯定可以遍历索引并忽略数据吧? - Tordek
我同意@Tordek的观点,没有必要返回数据文件!! - Foad Tahmasebi
确实,50是最常见的值,因此索引不使用它是有很好的原因的。您可以尝试SET enable_seqscan = OFF;,然后运行相同的EXPLAIN ANALYZE来查看规划器是否正确。 - Kouber Saparev
3
关于最常见的值:这是错误的。在某些数据库管理系统中可能是这种情况,但在PostgreSQL中,除非您在索引定义中明确使用WHERE从句创建了一个部分索引,否则b树索引包含所有值,无论它们是否常见。它对于像索引仅扫描或高效地返回索引排序结果之类的操作非常有用。不过,你的第二个观点是正确的:很可能索引在此处未被使用,因为它的选择性不足,并且通过将enable_seqscan = off进行测试可以查看相对成本估计。 - Craig Ringer
1
@Arshen 很可能你的 random_page_costseq_page_cost 没有准确反映出你系统的实际性能。或者是查询规划器估算不够准确。不过这并不是很大的差异。 - Craig Ringer
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接