PostgreSQL查询非常慢,限制为1

dluptydi  于 2023-06-29  发布在  PostgreSQL
关注(0)|答案(4)|浏览(188)

当我添加一个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)

一般来说,我认为这与计划者对执行成本做出错误的假设有关,因此选择了一个较慢的执行计划。
这是真实的的原因吗?有解决办法吗?

oewdyzsn

oewdyzsn1#

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

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

j0pj023g2#

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

  • limit 1使Postgres查找单个行,并且在这样做时,它假设您的object_id足够常见,因此它将在索引扫描中快速显示。

根据您提供的统计数据,它的想法可能是,它平均需要读取~70行才能找到一个合适的行;它只是没有意识到object_id和timestamp与它实际读取表大部分内容的点相关。

  • 相比之下,limit 3使它意识到它已经足够罕见了,所以它认真考虑(并最终......)使用您想要的object_id对预期的1700行进行top-n排序,理由是这样做可能更便宜。

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

  • 没有limit子句意味着它无论如何都会获取1700,所以它直接获取object_id上的索引。

解决方案,顺便说一下:

(object_id, timestamp)(object_id, timestamp desc)上添加索引。

piztneat

piztneat3#

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

analyze $table_name;

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

7bsow1i6

7bsow1i64#

不是一个修复,但可以肯定的是,从limit 1切换到limit 50(对我来说)并返回第一个结果行要快得多...在这个示例中是Postgres 9.x。只是想我会提到它作为一个变通方案提到的行动。

相关问题