PostgreSQL查询非常慢,限制为1

pat*_*pat 38 postgresql performance limit

添加a时,我的查询速度很慢limit 1.

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

 timestamp |  objectID |  value
--------------------------------
 2014-01-27|       234 | ksghdf
Run Code Online (Sandbox Code Playgroud)

每个对象我想得到最新的价值:

SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC LIMIT 1;
Run Code Online (Sandbox Code Playgroud)

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

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

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

一个解释告诉我,无限制的查询使用索引,其中查询与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)`
Run Code Online (Sandbox Code Playgroud)

快速查询:

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)
Run Code Online (Sandbox Code Playgroud)

该表包含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)
Run Code Online (Sandbox Code Playgroud)

总的来说,我认为它与计划者对排出成本做出错误的假设有关,因此选择较慢的执行计划.

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

Bre*_*Nee 41

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

SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp, objectID DESC limit 1;
Run Code Online (Sandbox Code Playgroud)

  • 好一个。是否有可能得到解释为什么会这样? (6认同)
  • pg 列表上对此错误的讨论:https://www.postgresql.org/message-id/flat/CA%2BU5nMLbXfUT9cWDHJ3tpxjC3bTWqizBKqTwDgzebCB5bAGCgg%40mail.gmail.com (3认同)
  • 哈!太棒了!彻底修复! (2认同)
  • 与上述答案和所有评论不同,此答案确实有效。 (2认同)

Den*_*rdy 31

我想,你遇到的问题与缺乏行相关的统计数据有关.如果使用的是最新版本的Postgres,请考虑将其报告给pg-bugs以供参考.

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

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

    基于您给出的数据,可能是它需要平均读取~70行才能找到适合的行; 它只是没有意识到object_id和timestamp与它实际上要读取表的大部分的点相关联.

  • limit 3相比之下,它让它意识到这是不常见的,所以它认真考虑(并最终......)top-n按照object_id你想要的方式排序预期的1700行,理由是这样做可能更便宜.

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

  • 没有limit条款意味着它无论如何都会获取1700,所以它直接指向索引object_id.

解决方案,顺便说一句:在(object_id, timestamp)或上添加一个索引(object_id, timestamp desc).


Dan*_*ner 10

我开始在更新频繁的表上出现类似的症状,我的情况需要的是

analyze $table_name;
Run Code Online (Sandbox Code Playgroud)

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