使用LIMIT是否可以提高性能,并且引人注意?


11

我想了解以下内容。
假设我有一个复杂的查询,假设一个5个表的联接按求和和排序。
抛开对查询本身的任何优化(例如索引等)。使用
是否有任何明显的性能优势LIMIT?我认为必须在应用LIMIT 之前处理所有查询(和结果),因此使用LIMIT检索结果的子集,这是否会带来重大/显着的改进?


2
我建议你阅读这一点,该案件LIMIT提高效率:优化LIMIT查询
ypercubeᵀᴹ

Answers:


10

如果您想利用LIMIT性能来提高性能,则需要

  • 了解您正在检索的数据
  • 正确索引正确的列顺序
  • 负责重构查询
  • LIMIT之前使用JOIN

如果您可以对这些原则进行精心设计,它们将大有帮助。

我通过观看此YouTube视频(通过法语口音仔细聆听)了解了这些概念

我用这些概念回答了一个非常棘手的StackOverflow问题,该问题涉及从某些表中获取前40名文章:2011年5月12日:从Join Table中获取一行

我对该问题的回答(2011年5月16日)中,我编写了以下查询并进行了彻底的测试:

SELECT
  AAA.author_id,
  AAA.date_created,
  IFNULL(BBB.title,'<NO_TITLE>') title,
  IFNULL(CCC.filename,'<NO-IMAGE>') filename,
  IFNULL(CCC.date_added,'<NO-IMAGE-DATE>') image_date
FROM
(
  SELECT
    AA.id,
    AA.date_added,
    BB.author_id,
    BB.date_created
  FROM
  (
    SELECT
      A.id,IFNULL(MAX(B.date_added),'1900-01-01 00:00:00') date_added
      FROM (SELECT id FROM articles ORDER BY date_created DESC LIMIT 40) A
      LEFT JOIN article_images B ON A.id = B.article_id
      GROUP BY A.id
  ) AA
  INNER JOIN articles BB USING (id)
) AAA
LEFT JOIN article_contents BBB ON AAA.id=BBB.article_id
LEFT JOIN article_images CCC
ON (AAA.id=CCC.article_id AND AAA.date_added=CCC.date_added)
ORDER BY AAA.date_created DESC;

请注意查询中的行 LIMIT

      FROM (SELECT id FROM articles ORDER BY date_created DESC LIMIT 40) A

该子查询被深埋在三个级别中。这使我可以使用来获取最后40篇文章LIMIT。然后,我随后执行了必要的JOIN。

得到教训

  • 否则LIMIT里面的子查询不一定是因为指数的基数,数据内容,并从结果集大小的答案LIMIT。如果您拥有所有“连续的鸭子”(请牢记查询的四个原则),则可以获得令人惊讶的良好结果。
  • LIMIT仅通过收集键来使查询尽可能简单。

那么(A [LEFT] JOIN B) LIMIT 100等于(A LIMIT 100) [LEFT] JOIN (B LIMIT 100)?当[LEFT] JOIN手段外或内连接
吉姆·

更像是(A LIMIT 100) [LEFT] JOIN B。这个想法是用来LIMIT尽早确定结果集的大小。我也使用LEFT JOIN代替,INNER JOIN因为LEFT JOIN会保留左侧键的顺序。
RolandoMySQLDBA

@吉姆不,他们不是。有时,它们是这样的:(A LEFT JOIN B) GROUP BY A.pk LIMIT 100通常可以重写为(A LIMIT 100) LEFT JOIN B GROUP BY A.pk(此处没有INNER JOIN,对于内部联接,它们将不相等。)Rolando的例子就是这种情况。
ypercubeᵀᴹ

@ypercube:因此,使用内部联接没有什么事可以受益于LIMIT?
吉姆(Jim)

我指的是Rolando概述的重写策略。具有JOIN和LIMIT的查询也可能会受益。或不。这取决于。
ypercubeᵀᴹ

2

执行查询时,首先将其转换为由多个运算符组成的计划。运算符有两种基本类型:阻塞和非阻塞。非阻塞运算符从其子级或子级中为它请求的每一行检索一行(或几行)。另一方面,阻塞运算符必须读入并处理其所有子级的整个行集,然后才能产生任何输出。

