【问题标题】:Slow query with order and limit clause but only if there are no records带有 order 和 limit 子句的慢查询,但只有在没有记录的情况下
【发布时间】:2016-05-16 04:56:47
【问题描述】:

我正在运行以下查询:

SELECT * FROM foo WHERE name = 'Bob' ORDER BY address DESC LIMIT 25 OFFSET 1

因为我的表中有 name = 'Bob' 的记录,所以在 10M 记录的表上查询时间很快(<.5>

但是,如果我搜索 name = 'Susan',则查询需要 45 秒以上。我在 name = 'Susan' 的表中没有记录。

我对每个姓名和地址都有一个索引。我已经清空了表格,对其进行了分析,甚至尝试重新编写查询:

SELECT * FROM (SELECT * FROM foo WHERE name = 'Bob' ORDER BY address DESC) f LIMIT 25 OFFSET 1

并且找不到任何解决方案。我不确定如何进行。请注意,这与this post 不同,因为我的缓慢仅在没有记录时才会发生。

编辑: 如果我取出 ORDER BY 地址,它就会运行得很快。显然,我需要那个。我试过重写它(没有成功):

SELECT * FROM (SELECT * FROM foo WHERE name = 'Bob') f ORDER BY address DESC LIMIT 25 OFFSET 1

【问题讨论】:

  • "slow" 当大表中没有 匹配 行时可能意味着它正在执行表扫描。请提供解释计划。还怀疑您为我们的消费“简化”了这个,但细节可能很重要。

标签: postgresql postgresql-9.4


【解决方案1】:

检查execution plan 以查看正在使用的索引。在这种情况下,名称和地址的单独索引是不够的。您应该为此查询创建名称的组合索引,然后是地址。

将索引视为某些列的系统维护副本,其顺序与原始顺序不同。在这种情况下,您希望首先按名称查找匹配项,然后在地址上进行抢七,然后直到您有足够的名称匹配项或用完名称匹配项。

通过在多列索引中将名称放在首位,索引将按名称优先排序。然后地址将作为我们的决胜局。


在原始索引下,如果地址索引是选择的索引,那么查询的速度将根据它找到匹配项的速度而有所不同。

计划(英文)是:遍历所有已经按地址排序的行,丢弃任何与名称不匹配的行,继续直到我们有足够的时间。

所以如果你没有得到 25 个匹配项,你就阅读整个表格!

使用我提出的多列索引,计划(英文)将是:继续遍历所有恰好已经按地址排序的名称匹配行。从第一个开始,直到你有足够的时间。如果你用完了,就停下来。

【讨论】:

  • 另外注意:如果你学会阅读执行计划,你将成为精英前1%的sql程序员。权力是你的。
【解决方案2】:

由于情况是没有 Order By 的查询比有 Order By 子句的查询快得多;我会做 2 个查询:

-一个没有order by,限制1,知道你是否至少有一条记录。

如果您至少有一个,使用 Order by 运行查询是安全的。

-如果没有记录,则不需要运行第二个查询。

是的,这不是一个解决方案,但它可以让您交付您的项目。只需确保您在交付后创建一张票来处理技术债务;)否则您的首席开发人员会让您着火。

然后,要解决真正的技术问题,了解您创建了哪些索引会很有用。没有这些就很难给你一个合适的解决方案!

【讨论】:

  • 虽然您可能从问题中推断出 1 条记录可以避免该问题,但我怀疑您需要确认有足够的 (26) 条匹配记录以避免读取整个表格。
猜你喜欢
  • 2023-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-08
  • 2014-09-27
  • 1970-01-01
  • 2021-03-23
  • 2011-12-07
相关资源
最近更新 更多