IN が遅いのはなぜですか?アプリケーションでサブクエリを使用すると、SQL ステートメントのクエリ パフォーマンスが非常に悪くなります。例えば: SELECT driver_id FROM driver where driver_id in (SELECT driver_id FROM driver where _create_date > '2016-07-25 00:00:00'); 独立したサブクエリは、条件を満たす driver_id を返します。この問題は解決されましたが、6 秒かかります。EXPLAIN を通じて SQL ステートメントの実行プランを表示できます。 上記の SQL ステートメントが相関サブクエリになることがわかります。EXPLAIN EXTENDED コマンドと SHOW WARNINGS コマンドを実行すると、次の結果が表示されます。 次のようにコードをコピーします。 `northwind`.`driver`.`driver_id` を `northwind`.`driver` から `driver_id` として選択します。ここで、<in_optimizer>(`northwind`.`driver`.`driver_id`,<exists>(`northwind`.`driver` から 1 を選択、ここで、((`northwind`.`driver`.`_create_date` > '2016-07-25 00:00:00') かつ (<cache>(`northwind`.`driver`.`driver_id`) = `northwind`.`driver`.`driver_id`)))) MySql オプティマイザが IN 句を EXISTS の相関サブクエリに直接変換していることがわかります。相関 IN サブクエリを次に示します。 SELECT driver_id FROM driver where driver_id in (SELECT driver_id FROM user where user.uid = driver.driver_id); SQL ステートメントの実行プランを表示します。 これは相関サブクエリです。EXPLAIN EXTENDED コマンドと SHOW WARNINGS コマンドを実行すると、次の結果が表示されます。 次のようにコードをコピーします。 `northwind`.`driver`.`driver_id` を `northwind`.`driver` から `driver_id` として選択します。ここで、<in_optimizer>(`northwind`.`driver`.`driver_id`,<exists>(`northwind`.`user` から 1 を選択します。ここで、((`northwind`.`user`.`uid` = `northwind`.`driver`.`driver_id`) かつ (<cache>(`northwind`.`driver`.`driver_id`) = `northwind`.`driver`.`driver_id`)))) MySQL 5.5 より前のオプティマイザは、独立サブクエリか相関サブクエリかに関係なく、IN を EXISTS ステートメントに変換していることがわかります。サブクエリと外部クエリがそれぞれ M 行と N 行を返す場合、サブクエリは O(N+M) ではなく O(N+N*M) でスキャンされます。これがINが遅い理由です。 INとEXISTSのどちらが速いでしょうか?Baidu は、IN と EXISTS が同等に効率的であると言うのは間違いであると主張する多くの記事をオンラインで発見しました。 クエリ対象の 2 つのテーブルのサイズが同じであれば、in と exists の使用にほとんど違いはありません。 逆に 2: 上記の説明をまとめると、主な理由はインデックスの使用にあると個人的には思います。いずれにしても、大きなテーブルのインデックスを使用すれば、効率を向上できます。 しかし、この記事を編集する際に何度もテストしましたが、上記にまとめた結果は得られませんでした。以下はテスト SQL ステートメントです。まず、外部テーブルは大きなテーブルで、内部テーブルは小さなテーブルです。 (例1) SELECT count(driver_id) FROM driver where driver_id in (SELECT uid FROM user); SELECT count(driver_id) FROM driver where exists (SELECT 1 FROM user where uid = driver.driver_id); 実行結果は次のとおりです。 すると外側は小さなテーブル、内側は大きなテーブルになります。 (例2) user から count(uid) を選択します。uid は (SELECT driver_id FROM driver) にあります。 存在するユーザーから count(uid) を選択します (driver.driver_id = user.uid の場合、SELECT 1 FROM driver)。 実行結果は次のとおりです。 いずれの場合でも、IN と EXISTS の実行効率はまったく同じであることがわかります。これに基づいて、例 1 の最初と 2 番目の SQL ステートメントの実行プランを次のように確認し続けます。 IN と EXISTS の実行プランは同じであることがわかり、このことから、2 つの実行効率は同じになるはずだという結論が導き出されます。 「MySql Technology Insider: SQL プログラミング」: この本では、多くの DBA が EXISTS の方が IN よりも効率的であると考えていることが説明されています。当時のオプティマイザーがあまり安定していなかったか、十分に優れていなかった可能性があります。ただし、ほとんどの場合、IN と EXISTS の実行プランは同じです。 効率を向上させるにはどうすればいいでしょうか?上記の例 2 の SQL ステートメントは、実行に約 8 秒かかります。クエリが遅いのは M*N の存在によるものですが、最適化することは可能です。遅い理由は、内部クエリを外部クエリと比較するたびに、テーブルを 1 回トラバースする必要があるためです。別の方法として、サブクエリのレイヤーをネストして、複数のトラバース操作を回避することもできます。ステートメントは次のとおりです。 SELECT count(driver_id) FROM driver where exists (SELECT uid FROM (SELECT uid from user) as b where b.uid = driver.driver_id); 実行効果は以下のようになります。 最適化により実行時間が 6 秒以上短縮されることがわかります。以下は SQL 実行プランです。 これは依然として相関サブクエリですが、内部のトラバーサル クエリ操作は削減されます。したがって、事前クエリを使用すると、トラバーサル操作を減らし、効率を向上させることができます。 実際、実際のプログラミングでは、多くの開発者は結合テーブルクエリを使用せず、まず 1 つのテーブルからデータを取得してから、別のテーブルで WHEREIN 操作を実行することを選択します。原理は、上記の SQL ステートメントで実装されているものと同じです。 MySQL 5.6 はサブクエリを最適化しますか?セミジョイン戦略オプティマイザーは、IN ステートメントがリージョン テーブルから各リージョン キーのインスタンスを 1 つ返すサブクエリを必要とすることを認識します。これにより、MySQL は SELECT ステートメントを半結合方式で実行するため、レコードに一致するグローバル テーブルの各リージョンのインスタンスは 1 つだけになります。 準結合と通常の結合には、2 つの非常に重要な違いがあります。
したがって、セミ結合の結果は、多くの場合、外部テーブルのレコードのサブセットになります。有効性の観点から見ると、セミ結合の最適化は、内部テーブルから重複項目を効果的に排除することです。MySQL は、重複項目を排除するために 4 つの異なるセミ結合実行戦略を適用します。 テーブルプルアウトの最適化
場合によっては、サブクエリを JOIN として書き換えることもできます。次に例を示します。 SELECT OrderID FROM Orders where EmployeeID IN (select EmployeeID from Employees where EmployeeID > 3); OrderID が一意であること、つまり主キーまたは一意のインデックスであることがわかっている場合、SQL ステートメントは Join として書き換えられます。 SELECT OrderID FROM Orders join Employees where Orders.EmployeeID = Employees.EmployeeID and Employees.EmployeeID > 3; テーブル プルアウトの目的は、一意のインデックスに基づいてサブクエリを JOIN ステートメントに書き換えることです。MySQL 5.5 では、上記の SQL ステートメントの実行プランは次のようになります。 EXPLAIN EXTENDED コマンドと SHOW WARNINGS コマンドを使用すると、次の結果が表示されます。 次のようにコードをコピーします。 `northwind`.`Orders`.`OrderID` を `northwind`.`Orders` から `OrderID` として選択します。ここで、<in_optimizer>(`northwind`.`Orders`.`EmployeeID`,<exists>(<primary_index_lookup>(<cache>(`northwind`.`Orders`.`EmployeeID`) in Employees on PRIMARY where ((`northwind`.`Employees`.`EmployeeID` > 3) and (<cache>(`northwind`.`Orders`.`EmployeeID`) = `northwind`.`Employees`.`EmployeeID`))))) まさに上記のように遅いのはなぜでしょうか? MySQL 5.6 では、オプティマイザは SQL ステートメントを書き換えて、次の実行プランを取得します。 MySQL 5.6 では、オプティマイザは独立したサブクエリを相関サブクエリに書き換えません。オプティマイザの実行モードは、EXPLAIN EXTENDED コマンドと SHOW WARNINGS コマンドを通じて取得されます。 次のようにコードをコピーします。 /* #1 を選択 */ select `northwind`.`orders`.`OrderID` AS `OrderID` from `northwind`.`employees` join `northwind`.`orders` where ((`northwind`.`orders`.`EmployeeID` = `northwind`.`employees`.`EmployeeID`) and (`northwind`.`employees`.`EmployeeID` > 3)) 明らかに、オプティマイザーは上記のサブクエリを JOIN ステートメントに書き換えます。これがテーブル プルアウトの最適化です。 重複排除の最適化セミ結合を結合と同様に実行し、一時テーブルを使用して重複レコードを削除します。 上記の内部テーブルにある列は一意であるため、オプティマイザーはサブクエリを JOIN ステートメントに書き換えて、SQL 実行の効率を向上させます。重複排除最適化とは、外部クエリ条件の列が一意であり、MySql オプティマイザーが最初にサブクエリによって見つかった結果を重複排除することを意味します。たとえば、次の SQL ステートメント: SELECT ContactName FROM Customers where CustomerID in (select CustomerID from Orders where OrderID > 10000 and Customers.Country = Orders.ShipCountry); CustomerID は主キーであるため、サブクエリから取得された結果は重複排除される必要があります。 MySql 5.6 の実行プラン: 「追加」オプションの「一時を開始」は重複排除用の一時テーブルを作成することを意味し、「一時を終了」は一時テーブルを削除することを意味します。 EXPLAIN EXTENDED コマンドと SHOW WARNINGS コマンドを使用すると、オプティマイザの実行モードは次のように取得されます。 次のようにコードをコピーします。 /* #1 を選択 */ select `northwind`.`customers`.`ContactName` AS `ContactName` from `northwind`.`customers` semi join (`northwind`.`orders`) where ((`northwind`.`customers`.`CustomerID` = `northwind`.`orders`.`CustomerID`) and (`northwind`.`customers`.`Country` = `northwind`.`orders`.`ShipCountry`) and (`northwind`.`orders`.`OrderID` > 10000)) Table Pullout 最適化とは異なり、join ではなく semi join が表示されます。これは、重複排除作業が増えるためです。上記の実行プランの場合、スキャンコストは約 830+830*1=1660 回になります。 ご覧のとおり、MySql 5.5 では、ステートメントは依然として相関サブクエリに変換され、スキャン コストは約 93+93*9=930 回になります。 最適化後、MySql 5.6 のスキャン コストは 5.5 よりも高いことがわかります。実際、これは 2 つのテーブルが小さい場合の結果です。テーブルが非常に大きい場合は、最適化の効果が非常に明白になります。 マテリアライゼーションの最適化
上記のサブクエリは相関サブクエリです。サブクエリが独立サブクエリの場合、オプティマイザーは、図に示すように、独立サブクエリの結果を単一のマテリアライズド一時テーブルに書き込むことを選択できます。 JOIN の順序に応じて、マテリアライゼーションの最適化は次のように分けられます。
次のサブクエリは、マテリアライゼーションを使用して最適化できます。 SELECT OrderID FROM Orders where OrderID in (select OrderID from `Order Details` where UnitPrice < 50 ); SQL ステートメントの実行プラン: JOIN を実行するとき (つまり、ID 1 のステップ)、最初にスキャンされるテーブルは Orders であり、次に subquery2 であることがわかります。これは、マテリアライゼーション ルックアップの最適化です。次の SQL の場合: select * FROM driver where driver_id in (select uid from user); SQL ステートメントの実行プラン: 最初にサブクエリ 2 がスキャンされ、次にドライバー テーブルがスキャンされます。これがマテリアライゼーション スキャンの最適化です。 FirstMacth 最適化内部テーブルをスキャンして行の組み合わせを探すときに、特定の値グループのインスタンスが複数ある場合は、すべてを返すのではなく、1 つを選択します。これにより、スキャンが「ショートカット」され、不要な行が生成されなくなります。これにより、テーブル スキャンの早期終了メカニズムが提供され、不要なレコードの生成が排除されます。 セミ結合の FirstMatch 戦略がサブクエリを実行する方法は、以前の MySQL バージョンの IN-TO-EXISTS と非常によく似ています。外部テーブル内の一致するレコードごとに、MySQL は内部テーブル内の一致をチェックします。一致が見つかった場合、外部テーブルからレコードを返します。一致するものが見つからない場合にのみ、エンジンはフォールバックして内部テーブル全体をスキャンします。 LooseScan の最適化各サブクエリの値グループから単一の値を選択できるようにするインデックスを使用して、サブクエリ テーブルをスキャンします。 セミジョイン変数Duplicate Weedout を除くこれらの各戦略は、optimizer_switch システム変数を使用して有効または無効にできます。semijoin フラグは、準結合が使用されるかどうかを制御します。これがオンに設定されている場合、firstmatch、looscan、および materialization フラグにより、許可された準結合戦略をより細かく制御できます。これらのフラグはデフォルトでオンになっています。Duplicate Weedout を除く各戦略は、optimizer_switch システム変数を使用して有効または無効にできます。semijoin フラグは、準結合の最適化が有効かどうかを制御します。これがオンに設定されている場合、他の戦略でも独立した変数制御が可能になります。 5.6 ではすべての変数がデフォルトで有効になっています。 mysql> @@optimizer_switch\G を選択します。 ************************** 1. 行 **************************** @@optimizer_switch: index_merge=on、index_merge_union=on、index_merge_sort_union=on、index_merge_intersection=on、engine_condition_pushdown=on、index_condition_pushdown=on、mrr=on、mrr_cost_based=on、block_nested_loop=on、batched_key_access=off、materialization=on、semijoin=on、loosescan=on、firstmatch=on、duplicateweedout=on、subquery_materialization_cost_based=on、use_index_extensions=on、condition_fanout_filter=on、derived_merge=on セット内の 1 行 (0.00 秒) EXPLAINビュー戦略
上記の紹介では、FirstMacth 最適化と LooseScan 最適化の具体的な効果を示す良い例はありません。コミュニケーションと学習の機会があります。 参照する「MySql テクノロジー インサイダー: SQL プログラミング」 http://dev.mysql.com/doc/refman/5.6/en/サブクエリ最適化.html http://tech.it168.com/a2013/0506/1479/000001479749.shtml これで、MySql サブクエリ IN の実行と最適化に関するこの記事は終了です。MySql サブクエリ IN に関するその他の関連コンテンツについては、123WORDPRESS.COM で以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。 以下もご興味があるかもしれません:
|
<<: Vue3 における computed の新しい使用例のまとめ
>>: ページ内の検索エンジンの呼び出しはBaiduを例に挙げています
みなさんこんにちは。今日は12連休ですが、何かお買い物はしましたか?今日は「Linux View S...
目次序文1. オブジェクト.freeze() 2. オブジェクト.seal() 3. オブジェクト....
1. 簡単なJavaプログラムを書く パブリッククラステストシェル{ パブリック静的voidメイン(...
Kubernetes は面白そうだったので(ギリシャ人なら名前に問題があると思うでしょうが)、ずっと...
目次1. はじめに2. RC および RR 分離レベル2.1. RRトランザクション分離レベルでのク...
この記事では、モバイル署名機能を実装するためのJavaScriptの具体的なコードを参考までに共有し...
1. 属性へのリンク(1)ルーティングパスを配置する(2)指定された形式でオブジェクトを配置する{パ...
結果:実装コード: html <!-- よろしければハートを付けてください! --> &...
日々の運用・保守作業において、MySQL データベースのバックアップは重要です。ウェブサイトにとって...
ケース1 vue-cliはvue3プロジェクトをビルドし、プロジェクトをLinuxサーバーにアップロ...
メモ: とにかく体験してみましょう。記録: NO.209この例の環境:仮想マシン: vmwareオペ...
CSS では、テキストは私たちが毎日扱う最も一般的なものの 1 つです。テキストの場合、テキストの装...
序文この記事は主に CentOS7 で PHP スケジュールタスクを実行することに関する関連コンテン...
animation-name アニメーション名。複数のアニメーションがバインドされていることを示す...
Awk は、ソートを含む他の一般的なユーティリティによって実行できるいくつかのタスクを実行できる強...