I. はじめにまず、MySQL のバージョンについて説明します。 mysql> バージョンを選択します(); +-----------+ | バージョン() | +-----------+ | 5.7.17 | +-----------+ セット内の1行(0.00秒) テーブル構造: mysql> desc テスト; +--------+----------------------+------+-----+---------+----------------+ | フィールド | タイプ | Null | キー | デフォルト | 追加 | +--------+----------------------+------+-----+---------+----------------+ | id | bigint(20) unsigned | NO | PRI | NULL | auto_increment | | val | int(10) 符号なし | NO | MUL | | | | ソース | int(10) 符号なし | NO | | | | +--------+----------------------+------+-----+---------+----------------+ セット内の 3 行 (.00 秒) id は自動インクリメントの主キーであり、val は一意でないインデックスです。 合計500万件の大量のデータを投入します。 mysql> テストから count(*) を選択します。 +----------+ | カウント(*) | +----------+ |5242882| +----------+ セット1列(4.25秒) 制限オフセット行のオフセットが大きい場合、効率の問題が発生することが分かっています。 mysql> select * from test where val=4 limit 300000,5; +---------+-----+--------+ | id | 値 | ソース | +---------+-----+--------+ | 3327622 | 4 | 4 | | 3327632 | 4 | 4 | | 3327642 | 4 | 4 | | 3327652 | 4 | 4 | | 3327662 | 4 | 4 | +---------+-----+--------+ 5 列セット (15.98 秒) 同じ目的を達成するために、通常は次のように書き直します。 mysql> select * from test a 内部結合 (select id from test where val=4 limit 300000,5) b on a.id=b.id; +---------+-----+--------+---------+ | id | val | ソース | id | +---------+-----+--------+---------+ | 3327622 | 4 | 4 | 3327622 | | 3327632 | 4 | 4 | 3327632 | | 3327642 | 4 | 4 | 3327642 | | 3327652 | 4 | 4 | 3327652 | | 3327662 | 4 | 4 | 3327662 | +---------+-----+--------+---------+ セット5行(0.38秒) 時間の違いは明らかです。 なぜ上記のような結果が表示されるのでしょうか? select * from test where val=4 limit 300000,5; のクエリ プロセスを見てみましょう。 インデックス リーフ ノード データが照会されます。 リーフ ノードの主キー値に基づいて、クラスター化インデックス上のすべての必須フィールド値をクエリします。 次の図のようになります。 上記のように、インデックス ノードを 300,005 回クエリし、クラスター化インデックス データを 300,005 回クエリし、最後に最初の 300,000 件の結果をフィルターして最後の 5 件を取り出す必要があります。 MySQL は、クラスター化インデックスのデータをクエリするために大量のランダム I/O を費やし、300,000 回のランダム I/O によってクエリされたデータは結果セットに表示されません。 誰かが必ずこう尋ねるでしょう: インデックスは最初に使用されるので、最初にインデックス リーフ ノードに沿って必要な最後の 5 つのノードまでクエリを実行し、次にクラスター化インデックス内の実際のデータをクエリするのはなぜですか。これには、次の図のプロセスと同様に、5 つのランダム I/O のみが必要です。 実は私もこの質問をしたいんです。 確認上記の推論を確認するために実際にいくつかの操作を実行してみましょう。 私はこれを間接的にしか確認できません: InnoDB にはバッファプールがあります。データ ページやインデックス ページなど、最近アクセスされたデータ ページが含まれます。したがって、バッファー プール内のデータ ページの数を比較するには、2 つの SQL ステートメントを実行する必要があります。予測結果では、 mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like '%test%' group by index_name; 空セット (0.04 秒) 現在、バッファー プール内にテスト テーブルに関するデータ ページが存在しないことがわかります。 mysql> select * from test where val=4 limit 300000,5; +---------+-----+--------+ | id | 値 | ソース | +---------+-----+--------+ | 3327622 | 4 | 4 | | 3327632 | 4 | 4 | | 3327642 | 4 | 4 | | 3327652 | 4 | 4 | | 3327662 | 4 | 4 | +---------+-----+--------+ セット5列(26.19秒) mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like '%test%' group by index_name; +------------+-----------+ | インデックス名 | カウント(*) | +------------+-----------+ | プライマリ | 4098 | | 値 | 208 | +------------+-----------+ セット2列(0.04秒) この時点で、バッファー プールにはテスト テーブルのデータ ページが 4098 ページ、インデックス ページが 208 ページあることがわかります。 mysqladmin シャットダウン /usr/local/bin/mysqld_safe & mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like '%test%' group by index_name; 空セット (0.03 秒) SQL を実行します: mysql> select * from test a 内部結合 (select id from test where val=4 limit 300000,5) b on a.id=b.id; +---------+-----+--------+---------+ | id | val | ソース | id | +---------+-----+--------+---------+ | 3327622 | 4 | 4 | 3327622 | | 3327632 | 4 | 4 | 3327632 | | 3327642 | 4 | 4 | 3327642 | | 3327652 | 4 | 4 | 3327652 | | 3327662 | 4 | 4 | 3327662 | +---------+-----+--------+---------+ セットに5行(0.09秒) mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like '%test%' group by index_name; +------------+-----------+ | インデックス名 | カウント(*) | +------------+-----------+ | プライマリ | 5 | | 値 | 390 | +------------+-----------+ セットに2行(0.03秒) 2 つの違いは明らかです。最初の SQL は 4098 のデータ ページをバッファー プールにロードしますが、2 番目の SQL は 5 つのデータ ページのみをバッファー プールにロードします。私たちの予測通りです。これにより、最初の SQL ステートメントが遅い理由も確認できます。大量の役に立たないデータ行 (300,000) を読み取ってから破棄します。 そして、これは問題を引き起こします。あまりホットではないデータ ページを大量にバッファー プールにロードすると、バッファー プールの汚染が発生し、バッファー プールのスペースが占有されます。 発生した問題再起動のたびにバッファー プールがクリアされるようにするには、innodb_buffer_pool_dump_at_shutdown と innodb_buffer_pool_load_at_startup をオフにする必要があります。これら 2 つのオプションは、データベースのシャットダウン時にバッファー プール データをダンプすることと、データベースの起動時にディスクにバックアップ バッファー プール データをロードすることを制御します。 参考文献: 1. https://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ 2. https://dev.mysql.com/doc/refman/5.7/en/innodb-information-schema-buffer-pool-tables.html SQL実行効率の詳細については、以下の関連記事をご覧ください。 以下もご興味があるかもしれません:
|
<<: JS の FileReader を介して .txt ファイルの内容を取得する方法
>>: HTML で dl(dt,dd)、ul(li)、ol(li) を使用する方法
目次1. フロントエンドのリーディングプロセス: 2. プラグインの使用と初期化2.1 vue-ad...
序文データベースの応用において、プログラマーは継続的な実践を通じて多くの経験を積んできました。これら...
序文ほとんどの人は、システム ディスク ストレージが少ないときにこの操作を実行するか、Linux シ...
「downloadlaod」を直接クリックしてもダウンロードできない場合は、ここからダウンロードす...
一般的に、リストには選択機能があり、単一選択、二重選択、複数選択が非常に一般的です。カスタム ループ...
Vueでは、ローカルコンポーネントを自分で定義(登録)することができます。コンポーネント名を定義する...
最初はブラウザのスクロールバーのスタイルを変更して効果を実現したいと思っていましたが、情報を調べてみ...
これまでは、拡張子が .msi のファイル、つまり、完全なインストールが使用されていました。しかし、...
1|0 背景プロジェクトの要件により、各月の履歴在庫データをアーカイブしてバックアップする必要があり...
目次序文ベジェ曲線の紹介二次ベジェ曲線3次ベジェ曲線ベジェ曲線計算機能フィッティングアルゴリズム付録...
導入: AD は Active Directory の略称で、中国語では Active Direct...
DREAMWEAVER を開き、新しい HTML を作成します。 。ボディの特性: bgcolor...
GNU Parallel は、1 台以上のコンピューター上で計算タスクを並列に実行するためのシェル ...
目次インデックスモデルB+ツリーインデックスの選択インデックスの最適化インデックスの選択性カバーイン...
1. nginx仮想ホストの設定仮想ホストを使用すると、実行する Web サイトごとに個別の Ngi...