排序是典型的阻塞运算符。因此,按顺序进行选择不会从限制中获得很多好处。但是,有些RDBMS可以利用排序算法,该算法需要更少的内存,并且在提供limit子句时更快。在这种情况下,仅存储当前的前n行并将其移出内存就足够了。这可以带来显着的性能提升。但是,我不是100%确信MySQL具有这种能力。

无论哪种方式,即使是极限排序,在产生第一条输出行之前,仍然需要处理整个输入行集。虽然该算法(如果实施)可以加快排序速度,但如果查询的其余部分是最昂贵的部分,则由于提供了限制,总执行时间将不会显着改善。


我对答案有点困惑。您提到的是排序,但分组也是吗?因此,例如,如果我删除了订单依据并坚持使用分组依据,您的答案仍然适用吗?还是需要其他分析?
吉姆(Jim)

根据查询和存在的索引,GROUP BY可能会导致计划不包含阻塞运算符。
塞巴斯蒂安·梅因

0

就我而言,即使我(仍然)不明白为什么,我也可以说

SELECT g0_.id AS id_0, COUNT(a1_.id_tarifs) AS sclr_1
FROM groupe_jardinerie g0_
INNER JOIN articles_tarifs a1_
  ON (a1_.groupe_jardinerie_id = g0_.id)
WHERE g0_.centrale_id = 511
  AND a1_.date_fin_tarif >= '2018-01-29 10:46:35'
GROUP BY g0_.id;

(result set)

8 rows in set (**18.14 sec**)

注意时间:18秒。相同的请求,但LIMIT很大:

SELECT g0_.id AS id_0, COUNT(a1_.id_tarifs) AS sclr_1 
FROM groupe_jardinerie g0_
INNER JOIN articles_tarifs a1_
  ON (a1_.groupe_jardinerie_id = g0_.id)
WHERE g0_.centrale_id = 511 
  AND a1_.date_fin_tarif >= '2018-01-29 10:46:35'
GROUP BY g0_.id
LIMIT 100000000000;

(exact same result set)

8 rows in set (**1.32 sec**)

快十倍以上!!!

对于两个请求,EXPLAIN给出相同的结果。

+----+-------------+-------+------------+--------+---------------------------------------------------+---------+---------+------------------------------+--------+----------+----------------------------------------------+
| id | select_type | table | partitions | type   | possible_keys                                     | key     | key_len | ref                          | rows   | filtered | Extra                                        |
+----+-------------+-------+------------+--------+---------------------------------------------------+---------+---------+------------------------------+--------+----------+----------------------------------------------+
|  1 | SIMPLE      | a1_   | NULL       | ALL    | IDX_438010BBC10784EF                              | NULL    | NULL    | NULL                         | 795135 |    33.33 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | g0_   | NULL       | eq_ref | PRIMARY,IDX_9CA5CF6758A1D71F,IDX_9CA5CF67670C757F | PRIMARY | 4       | phs.a1_.groupe_jardinerie_id |      1 |    50.00 | Using where                                  |
+----+-------------+-------+------------+--------+---------------------------------------------------+---------+---------+------------------------------+--------+----------+----------------------------------------------+

LIMIT应该仅会限制结果集(即,如果我执行LIMIT 4,我只会得到上述结果集的前4行)。


可怕的是,您使用的是哪个版本?您可以创建一个简化的测试用例吗?
埃文·卡罗尔

1
您的答案并未证明会带来任何新的好处LIMIT。您的第一个查询将在18秒内运行,并给出结果集。由于第一个查询,第二个查询中的所有数据已经​​缓存在InnoDB缓冲池中,因此,第二个查询当然必须更快,即使重新启动mysql,运行第一个查询,重新启动mysql并运行第二个查询,您将得到相同的结果。。获得更好的结果LIMIT只能来自以下方面:1)LIMIT之前JOIN,2)排序顺序为ASCDESC
RolandoMySQLDBA

感谢您的关注。创建简化的测试用例可能很困难。
皮埃尔-奥利维尔·瓦雷斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.