为什么MAX()比ORDER BY慢100倍... LIMIT 1?

Vik*_*ahl 7 mysql performance query-optimization

我有一个表foo(包括其他20个)列bar,baz并且quux索引为bazquux.该表有~500k行.

为什么以下查询的速度差异如此之大?查询A需要0.3秒,而查询B需要28秒.

查询A.

select baz from foo
    where bar = :bar
    and quux = (select quux from foo where bar = :bar order by quux desc limit 1)
Run Code Online (Sandbox Code Playgroud)

说明

id  select_type table   type    possible_keys   key     key_len ref     rows    Extra
1   PRIMARY     foo     ref     quuxIdx         quuxIdx 9       const   2       "Using where"
2   SUBQUERY    foo     index   NULL            quuxIdx 9       NULL    1       "Using where"
Run Code Online (Sandbox Code Playgroud)

查询B.

select baz from foo
    where bar = :bar
    and quux = (select MAX(quux) from foo where bar = :bar)
Run Code Online (Sandbox Code Playgroud)

说明

id  select_type table   type    possible_keys   key     key_len ref     rows    Extra
1   PRIMARY     foo     ref     quuxIdx         quuxIdx 9       const   2       "Using where"
2   SUBQUERY    foo     ALL     NULL            NULL    NULL    NULL    448060  "Using where"
Run Code Online (Sandbox Code Playgroud)

我使用MySQL 5.1.34.

Mar*_*ers 8

你应该添加一个索引(bar, quux).

没有这个索引,MySQL无法看到如何有效地执行查询,因此它必须从各种低效的查询计划中进行选择.

在第一个示例中,它扫描quux索引,对于找到的每一行,查找bar原始表中的相应值.这需要两倍的时间来检查每一行,但幸运的是,具有正确值的行bar接近其扫描的开始,因此它可以停止.这可能是因为bar您搜索的价值经常发生,因此幸运的机会非常高.因此,它可能只需要检查行了一把它找到一个匹配之前,所以尽管它需要两倍的时间来检查每一行,只有少数行检查事实给出了一个巨大的整体节约.由于您没有索引bar,MySQL事先并不知道该值:bar经常发生,因此无法知道此查询会很快.

在第二个示例中,它使用不同的计划,始终扫描整个表.直接从表中读取每一行,而不使用索引.这意味着每行读取速度很快,但由于您有很多行,因此整体速度很慢.如果没有匹配的行将:bar是更快的查询计划.但是,如果大约1%的行具有所需的值bar,则与上述计划相比,使用此查询计划将(非常)慢约100倍.由于你没有索引bar,MySQL事先并不知道.

你也可以只添加缺少的索引,然后这两个查询会快.