LIMITを使用するとパフォーマンスが向上しますか?


11

以下について理解したい。
合計と順序付けによるグループと5つのテーブルの結合を例に挙げると、複雑なクエリがあるとします。
余談せるなど、クエリ自体例えばインデックスに任意の最適化を
使用して、任意の大幅なパフォーマンス上の利点はありますかLIMIT?LIMITを適用する前にすべてのクエリ(および結果)を処理する必要があると思います。そのため、LIMITを使用して結果のサブセットを取得すると、大幅に改善されますか?


2
私は例のために、あなたがこれを読むことをお勧めLIMIT効率を向上させることができる:最適化LIMITクエリを
ypercubeᵀᴹ

回答:


10

LIMITパフォーマンスを向上させるために利用したい場合は、

  • 取得しているデータを理解する
  • 正しい列のシーケンスの適切なインデックス付け
  • クエリのリファクタリングを担当する
  • LIMIT以前に使用JOIN

これらの原則は、それらを調整することができれば、長い道のりを行くことができます。

私はこのYouTubeビデオを見てこれらの概念を学びました(フランスのアクセントを注意深く聞いてください)

:私はいくつかのテーブルから上位40記事得ることについて非常に厳しいStackOverflowの質問に答えるために、これらの概念を使用2011年5月12日を表に参加からの単一の行の取得を

ではその質問への私の答え(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

このサブクエリは、3レベルの深さに埋め込まれています。これにより、を使用して最新の40件の記事を取得できましたLIMIT。その後、必要なJOINを実行しました。

学んだ教訓

  • こうLIMITサブクエリ内では常にあるため、インデックスのカーディナリティ、データ内容、およびからの結果セットのサイズの答えではないかもしれませんLIMIT。すべての「アヒルが並んでいる」場合(クエリについて4つの原則を念頭に置いてください)、驚くほど良い結果が得られます。
  • LIMITキーのみを収集して行う場合は、クエリをできるだけ単純にします。

だから、(A [LEFT] JOIN B) LIMIT 100と等価ですか(A LIMIT 100) [LEFT] JOIN (B LIMIT 100)?ここ[LEFT] JOINで、外部結合または内部結合を意味します
Jim

それはもっと似てい(A LIMIT 100) [LEFT] JOIN Bます。アイデアはLIMIT、結果セットのサイズをできるだけ早く決定するために使用することです。私はまた、使用LEFT JOINの代わりをINNER JOINするのでLEFT JOIN、左側のキーの順序を保持します。
RolandoMySQLDBA 2013年

@ジムいいえ、そうではありません。時々、これらは次のように(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から利益を得るために何かすることはありませんか?
ジム

私はRolandoによって概説された書き換え戦略に言及していました。JOINとLIMITを使用したクエリにもメリットがあります。か否か。場合によります。
ypercubeᵀᴹ

2

クエリが実行されると、最初にいくつかの演算子で構成されるプランに変換されます。演算子には、ブロックと非ブロックの2つの基本タイプがあります。Non-Blocking Operatorは、要求された各行の子から、1つまたは複数の行を取得します。一方、ブロッキングオペレーターは、出力を生成する前に、すべての子の行セット全体を読み込んで処理する必要があります。

ソートは典型的なブロッキングオペレーターです。そのため、select by order byは制限からあまりメリットがありません。ただし、必要なメモリが少なく、limit句が指定されている場合はより高速なソートアルゴリズムを利用できるRDBMSがあります。この場合、現在の最初のn行を格納し、以前の行が発生したときにそれらをメモリから移動するだけで十分です。これにより、パフォーマンスが大幅に向上する可能性があります。ただし、MySQLにその機能があるかどうかは100%わかりません。

どちらの方法でも、最初の出力行を生成する前に、制限ソートでも入力行セット全体を処理する必要があります。このアルゴリズムを実装すると、ソートを高速化できますが、クエリの残りの部分が最もコストのかかる部分である場合は、提供された制限のため、合計実行時間は大幅に改善されません。


私はその答えに少し混乱しています。並べ替えについて言及しましたが、並べ替えでグループ化しませんか?それで、たとえば、私が注文を削除してグループに固執した場合、あなたの答えはまだ適用されますか?または別の分析が必要ですか?
ジム

存在するクエリとインデックスによっては、GROUP BYブロッキングオペレーターが含まれない計画につながる可能性があります。
Sebastian Meine 2013年

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秒。大きな制限付きの同じリクエスト:

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**)

10倍以上速く!!!

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秒で実行され、結果セットが返されます。最初のクエリにより、2番目のクエリのすべてのデータはすでにInnoDBバッファープールにキャッシュされているため、当然、2番目のクエリはより高速である必要があります。クエリでも、同じ結果が得られます。。より良い結果をLIMIT得るLIMITにはJOIN、1)前に、2)ソート順でのLIMIT ASCまたはDESC
RolandoMySQLDBA 2018年

関心をお寄せいただきありがとうございます。簡単なテストケースを作成するのは難しい場合があります。
Pierre-Olivier Vares
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.