PostgreSQL查询非常慢,限制为1

时间:2014-01-27 16:01:11

标签: performance postgresql limit

添加limit 1后,我的查询速度变慢。

我有一个表object_values,其中包含对象的带时间戳值:

 timestamp |  objectID |  value
--------------------------------
 2014-01-27|       234 | ksghdf

每个对象我想获得最新值:

SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC LIMIT 1;

(超过10分钟后我取消了查询)

当没有给定objectID的值时,此查询非常慢(如果有结果则速度很快)。 如果我删除了限制,它几乎可以立即告诉我没有结果:

SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC;  
...  
Time: 0.463 ms

解释显示无限制查询使用索引,而limit 1的查询不使用索引:

查询缓慢:

explain SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC limit 1;  
QUERY PLAN`
----------------------------------------------------------------------------------------------------------------------------
Limit  (cost=0.00..2350.44 rows=1 width=126)
->  Index Scan Backward using object_values_timestamp on object_values  (cost=0.00..3995743.59 rows=1700 width=126)
     Filter: (objectID = 53708)`

快速查询:

explain SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC;
                                                  QUERY PLAN
--------------------------------------------------------------------------------------------------------------
 Sort  (cost=6540.86..6545.11 rows=1700 width=126)
   Sort Key: timestamp
   ->  Index Scan using object_values_objectID on working_hours_t  (cost=0.00..6449.65 rows=1700 width=126)
         Index Cond: (objectID = 53708)

该表包含44,884,559行和66,762个不同的objectID 我在两个字段上都有单独的索引:timestampobjectID 我在桌子上做了vacuum analyze,我已经重新编制了表格。

此外,当我将限制设置为3或更高时,慢查询变得很快:

explain SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC limit 3;
                                                     QUERY PLAN
--------------------------------------------------------------------------------------------------------------------
 Limit  (cost=6471.62..6471.63 rows=3 width=126)
   ->  Sort  (cost=6471.62..6475.87 rows=1700 width=126)
         Sort Key: timestamp
         ->  Index Scan using object_values_objectID on object_values  (cost=0.00..6449.65 rows=1700 width=126)
               Index Cond: (objectID = 53708)

一般来说,我认为这与计划员对例外成本做出错误的假设有关,因此选择较慢的执行计划。

这是真正的原因吗?有解决方案吗?

4 个答案:

答案 0 :(得分:34)

您可以通过向查询添加不需要的ORDER BY子句来避免此问题。

SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp, objectID DESC limit 1;

答案 1 :(得分:28)

我遇到了一个问题,我认为这个问题与缺乏关于行相关性的统计数据有关。如果使用最新版本的Postgres,请考虑将其报告给pg-bugs以供参考。

我建议你的计划的解释是:

  • limit 1使Postgres寻找单行,并且这样做会假定您的object_id足够常见,以至于它会在索引扫描中合理快速地显示出来。

    根据您提供的数据,他们的想法可能是,平均需要阅读~70行才能找到适合的行;它只是没有意识到object_id和时间戳与它实际上要读取表的大部分的点相关联。

  • 相比之下,
  • limit 3让它意识到这种情况并不常见,因此它认真考虑(并最终......)top-n使用您想要的object_id对预期的1700行进行排序,理由是这样做可能会更便宜。

    例如,它可能知道这些行的分布是这样的,它们都被打包在磁盘上的同一区域。

  • 没有limit条款意味着它无论如何都会获取1700,因此它会直接转到object_id上的索引。

解决方案,顺便说一下:在(object_id, timestamp)(object_id, timestamp desc)上添加索引。

答案 2 :(得分:1)

我在大量更新的表上开始出现类似的症状,而我的情况是

analyze $table_name;

在这种情况下,需要刷新统计信息,然后可以修复正在发生的缓慢查询计划。
辅助文档:https://www.postgresql.org/docs/current/sql-analyze.html

答案 3 :(得分:1)

不是修复,但确实从 VACUUM FULL 切换到 limit 1(对我而言)并返回第一个结果行的速度要快得多......在这种情况下是 Postgres 9.x。只是想我会提到它作为 OP 提到的解决方法。