大表中索引扫描缓慢


12

使用PostgreSQL 9.2时,我在相对较大的表(200+百万行)上的慢查询遇到了麻烦。我没有尝试任何疯狂的事情,只是增加了历史价值。以下是查询和查询计划输出。

我的表格布局:

                                   Table "public.energy_energyentry"
  Column   |           Type           |                            Modifiers
-----------+--------------------------+-----------------------------------------------------------------
 id        | integer                  | not null default nextval('energy_energyentry_id_seq'::regclass)
 prop_id   | integer                  | not null
 timestamp | timestamp with time zone | not null
 value     | double precision         | not null
Indexes:
    "energy_energyentry_pkey" PRIMARY KEY, btree (id)
    "energy_energyentry_prop_id" btree (prop_id)
    "energy_energyentry_prop_id_timestamp_idx" btree (prop_id, "timestamp")
Foreign-key constraints:
    "energy_energyentry_prop_id_fkey" FOREIGN KEY (prop_id) REFERENCES gateway_peripheralproperty(id) DEFERRABLE INITIALLY DEFERRED

数据范围是从2012年1月1日到现在,并且不断添加新数据。prop_id外键中大约有2.2k个不同的值,分布均匀。

我注意到行估算值相差不远,但是成本估算值似乎大了4倍。这可能不是问题,但是我能做些什么吗?

我希望磁盘访问可能是个问题,因为表并非一直在内存中。

EXPLAIN ANALYZE 
SELECT SUM("value") 
FROM "energy_energyentry" 
WHERE 
  "prop_id"=82411 
  AND "timestamp">'2014-06-11' 
  AND "timestamp"<'2014-11-11'
;
 Aggregate  (cost=214481.45..214481.46 rows=1 width=8) (actual time=51504.814..51504.814 rows=1 loops=1)
   ->  Index Scan using energy_energyentry_prop_id_timestamp_idx on  energy_energyentry (cost=0.00..214434.08 rows=18947 width=8) (actual time=136.030..51488.321 rows=13578 loops=1)
         Index Cond: ((prop_id = 82411) AND ("timestamp" > '2014-06-11 00:00:00+00'::timestamp with time zone) AND ("timestamp" < '2014-11-11 00:00:00+00'::timestamp with time zone))
 Total runtime: 51504.841 ms

任何建议如何使它更快?
我也很好,只是听到我没有做任何奇怪的事情。


1
请告诉我们您的表是什么样,它具有什么索引以及数据的传播。
Colin't Hart

我添加了您要求的其他信息。邓诺,我是否错过了任何事情。
Exelian 2014年

2
奇怪:您的解释分析显示prop_time_idx,而表定义显示entry_prop_id_timestamp_idx。这是相同的索引吗?请解决。
Colin't Hart

如果通过参考“的成本估计似乎是一个因素,更大的4倍”的事实,即成本数字是4倍左右的人的实际时间,那么请注意,这两个无关彼此。成本只是一个估计,可以帮助查询优化器选择外观最佳的计划。在此上下文之外,它通常是无意义的值。
dezso 2014年

1
您的日期范围代表了表格的百分之几(不考虑的值prop)?如果只占很小的百分比,也许索引("timestamp", prop)会更好。具有相同前导列的多个索引(prop在您的情况下)通常也是多余的。
Colin't Hart 2014年

Answers:


10

您的表很大,跨整个表的任何索引也是如此。假如说:

  • 仅输入新数据(带有timestamp = now()
  • 现有行既不会更改也不会删除。
  • 您自2012年1月1日以来拥有数据,但查询主要针对当年(?)

我建议使用一个多列局部索引(覆盖!)

CREATE INDEX ON energy_energyentry (prop_id, "timestamp", value)
WHERE "timestamp" >= '2014-01-01 0:0';  -- adapt to your needs

仅包括定期查询的时间范围。随着时间的流逝,有效性随着新条目的增加而降低。不时重新创建索引。(您可能需要调整您的查询。)请参阅下面的链接的答案。

仅包括最后一列的值,以便从中获得仅索引的扫描。积极的自动抽空设置可以通过使可见性图保持最新状态而有所帮助,例如@jjanes已经提到过

部分索引应该更容易装入RAM,并在其中停留更长时间。

您可能需要WHERE在查询中包括此条件,以使计划者理解该索引适用于该查询,例如:

SELECT sum(value) AS sum_value
FROM   energy_energyentry
WHERE  prop_id = 82411 
AND   "timestamp" > '2014-06-11 0:0' 
AND   "timestamp" < '2014-11-11 0:0'
AND   "timestamp" >= '2014-01-01 0:0'; -- seems redundant, but may be needed

由于您的查询要汇总很多行(rows=13578),因此即使使用仅索引扫描,也要花费一些时间。不过,它不应在50秒左右。在任何中途的硬件上不到一秒钟。

相关的(但是忽略CLUSTERFILLFACTOR,如果您可以从中获得仅索引扫描,则两者都不相关)

旁:
由于您当前在上有一个索引(prop_id, "timestamp"),因此上的附加索引(prop_id)可能会花费不菲的价值:


既然Postgres支持BRIN索引,在这里有用吗?我计划在postgres上的数据上存储约1.4亿行,BRIN是用于这么大表的正确索引吗?
艾莉亚

2

如果在(prop_id,“ timestamp”,“ value”)上建立索引,则它可以使用仅索引扫描来计算值,而无需访问表。这样可以节省大量的随机磁盘访问。

为了获得最大的收益,您需要积极地清理桌子。对于希望有效支持仅索引扫描的仅插入表,默认的autovac设置不够积极。


增加值确实可能很有趣,我将看看是否可以加快速度。您对我可以查看的真空设置或文档有任何建议吗?
Exelian 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.