为什么此查询在PostgreSQL中不使用仅索引扫描?

Foa*_*ebi 3 postgresql

我有一个包含28列和7M条记录的表,没有主键。

CREATE TABLE records (
  direction smallint,
  exporters_id integer,
  time_stamp integer
  ...
)
Run Code Online (Sandbox Code Playgroud)

我在此表和真空表上创建索引,之后(自动真空开启)

CREATE INDEX exporter_dir_time_only_index ON sacopre_records
USING btree (exporters_id, direction, time_stamp);
Run Code Online (Sandbox Code Playgroud)

我想执行此查询

SELECT count(exporters_id) FROM records WHERE exporters_id = 50
Run Code Online (Sandbox Code Playgroud)

该表具有6982224记录,且exporters_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
Run Code Online (Sandbox Code Playgroud)

但是当我将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
Run Code Online (Sandbox Code Playgroud)

问题出在哪里?

Kou*_*rev 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
Run Code Online (Sandbox Code Playgroud)

您的过滤器只删除了表中6982149行中的2663行,因此执行顺序扫描实际上比使用索引要快,因为磁盘头无论如何都要通过6982149-2663 = 6979486记录。磁盘头开始按顺序读取整个表,并且正在删除不符合您条件的微小部分(0.000004%)。在索引扫描的情况下,它应该从索引文件中跳转并返回到数据文件6979486次,这肯定比现在的1.5秒要慢!

  • @KouberSaparev关于最常见的值:这是错误的。在某些DBMS中可能是这种情况,但是在PostgreSQL中,b树索引*确实*包含所有公共或其他值,除非您通过索引定义中的显式`WHERE`子句将其作为部分索引。这样做也很有用,例如仅索引扫描或有效返回索引排序的结果。不过,您的第二点是正确的:很有可能在这里未使用该索引,因为它的选择性不够,使用`enable_seqscan = off`进行测试对于查看相对成本估算非常有用。 (3认同)
  • “并返回到数据文件”...但他们正在对索引字段执行`COUNT`,当然引擎可以遍历索引并忽略数据吗? (2认同)