使用 order 和 limit 子句的慢查询,但前提是没有记录

Slow query with order and limit clause but only if there are no records

我是运行以下查询:

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

因为我在 table 中有名称 = 'Bob' 的记录,所以在 table 的 10M 记录(<.5 秒)

上查询时间很快

但是,如果我搜索 name = 'Susan',则查询需要超过 45 秒。我在 table where name = 'Susan'.

中没有记录

我对姓名和地址都有一个索引。我清理了 table,分析了它,甚至尝试重写查询:

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

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

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

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

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

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

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

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

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

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


在原始索引下,如果选择了地址索引,则查询速度将根据找到匹配项的速度而变化。

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

因此,如果您没有获得 25 个匹配项,您将阅读整个 table!

使用我提议的多列索引,计划(英文)将是:继续遍历所有恰好已按地址排序的名称匹配行。从第一个开始,直到你吃饱为止。如果你 运行 出局,就停止。