【问题标题】:PostgreSQL query very slow with limit 1PostgreSQL 查询非常慢,限制为 1
【发布时间】:2014-02-18 13:38:58
【问题描述】:

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

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

这是真正的原因吗?有解决办法吗?

【问题讨论】:

  • 这个问题是否在 pg-bugs 上提出并在最新版本的 postgres 中得到解决?
  • @ShiwanginiShishulkar - 我在问

标签: postgresql performance query-optimization limit


【解决方案1】:

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

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

【讨论】:

  • 哈!太棒了!彻底解决!
  • 这个答案确实有效,不像答案和上面的所有cmets。
  • 太棒了!只需提升我的查询并可以在运行时使用它。谢谢!
  • 好一个。是否有可能解释为什么会这样?
  • pg列表上关于这个bug的讨论:postgresql.org/message-id/flat/…
【解决方案2】:

我认为,您遇到的问题与缺乏行相关性统计数据有关。如果使用的是最新版本的 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) 上添加索引。

【讨论】:

  • 对于“限制 1”的情况,您的意思是表扫描吗?你写了索引扫描
  • @harmic:OP 在那里进行了索引扫描……不一定是整个表,但肯定比 PG 认为的要多得多。
  • 你是对的!我只阅读了 OP 的文本,他说它没有使用索引。但它选择扫描时间戳索引;奇怪的选择
  • @Denis:感谢您的回复,我已经认为解释会是这样的。组合索引确实解决了它,您的回复让我对索引、排序和组合索引有了很多了解。感谢那。由于问题是基于统计数据的,它可能只是在表格填满时才出现?!
  • 我认为丹尼斯的意思是,当您向表中添加行时,两者都在增加。如果它是created_on 时间戳,而不是updated_on,那么这意味着它们是严格相关的——较大的 ID 将始终与较大的时间戳配对。如果它在更新时发生变化,至少还有一个“默认”相关性可能会随着时间的推移而降低(随着行的更新)。
【解决方案3】:

我开始在更新繁重的桌子上出现类似的症状,而我的情况需要的是

analyze $table_name;

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

【讨论】:

  • 哇,当您创建索引或尝试在本地开发中找到最佳查询时,这改变了游戏规则!非常感谢!
  • 这对我们的案例没有帮助。如果您的数据库发生了很大变化,那真是个好主意! :)
【解决方案4】:

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

【讨论】:

  • 我实际上遇到了与LIMIT 50 类似的问题(工作正常,没有任何限制,查询返回大约 2000 行)。所以这可能取决于许多变量,当 PG 选择不同的计划时,它通常是我们无法控制的,即使在ANALYSE 之后也是如此。
猜你喜欢
  • 2022-10-19
  • 2015-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多