この記事は、Huawei クラウド コミュニティ「[MySQL テクノロジー コラム] GaussDB (for MySQL) Big IN Query Optimization」 (著者: GaussDB データベース) から共有されたものです。
背景の紹介
運用環境では、フィルタリングやクエリを実行するための顧客のビジネス SQL ステートメントに遭遇し、集計処理を実行することがよくあります。IN 述語リストには数千、場合によっては数万の定数値が含まれています。以下に示すように、このようなステートメントの実行時間は非常に長くなります。
MySQLの最適化
オープン ソース MySQL がカラム IN (const1、const2、...) を処理するとき、カラムにインデックスがある場合、オプティマイザはスキャンに範囲スキャンを選択し、そうでない場合はフル テーブル スキャンを使用します。 range_optimizer_max_mem_size システム変数は、範囲最適化プロセスの解析中に使用できる最大メモリを制御します。 IN 述語にリスト要素が多数ある場合、各 IN の内容は OR として扱われ、要素が多数ある場合は、より多くのメモリが使用されます。メモリ使用量が定義された最大メモリを超えると、範囲の最適化が失敗し、オプティマイザがフル テーブル スキャンに変換するなどの戦略を変更するため、クエリのパフォーマンスが低下します。
この最適化問題については、range_optimizer_max_mem_size を調整することで対応できます。 range_optimizer_max_mem_size で定義されるメモリはセッション レベルであり、このタイプのステートメントを実行する各セッションは同じメモリを占有します。大規模な同時実行シナリオでは、インスタンスのメモリ使用量が過剰になり、インスタンス OOM のリスクが生じます。
範囲クエリの場合、MySQL は eq_range_index_dive_limit システム変数を定義して、オプティマイザが同等の範囲クエリを処理するときにインデックス ダイビング (index div) を実行するかどうかを制御します。インデックス ダイビングでは、インデックスを使用してタプル数の記述を完了するため、より正確な情報が得られ、クエリ戦略の最適化が向上しますが、実行時間も長くなります。 INの組み合わせ数が一定数を超える場合、システムは静的なインデックスの統計情報値を使用してインデックスを選択します。この方法で得られる結果は正確である必要があります。これにより、MySQL がインデックスを適切に利用できなくなり、パフォーマンスが低下する可能性があります。
GaussDB の Big IN 最適化 (MySQL 用)
列 IN (const1、const2、...)
列 IN (SELECT ... FROM 一時テーブル)
連結は 2 つの順序で行うことができます。
-
マテリアライゼーションスキャン: マテリアライズされたテーブルから外観までの、マテリアライズされたテーブルの完全なテーブルスキャンを示します。
-
マテリアライズ・ルックアップ: 外観からマテリアライズされた表まで、メイン・ビルダーを使用してマテリアライズされた表内のデータを検索できることを示します。
物理的および化学的スキャン
-
サブクエリを実行し、インデックス auto_distinct_key を使用し、同時に結果の重複を排除します。
-
前のステップの結果を一時テーブル テンプレート 1 に保存します。
-
一時テーブルからデータの行を取得し、外観で補足条件を満たす行を見つけます。
-
一時テーブルの走査が完了するまで、手順 3 を繰り返します。
実体化された検索
-
最初にサブクエリを実行します。
-
前のステップで取得した結果を一時テーブルに保存します。
-
アピアランスからデータ行を取得し、マテリアライズされた一時テーブルに移動して補足条件を満たす行を検索し、マテリアライズされたテーブルの主キーを使用して、一度に 1 行をスキャンします。
-
全体の外観が表示されるまで 3 を繰り返します。
オプティマイザは、内部の外観のサイズに応じて、異なる連結順序を選択します。実際のシナリオでは、一般にクエリされるテーブル内のデータの量は非常に多く、数千万、場合によっては数億に達します。IN リスト内の要素の数はテーブルの数よりもはるかに少ないため、オプティマイザはマテリアライゼーションを選択します。 -scan メソッドでスキャンを行います。外観クエリ インデックス中に主キーが使用される場合、最適化後にスキャンされる行の総数は N になります。M が N よりもはるかに大きい場合、パフォーマンスの向上は明らかです。
説明書
rds_in_predicate_conversion_threshold パラメータは、SQL ステートメントの IN 述語リストの要素数がパラメータの値を超えると、IN 述語の下部にあるクエリ関数を変更するためのスイッチです。この関数は、この変数の値を通じて使用されます。以下は、最適化の使用を示す簡単な例です。
テーブル構造
テーブル t1(id int, a int, key idx1(a)) を作成します。
select * from t1 where a in (1,2,3,4,5);
set rds_in_predicate_conversion_threshold = 0 および set range_optimizer_max_mem_size=1 を設定して、大規模 IN 述語最適化機能と範囲スキャン最適化戦略をオフにします。 結果は次のとおりです。
> rds_in_predicate_conversion_threshold = 0 を設定します。 > range_optimizer_max_mem_size=1 を設定します。 > select * from t1 where a in (1,2,3,4,5); について説明します。 結果は次のとおりです。 +-----+---------------+----------+-----------+------+-- -------------+------+-----------+------+------+----- -----+---------------+ | ID |選択タイプ |テーブル |パーティション |タイプ |可能なキー |キー |キー長 |参照 |行 |フィルタリング済み |番外編 | +-----+---------------+----------+-----------+------+-- -------------+------+-----------+------+------+----- -----+---------------+ | 1 |シンプル | t3 | NULL |すべて |キー1 | NULL | NULL | NULL | 3 | 50.00 | where | を使用する+-----+---------------+----------+-----------+------+-- -------------+------+-----------+------+------+----- ----------+---------------+ セット内 1 行、警告 2 つ (0.00 秒) 警告を表示します。 +--------+-----+-------------------------------- -------------------------------------------------- ---------------------------------------+ |レベル |コード |メッセージ | +--------+-----+-------------------------------- -------------------------------------------------- ---------------------------------------+ |警告 | 3170 | 「range_optimizer_max_mem_size」のメモリ容量 1 バイトを超えました。このクエリでは範囲の最適化が行われませんでした。 | |注 | 1003 | /* select#1 */ select `test`.`t3`.`id` AS `id`,`test`.`t3`.`a` AS `a` from `test`.`t3` where (`テスト`.`t3`.`a` in (3,4,5)) | +--------+-----+-------------------------------- -------------------------------------------------- --------------------------------------- + 2 行セット (0.00 秒)
上記のステートメントの実行時に警告が報告されたことがわかりました。警告情報は、範囲の最適化プロセス中に使用されたメモリが range_optimizer_max_mem_size を超えたため、ステートメントでは範囲制限の最適化が使用されなかったことを示しています。その結果、スキャン タイプは ALL に変更され、フル テーブル スキャンになります。
大規模 IN 述語最適化オプションを有効にするには、set rds_in_predicate_conversion_threshold = 3 を設定します。これは、IN 述語リストの要素が 3 を超えると、大規模 IN キュー クエリ最適化戦略がアクティブ化されることを意味します。 EXPLAIN FORMAT=TREE ステートメントを実行して、最適化が有効かどうかを確認します。
> rds_in_predicate_conversion_threshold = 3 を設定します。 > Explain format=tree select * from t1 where a in (1,2,3,4,5); +------------------------------------------------ -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------+ |説明する | +------------------------------------------------ -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------+ | -> ネストされたループ内部結合 (コスト = 0.70 行 = 1) -> フィルター: (t1.a は null ではありません) (コスト = 0.35 行 = 1) -> t1 でのテーブル スキャン (コスト = 0.35 行 = 1) -> <auto_distinct_key> を使用した <in_predicate_2> の単一行インデックス検索 (a=t1.a) (コスト = 0.35 行 = 1) | +------------------------------------------------ -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------+ セット内の 1 行 (0.00 秒)
実行プランの <in_predicate_*> (* は数字) テーブルは Big INTool で構築される一時テーブルで、IN 述語リスト内のすべてのデータが格納されます。
使用制限
Big IN 最適化でサポートされるクエリ ステートメントには、次のステートメント リストが含まれます。
-
選ぶ
-
挿入...選択
-
置換...選択
-
サポートの視点
-
準備されたSTMT
制約と制限
Big IN ローター クエリは、パフォーマンスを実現するために mysql が提供するサブクエリ最適化ソリューションを使用します。そのため、使用には次の制限があります。そうしないとパフォーマンスが低下します。
-
インデックス作成を使用できないシナリオはサポートされていません
-
定数 IN LIST のみをサポートします (NOW()、?、およびテーブル クエリを含まないその他のステートメントを含む)
-
ストアド プロシージャ/関数/トリガーはサポートされていません
-
サポートされていないか、存在しません
典型的なシナリオのテストの比較
テーブルのテスト構造は次のとおりです。
CREATE TABLE `sbtest1` ( `id` int NOT NULL AUTO_INCREMENT, `k` int NOT NULL DEFAULT '0', `c` char(120) NOT NULL DEFAULT '', `pad` char(60) NOT NULL DEFAULT '' 、主キー (`id`)、キー `k_1` (`k`) ) ENGINE=InnoDB; テーブルのデータ量は1000wです。 > sbtest1 から count(*) を選択します。 +----------+ |カウント(*) | +----------+ | 10000000 | +----------+
クエリ ステートメントは次のとおりです。条件フィールドにはインデックスが付けられ、IN リストには 10,000 個の定数が含まれています。
select count(*) from sbtest1 where k in (2708275,5580784,7626186,8747250,228703,4589267,5938459,6982345,2665948,4830545,4929382,8723757,354179,1903 875,5111120,5471341,7098051,3113388,2584956,6550102 ,2842606,2744112,7077924,4580644,5515358,1787655,6391388,6044316,2658197,5628504,413887,6058866,3321587,1430333,445303,73 73496,9133196,6760595,4735642,4756387,9845147,9362192,7271805,4351748,6625915 ,3813276,4236692,8308973,4407131,9481423,3301846,432577,810938,3830320,6120078,6765157,6456566,6649509,1123840,2906490,99 65014,3725748, ... );
パフォーマンスの比較を次の図に示します。
リスト内最適化後は、元の方法と比較してパフォーマンスが 36 倍向上していることがわかります。
クリックしてフォローし、できるだけ早くHuawei Cloudの新しいテクノロジーについて学びましょう~
高校生が成人式として独自のオープンソースプログラミング言語を作成―ネットユーザーの鋭いコメント: アップル、M4チップ RustDeskをリリース 不正行為横行で国内サービス停止 雲峰氏がアリババを辞任。将来的には、Windows プラットフォームの タオバオ (taabao.com) で独立したゲームを制作する予定です。Web バージョンの最適化作業を再開し、 プログラマの目的地、 Visual Studio Code 1.89 が最も一般的に使用される Java LTS バージョンである Java 17 をリリースします。Windows 10 には、市場シェアは70%、Windows 11は減少し続けるOpen Source Daily | GoogleはオープンソースのRabbit R1を支持、Microsoftの不安と野心;