未使用但影响查询的索引


8

我有一个带有一些数字和一些其他数据的PostgreSQL 9.3表:

CREATE TABLE mytable (
    myid BIGINT,
    somedata BYTEA
)

该表当前有约10M条记录,并占用1GB磁盘空间。myid不连续。

我想计算100000个连续数字的每个块中有多少行:

SELECT myid/100000 AS block, count(*) AS total FROM mytable GROUP BY myid/100000;

这将返回大约3500行。

我注意到,即使查询计划根本没有提及某个索引,该索引的存在也会显着加快此查询的速度。没有索引的查询计划:

db=> EXPLAIN (ANALYZE TRUE, VERBOSE TRUE) SELECT myid/100000 AS block, count(*) AS total FROM mytable GROUP BY myid/100000;
                                                               QUERY PLAN                                                               
----------------------------------------------------------------------------------------------------------------------------------------
 GroupAggregate  (cost=1636639.92..1709958.65 rows=496942 width=8) (actual time=6783.763..8888.841 rows=3460 loops=1)
   Output: ((myid / 100000)), count(*)
   ->  Sort  (cost=1636639.92..1659008.91 rows=8947594 width=8) (actual time=6783.752..8005.831 rows=8947557 loops=1)
         Output: ((myid / 100000))
         Sort Key: ((mytable.myid / 100000))
         Sort Method: external merge  Disk: 157440kB
         ->  Seq Scan on public.mytable  (cost=0.00..236506.92 rows=8947594 width=8) (actual time=0.020..1674.838 rows=8947557 loops=1)
               Output: (myid / 100000)
 Total runtime: 8914.780 ms
(9 rows)

索引:

db=> CREATE INDEX myindex ON mytable ((myid/100000));
db=> VACUUM ANALYZE;

新的查询计划:

db=> EXPLAIN (ANALYZE TRUE, VERBOSE TRUE) SELECT myid/100000 AS block, count(*) AS total FROM mytable GROUP BY myid/100000;
                                                            QUERY PLAN                                                            
----------------------------------------------------------------------------------------------------------------------------------
 HashAggregate  (cost=281242.99..281285.97 rows=3439 width=8) (actual time=3190.189..3190.800 rows=3460 loops=1)
   Output: ((myid / 100000)), count(*)
   ->  Seq Scan on public.mytable  (cost=0.00..236505.56 rows=8947485 width=8) (actual time=0.026..1659.571 rows=8947557 loops=1)
         Output: (myid / 100000)
 Total runtime: 3190.975 ms
(5 rows)

因此,查询计划和运行时相差很大(几乎三倍),但都没有提到索引。此行为在我的开发机上是完全可重现的:我经历了删除索引,测试查询多次,重新创建索引,再次测试查询的几个周期。这里发生了什么事?


我不是分析Postgres的查询计划的专家,但是我猜想索引用于该HashAggregate方法(并且不需要排序),因此您可以获得更好的性能。为什么在计划中未提及该索引,我却一无所知。
ypercubeᵀᴹ

如果使用以下命令启用详细模式,计划的输出是否会更改explain (analyze true, verbose true) ...
a_horse_with_no_name 2014年

如果您可以将此内容简化为一个独立的测试用例,那就太好了。看起来确实很奇怪。
Craig Ringer 2014年

@a_horse_with_no_name:是的,它有所变化-我已用问题中的详细计划替换了查询计划。但是那个查询计划仍然根本没有提到索引。
liori 2014年

如果带有索引的id列上的可用统计信息(尤其是基数和可能的最小/最大值)多于没有索引,那可能会通过方法选择来更改优化器的组,即使最终根本不使用索引也是如此。 。(我根本不了解postgres的优化程序和统计信息,所以不知道是否可能是这种情况。)
Mat

Answers:


3

VACUUM ANALYZE使您的示例与众不同。另外,如@jjanes提供的那样,功能索引的其他统计信息。每个文档:

pg_statistic还存储有关索引表达式值的统计数据。这些描述就好像它们是实际数据列一样。特别是starelid引用索引。但是,不会为普通的非表达式索引列创建任何条目,因为它与基础表列的条目是多余的。

但是,创建索引本身并不会导致Postgres收集统计信息。尝试:

CREATE INDEX myindex ON mytable ((myid/100000));
SELECT * FROM pg_statistic WHERE starelid = 'myindex'::regclass;

在您运行第一个ANALYZE(或VACUUM ANALYZE,或启动autovacuum守护程序)之前,不返回任何内容。

ANALYZE mytable;
SELECT * FROM pg_statistic WHERE starelid = 'myindex'::regclass;

现在,您将看到添加的统计信息。

由于无论如何都必须读取整个表,因此Postgres将使用顺序扫描,除非它期望myid/100000进行转换的开销足够大,而事实并非如此。

如果索引比表小得多,则唯一的机会是仅索引扫描 -并且满足仅索引扫描的前提条件。Postgres Wiki手册中的详细信息

只要不使用该功能指标,从附加统计信息中获得的附带好处就不会多。如果该表是只读表,那么成本会很低-但是话又说回来,我们很可能会立即看到仅索引扫描。

也许您可以通过为设置更高的统计信息目标来实现更好的查询计划mytable.myid。这样只会产生少量费用。更多:


感谢您的解释,这对于理解问题非常有帮助。就我而言,我极有可能需要一个附加myid/100000 BETWEEN somevalue AND othervalue条件,因此无论如何该索引都将用于查询计划中—我刚刚问了这个问题,因为我不明白为什么索引在整个表中很有用。
liori

@liori:您可以用(根据您的类型考虑舍入效果)来覆盖WHERE myid BETWEEN somevalue*100000 AND othervalue*100000,并且您可能已经在上有了一个普通索引myid,因此您可以在没有附加专门索引的情况下进行操作。可能会更有效率。
Erwin Brandstetter 2014年

6

创建表达式索引时,它将导致PostgreSQL收集有关该表达式的统计信息。有了这些统计信息,它现在可以准确估计查询将返回的聚合行的数量,这使它可以做出更好的计划选择。

特别是在这种情况下,如果没有这些额外的统计信息,它会认为哈希表太大而无法容纳在work_mem中,因此它没有选择该方法。


我认为计划者没有考虑到价值work_mem。如果您提出它以使排序适合内存,则仍将使用相同的计划。在这里让我指出,时间差(大部分时间差)来自外部磁盘排序。
2014年

1
@dezso如果您实验性地将需要适合内存中排序的work_mem的值增加一倍或两倍,该怎么办?排序和散列具有不同的开销估计,并且估计本身也不十分精确。另外,您使用的是哪个次要版本9.3?
jjanes 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.