为什么Postgres不在简单的GROUP BY上使用索引?

时间:2017-07-06 17:40:21

标签: postgresql indexing group-by

我创建了一个带有type列索引的36M行表:

CREATE TABLE items AS
  SELECT
    (random()*36000000)::integer AS id,
    (random()*10000)::integer AS type,
    md5(random()::text) AS s
  FROM
    generate_series(1,36000000);
CREATE INDEX items_type_idx ON items USING btree ("type");

我运行这个简单的查询并期望postgresql使用我的索引:

explain select count(*) from "items" group by "type";

但是查询计划程序决定改为使用Seq Scan:

HashAggregate  (cost=734592.00..734627.90 rows=3590 width=12) (actual time=6477.913..6478.344 rows=3601 loops=1)
  Group Key: type
  ->  Seq Scan on items  (cost=0.00..554593.00 rows=35999800 width=4) (actual time=0.044..1820.522 rows=36000000 loops=1)
Planning time: 0.107 ms
Execution time: 6478.525 ms

没有EXPLAIN的时间: 5s 979ms

我尝试了herehere的几个解决方案:

  • 运行VACUUM ANALYZEVACUUM ANALYZE
  • 配置default_statistics_targetrandom_page_costwork_mem

但除了设置enable_seqscan = OFF之外没有其他任何帮助:

SET enable_seqscan = OFF;
explain select count(*) from "items" group by "type";

GroupAggregate  (cost=0.56..1114880.46 rows=3590 width=12) (actual time=5.637..5256.406 rows=3601 loops=1)
  Group Key: type
  ->  Index Only Scan using items_type_idx on items  (cost=0.56..934845.56 rows=35999800 width=4) (actual time=0.074..2783.896 rows=36000000 loops=1)
        Heap Fetches: 0
Planning time: 0.103 ms
Execution time: 5256.667 ms

没有EXPLAIN的时间: 659ms

使用索引扫描的查询在我的机器上快了大约10倍。

有没有比设置enable_seqscan更好的解决方案?

UPD1

我的postgresql版本是9.6.3,work_mem = 4MB(试过64MB),random_page_cost = 4(试过1.1),max_parallel_workers_per_gather = 0(试过4)。

UPD2

我尝试使用随机数填充类型列,但使用i / 10000使pg_stats.correlation = 1 - 仍然是seqscan。

UPD3

@jgh是100%正确的:

  

这通常仅在表的行宽比某些索引

宽得多时才会发生

我已经制作了大列data,现在使用了postgres。谢谢大家!

1 个答案:

答案 0 :(得分:2)

Index-only scans wiki说

  

重要的是要意识到计划者关心的问题   最小化查询的总成本。随着数据库,成本   I / O通常占主导地位。出于这个原因,"计数(*)没有任何   谓词"如果索引是,查询将仅使用仅索引扫描   比它的表小得多。这通常只发生在   表格的行宽比某些索引宽得多

  

仅限索引扫描仅在计划者猜测时才使用   将减少所需的I / O总量   不完善的基于成本的建模。这一切都严重依赖于可见性   如果无论如何都会使用索引(即选择性如何)   谓词是等等),如果实际上有一个可用的索引   原则上可以由仅索引扫描使用

因此,您的指数不被视为"显着更小"并且要读取整个数据集,这导致计划者使用seq扫描